library ieee; use ieee.std_logic_1164.all; library vunit_lib; context vunit_lib.vunit_context; library i2c; use work.tb_pkg.all; use work.tb_i2c_pkg.all; use work.tb_i2c_master_pkg.all; use work.tb_i2c_slave_pkg.all; entity master_tb is generic ( runner_cfg : string); end entity master_tb; architecture tb of master_tb is constant CLK_PERIOD : time := 10 ns; signal rst_n : std_logic := '0'; signal master_sda_enable : std_logic; signal master_sda : std_logic; signal slave_address : std_logic_vector(6 downto 0); signal master_scl_enable : std_logic; signal master_scl : std_logic; signal dev_busy, bus_busy : std_logic; signal err_noack_address, err_noack_data, err_arbitration, err_general : std_logic; signal rw : std_logic; signal master_stop, master_start, master_run : std_logic := '0'; signal waiting : std_logic; signal rx_confirm : std_logic := '0'; signal tx_valid : std_logic := '0'; signal tx_data : std_logic_vector(7 downto 0) := (others => '0'); signal one : std_logic := '1'; signal zero : std_logic := '0'; constant SCL_MIN_STABLE_CYCLES : natural := 10; constant TIMEOUT : time := SCL_MIN_STABLE_CYCLES * CLK_PERIOD * 2; signal stable_check : std_logic := '0'; begin -- architecture tb clk <= not clk after CLK_PERIOD / 2; rst_n <= '1' after 2 * CLK_PERIOD; sda <= 'H'; scl <= 'H'; sda <= '0' when master_sda_enable = '1' else 'Z'; scl <= '0' when master_scl_enable = '1' else 'Z'; master_sda <= '1' when sda = 'H' else sda; master_scl <= '1' when scl = 'H' else scl; uut : entity i2c.master generic map ( SCL_FALLING_DELAY => 1, SCL_MIN_STABLE_CYCLES => SCL_MIN_STABLE_CYCLES) port map ( clk_i => clk, rst_in => rst_n, -- slave_address_i => slave_address, -- generate_ack_i => '1', expect_ack_i => '1', -- rx_valid_o => rx_valid, rx_data_o => rx_data, rx_confirm_i => rx_confirm, -- tx_ready_o => tx_ready, tx_valid_i => tx_valid, tx_data_i => tx_data, tx_clear_buffer_i => '0', -- err_noack_data_o => err_noack_data, err_noack_address_o => err_noack_address, err_arbitration_o => err_arbitration, err_general_o => err_general, -- stop_i => master_stop, start_i => master_start, run_i => master_run, rw_i => rw, -- dev_busy_o => dev_busy, bus_busy_o => bus_busy, waiting_o => waiting, -- sda_i => master_sda, scl_i => master_scl, sda_enable_o => master_sda_enable, scl_enable_o => master_scl_enable); -- stable sda_enable when scl high -- TODO ensure active only when no start/stop -- conditions should be generated... -- sda_stability_check: check_stable(clk, one, scl, not_scl, master_sda_enable); -- sda_stable: check_stable(clk, stable_check, one, zero, master_sda); scl_stable: check_stable(clk, stable_check, one, zero, master_scl); main: process is procedure request_start( constant address : in std_logic_vector(6 downto 0); constant rw_i : in std_logic; constant stop : in std_logic := '0') is begin -- procedure request_start slave_address <= address; rw <= rw_i; master_start <= '1'; master_stop <= stop; master_run <= '1'; wait until falling_edge(clk); master_start <= '0'; master_stop <= '0'; end procedure request_start; procedure request_stop is begin -- procedure request_stop master_start <= '0'; master_stop <= '1'; master_run <= '0'; wait until falling_edge(clk); master_stop <= '0'; end procedure request_stop; procedure check_errors ( constant exp_noack_address : in std_logic := '0'; constant exp_noack_data : in std_logic := '0'; constant exp_arbitration : in std_logic := '0'; constant exp_general : in std_logic := '0') is begin -- procedure check_errors check_equal(err_noack_address, exp_noack_address, "Noack address error not as expected."); check_equal(err_noack_data, exp_noack_data, "Noack data error not as expected."); check_equal(err_arbitration, exp_arbitration, "Arbitration error not as expected."); check_equal(err_general, exp_general, "Gneral error not as expected."); end procedure check_errors; begin -- process main wait until rst_n = '1'; wait until falling_edge(clk); test_runner_setup(runner, runner_cfg); set_stop_level(failure); while test_suite loop if run("simple_read") then request_start("1110101", '1', stop => '1'); i2c_slave_check_start("1110101", '1', TIMEOUT, scl, sda); i2c_slave_transmit("11101010", TIMEOUT, scl => scl, sda => sda); rx_read_data("11101010", rx_confirm); check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("simple_write") then request_start("1110101", '0', stop => '1'); tx_write_data("11101010", tx_data, tx_valid); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); i2c_slave_receive("11101010", TIMEOUT, scl, sda); check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("multi_read") then request_start("1110101", '1'); i2c_slave_check_start("1110101", '1', TIMEOUT, scl, sda); i2c_slave_transmit("11101010", TIMEOUT, scl => scl, sda => sda); rx_read_data("11101010", rx_confirm); i2c_slave_transmit("00001111", TIMEOUT, scl => scl, sda => sda); rx_read_data("00001111", rx_confirm); i2c_slave_transmit("11110000", TIMEOUT, scl => scl, sda => sda); rx_read_data("11110000", rx_confirm); request_stop; check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("multi_write") then tx_write_data("11101010", tx_data, tx_valid); tx_write_data("00011100", tx_data, tx_valid); request_start("1110101", '0'); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); i2c_slave_receive("11101010", TIMEOUT, scl, sda); tx_write_data("00000000", tx_data, tx_valid); i2c_slave_receive("00011100", TIMEOUT, scl, sda); i2c_slave_receive("00000000", TIMEOUT, scl, sda); request_stop; check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("waiting_write_tx") then request_start("1110101", '0'); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); check_errors; wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); for i in 0 to 100 loop check_equal(waiting, '1', "Waiting wrong"); check_equal(scl, '0', "SCL wrong"); wait until falling_edge(clk); end loop; -- i check_errors; tx_write_data("00000000", tx_data, tx_valid); check_equal(waiting, '0'); i2c_slave_receive("00000000", 2 * TIMEOUT, scl, sda); request_stop; check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("waiting_stop") then tx_write_data("00000000", tx_data, tx_valid); request_start("1110101", '0'); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); i2c_slave_receive("00000000", 2 * TIMEOUT, scl, sda); wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); check_equal(waiting, '1'); request_stop; check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("waiting_right_away_stop") then request_start("1110101", '0', stop => '1'); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("write_read") then tx_write_data("11101010", tx_data, tx_valid); tx_write_data("00001111", tx_data, tx_valid); request_start("0001011", '0'); i2c_slave_check_start("0001011", '0', TIMEOUT, scl, sda); i2c_slave_receive("11101010", TIMEOUT, scl, sda); i2c_slave_receive("00001111", TIMEOUT, scl, sda); check_errors; request_start("0001011", '1', stop => '1'); i2c_slave_check_start("0001011", '1', 2 * TIMEOUT, scl, sda); check_errors; i2c_slave_transmit("00010101", TIMEOUT, scl, sda); rx_read_data("00010101", rx_confirm); check_errors; i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors; elsif run("lost_arbitration_early") then request_start("1110101", '0', stop => '1'); i2c_master_start("0000000", '0', scl, sda, exp_ack => '0'); check_errors(exp_arbitration => '1'); elsif run("lost_arbitration_late") then request_start("1110101", '0', stop => '1'); tx_write_data("11101010", tx_data, tx_valid); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); sda <= '0'; wait_for_scl_rise(timeout, scl); wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); scl_fall(scl); -- have to do manually (simulate another master) sda <= 'Z'; check_errors(exp_arbitration => '1'); elsif run("unexpected_start") then request_start("1110101", '0', stop => '1'); tx_write_data("11101010", tx_data, tx_valid); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); wait_for_scl_rise(timeout, scl); wait_for_scl_fall(timeout, scl); wait_for_scl_rise(timeout, scl); wait until falling_edge(clk); sda <= '0'; -- start wait until falling_edge(clk); wait until falling_edge(clk); wait until falling_edge(clk); check_errors(exp_general => '1'); elsif run("noack_address") then request_start("1110101", '0'); tx_write_data("11101010", tx_data, tx_valid); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda, ack => '0'); check_errors(exp_noack_address => '1'); i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors(exp_noack_address => '1'); elsif run("noack_data") then request_start("1110101", '0'); tx_write_data("11101010", tx_data, tx_valid); i2c_slave_check_start("1110101", '0', TIMEOUT, scl, sda); i2c_slave_receive("11101010", TIMEOUT, scl, sda, ack => '0'); check_errors(exp_noack_data => '1'); i2c_slave_check_stop(TIMEOUT, scl, sda); check_errors(exp_noack_data => '1'); elsif run("bus_busy") then check_equal(bus_busy, '0'); check_equal(dev_busy, '0'); i2c_master_start("0000000", '0', scl, sda, exp_ack => '0'); check_equal(bus_busy, '1', "Bus busy wrong."); check_equal(dev_busy, '0'); i2c_master_stop(scl, sda); wait until falling_edge(clk); wait until falling_edge(clk); check_equal(bus_busy, '0'); check_equal(dev_busy, '0'); check_errors; elsif run("bus_busy_start_request") then check_equal(bus_busy, '0'); check_equal(dev_busy, '0'); i2c_master_start("0000000", '0', scl, sda, exp_ack => '0'); request_start("1111100", '0'); check_equal(bus_busy, '1', "Bus busy wrong."); check_equal(dev_busy, '0'); i2c_master_stop(scl, sda); -- give bus away i2c_slave_check_start("1111100", '0', TIMEOUT, scl, sda); check_errors; elsif run("arbitration_lost_start_request") then check_equal(bus_busy, '0'); check_equal(dev_busy, '0'); request_start("1110101", '1'); i2c_master_start("0000000", '1', scl, sda, exp_ack => '0'); check_errors(exp_arbitration => '1'); check_equal(bus_busy, '1', "Bus busy wrong."); check_equal(dev_busy, '0'); request_start("1110101", '1', stop => '1'); i2c_master_transmit("11111111", scl, sda, exp_ack => '0'); i2c_master_stop(scl, sda); -- give bus away wait until falling_edge(clk); wait until falling_edge(clk); check_errors(exp_arbitration => '1'); i2c_slave_check_start("1110101", '1', TIMEOUT, scl, sda); i2c_slave_transmit("11111111", TIMEOUT, scl, sda); i2c_slave_check_stop(TIMEOUT, scl, sda); rx_read_data("11111111", rx_confirm); end if; end loop; stable_check <= '1'; wait for TIMEOUT * 10; test_runner_cleanup(runner); end process main; end architecture tb;