From da60d1f13356b798f9fc9a7b734c9f81ec1187b7 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 4 Jan 2024 17:26:25 +0100 Subject: [PATCH] tests: add startstop condition generator testbench --- tb/i2c/startstop_condition_generator_tb.vhd | 342 ++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 tb/i2c/startstop_condition_generator_tb.vhd diff --git a/tb/i2c/startstop_condition_generator_tb.vhd b/tb/i2c/startstop_condition_generator_tb.vhd new file mode 100644 index 0000000..8b7b1b7 --- /dev/null +++ b/tb/i2c/startstop_condition_generator_tb.vhd @@ -0,0 +1,342 @@ +library ieee; +use ieee.std_logic_1164.all; + +library i2c; +library vunit_lib; +context vunit_lib.vunit_context; + +entity startstop_condition_generator_tb is + + generic ( + runner_cfg : string); + +end entity startstop_condition_generator_tb; + +architecture tb of startstop_condition_generator_tb is + signal clk : std_logic := '0'; + constant CLK_PERIOD : time := 10 ns; + + signal rst_n : std_logic := '0'; + + signal sda : std_logic := 'H'; + signal slave_sda : std_logic := 'H'; + signal sda_enable : std_logic; + + signal scl : std_logic := 'H'; + signal not_scl : std_logic; + + signal scl_rising : std_logic := '0'; + signal scl_falling : std_logic := '0'; + signal scl_falling_delayed : std_logic := '0'; + + signal gen_start, gen_stop : std_logic := '0'; + signal req_scl_rise, req_scl_fall : std_logic := '0'; + + signal start_condition : std_logic := '0'; + signal stop_condition : std_logic := '0'; + + signal done : std_logic := '0'; + + constant DELAY : natural := 5; + constant TIMEOUT : time := DELAY * CLK_PERIOD * 2; + signal one : std_logic := '1'; + signal zero : std_logic := '0'; +begin -- architecture tb + + clk <= not clk after CLK_PERIOD/2; + rst_n <= '1' after 2*CLK_PERIOD; + + sda <= 'H'; + sda <= '0' when sda_enable = '1' else 'Z'; + + slave_sda <= '1' when sda = 'H' else sda; + + not_scl <= not scl; + scl <= 'H'; + + uut : entity i2c.startstop_condition_generator + generic map ( + DELAY => DELAY) + port map ( + clk_i => clk, + rst_in => rst_n, + sda_i => slave_sda, + sda_enable_o => sda_enable, + scl_rising_i => scl_rising, + scl_falling_i => scl_falling, + scl_falling_delayed_i => scl_falling_delayed, + start_condition_i => start_condition, + stop_condition_i => stop_condition, + gen_start_i => gen_start, + gen_stop_i => gen_stop, + req_scl_fall_o => req_scl_fall, + req_scl_rise_o => req_scl_rise, + done_o => done); + + main: process is + procedure wait_delay(constant delay : in integer) is + begin -- procedure wait_delay + for i in 0 to delay loop + wait until falling_edge(clk); + end loop; -- i + wait until falling_edge(clk); + end procedure wait_delay; + + procedure req_clear is + begin -- procedure req_clear + gen_start <= '0'; + gen_stop <= '0'; + end procedure req_clear; + + procedure req_start is + begin -- procedure req_start + req_clear; + gen_start <= '1'; + end procedure req_start; + + procedure req_stop is + begin -- procedure req_start + req_clear; + gen_stop <= '1'; + end procedure req_stop; + + procedure wait_start_condition(nowait: std_logic := '0') is + begin -- procedure wait_start_condition + check_equal(start_condition, '0', "Got start condition already when supposed to wait for it."); + wait until rising_edge(start_condition) or rising_edge(stop_condition) for TIMEOUT; + check_equal(stop_condition, '0', "Got stop condition instead of start."); + check_equal(start_condition, '1', "Waiting for start condition timed out."); + wait until falling_edge(clk); + end procedure wait_start_condition; + + procedure wait_stop_condition is + begin -- procedure wait_start_condition + check_equal(stop_condition, '0', "Got stop condition already when supposed to wait for it."); + wait until rising_edge(stop_condition) or rising_edge(start_condition) for TIMEOUT; + check_equal(start_condition, '0', "Got start condition instead of stop."); + check_equal(stop_condition, '1', "Waiting for stop condition timed out."); + wait until falling_edge(clk); + end procedure wait_stop_condition; + + procedure wait_done is + begin -- procedure wait_done + wait until rising_edge(done) for TIMEOUT; + check_equal(done, '1', "Expected done."); + wait until falling_edge(clk); + end procedure wait_done; + + procedure comply_scl_rise(constant time_out: time := TIMEOUT) is + begin -- procedure comply_scl_rise + wait until req_scl_rise = '1' or req_scl_fall = '1' for time_out; + check_equal(req_scl_rise, '1', "Should want rise"); + check_equal(req_scl_fall, '0', "Should want rise, not fall"); + scl <= 'Z'; + wait until falling_edge(clk); + end procedure comply_scl_rise; + + procedure comply_scl_fall is + begin -- procedure comply_scl_rise + wait until req_scl_fall = '1' or req_scl_rise = '1' for TIMEOUT; + check_equal(req_scl_fall, '1', "Should want fall"); + check_equal(req_scl_rise, '0', "Should want fall, not rise"); + scl <= '0'; + wait until falling_edge(clk); + end procedure comply_scl_fall; + + procedure check_no_reqs is + begin -- procedure check_no_reqs + check_equal(sda_enable, '0', "Should not hold sda enable after everything done."); + wait until req_scl_fall = '1' or req_scl_rise = '1' or start_condition = '1' or stop_condition = '1' for TIMEOUT; + check_equal(req_scl_fall, '0', "SCL fall was requested even though there shouldn't be any more requests."); + check_equal(req_scl_rise, '0', "SCL rise was requested even though there shouldn't be any more requests."); + check_equal(start_condition, '0', "Start condition triggered even though there shouldn't be any more conditions."); + check_equal(stop_condition, '0', "Stop condition triggered even though there shouldn't be any more conditions."); + check_equal(sda_enable, '0', "Should not hold sda enable after everything done."); + wait until falling_edge(clk); + end procedure check_no_reqs; + begin -- process main + wait until rst_n = '1'; + wait until falling_edge(clk); + + check_equal(scl, 'H', "begin SCL not high"); + + test_runner_setup(runner, runner_cfg); + + while test_suite loop + if run("start") then + + req_start; + wait_start_condition; + comply_scl_fall; + wait_done; + check_no_reqs; + + elsif run("start_from_wrong_sda") then + + sda <= '0'; + req_start; + comply_scl_fall; + sda <= 'H';-- cannot hold SDA from outside + comply_scl_rise(2*TIMEOUT); + wait_start_condition; + comply_scl_fall; + wait_done; + req_clear; + check_no_reqs; + + elsif run("repeated_start") then + + scl <= '0'; + wait until falling_edge(scl_falling_delayed); + req_start; + comply_scl_rise; + wait_start_condition; + comply_scl_fall; + wait_done; + req_clear; + check_no_reqs; + + elsif run("stop") then + scl <= '0'; + wait until falling_edge(scl_falling_delayed); + req_stop; + comply_scl_rise; + wait_stop_condition; + wait_done; + req_clear; + check_no_reqs; + + elsif run("stop_from_high_scl") then + sda <= '0'; + req_stop; + wait for TIMEOUT; + -- is not holding sda low + check_equal(sda_enable, '0'); + -- that means sda would be + -- high if not held by the + -- testbench + sda <= 'Z'; + wait_done; + check_no_reqs; + elsif run("stop_from_high_scl_wrong_sda") then + req_stop; + comply_scl_fall; + comply_scl_rise(2*TIMEOUT); + wait_stop_condition; + wait_done; + req_clear; + check_no_reqs; + elsif run("start_start_stop") then + + req_start; + wait_start_condition; + comply_scl_fall; + wait_done; + + req_clear; + wait until falling_edge(clk); + + check_equal(scl, '0'); + + req_start; + comply_scl_rise; + wait_start_condition; + comply_scl_fall; + wait_done; + + req_clear; + wait until falling_edge(clk); + + check_equal(scl, '0'); + + req_stop; + comply_scl_rise; + wait_stop_condition; + wait_done; + check_no_reqs; + + check_equal(scl, 'H'); + + elsif run("start_stop_start") then + req_start; + wait_start_condition; + comply_scl_fall; + wait_done; + + req_clear; + wait until falling_edge(clk); + + check_equal(scl, '0'); + + req_stop; + comply_scl_rise; + wait_stop_condition; + wait_done; + + req_clear; + wait until falling_edge(clk); + + check_equal(scl, 'H'); + + req_start; + wait_start_condition; + comply_scl_fall; + wait_done; + check_no_reqs; + end if; + end loop; + + test_runner_cleanup(runner); + end process main; + + set_scl_rising: process is + begin -- process scl_rising + wait until rising_edge(scl); + scl_rising <= '1'; + wait until falling_edge(clk); + wait until rising_edge(clk); + scl_rising <= '0'; + end process set_scl_rising; + + set_scl_falling: process is + begin -- process scl_rising + wait until falling_edge(scl); + scl_falling <= '1'; + wait until rising_edge(clk); + wait until falling_edge(clk); + scl_falling <= '0'; + end process set_scl_falling; + + set_delayed_scl_falling: process is + begin -- process scl_rising + wait until falling_edge(scl); + wait until falling_edge(clk); + scl_falling_delayed <= '1'; + wait until rising_edge(clk); + wait until falling_edge(clk); + scl_falling_delayed <= '0'; + end process set_delayed_scl_falling; + + set_start_condition: process is + begin -- process scl_rising + wait until falling_edge(sda); + if scl = 'H' then + start_condition <= '1'; + wait until rising_edge(clk); + wait until falling_edge(clk); + start_condition <= '0'; + end if; + end process set_start_condition; + + set_stop_condition: process is + begin -- process scl_rising + wait until rising_edge(sda); + if scl = 'H' then + stop_condition <= '1'; + wait until rising_edge(clk); + wait until falling_edge(clk); + stop_condition <= '0'; + end if; + end process set_stop_condition; + +end architecture tb; -- 2.49.0