diff --git a/uart_aes/rtl/uart_aes.vhd b/uart_aes/rtl/uart_aes.vhd new file mode 100644 index 0000000..6b88dd6 --- /dev/null +++ b/uart_aes/rtl/uart_aes.vhd @@ -0,0 +1,155 @@ +-- This design implements a register file which can +-- be accessed by an UART with 9600 baud +-- +-- See into uart_ctrl.vhd for documentation of the protocol +-- used to read / write the register file. + + +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library gatemate; +use gatemate.components.all; + +library cryptocores; + +use work.uart_aes_types.all; + + +entity uart_aes is +port ( + clk_i : in std_logic; -- 10 MHz clock + rst_n_i : in std_logic; -- SW3 button + uart_rx_i : in std_logic; -- PMODA IO3 + uart_tx_o : out std_logic; -- PMODA IO5 + led_n_o : out std_logic_vector(3 downto 0) +); +end entity uart_aes; + + +architecture rtl of uart_aes is + + signal s_pll_clk : std_logic; + signal s_pll_lock : std_logic; + + signal s_rst_n : std_logic; + signal s_cfg_end : std_logic; + + signal s_uart_rx_tdata : std_logic_vector(7 downto 0); + signal s_uart_rx_tvalid : std_logic; + signal s_uart_rx_tready : std_logic; + + signal s_uart_tx_tdata : std_logic_vector(7 downto 0); + signal s_uart_tx_tvalid : std_logic; + signal s_uart_tx_tready : std_logic; + + signal s_ctrl_aes_m2s : t_axis_ctrl_aes_m2s; + signal s_ctrl_aes_s2m : t_axis_s2m; + signal s_aes_ctrl_m2s : t_axis_aes_ctrl_m2s; + signal s_aes_ctrl_s2m : t_axis_s2m; + +begin + + pll : CC_PLL + generic map ( + REF_CLK => "10", + OUT_CLK => "10", + PERF_MD => "SPEED" + ) + port map ( + CLK_REF => clk_i, + CLK_FEEDBACK => '0', + USR_CLK_REF => '0', + USR_LOCKED_STDY_RST => '0', + USR_PLL_LOCKED_STDY => open, + USR_PLL_LOCKED => s_pll_lock, + CLK270 => open, + CLK180 => open, + CLK0 => s_pll_clk, + CLK90 => open, + CLK_REF_OUT => open + ); + + cfg_end_inst : CC_CFG_END + port map ( + CFG_END => s_cfg_end + ); + + uart_rx : entity work.uart_rx + generic map ( + CLK_DIV => 1040 + ) + port map ( + -- globals + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- axis user interface + tdata_o => s_uart_rx_tdata, + tvalid_o => s_uart_rx_tvalid, + tready_i => s_uart_rx_tready, + -- uart interface + rx_i => uart_rx_i + ); + + uart_ctrl : entity work.uart_ctrl + port map ( + -- globals + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- uart rx interface + tdata_i => s_uart_rx_tdata, + tvalid_i => s_uart_rx_tvalid, + tready_o => s_uart_rx_tready, + -- uart tx interface + tdata_o => s_uart_tx_tdata, + tvalid_o => s_uart_tx_tvalid, + tready_i => s_uart_tx_tready, + -- aes out + ctrl_aes_o => s_ctrl_aes_m2s, + ctrl_aes_i => s_ctrl_aes_s2m, + -- aes in + aes_ctrl_i => s_aes_ctrl_m2s, + aes_ctrl_o => s_aes_ctrl_s2m + ); + + aes_inst : entity cryptocores.ctraes + port map ( + reset_i => s_rst_n, + clk_i => s_pll_clk, + start_i => s_ctrl_aes_m2s.tuser.start, + nonce_i => s_ctrl_aes_m2s.tuser.nonce, + key_i => s_ctrl_aes_m2s.tuser.key, + data_i => s_ctrl_aes_m2s.tdata, + valid_i => s_ctrl_aes_m2s.tvalid, + accept_o => s_ctrl_aes_s2m.tready, + data_o => s_aes_ctrl_m2s.tdata, + valid_o => s_aes_ctrl_m2s.tvalid, + accept_i => s_aes_ctrl_s2m.tready + ); + + uart_tx : entity work.uart_tx + generic map ( + CLK_DIV => 1040 + ) + port map ( + -- globals + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- axis user interface + tdata_i => s_uart_tx_tdata, + tvalid_i => s_uart_tx_tvalid, + tready_o => s_uart_tx_tready, + -- uart interface + tx_o => uart_tx_o + ); + + s_rst_n <= rst_n_i and s_pll_lock and s_cfg_end; + + -- Lets some LEDs blink + led_n_o(0) <= rst_n_i; -- reset button + led_n_o(1) <= s_uart_rx_tready and s_uart_tx_tvalid; -- uart ctrl ready + led_n_o(2) <= not s_uart_rx_tready; -- uart received + led_n_o(3) <= not s_uart_tx_tvalid; -- uart send + +end architecture; diff --git a/uart_aes/rtl/uart_aes_types.vhd b/uart_aes/rtl/uart_aes_types.vhd new file mode 100644 index 0000000..e9056a4 --- /dev/null +++ b/uart_aes/rtl/uart_aes_types.vhd @@ -0,0 +1,40 @@ +library ieee; +use ieee.std_logic_1164.all; + + +package uart_aes_types is + + type t_axis_ctrl_aes_tuser is record + start : std_logic; + key : std_logic_vector(0 to 127); + nonce : std_logic_vector(0 to 95); + end record; + + type t_axis_ctrl_aes_m2s is record + tuser : t_axis_ctrl_aes_tuser; + tdata : std_logic_vector(0 to 127); + tvalid : std_logic; + end record; + + type t_axis_m2s is record + tdata : std_logic_vector(0 to 127); + tvalid : std_logic; + end record; + + alias t_axis_aes_ctrl_m2s is t_axis_m2s; + + type t_axis_s2m is record + tready : std_logic; + end record; + + -- No dout reg necessary, as we simply use AES tdata output + type t_reg_file is record + ctrl : std_logic_vector(7 downto 0); + key : std_logic_vector(0 to 127); + nonce : std_logic_vector(0 to 95); + din : std_logic_vector(0 to 127); + end record; + + constant c_reg_file_init : t_reg_file := (8x"0", 128x"0", 96x"0", 128x"0"); + +end package; diff --git a/uart_aes/rtl/uart_ctrl.vhd b/uart_aes/rtl/uart_ctrl.vhd new file mode 100644 index 0000000..ef6ffaa --- /dev/null +++ b/uart_aes/rtl/uart_ctrl.vhd @@ -0,0 +1,209 @@ +-- UART register + +-- Register file with 8 registers storing values of one byte each. +-- +-- The first received byte on the axis in port contains command & address: +-- +-- 7 reserved +-- 6:4 register address +-- 3:0 command +-- 0x0 read +-- 0x1 write +-- +-- In case of a write command, the payload has to follow +-- with the next byte. +-- +-- In case of a read command, the value of the addressed +-- register is returned on the axis out port. +-- +-- Register at address 0 is special. It contains the version +-- and is read-only. Writes to that register are ignored. + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.uart_aes_types.all; + + +entity uart_ctrl is + port ( + -- globals + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis in + tdata_i : in std_logic_vector(7 downto 0); + tvalid_i : in std_logic; + tready_o : out std_logic; + -- axis out + tdata_o : out std_logic_vector(7 downto 0); + tvalid_o : out std_logic; + tready_i : in std_logic; + -- aes out + ctrl_aes_o : out t_axis_ctrl_aes_m2s; + ctrl_aes_i : in t_axis_s2m; + -- aes in + aes_ctrl_i : in t_axis_aes_ctrl_m2s; + aes_ctrl_o : out t_axis_s2m + ); +end entity uart_ctrl; + + +architecture rtl of uart_ctrl is + + type t_state is (IDLE, GET_CMD, RECV_DATA, SEND_DATA); + signal s_state : t_state; + + signal s_reg_file : t_reg_file; + + signal s_reg_addr : natural range 0 to 7; + signal s_reg_data : std_logic_vector(7 downto 0); + + subtype t_cmd is std_ulogic_vector(3 downto 0); + constant c_read : t_cmd := x"0"; + constant c_write : t_cmd := x"1"; + + alias a_tdata_cmd is tdata_i(3 downto 0); + alias a_tdata_addr is tdata_i(6 downto 4); + + constant c_ctrl_addr : natural := 0; + constant c_key_addr : natural := 1; + constant c_nonce_addr : natural := 2; + constant c_din_addr : natural := 3; + constant c_out_addr : natural := 4; + + constant AES_RESET : natural := 0; -- Reset key & din registers + constant CTR_START : natural := 1; -- 1st round of counter mode + constant AES_START : natural := 2; -- start AES engine (cleared with AES_END) + constant AES_END : natural := 3; -- AES engine finished + + type reg_acc_cnt_t is array (natural range <>) of unsigned(3 downto 0); + signal read_acc_cnt : reg_acc_cnt_t(0 to 3); + signal write_acc_cnt : reg_acc_cnt_t(0 to 2); + +begin + + -- Register memory + process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_reg_data <= (others => '0'); + s_reg_file <= c_reg_file_init; + read_acc_cnt <= (others => x"0"); + write_acc_cnt <= (others => x"0"); + elsif (rising_edge(clk_i)) then + -- Register write + if (s_state = RECV_DATA and tvalid_i = '1') then + case s_reg_addr is + when 0 => s_reg_file.ctrl <= tdata_i; + -- Clear all regs when AES_RESET bit set + if (tdata_i(AES_RESET)) then + s_reg_file.ctrl <= (others => '0'); + s_reg_file.key <= (others => '0'); + s_reg_file.nonce <= (others => '0'); + s_reg_file.din <= (others => '0'); + write_acc_cnt <= (others => x"0"); + read_acc_cnt <= (others => x"0"); + end if; + when 1 => write_acc_cnt(0) <= write_acc_cnt(0) + 1; + s_reg_file.key(to_integer(write_acc_cnt(0))*8 to to_integer(write_acc_cnt(0))*8+7) <= tdata_i; + when 2 => if (write_acc_cnt(1) = 11) then + write_acc_cnt(1) <= x"0"; + else + write_acc_cnt(1) <= write_acc_cnt(1) + 1; + end if; + s_reg_file.nonce(to_integer(write_acc_cnt(1))*8 to to_integer(write_acc_cnt(1))*8+7) <= tdata_i; + when 3 => write_acc_cnt(2) <= write_acc_cnt(2) + 1; + s_reg_file.din(to_integer(write_acc_cnt(2))*8 to to_integer(write_acc_cnt(2))*8+7) <= tdata_i; + when others => null; + end case; + end if; + -- Register read + aes_ctrl_o.tready <= '0'; + if (s_state = GET_CMD and a_tdata_cmd = c_read) then + case s_reg_addr is + when 0 => s_reg_data <= s_reg_file.ctrl; + when 1 => read_acc_cnt(0) <= read_acc_cnt(0) + 1; + s_reg_data <= s_reg_file.key(to_integer(read_acc_cnt(0))*8 to to_integer(read_acc_cnt(0))*8+7); + when 2 => if (read_acc_cnt(1) = 11) then + read_acc_cnt(1) <= x"0"; + else + read_acc_cnt(1) <= read_acc_cnt(1) + 1; + end if; + s_reg_data <= s_reg_file.nonce(to_integer(read_acc_cnt(1))*8 to to_integer(read_acc_cnt(1))*8+7); + when 3 => read_acc_cnt(2) <= read_acc_cnt(2) + 1; + s_reg_data <= s_reg_file.din(to_integer(read_acc_cnt(2))*8 to to_integer(read_acc_cnt(2))*8+7); + when 4 => read_acc_cnt(3) <= read_acc_cnt(3) + 1; + s_reg_data <= aes_ctrl_i.tdata(to_integer(read_acc_cnt(3))*8 to to_integer(read_acc_cnt(3))*8+7); + if (read_acc_cnt(3) = 15) then + aes_ctrl_o.tready <= aes_ctrl_i.tvalid; + end if; + when others => s_reg_data <= (others => '0'); + end case; + end if; + + -- Set AES_END when AES out data is valid + -- Reset when AES out data was accepted (all 16 bytes of AES output data were read) + if (aes_ctrl_o.tready) then + s_reg_file.ctrl(AES_END) <= '0'; + elsif (aes_ctrl_i.tvalid) then + s_reg_file.ctrl(AES_END) <= '1'; + end if; + + -- Reset AES_START & CTR_START when AES engine accepts in data + if (ctrl_aes_i.tready and s_reg_file.ctrl(AES_START)) then + s_reg_file.ctrl(AES_START) <= '0'; + s_reg_file.ctrl(CTR_START) <= '0'; + end if; + end if; + end process; + + -- Control state machine + process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_state <= IDLE; + s_reg_addr <= 0; + elsif (rising_edge(clk_i)) then + case s_state is + when IDLE => + if (tvalid_i) then + s_state <= GET_CMD; + s_reg_addr <= to_integer(unsigned(a_tdata_addr)); + end if; + when GET_CMD => + if (a_tdata_cmd = c_read) then + s_state <= SEND_DATA; + elsif (a_tdata_cmd = c_write) then + s_state <= RECV_DATA; + else + s_state <= IDLE; + end if; + when RECV_DATA => + if (tvalid_i) then + s_state <= IDLE; + end if; + when SEND_DATA => + if (tready_i) then + s_state <= IDLE; + end if; + when others => + null; + end case; + end if; + end process; + + tready_o <= '1' when s_state = GET_CMD or s_state = RECV_DATA else '0'; + + tdata_o <= s_reg_data; + tvalid_o <= '1' when s_state = SEND_DATA else '0'; + + ctrl_aes_o.tuser.start <= s_reg_file.ctrl(CTR_START); + ctrl_aes_o.tuser.key <= s_reg_file.key; + ctrl_aes_o.tuser.nonce <= s_reg_file.nonce; + ctrl_aes_o.tdata <= s_reg_file.din; + ctrl_aes_o.tvalid <= s_reg_file.ctrl(AES_START); + + +end architecture; diff --git a/uart_aes/rtl/uart_rx.vhd b/uart_aes/rtl/uart_rx.vhd new file mode 100644 index 0000000..680e747 --- /dev/null +++ b/uart_aes/rtl/uart_rx.vhd @@ -0,0 +1,105 @@ +-- Copyright (c) 2022 by Torsten Meissner +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity uart_rx is + generic ( + CLK_DIV : natural := 10 + ); + port ( + -- globals + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis user interface + tdata_o : out std_logic_vector(7 downto 0); + tvalid_o : out std_logic; + tready_i : in std_logic; + -- uart interface + rx_i : in std_logic + ); +end entity uart_rx; + + +architecture rtl of uart_rx is + + type t_uart_state is (IDLE, RECEIVE, VALID); + signal s_uart_state : t_uart_state; + + signal s_clk_en : std_logic; + signal s_clk_cnt : natural range 0 to CLK_DIV-1; + signal s_bit_cnt : natural range 0 to tdata_o'length+1; + signal s_rx_d : std_logic_vector(3 downto 0); + +begin + + ClkDivP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_clk_cnt <= CLK_DIV-1; + elsif (rising_edge(clk_i)) then + if (s_uart_state = IDLE) then + s_clk_cnt <= CLK_DIV-2; + elsif (s_uart_state = RECEIVE) then + if (s_clk_cnt = 0) then + s_clk_cnt <= CLK_DIV-1; + else + s_clk_cnt <= s_clk_cnt - 1; + end if; + end if; + end if; + end process ClkDivP; + + s_clk_en <= '1' when s_uart_state = RECEIVE and s_clk_cnt = CLK_DIV/2-1 else '0'; + + RxP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_uart_state <= IDLE; + tdata_o <= (others => '0'); + s_rx_d <= x"1"; + s_bit_cnt <= 0; + elsif (rising_edge(clk_i)) then + s_rx_d <= s_rx_d(2 downto 0) & rx_i; + FsmL : case s_uart_state is + when IDLE => + s_bit_cnt <= tdata_o'length+1; + if (s_rx_d = "1000") then + s_uart_state <= RECEIVE; + end if; + when RECEIVE => + if (s_clk_en) then + if (s_bit_cnt = 0) then + s_uart_state <= VALID; + else + tdata_o <= s_rx_d(3) & tdata_o(tdata_o'length-1 downto 1); + s_bit_cnt <= s_bit_cnt - 1; + end if; + end if; + when VALID => + if (tready_i) then + s_uart_state <= IDLE; + end if; + end case; + end if; + end process RxP; + + tvalid_o <= '1' when s_uart_state = VALID else '0'; + +end architecture rtl; diff --git a/uart_aes/rtl/uart_tx.vhd b/uart_aes/rtl/uart_tx.vhd new file mode 100644 index 0000000..5ce450c --- /dev/null +++ b/uart_aes/rtl/uart_tx.vhd @@ -0,0 +1,102 @@ +-- Copyright (c) 2022 by Torsten Meissner +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity uart_tx is + generic ( + CLK_DIV : natural := 10 + ); + port ( + -- globals + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis user interface + tdata_i : in std_logic_vector(7 downto 0); + tvalid_i : in std_logic; + tready_o : out std_logic; + -- uart interface + tx_o : out std_logic + ); +end entity uart_tx; + + +architecture rtl of uart_tx is + + type t_uart_state is (IDLE, SEND); + signal s_uart_state : t_uart_state; + + signal s_data : std_logic_vector(tdata_i'length+1 downto 0); + signal s_clk_cnt : natural range 0 to CLK_DIV-1; + signal s_clk_en : std_logic; + signal s_bit_cnt : natural range 0 to s_data'length-1; + +begin + + ClkDivP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_clk_cnt <= CLK_DIV-1; + elsif (rising_edge(clk_i)) then + if (s_uart_state = IDLE) then + s_clk_cnt <= CLK_DIV-2; + elsif (s_uart_state = SEND) then + if (s_clk_cnt = 0) then + s_clk_cnt <= CLK_DIV-1; + else + s_clk_cnt <= s_clk_cnt - 1; + end if; + end if; + end if; + end process ClkDivP; + + s_clk_en <= '1' when s_uart_state = SEND and s_clk_cnt = 0 else '0'; + + TxP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_uart_state <= IDLE; + s_data <= (0 => '1', others => '0'); + s_bit_cnt <= 0; + elsif (rising_edge(clk_i)) then + FsmL : case s_uart_state is + when IDLE => + s_bit_cnt <= s_data'length-1; + if (tvalid_i) then + s_data <= '1' & tdata_i & '0'; + s_uart_state <= SEND; + end if; + when SEND => + if (s_clk_en) then + s_data <= '1' & s_data(s_data'length-1 downto 1); + if (s_bit_cnt = 0) then + s_uart_state <= IDLE; + else + s_bit_cnt <= s_bit_cnt - 1; + end if; + end if; + end case; + end if; + end process TxP; + + tready_o <= '1' when s_uart_state = IDLE else '0'; + + tx_o <= s_data(0); + +end architecture rtl; diff --git a/uart_aes/sim/Makefile b/uart_aes/sim/Makefile new file mode 100644 index 0000000..7cf9646 --- /dev/null +++ b/uart_aes/sim/Makefile @@ -0,0 +1,57 @@ +DESIGN_NAME := uart_aes + +AES_DIR := ../../cryptocores/aes/rtl/vhdl +CRYPTO_SRC := \ + $(AES_DIR)/aes_pkg.vhd \ + $(AES_DIR)/aes_enc.vhd \ + $(AES_DIR)/aes_dec.vhd \ + $(AES_DIR)/aes.vhd \ + $(AES_DIR)/../../../ctraes/rtl/vhdl/ctraes.vhd + +WORK_FILES := \ + ../rtl/uart_aes_types.vhd \ + ../rtl/uart_tx.vhd \ + ../rtl/uart_rx.vhd \ + ../rtl/uart_ctrl.vhd \ + ../rtl/${DESIGN_NAME}.vhd \ + uart_aes_sim.vhd \ + uart_aes_ref.vhd \ + tb_${DESIGN_NAME}.vhd + +GM_FILES := ../../lib/rtl_components.vhd ../../lib/sim_components.vhd + +SIM_SRC := tb_${DESIGN_NAME}.vhd +SIM_FLAGS := --std=08 -fpsl --workdir=work -Pwork + + +.PHONY: all compile sim clean + +all: sim +compile: tb_${DESIGN_NAME} + +work/work-obj08.cf: ${WORK_FILES} work/gatemate-obj08.cf work/cryptocores-obj08.cf + mkdir -p work + ghdl -a ${SIM_FLAGS} --work=work ${WORK_FILES} + +work/cryptocores-obj08.cf: ${CRYPTO_SRC} + mkdir -p work + ghdl -a $(SIM_FLAGS) --work=cryptocores ${CRYPTO_SRC} + +work/gatemate-obj08.cf: ${GM_FILES} + mkdir -p work + ghdl -a ${SIM_FLAGS} --work=gatemate ${GM_FILES} + +tb_${DESIGN_NAME}: work/work-obj08.cf uart_aes_ref.c + @echo "Elaborate testbench & design ..." + ghdl -e ${SIM_FLAGS} -Wl,uart_aes_ref.c -Wl,-lcrypto -Wl,-lssl $@ + +sim: tb_${DESIGN_NAME} + @echo "Run testbench ..." + ghdl -r ${SIM_FLAGS} tb_${DESIGN_NAME} --assert-level=error --ieee-asserts=disable --wave=tb_${DESIGN_NAME}.ghw + +work: + mkdir $@ + +clean: + @echo "Cleaning simulation files ..." + rm -rf tb_${DESIGN_NAME} tb_${DESIGN_NAME}.ghw *.o work/ diff --git a/uart_aes/sim/tb_uart_aes.tcl b/uart_aes/sim/tb_uart_aes.tcl new file mode 100644 index 0000000..1e0d84d --- /dev/null +++ b/uart_aes/sim/tb_uart_aes.tcl @@ -0,0 +1,13 @@ +set signals [list] +lappend signals "top.tb_uart_aes.dut.aes_inst.reset_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.clk_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.valid_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.accept_o" +lappend signals "top.tb_uart_aes.dut.aes_inst.start_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.key_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.nonce_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.data_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.valid_o" +lappend signals "top.tb_uart_aes.dut.aes_inst.accept_i" +lappend signals "top.tb_uart_aes.dut.aes_inst.data_o" +set num_added [ gtkwave::addSignalsFromList $signals ] diff --git a/uart_aes/sim/tb_uart_aes.vhd b/uart_aes/sim/tb_uart_aes.vhd new file mode 100644 index 0000000..ed3a409 --- /dev/null +++ b/uart_aes/sim/tb_uart_aes.vhd @@ -0,0 +1,73 @@ +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use std.env.all; + +use work.uart_aes_ref.all; + +entity tb_uart_aes is +end entity tb_uart_aes; + + +architecture sim of tb_uart_aes is + + signal s_clk : std_logic := '1'; + signal s_rst_n : std_logic := '0'; + + signal s_uart_rx : std_logic := '1'; + signal s_uart_tx : std_logic; + + constant c_baudrate : natural := 9600; + constant c_period_ns : time := 1_000_000_000 / c_baudrate * ns; + + package uart_aes_sim_inst is new work.uart_aes_sim + generic map (period_ns => c_period_ns); + + use uart_aes_sim_inst.all; + +begin + + dut : entity work.uart_aes + port map ( + clk_i => s_clk, + rst_n_i => s_rst_n, + uart_rx_i => s_uart_rx, + uart_tx_o => s_uart_tx + ); + + s_rst_n <= '1' after 120 ns; + s_clk <= not s_clk after 50 ns; + + TestP : process is + variable v_data : std_logic_vector(7 downto 0); + variable v_uart_data : std_logic_vector(0 to 127); + variable v_key : std_logic_vector(0 to 127); + variable v_nonce : std_logic_vector(0 to 95); + variable v_in_data : std_logic_vector(0 to 127); + variable v_ref_data : std_logic_vector(0 to 127); + begin + wait until s_rst_n; + wait until rising_edge(s_clk); + wait for 200 us; + v_key := x"0123456789ABCDEF0123456789ABCDEF"; + v_nonce := x"0123456789ABCDEF01234567"; + aes_setup(v_key, v_nonce, s_uart_rx); + for i in 0 to 7 loop + report "Test round " & to_string(i); + v_in_data := x"0123456789ABCDEF0123456789ABCDEF"; + aes_write(v_in_data, s_uart_rx); + aes_crypt(s_uart_rx, s_uart_tx); + aes_read(v_uart_data, s_uart_rx, s_uart_tx); + -- Calc reference data + cryptData(swap(v_in_data), swap(v_key), swap(v_nonce & 32x"0"), i=0, i=7, v_ref_data, v_in_data'length/8); + assert v_uart_data = swap(v_ref_data) + report "Encryption error: Expected 0x" & to_hstring(swap(v_ref_data)) & ", got 0x" & to_hstring(v_uart_data) + severity failure; + end loop; + wait for 100 us; + report "Simulation finished without errors"; + stop(0); + end process; + +end architecture; diff --git a/uart_aes/sim/uart_aes_ref.c b/uart_aes/sim/uart_aes_ref.c new file mode 100644 index 0000000..8702e6b --- /dev/null +++ b/uart_aes/sim/uart_aes_ref.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include + +static const char HDL_LOGIC_CHAR[] = { 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'}; + +enum HDL_LOGIC_STATES { +HDL_U = 0, +HDL_X = 1, +HDL_0 = 2, +HDL_1 = 3, +HDL_Z = 4, +HDL_W = 5, +HDL_L = 6, +HDL_H = 7, +HDL_D = 8, +}; + +EVP_CIPHER_CTX *ctx; + +void slv_to_uchar(char* datain, unsigned char* dataout, int bytelen) { + + for (int i = 0; i < bytelen; i++) { + for (int y = 0; y < 8; y++) { + if (*datain == HDL_1) { + *dataout |= 1 << y; + } else if (*datain == HDL_0) { + *dataout &= ~(1 << y); + } + datain++; + } + dataout++; + } + + return; + +} + + +void slv_to_string(char* datain, char* dataout, int bytelen) { + + for (int i = 0; i < bytelen; i++) { + *dataout = HDL_LOGIC_CHAR[*datain]; + datain++; + dataout++; + } + + return; + +} + + +void uchar_to_slv(unsigned char* datain, char* dataout, int bytelen) { + + for (int i = 0; i < bytelen; i++) { + for (int y = 0; y < 8; y++) { + if ((*datain >> y) & 1 == 1) { + *dataout = HDL_1 ; + } else { + *dataout = HDL_0; + } + dataout++; + } + datain++; + } + + return; + +} + + +void handleErrors(void) { + + ERR_print_errors_fp(stderr); + abort(); + +} + + +void init(unsigned char *key, unsigned char *iv) { + + // Create and initialise the context + if(!(ctx = EVP_CIPHER_CTX_new())) + handleErrors(); + + // Initialise the encryption operation, no IV needed in CTR mode + if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv)) + handleErrors(); + + // We don't want padding + if(1 != EVP_CIPHER_CTX_set_padding(ctx, 0)) + handleErrors(); + +} + + +int encrypt(unsigned char *plaintext, + int plaintext_len, + unsigned char *ciphertext) { + + int len = 0; + + // Provide the message to be encrypted, and obtain the encrypted output + if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) + handleErrors(); + + return len; + +} + + +int finalize(void) { + + int len = 0; + unsigned char data[16]; + + // Finalise the encryption. No further bytes are written as padding is switched off + if (1 != EVP_EncryptFinal_ex(ctx, data, &len)) + handleErrors(); + + // Clean up + EVP_CIPHER_CTX_free(ctx); + + return len; + +} + + +void cryptData(char* datain, char* key, char* iv, char start, char final, char* dataout, int bytelen) { + + int crypt_len; + unsigned char c_din[bytelen]; + unsigned char c_key[bytelen]; + unsigned char c_iv[bytelen]; + unsigned char c_dout[bytelen]; + + slv_to_uchar(datain, c_din, bytelen); + slv_to_uchar(key, c_key, bytelen); + slv_to_uchar(iv, c_iv, bytelen); + + if (start) { + init(c_key, c_iv); + } + + crypt_len = encrypt(c_din, bytelen, c_dout); + + if (crypt_len != bytelen) { + printf("Warning: encrypt() returned with unexpected length %d\n", crypt_len); + } + + if (final) { + crypt_len = finalize(); + if (crypt_len != 0) { + printf("Warning: finalize() returned with unexpected length %d\n", crypt_len); + } + } + + uchar_to_slv(c_dout, dataout, bytelen); + + return; + +} diff --git a/uart_aes/sim/uart_aes_ref.vhd b/uart_aes/sim/uart_aes_ref.vhd new file mode 100644 index 0000000..3330db0 --- /dev/null +++ b/uart_aes/sim/uart_aes_ref.vhd @@ -0,0 +1,46 @@ +library ieee ; +use ieee.std_logic_1164.all; + + +package uart_aes_ref is + + procedure cryptData(datain : in std_logic_vector(0 to 127); + key : in std_logic_vector(0 to 127); + iv : in std_logic_vector(0 to 127); + start : in boolean; + final : in boolean; + dataout : out std_logic_vector(0 to 127); + bytelen : in integer); + + attribute foreign of cryptData: procedure is "VHPIDIRECT cryptData"; + + function swap (datain : std_logic_vector(0 to 127)) return std_logic_vector; + +end package; + + +package body uart_aes_ref is + + procedure cryptData(datain : in std_logic_vector(0 to 127); + key : in std_logic_vector(0 to 127); + iv : in std_logic_vector(0 to 127); + start : in boolean; + final : in boolean; + dataout : out std_logic_vector(0 to 127); + bytelen : in integer) is + begin + report "VHPIDIRECT cryptData" severity failure; + end procedure; + + function swap (datain : std_logic_vector(0 to 127)) return std_logic_vector is + variable v_data : std_logic_vector(0 to 127); + begin + for i in 0 to 15 loop + for y in 0 to 7 loop + v_data((i*8)+y) := datain((i*8)+7-y); + end loop; + end loop; + return v_data; + end function; + +end package body; diff --git a/uart_aes/sim/uart_aes_sim.vhd b/uart_aes/sim/uart_aes_sim.vhd new file mode 100644 index 0000000..e22bfb9 --- /dev/null +++ b/uart_aes/sim/uart_aes_sim.vhd @@ -0,0 +1,125 @@ +library ieee ; +use ieee.std_logic_1164.all; + + +package uart_aes_sim is + + generic ( + period_ns : time + ); + + procedure uart_send ( data : in std_logic_vector(7 downto 0); + signal tx : out std_logic); + + procedure uart_recv ( data : out std_logic_vector(7 downto 0); + signal rx : in std_logic); + + procedure aes_setup ( key : in std_logic_vector(0 to 127); + nonce : in std_logic_vector(0 to 95); + signal tx : out std_logic); + + procedure aes_write ( data : in std_logic_vector(0 to 127); + signal tx : out std_logic); + + procedure aes_read ( data : out std_logic_vector(0 to 127); + signal tx : out std_logic; + signal rx : in std_logic); + + procedure aes_crypt (signal tx : out std_logic; + signal rx : in std_logic); + +end package; + +package body uart_aes_sim is + + procedure uart_send ( data : in std_logic_vector(7 downto 0); + signal tx : out std_logic) is + begin + report "UART send: 0x" & to_hstring(data); + wait for period_ns; + tx <= '0'; + wait for period_ns; + for i in 0 to 7 loop + tx <= data(i); + wait for period_ns; + end loop; + tx <= '1'; + wait for 0 ns; + end procedure; + + procedure uart_recv ( data : out std_logic_vector(7 downto 0); + signal rx : in std_logic) is + begin + wait until not rx; + wait for period_ns; -- Skip start bit + wait for period_ns/2; + for i in 0 to 7 loop + data(i) := rx; + wait for period_ns; + end loop; + report "UART recv: 0x" & to_hstring(data); + end procedure; + + procedure aes_setup ( key : in std_logic_vector(0 to 127); + nonce : in std_logic_vector(0 to 95); + signal tx : out std_logic) is + begin + -- Reset control register + uart_send(x"01", tx); + uart_send(x"01", tx); + -- Write key register + for i in 0 to 15 loop + uart_send(x"11", tx); + uart_send(key(i*8 to i*8+7), tx); + end loop; + -- Write nonce register + for i in 0 to 11 loop + uart_send(x"21", tx); + uart_send(nonce(i*8 to i*8+7), tx); + end loop; + -- Set control registers CTR_START bit + uart_send(x"01", tx); + uart_send(x"02", tx); + end procedure; + + procedure aes_write ( data : in std_logic_vector(0 to 127); + signal tx : out std_logic) is + begin + -- Write din register + for i in 0 to 15 loop + uart_send(x"31", tx); + uart_send(data(i*8 to i*8+7), tx); + end loop; + end procedure; + + procedure aes_read ( data : out std_logic_vector(0 to 127); + signal tx : out std_logic; + signal rx : in std_logic) is + variable v_data : std_logic_vector(7 downto 0); + begin + -- Check for valid AES output data + loop + uart_send(x"00", tx); + uart_recv(v_data, rx); + exit when v_data(3); + end loop; + -- Read dout register + for i in 0 to 15 loop + uart_send(x"40", tx); + uart_recv(data(i*8 to i*8+7), rx); + end loop; + end procedure; + + procedure aes_crypt (signal tx : out std_logic; + signal rx : in std_logic) is + variable v_data : std_logic_vector(7 downto 0); + begin + uart_send(x"00", tx); + uart_recv(v_data, rx); + v_data(2) := '1'; + -- Set control registers CTR_START bit + uart_send(x"01", tx); + uart_send(v_data, tx); + end procedure; + +end package body; diff --git a/uart_aes/syn/Makefile b/uart_aes/syn/Makefile new file mode 100644 index 0000000..ad1317c --- /dev/null +++ b/uart_aes/syn/Makefile @@ -0,0 +1,72 @@ +DESIGN_NAME := uart_aes + +AES_DIR := ../../cryptocores/aes/rtl/vhdl +CRYPTO_SRC := \ + $(AES_DIR)/aes_pkg.vhd \ + $(AES_DIR)/aes_enc.vhd \ + $(AES_DIR)/aes_dec.vhd \ + $(AES_DIR)/aes.vhd \ + $(AES_DIR)/../../../ctraes/rtl/vhdl/ctraes.vhd + +WORK_FILES := \ + ../rtl/uart_aes_types.vhd \ + ../rtl/uart_tx.vhd \ + ../rtl/uart_rx.vhd \ + ../rtl/uart_ctrl.vhd \ + ../rtl/${DESIGN_NAME}.vhd + +GM_FILES := ../../lib/rtl_components.vhd + +GHDL_FLAGS := --std=08 --workdir=build -Pbuild +ICARUSFLAGS := -Wall -Winfloop -g2012 -gspecify -Ttyp + +YOSYSPIPE := -nomx8 +PNRFLAGS := -om 3 -cCP on +PNRTOOL := $(shell which p_r) + + +.PHONY: all syn imp prog syn_sim imp_sim + +all: imp +syn: ${DESIGN_NAME}.v +imp: ${DESIGN_NAME}.bit + +build/work-obj08.cf: ${WORK_FILES} build/gatemate-obj08.cf build/cryptocores-obj08.cf + ghdl -a ${GHDL_FLAGS} --work=work ${WORK_FILES} + +build/cryptocores-obj08.cf: ${CRYPTO_SRC} + ghdl -a $(GHDL_FLAGS) --work=cryptocores ${CRYPTO_SRC} + +build/gatemate-obj08.cf: ${GM_FILES} + mkdir -p build + ghdl -a ${GHDL_FLAGS} --work=gatemate ${GM_FILES} + +# Synthesis target for implementation +${DESIGN_NAME}.v: build/work-obj08.cf + yosys -m ghdl -p 'ghdl ${GHDL_FLAGS} --warn-no-binding --no-formal ${DESIGN_NAME}; synth_gatemate -top $(DESIGN_NAME) ${YOSYSPIPE} -vlog $@' \ + 2>&1 | tee build/yosys-report.txt + +# Implementation target for FPGA +${DESIGN_NAME}.bit: ${DESIGN_NAME}.v ${DESIGN_NAME}.ccf + cd build && \ + ${PNRTOOL} -i ../${DESIGN_NAME}.v -o $@ --ccf ../${DESIGN_NAME}.ccf $(PNRFLAGS) \ + 2>&1 | tee p_r-report.txt && \ + mv ${DESIGN_NAME}*.bit ../$@ + +# Post-synthesis simulation target +syn_sim: ${DESIGN_NAME}.v + iverilog ${ICARUSFLAGS} -o tb_${DESIGN_NAME}_syn.vvp ${DESIGN_NAME}.v tb_${DESIGN_NAME}.v /usr/local/share/yosys/gatemate/cells_sim.v + vvp -N tb_${DESIGN_NAME}_syn.vvp -fst + +# Post-implementation simulation target +imp_sim: ${DESIGN_NAME}.bit + iverilog ${ICARUSFLAGS} -o tb_${DESIGN_NAME}_imp.vvp build/${DESIGN_NAME}_00.v tb_${DESIGN_NAME}.v /opt/cc-toolchain-linux/bin/p_r/cpelib.v + vvp -N tb_${DESIGN_NAME}_imp.vvp -fst + +# FPGA FW load per JTAG +prog: ${DESIGN_NAME}.bit + openFPGALoader -b gatemate_evb_jtag $< + +clean : + echo "# Cleaning files" + rm -rf build ${DESIGN_NAME}.v ${DESIGN_NAME}_sim.v ${DESIGN_NAME}.vhd ${DESIGN_NAME}.bit *.vvp *.fst diff --git a/uart_aes/syn/read_data.txt b/uart_aes/syn/read_data.txt new file mode 100644 index 0000000..a67fb1c --- /dev/null +++ b/uart_aes/syn/read_data.txt @@ -0,0 +1,16 @@ +A0 +55 +A0 +62 +BC +DD +C3 +4C +33 +FE +9F +A6 +0C +FB +6F +2D \ No newline at end of file diff --git a/uart_aes/syn/tb_uart_aes.v b/uart_aes/syn/tb_uart_aes.v new file mode 100644 index 0000000..eb6e221 --- /dev/null +++ b/uart_aes/syn/tb_uart_aes.v @@ -0,0 +1,149 @@ +`timescale 1 ns/100 ps // time-unit = 1 ns, precision = 10 ps + +// simplified CC_PLL model +module CC_PLL #( + parameter REF_CLK = "", // e.g. "10.0" + parameter OUT_CLK = "", // e.g. "50.0" + parameter PERF_MD = "", // LOWPOWER, ECONOMY, SPEED + parameter LOW_JITTER = 1, + parameter CI_FILTER_CONST = 2, + parameter CP_FILTER_CONST = 4 +)( + input CLK_REF, CLK_FEEDBACK, USR_CLK_REF, + input USR_LOCKED_STDY_RST, USR_SET_SEL, + output USR_PLL_LOCKED_STDY, USR_PLL_LOCKED, + output CLK270, CLK180, CLK90, CLK0, CLK_REF_OUT +); + + reg r_pll_clk; + reg r_user_pll_locked; + + // OUT_FREQ = 10 MHz + integer clk_half_period = 50; + + initial begin + r_pll_clk = 1'b0; + r_user_pll_locked = 1'b1; + end + + always #clk_half_period r_pll_clk = ~r_pll_clk; + + assign CLK0 = r_pll_clk; + assign USR_PLL_LOCKED = r_user_pll_locked; + +endmodule + + +// simplified CC_CFG_END model +module CC_CFG_END ( + output CFG_END +); + + assign CFG_END = 1'b1; + +endmodule + +module tb_uart_aes; + + // DUT in/out + reg clk = 1'b0; + reg rst_n = 1'b1; + reg uart_rx; + wire uart_tx; + wire [3:0] led_n; + + // Testbench variables + reg [7:0] tx_data = 8'h0; + reg [7:0] rx_data = 8'h0; + + // Testbench 1/2 clock period + localparam clk_half_period = 50; + + // UART period calculation (9600 baud) + localparam uart_bit_period = 1000000000 / 9600; + localparam uart_bit_half_period = uart_bit_period/2; + + uart_aes UUT (.clk_i(clk), .rst_n_i(rst_n), .uart_rx_i(uart_rx), .uart_tx_o(uart_tx), .led_n_o(led_n)); + + // set dumpfile + initial begin + $dumpfile ("tb_uart_aes.fst"); + $dumpvars (0, tb_uart_aes); + end + + // Setup simulation + initial begin + uart_rx = 1'b1; + #1 rst_n = 1'b0; + #120 rst_n = 1'b1; + end + + // Generate 10 mhz clock + always #clk_half_period clk = !clk; + + reg [7:0] write_cmds [0:89]; + reg [7:0] read_data [0:15]; + + // read in test data files + initial begin + $readmemh("write_cmds.txt", write_cmds); + $readmemh("read_data.txt", read_data); + end + + // Stimuli generator + initial + forever @(posedge rst_n) begin + uart_rx = 1'b1; + #uart_bit_period; + // start crypto + for (integer i = 0; i < $size(write_cmds); i = i + 1) begin + tx_data = write_cmds[i]; + $display ("UART send: 0x%h", tx_data); + uart_rx = 1'b0; + #uart_bit_period; + for (integer i = 0; i <= 7; i = i + 1) begin + uart_rx = tx_data[i]; + #uart_bit_period; + end + uart_rx = 1'b1; + #uart_bit_period; + #uart_bit_period; + end + // Request read of data out register + for (integer i = 0; i <= 15; i = i + 1) begin + tx_data = 8'h40; + $display ("UART send: 0x%h", tx_data); + uart_rx = 1'b0; + #uart_bit_period; + for (integer i = 0; i <= 7; i = i + 1) begin + uart_rx = tx_data[i]; + #uart_bit_period; + end + uart_rx = 1'b1; + #uart_bit_period; + #uart_bit_period; + end + end + + // Checker + initial begin + @(posedge rst_n) + for (integer i = 0; i <= 15; i = i + 1) begin + @(negedge uart_tx) + #uart_bit_period; + #uart_bit_half_period; + for (integer i = 0; i <= 7; i = i + 1) begin + rx_data[i] = uart_tx; + #uart_bit_period; + end + assert (rx_data == read_data[i]) + $display("UART recv: 0x%h", rx_data); + else + $error("UART receive error, got 0x%h, expected 0x%h", rx_data, read_data[i]); + end + $display ("UART tests finished"); + $finish; + end + + +endmodule diff --git a/uart_aes/syn/uart_aes.ccf b/uart_aes/syn/uart_aes.ccf new file mode 100644 index 0000000..bb88665 --- /dev/null +++ b/uart_aes/syn/uart_aes.ccf @@ -0,0 +1,21 @@ +# Configuration for the Gatemate eval board + +Pin_in "clk_i" Loc = "IO_SB_A8" | SCHMITT_TRIGGER=true; +Pin_in "rst_n_i" Loc = "IO_EB_B0"; # SW3 + +Pin_in "uart_rx_i" Loc = "IO_NB_A1"; # PMODA IO3 +Pin_out "uart_tx_o" Loc = "IO_NB_A0"; # PMODA IO1 + +Pin_out "led_n_o[0]" Loc = "IO_EB_B1"; # LED D1 +Pin_out "led_n_o[1]" Loc = "IO_EB_B2"; # LED D2 +Pin_out "led_n_o[2]" Loc = "IO_EB_B3"; # LED D3 +Pin_out "led_n_o[3]" Loc = "IO_EB_B4"; # LED D4 + +#Pin_out "debug_o[0]" Loc = "IO_NB_A4"; +#Pin_out "debug_o[1]" Loc = "IO_NB_A5"; +#Pin_out "debug_o[2]" Loc = "IO_NB_A6"; +#Pin_out "debug_o[3]" Loc = "IO_NB_A7"; +#Pin_out "debug_o[4]" Loc = "IO_NB_B4"; +#Pin_out "debug_o[5]" Loc = "IO_NB_B5"; +#Pin_out "debug_o[6]" Loc = "IO_NB_B6"; +#Pin_out "debug_o[7]" Loc = "IO_NB_B7"; diff --git a/uart_aes/syn/write_cmds.txt b/uart_aes/syn/write_cmds.txt new file mode 100644 index 0000000..a776a8f --- /dev/null +++ b/uart_aes/syn/write_cmds.txt @@ -0,0 +1,90 @@ +11 +01 +11 +23 +11 +45 +11 +67 +11 +89 +11 +AB +11 +CD +11 +EF +11 +01 +11 +23 +11 +45 +11 +67 +11 +89 +11 +AB +11 +CD +11 +EF +21 +01 +21 +23 +21 +45 +21 +67 +21 +89 +21 +AB +21 +CD +21 +EF +21 +01 +21 +23 +21 +45 +21 +67 +31 +01 +31 +23 +31 +45 +31 +67 +31 +89 +31 +AB +31 +CD +31 +EF +31 +01 +31 +23 +31 +45 +31 +67 +31 +89 +31 +AB +31 +CD +31 +EF +01 +06 \ No newline at end of file