From 0df7a047bebf29853504181428c71d950a4f8274 Mon Sep 17 00:00:00 2001 From: tmeissner Date: Fri, 30 Dec 2022 21:40:59 +0100 Subject: [PATCH] Add uart_trng design --- README.md | 4 + uart_loop/syn/tb_uart_loop.v | 2 +- uart_trng/rtl/firo.vhd | 53 +++++++ uart_trng/rtl/firo_ctrl.vhd | 189 +++++++++++++++++++++++++ uart_trng/rtl/uart_trng.vhd | 137 ++++++++++++++++++ uart_trng/rtl/uart_tx.vhd | 102 +++++++++++++ uart_trng/sim/Makefile | 30 ++++ uart_trng/sim/tb_uart_trng.vhd | 80 +++++++++++ uart_trng/sw/Makefile | 7 + uart_trng/sw/uart_trng.c | 252 +++++++++++++++++++++++++++++++++ uart_trng/syn/Makefile | 51 +++++++ uart_trng/syn/tb_uart_trng.v | 125 ++++++++++++++++ uart_trng/syn/uart_trng.ccf | 15 ++ 13 files changed, 1046 insertions(+), 1 deletion(-) create mode 100644 uart_trng/rtl/firo.vhd create mode 100644 uart_trng/rtl/firo_ctrl.vhd create mode 100644 uart_trng/rtl/uart_trng.vhd create mode 100644 uart_trng/rtl/uart_tx.vhd create mode 100644 uart_trng/sim/Makefile create mode 100644 uart_trng/sim/tb_uart_trng.vhd create mode 100644 uart_trng/sw/Makefile create mode 100644 uart_trng/sw/uart_trng.c create mode 100644 uart_trng/syn/Makefile create mode 100644 uart_trng/syn/tb_uart_trng.v create mode 100644 uart_trng/syn/uart_trng.ccf diff --git a/README.md b/README.md index 4588d4d..c463eb6 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ Register file which can be accessed through UART. It uses *CC_PLL* & *CC_CFG_END 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. +### uart_trng + +An implementation of a TRNG which allows to read random data from the FPGA via UART. Inclusive a software tool for easy access. Random generation is based on a fibonacci ring oscillator (FiRo) with toggle flip-flop and von Neumann post-processing. + ## Further Ressources * [GateMate FPGA](https://www.colognechip.com/programmable-logic/gatemate) diff --git a/uart_loop/syn/tb_uart_loop.v b/uart_loop/syn/tb_uart_loop.v index 1d96441..a4bb9b1 100644 --- a/uart_loop/syn/tb_uart_loop.v +++ b/uart_loop/syn/tb_uart_loop.v @@ -96,7 +96,7 @@ module tb_uart_loop; end uart_rx = 1'b1; #uart_bit_period; - #uart_bit_period + #uart_bit_period; #uart_bit_period; end end diff --git a/uart_trng/rtl/firo.vhd b/uart_trng/rtl/firo.vhd new file mode 100644 index 0000000..5ae97b3 --- /dev/null +++ b/uart_trng/rtl/firo.vhd @@ -0,0 +1,53 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity firo is +generic ( + TOGGLE : boolean := true +); +port ( + frun_i : in std_logic; + fdata_o : out std_logic +); +end entity firo; + + +architecture rtl of firo is + + -- signal for inverter loop + signal s_ring : std_logic_vector(15 downto 0); + signal s_tff : std_logic; + + -- attributes for synplify synthesis tool to preserve inverter loop + attribute syn_keep : boolean; + attribute syn_hier : string; + attribute syn_hier of rtl : architecture is "hard"; + attribute syn_keep of s_ring : signal is true; + attribute syn_keep of s_tff : signal is true; + +begin + + firoring : for index in 1 to 15 generate + s_ring(index) <= not(s_ring(index - 1)); + end generate; + + s_ring(0) <= (s_ring(15) xor s_ring(14) xor s_ring(7) xor s_ring(6) xor s_ring(5) xor s_ring(4) xor s_ring(2)) and frun_i; + + with_toggle : if TOGGLE generate + tffP : process(frun_i, s_ring(15)) is + begin + if (not frun_i) then + s_tff <= '0'; + elsif (rising_edge(s_ring(15))) then + s_tff <= not s_tff; + end if; + end process tffP; + fdata_o <= s_ring(15) xor s_tff; + else generate + fdata_o <= s_ring(15); + end generate; + + +end architecture rtl; diff --git a/uart_trng/rtl/firo_ctrl.vhd b/uart_trng/rtl/firo_ctrl.vhd new file mode 100644 index 0000000..0c867c7 --- /dev/null +++ b/uart_trng/rtl/firo_ctrl.vhd @@ -0,0 +1,189 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity firo_ctrl is +generic ( + EXTRACT : boolean := true +); +port ( + -- system + clk_i : in std_logic; + rst_n_i : in std_logic; + -- axis in + 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; + -- firo + frun_o : out std_logic; + fdata_i : in std_logic +); +end entity firo_ctrl; + + +architecture rtl of firo_ctrl is + + signal s_clk_counter : unsigned(4 downto 0); + signal s_run : std_logic; + signal s_firo_valid : std_logic; + + type t_neumann_state is (BIT1, BIT2, BIT3, BIT4); + signal s_neumann_state : t_neumann_state; + signal s_neumann_buffer : std_logic_vector(2 downto 0); + + type t_register_state is (SLEEP, COLLECT, VALID); + signal s_register_state : t_register_state; + signal s_register_enable : std_logic; + signal s_register_din : std_logic_vector(1 downto 0); + signal s_register_data : std_logic_vector(8 downto 0); + signal s_register_counter : unsigned(2 downto 0); + signal s_register_length : natural range 1 to 2; + + signal s_data : std_logic_vector(3 downto 0); + +begin + + frun_o <= s_run when s_register_state = COLLECT else '0'; + s_data <= s_neumann_buffer & fdata_i; + + ControllerP : process (clk_i) is + begin + if (rising_edge(clk_i)) then + if (s_register_state = SLEEP) then + s_clk_counter <= (others => '1'); + s_run <= '0'; + s_firo_valid <= '0'; + else + s_clk_counter <= s_clk_counter - 1; + s_firo_valid <= '0'; + if (s_clk_counter = 23 and s_run = '0') then + s_run <= '1'; + s_clk_counter <= (others => '1'); + end if; + if (s_clk_counter = 12 and s_run = '1') then + s_run <= '0'; + s_clk_counter <= (others => '1'); + end if; + if (s_clk_counter = 13 and s_run = '1') then + s_firo_valid <= '1'; + end if; + end if; + end if; + end process ControllerP; + + VON_NEUMANN : if EXTRACT generate + process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_neumann_state <= BIT1; + s_register_enable <= '0'; + s_register_din <= "00"; + elsif (rising_edge(clk_i)) then + case s_neumann_state is + when BIT1 => + s_register_enable <= '0'; + if (s_firo_valid) then + s_neumann_buffer(2) <= fdata_i; + s_neumann_state <= BIT2; + end if; + when BIT2 => + if (s_firo_valid) then + s_neumann_buffer(1) <= fdata_i; + s_neumann_state <= BIT3; + end if; + when BIT3 => + if (s_firo_valid) then + s_neumann_buffer(0) <= fdata_i; + s_neumann_state <= BIT4; + end if; + when BIT4 => + if (s_firo_valid) then + s_register_enable <= '1'; + s_register_length <= 1; + s_register_din <= "00"; + s_neumann_state <= BIT1; + case (s_data) is + when x"5" => + s_register_din <= "01"; + when x"1" | x"6" | x"7" => + s_register_length <= 2; + when x"2" | x"9" | x"b" => + s_register_din <= "01"; + s_register_length <= 2; + when x"4" | x"a" | x"d" => + s_register_din <= "10"; + s_register_length <= 2; + when x"8" | x"c" | x"e" => + s_register_din <= "11"; + s_register_length <= 2; + when x"0" | x"f" => + s_register_enable <= '0'; + when others => -- incl. x"3" + null; + end case; + end if; + when others => + null; + end case; + end if; + end process; + else generate + s_register_enable <= s_firo_valid; + s_register_din(0) <= fdata_i; + s_register_length <= 1; + end generate; + + ShiftRegisterP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_register_counter <= (others => '1'); + s_register_state <= SLEEP; + elsif (rising_edge(clk_i)) then + case s_register_state is + when SLEEP => + if (tvalid_i) then + s_register_state <= COLLECT; + s_register_data(0) <= s_register_data(8); + end if; + when COLLECT => + if (s_register_enable) then + if (s_register_counter = 0) then + s_register_data <= s_register_din(1) & s_register_data(6 downto 0) & s_register_din(0); + s_register_state <= VALID; + elsif (s_register_counter = 1) then + if (s_register_length = 1) then + s_register_data(7 downto 0) <= s_register_data(6 downto 0) & s_register_din(0); + end if; + if (s_register_length = 2) then + s_register_data(7 downto 0) <= s_register_data(5 downto 0) & s_register_din; + s_register_state <= VALID; + end if; + else + if (s_register_length = 1) then + s_register_data(7 downto 0) <= s_register_data(6 downto 0) & s_register_din(0); + else + s_register_data(7 downto 0) <= s_register_data(5 downto 0) & s_register_din; + end if; + end if; + s_register_counter <= s_register_counter - s_register_length; + end if; + when VALID => + if (tready_i) then + s_register_state <= SLEEP; + end if; + when others => + null; + end case; + end if; + end process ShiftRegisterP; + + tready_o <= '1' when s_register_state = SLEEP else '0'; + + tvalid_o <= '1' when s_register_state = VALID else '0'; + tdata_o <= s_register_data(7 downto 0); + +end architecture rtl; diff --git a/uart_trng/rtl/uart_trng.vhd b/uart_trng/rtl/uart_trng.vhd new file mode 100644 index 0000000..327516b --- /dev/null +++ b/uart_trng/rtl/uart_trng.vhd @@ -0,0 +1,137 @@ +-- 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; +use ieee.math_real.all; + +library gatemate; +use gatemate.components.all; + + +entity uart_trng is +generic ( + SIM : natural := 0 +); +port ( + clk_i : in std_logic; -- 10 MHz clock + rst_n_i : in std_logic; -- SW3 button + uart_tx_o : out std_logic -- PMODA IO5 +); +end entity uart_trng; + + +architecture rtl of uart_trng 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_tx_tdata : std_logic_vector(7 downto 0); + signal s_uart_tx_tvalid : std_logic; + signal s_uart_tx_tready : std_logic; + + signal s_firo_run : std_logic; + signal s_firo_data : std_logic; + +begin + + pll : CC_PLL + generic map ( + REF_CLK => "10", + OUT_CLK => "10", + PERF_MD => "ECONOMY" + ) + 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 + ); + + firo_ctrl : entity work.firo_ctrl + generic map ( + EXTRACT => true + ) + port map ( + -- system + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- axis in + tvalid_i => '1', + tready_o => open, + -- axis out + tdata_o => s_uart_tx_tdata, + tvalid_o => s_uart_tx_tvalid, + tready_i => s_uart_tx_tready, + -- firo + frun_o => s_firo_run, + fdata_i => s_firo_data + ); + + SIMULATION : if (SIM /= 0) generate + -- simple random bit generator + RandomGenP : process (s_pll_clk, s_firo_run) is + variable v_seed1, v_seed2 : positive := 1; + variable v_real_rand : real; + begin + if (not s_firo_run) then + s_firo_data <= '0'; + elsif (s_pll_clk'event) then + uniform(v_seed1, v_seed2, v_real_rand); + if (v_real_rand < 0.5) then + s_firo_data <= '0'; + else + s_firo_data <= '1'; + end if; + end if; + end process RandomGenP; + else generate + firo : entity work.firo + generic map ( + TOGGLE => true + ) + port map ( + frun_i => s_firo_run, + fdata_o => s_firo_data + ); + end generate; + + 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; + +end architecture; diff --git a/uart_trng/rtl/uart_tx.vhd b/uart_trng/rtl/uart_tx.vhd new file mode 100644 index 0000000..5ce450c --- /dev/null +++ b/uart_trng/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_trng/sim/Makefile b/uart_trng/sim/Makefile new file mode 100644 index 0000000..2b35022 --- /dev/null +++ b/uart_trng/sim/Makefile @@ -0,0 +1,30 @@ +DESIGN_NAME := uart_trng +LIB_SRC := ../../lib/rtl_components.vhd ../../lib/sim_components.vhd +RTL_SRC := ../rtl/uart_tx.vhd ../rtl/firo.vhd ../rtl/firo_ctrl.vhd ../rtl/${DESIGN_NAME}.vhd +SIM_SRC := tb_${DESIGN_NAME}.vhd +SIM_FLAGS := --std=08 -fpsl --workdir=work + +.PHONY: all compile sim clean + +all: sim +compile: tb_${DESIGN_NAME} + +tb_${DESIGN_NAME}: ${LIB_SRC} ${RTL_SRC} ${SIM_SRC} + mkdir -p work + @echo "Analyze gatemate library ..." + ghdl -a ${SIM_FLAGS} --work=gatemate ${LIB_SRC} + @echo "Analyze testbench & design ..." + ghdl -a ${SIM_FLAGS} -Pwork ${RTL_SRC} ${SIM_SRC} + @echo "Elaborate testbench & design ..." + ghdl -e ${SIM_FLAGS} -Pwork $@ + +sim: tb_${DESIGN_NAME} + @echo "Run testbench ..." + ghdl -r ${SIM_FLAGS} -Pwork tb_${DESIGN_NAME} --assert-level=error --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_trng/sim/tb_uart_trng.vhd b/uart_trng/sim/tb_uart_trng.vhd new file mode 100644 index 0000000..56c81ad --- /dev/null +++ b/uart_trng/sim/tb_uart_trng.vhd @@ -0,0 +1,80 @@ +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use std.env.all; + + +entity tb_uart_trng is +end entity tb_uart_trng; + + +architecture sim of tb_uart_trng is + + signal s_clk : std_logic := '1'; + signal s_rst_n : std_logic := '0'; + + signal s_uart_tx : std_logic; + + constant c_baudrate : natural := 9600; + constant c_period_ns : time := 1000000000 / c_baudrate * ns; + + 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); + tx <= '0'; + wait for c_period_ns; + for i in 0 to 7 loop + tx <= data(i); + wait for c_period_ns; + end loop; + tx <= '1'; + wait for c_period_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 c_period_ns; -- Skip start bit + wait for c_period_ns/2; + for i in 0 to 7 loop + data(i) := rx; + wait for c_period_ns; + end loop; + report "UART recv: 0x" & to_hstring(data); + end procedure; + +begin + + dut : entity work.uart_trng + generic map ( + SIM => 1 + ) + port map ( + clk_i => s_clk, + rst_n_i => s_rst_n, + uart_tx_o => s_uart_tx + ); + + s_rst_n <= '1' after 120 ns; + s_clk <= not s_clk after 50 ns; + + ReceiveP : process is + type t_exp is array (0 to 7) of std_logic_vector(7 downto 0); + variable v_exp : t_exp; + variable v_data : std_logic_vector(7 downto 0); + begin + wait until s_rst_n; + wait until rising_edge(s_clk); + -- First read all registers + for i in 0 to 7 loop + uart_recv(v_data, s_uart_tx); + end loop; + wait for 200 us; + report "Simulation finished :-)"; + stop(0); + end process; + +end architecture; diff --git a/uart_trng/sw/Makefile b/uart_trng/sw/Makefile new file mode 100644 index 0000000..988bf5b --- /dev/null +++ b/uart_trng/sw/Makefile @@ -0,0 +1,7 @@ +all: uart_trng + +uart_trng: uart_trng.c + gcc -Wall uart_trng.c -o uart_trng + +clean: + rm -rf uart_trng diff --git a/uart_trng/sw/uart_trng.c b/uart_trng/sw/uart_trng.c new file mode 100644 index 0000000..272a3f6 --- /dev/null +++ b/uart_trng/sw/uart_trng.c @@ -0,0 +1,252 @@ +#include /* Standard input/output definitions */ +#include +#include /* Standard types */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ +#include /* File control definitions */ +#include /* Error number definitions */ +#include /* POSIX terminal control definitions */ +#include + + +#ifdef __APPLE__ +// define 460k baud rate, seems not to be available in headers on osx +#define B460800 460800 +#endif + + +void help(void); +int serialport_init(const char* serialport, unsigned int baud); +int serialport_read_until(int fd, unsigned int until); + + +// Checks for an argument (characters with a leading '-') +// Returns 0 if argument does not exist or the index of argument +// type in the argv[] array +int parArgTypExists (int argc, char *argv[], char argType) +{ + char tmp[3]; + + tmp[0] = '-'; + tmp[1] = argType; + tmp[2] = 0; + + if (argc > 1) { + for (int i = 1; i < argc; i++) { + if (!strcmp (argv[i], tmp)) + return i; + } + } + return 0; +} + + +// Get string argument value +// Returns 0 in error case, returns 1 if OK +// (string is limited to max. 4096 characters) +int parGetString (int argc, char *argv[], char argType, char *value) +{ + int a = parArgTypExists(argc, argv, argType); + + // Check for errors + if (a == 0) return 0; + if (a >= (argc -1)) return 0; + if (strlen(argv[a+1]) > 256) return 0; + + strcpy(value, argv[a+1]); + return 1; +} + + +// Get unsigned int argument value +// Returns 0 in error case, 1 if OK +int parGetUnsignedInt (int argc, char *argv[], char argType, + unsigned int *value) +{ + int a = parArgTypExists (argc, argv, argType); + + /* error checking */ + if (a == 0) return 0; + if (a >= (argc -1)) return 0; + + a = sscanf(argv[a+1], "%iu", value); + + return a; +} + + +void help(void) { + printf("Usage: uart_trng -p [OPTIONS]\n" + "\n" + "Options:\n" + " -h, --help Print this help message\n" + " -p, --port=serialport Serial port where the uart_trng device is plugged on\n" + " -b, --baud=baudrate Baudrate (bps) of uart_trng device, default = 9600\n" + " -s, --size number of bytes to receive from uart_trng device,\n" + " 0 for reading until break with Ctrl-c, default = 1024\n" + "\n"); +} + + +int main(int argc, char *argv[]) +{ + int fd = 0; + int r = 0; + char serialport[256]; + unsigned int baudrate = 9600; + unsigned int size = 1024; + + // check for command line arguments + if (argc == 1) { + help(); + return 1; + } + else { + if (parArgTypExists (argc, argv, 'h')) { + help(); + return 1; + } + // get serial port + if (parArgTypExists (argc, argv, 'p')) { + r = parGetString (argc, argv, 'p', serialport); + if (r == 0) { + return (1); + } + } else { + help(); + return (1); + } + // get baud rate + if (parArgTypExists (argc, argv, 'b')) { + r = parGetUnsignedInt (argc, argv, 'b', &baudrate); + if (r == 0) { + return (1); + } + } + // get read size in bytes + if (parArgTypExists (argc, argv, 's')) { + r = parGetUnsignedInt (argc, argv, 's', &size); + if (r == 0) { + return (1); + } + } + } + + fd = serialport_init(serialport, baudrate); + if (fd == -1) + return -1; + serialport_read_until(fd, size); + + exit(EXIT_SUCCESS); +} + + +int serialport_read_until(int fd, unsigned int until) +{ + char b[1]; + int index = 0; + int n; + while (1) { + while (1) { + // read one char + n = read(fd, b, 1); + // we had a read error + // check if there were no bytes to read or if it was a real error + if (n == -1) { + if (errno != EAGAIN) { + fprintf(stderr,"serial read error at byte %d", index); + return -1; + } + } else { + break; + } + } + // we got no byte, so wait 10 ms for the next try + if (n == 0) { + usleep (10 * 1000); + continue; + } + printf("%c",b[0]); + if (until == 0) { + continue; + } else { + index++; + if (index == until) { + break; + } + } + }; + + fprintf(stderr,"uart_trng: read %d random bytes from device\n", until); + return 0; +} + + +// takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1") +// and a baud rate (bps) and connects to that port at that speed and 8N1. +// opens the port in fully raw mode so you can send binary data. +// returns valid fd, or -1 on error +int serialport_init(const char* serialport, unsigned int baud) +{ + struct termios toptions; + int fd; + + fprintf(stderr,"init_serialport: opening port %s @ %u bps\n", + serialport, baud); + + fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd == -1) { + perror("init_serialport: Unable to open port "); + return -1; + } + + if (tcgetattr(fd, &toptions) < 0) { + perror("init_serialport: Couldn't get term attributes"); + return -1; + } + speed_t brate = baud; // let you override switch below if needed + switch(baud) { + case 4800: brate=B4800; break; + case 9600: brate=B9600; break; +#ifdef B14400 + case 14400: brate=B14400; break; +#endif + case 19200: brate=B19200; break; +#ifdef B28800 + case 28800: brate=B28800; break; +#endif + case 38400: brate=B38400; break; + case 57600: brate=B57600; break; + case 115200: brate=B115200; break; + case 230400: brate=B230400; break; + case 460800: brate=B460800; break; + } + cfsetispeed(&toptions, brate); + cfsetospeed(&toptions, brate); + + // 8N1 + toptions.c_cflag &= ~PARENB; + toptions.c_cflag &= ~CSTOPB; + toptions.c_cflag &= ~CSIZE; + toptions.c_cflag |= CS8; + // no flow control + toptions.c_cflag &= ~CRTSCTS; + + toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines + toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl + + toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw + toptions.c_oflag &= ~OPOST; // make raw + + // see: http://unixwiz.net/techtips/termios-vmin-vtime.html + toptions.c_cc[VMIN] = 0; + toptions.c_cc[VTIME] = 20; + + if( tcsetattr(fd, TCSANOW, &toptions) < 0) { + perror("init_serialport: Couldn't set term attributes"); + return -1; + } + + return fd; +} + diff --git a/uart_trng/syn/Makefile b/uart_trng/syn/Makefile new file mode 100644 index 0000000..338e7a8 --- /dev/null +++ b/uart_trng/syn/Makefile @@ -0,0 +1,51 @@ +DESIGN_NAME := uart_trng +WORK_FILES := ../rtl/uart_tx.vhd ../rtl/firo.vhd ../rtl/firo_ctrl.vhd ../rtl/${DESIGN_NAME}.vhd +GM_FILES := ../../lib/rtl_components.vhd +GHDL_FLAGS := --std=08 --workdir=build -Pbuild +YOSYSPIPE := -nomx8 -retime +# ATTENTION: -luttree option seems to mis-synthesize the design, broken with synth_gatemate? +PNRFLAGS := -om 2 +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 + ghdl -a ${GHDL_FLAGS} --work=work ${WORK_FILES} + +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 -g2012 -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 -g2012 -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_trng/syn/tb_uart_trng.v b/uart_trng/syn/tb_uart_trng.v new file mode 100644 index 0000000..00b727e --- /dev/null +++ b/uart_trng/syn/tb_uart_trng.v @@ -0,0 +1,125 @@ +`timescale 1 ns/10 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 = 1 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_reg; + + // DUT in/out + reg clk = 1'b0; + reg rst_n = 1'b1; + reg uart_rx; + wire uart_tx; + + // 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_reg UUT (.clk_i(clk), .rst_n_i(rst_n), .uart_rx_i(uart_rx), .uart_tx_o(uart_tx)); + + // set dumpfile + initial begin + $dumpfile ("tb_uart_reg.fst"); + $dumpvars (0, tb_uart_reg); + 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; + + // Stimuli generator + initial + forever @(posedge rst_n) begin + uart_rx = 1'b1; + #uart_bit_period; + for (integer tx = 0; tx < 32; tx = tx + 1) begin + tx_data = tx; + $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 + #uart_bit_period; + end + end + + // Checker + initial begin + @(posedge rst_n) + for (reg [7:0] rx = 0; rx < 32; rx = rx + 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 == rx) + $display("UART recv: 0x%h", rx_data); + else + $warning("UART receive error, got 0x%h, expected 0x%h", rx_data, rx); + end + $display ("UART tests finished"); + $finish; + end + + +endmodule diff --git a/uart_trng/syn/uart_trng.ccf b/uart_trng/syn/uart_trng.ccf new file mode 100644 index 0000000..8b92989 --- /dev/null +++ b/uart_trng/syn/uart_trng.ccf @@ -0,0 +1,15 @@ +# 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_out "uart_tx_o" Loc = "IO_NB_A0"; # PMODA IO1 + +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";