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;