From eca4097d59003ea73e8f3f334b12f8c2b3a4de27 Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Sun, 27 Nov 2016 09:47:23 +0100 Subject: [PATCH] Trivium: Add VHDL test bench. --- sim/tb_trivium.vhdl | 358 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 sim/tb_trivium.vhdl diff --git a/sim/tb_trivium.vhdl b/sim/tb_trivium.vhdl new file mode 100644 index 0000000..6dd31f8 --- /dev/null +++ b/sim/tb_trivium.vhdl @@ -0,0 +1,358 @@ +-- +-- Test bench for PRNG Trivium. +-- + +use std.textio.all; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity tb_trivium is +end entity; + +architecture arch of tb_trivium is + + type output_vector is record + pos: natural; + data: std_logic_vector(511 downto 0); + end record; + + type output_vectors is array (natural range <>) of output_vector; + + type test_vector is record + key: std_logic_vector(79 downto 0); + iv: std_logic_vector(79 downto 0); + data: output_vectors(0 to 3); +-- data: array (0 to 3) of output_vector; + end record; + + type test_vectors is array (natural range <>) of test_vector; + + constant testvec: test_vectors(0 to 1) := ( + 0 => ( key => x"0053A6F94C9FF24598EB", + iv => x"0D74DB42A91077DE45AC", + data => ( ( pos => 0, data => + x"F4CD954A717F26A7D6930830C4E7CF08" & + x"19F80E03F25F342C64ADC66ABA7F8A8E" & + x"6EAA49F23632AE3CD41A7BD290A0132F" & + x"81C6D4043B6E397D7388F3A03B5FE358" ), + ( pos => 65472, data => + x"C04C24A6938C8AF8A491D5E481271E0E" & + x"601338F01067A86A795CA493AA4FF265" & + x"619B8D448B706B7C88EE8395FC79E5B5" & + x"1AB40245BBF7773AE67DF86FCFB71F30" ), + ( pos => 65536, data => + x"011A0D7EC32FA102C66C164CFCB189AE" & + x"D9F6982E8C7370A6A37414781192CEB1" & + x"55C534C1C8C9E53FDEADF2D3D0577DAD" & + x"3A8EB2F6E5265F1E831C86844670BC69" ), + ( pos => 131008, data => + x"48107374A9CE3AAF78221AE77789247C" & + x"F6896A249ED75DCE0CF2D30EB9D889A0" & + x"C61C9F480E5C07381DED9FAB2AD54333" & + x"E82C89BA92E6E47FD828F1A66A8656E0" ))), + 1 => ( key => x"80000000000000000000", + iv => x"00000000000000000000", + data => ( ( pos => 0, data => + x"38EB86FF730D7A9CAF8DF13A4420540D" & + x"BB7B651464C87501552041C249F29A64" & + x"D2FBF515610921EBE06C8F92CECF7F80" & + x"98FF20CCCC6A62B97BE8EF7454FC80F9" ), + ( pos => 192, data => + x"EAF2625D411F61E41F6BAEEDDD5FE202" & + x"600BD472F6C9CD1E9134A745D900EF6C" & + x"023E4486538F09930CFD37157C0EB57C" & + x"3EF6C954C42E707D52B743AD83CFF297" ), + ( pos => 256, data => + x"9A203CF7B2F3F09C43D188AA13A5A202" & + x"1EE998C42F777E9B67C3FA221A0AA1B0" & + x"41AA9E86BC2F5C52AFF11F7D9EE480CB" & + x"1187B20EB46D582743A52D7CD080A24A" ), + ( pos => 448, data => + x"EBF14772061C210843C18CEA2D2A275A" & + x"E02FCB18E5D7942455FF77524E8A4CA5" & + x"1E369A847D1AEEFB9002FCD02342983C" & + x"EAFA9D487CC2032B10192CD416310FA4" ))) + ); + + signal clk: std_logic; + signal clock_active: boolean := false; + + signal x1_rst: std_logic; + signal x1_reseed: std_logic; + signal x1_newkey: std_logic_vector(79 downto 0); + signal x1_newiv: std_logic_vector(79 downto 0); + signal x1_out_ready: std_logic; + signal x1_out_valid: std_logic; + signal x1_out_data: std_logic_vector(0 downto 0); + + signal x8_rst: std_logic; + signal x8_reseed: std_logic; + signal x8_newkey: std_logic_vector(79 downto 0); + signal x8_newiv: std_logic_vector(79 downto 0); + signal x8_out_ready: std_logic; + signal x8_out_valid: std_logic; + signal x8_out_data: std_logic_vector(7 downto 0); + + signal x64_rst: std_logic; + signal x64_reseed: std_logic; + signal x64_newkey: std_logic_vector(79 downto 0); + signal x64_newiv: std_logic_vector(79 downto 0); + signal x64_out_ready: std_logic; + signal x64_out_valid: std_logic; + signal x64_out_data: std_logic_vector(63 downto 0); + + -- Convert bit vector to hexadecimal string. + function to_hex_string(s: std_logic_vector) + return string + is + constant alphabet: string(1 to 16) := "0123456789abcdef"; + variable y: string(1 to s'length/4); + begin + for i in y'range loop + y(i) := alphabet(to_integer(unsigned(s(s'high+4-4*i downto s'high+1-4*i))) + 1); + end loop; + return y; + end function; + + -- Reverse order of 8-bit groups within long bit vector. + function flipbits(x: std_logic_vector) return std_logic_vector is + variable y: std_logic_vector(x'length-1 downto 0); + begin + for p in 0 to x'length / 8 - 1 loop + y(p*8+7 downto p*8) := x(x'high-p*8 downto x'high-p*8-7); + end loop; + return y; + end function; + + -- Force interpretation of string literal. + function force_str(s: string) return string is + begin + return s; + end function; + + -- Test one of the instances of the RNG. + procedure test_inst(signal s_rst: out std_logic; + signal s_reseed: out std_logic; + signal s_newkey: out std_logic_vector(79 downto 0); + signal s_newiv: out std_logic_vector(79 downto 0); + signal s_ready: out std_logic; + signal s_valid: in std_logic; + signal s_data: in std_logic_vector) + is + constant nbit: natural := s_data'length; + constant init_duration: natural := 4 * 288 / nbit; + variable lin: line; + variable p: natural := 0; + variable bitpos: natural; + variable vk: natural; + variable w: std_logic_vector(511 downto 0); + variable wp: natural; + begin + -- Initialize inputs. + s_reseed <= '0'; + s_newkey <= (others => '0'); + s_newiv <= (others => '0'); + s_ready <= '0'; + + -- End reset. + wait until falling_edge(clk); + s_rst <= '0'; + + -- Loop over test vectors. + for k in testvec'range loop + + write(lin, force_str("key = ")); + write(lin, to_hex_string(testvec(k).key)); + writeline(output, lin); + write(lin, force_str("iv = ")); + write(lin, to_hex_string(testvec(k).iv)); + writeline(output, lin); + + -- Reseed generator, except for first test vector. + -- First test vector runs on initial seed. + if k /= 0 then + s_reseed <= '1'; + s_newkey <= flipbits(testvec(k).key); + s_newiv <= flipbits(testvec(k).iv); + wait until falling_edge(clk); + s_reseed <= '0'; + s_newkey <= (others => '0'); + s_newiv <= (others => '0'); + end if; + + -- Give generator time to complete initialization. + for i in 0 to init_duration loop + assert s_valid = '0' + report "Generator indicates VALID too early"; + if (p mod 3 = 0) or (p mod 5 = 0) or (p mod 17 = 0) then + s_ready <= '0'; + else + s_ready <= '1'; + end if; + wait until falling_edge(clk); + p := p + 1; + end loop; + + -- Start generating random bits. + bitpos := 0; + + -- Start looping over output vectors. + vk := testvec(k).data'low; + + while vk <= testvec(k).data'high loop + + -- Generate a block of bits. + assert s_valid = '1' report "Output not VALID"; + + if (p mod 3 = 0) or (p mod 5 = 0) or (p mod 17 = 0) then + -- Skipping this clock cycle. + s_ready <= '0'; + else + -- Consuming data this clock cycle. + s_ready <= '1'; + + if bitpos >= testvec(k).data(vk).pos * 8 then + -- Store bits in block. + wp := bitpos - testvec(k).data(vk).pos * 8; + assert wp = 0 or wp >= nbit + report "Invalid test vector offset"; + assert wp + nbit <= w'length + report "Invalid test vector offset"; + w(wp+nbit-1 downto wp) := s_data; + end if; + + bitpos := bitpos + nbit; + end if; + + wait until falling_edge(clk); + p := p + 1; + + if bitpos = testvec(k).data(vk).pos * 8 + w'length then + -- Reached end of current output vector. + + -- Dump output data to screen. + write(lin, force_str("out[")); + write(lin, testvec(k).data(vk).pos, right, 6); + write(lin, force_str("] = ")); + write(lin, to_hex_string(flipbits( w(127 downto 0)))); + writeline(output, lin); + for tk in 1 to w'length / 128 - 1 loop + write(lin, force_str(" ")); + write(lin, to_hex_string(flipbits( + w(128*tk+127 downto 128*tk)))); + writeline(output, lin); + end loop; + + -- Check against expected output vector. + assert w = flipbits(testvec(k).data(vk).data) + report "Unexpected output from RNG"; + + -- Go to next output vector. + vk := vk + 1; + end if; + + end loop; + + -- Go to next test vector. + writeline(output, lin); + end loop; + + -- Put instance back in reset. + s_rst <= '1'; + + end procedure; + +begin + + -- Instantiate PRNG with 1-bit output. + inst_x1: entity work.rng_trivium + generic map ( + num_bits => 1, + init_key => x"eb9845f29f4cf9a65300", + init_iv => x"ac45de7710a942db740d" ) + port map ( + clk => clk, + rst => x1_rst, + reseed => x1_reseed, + newkey => x1_newkey, + newiv => x1_newiv, + out_ready => x1_out_ready, + out_valid => x1_out_valid, + out_data => x1_out_data ); + + -- Instantiate PRNG with 8-bit output. + inst_x8: entity work.rng_trivium + generic map ( + num_bits => 8, + init_key => x"0053A6F94C9FF24598EB", + init_iv => x"0D74DB42A91077DE45AC" ) + port map ( + clk => clk, + rst => x8_rst, + reseed => x8_reseed, + newkey => x8_newkey, + newiv => x8_newiv, + out_ready => x8_out_ready, + out_valid => x8_out_valid, + out_data => x8_out_data ); + + -- Instantiate PRNG with 64-bit output. + inst_x64: entity work.rng_trivium + generic map ( + num_bits => 64, + init_key => x"eb9845f29f4cf9a65300", + init_iv => x"ac45de7710a942db740d" ) + port map ( + clk => clk, + rst => x64_rst, + reseed => x64_reseed, + newkey => x64_newkey, + newiv => x64_newiv, + out_ready => x64_out_ready, + out_valid => x64_out_valid, + out_data => x64_out_data ); + + -- Generate clock. + clk <= (not clk) after 10 ns when clock_active else '0'; + + -- Main simulation process. + process is + begin + + report "Start test bench"; + + -- Reset all instances. + x1_rst <= '1'; + x8_rst <= '1'; + x64_rst <= '1'; + + -- Start clock. + clock_active <= true; + wait for 30 ns; + + -- Test 1-bit instance. + report "Test 1-bit generator"; + test_inst(x1_rst, x1_reseed, x1_newkey, x1_newiv, + x1_out_ready, x1_out_valid, x1_out_data); + + -- Test 8-bit instance. + report "Test 8-bit generator"; + test_inst(x8_rst, x8_reseed, x8_newkey, x8_newiv, + x8_out_ready, x8_out_valid, x8_out_data); + + -- Test 64-bit instance. + report "Test 64-bit generator"; + test_inst(x64_rst, x64_reseed, x64_newkey, x64_newiv, + x64_out_ready, x64_out_valid, x64_out_data); + + -- End simulation. + report "End testbench"; + + clock_active <= false; + wait; + + end process; + +end architecture;