From a41330924e3b5a48d4590ac912ea56b094bcd423 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 4 Jan 2024 22:24:51 +0100 Subject: [PATCH] feat: prepare i2c slave tb model --- tb/i2c/scl_generator_tb.vhd | 4 +- tb/i2c/tb_i2c_pkg.vhd | 56 +++++++++++++++ tb/i2c/tb_i2c_slave_pkg.vhd | 138 ++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 tb/i2c/tb_i2c_slave_pkg.vhd diff --git a/tb/i2c/scl_generator_tb.vhd b/tb/i2c/scl_generator_tb.vhd index 92fb950..1adc3f1 100644 --- a/tb/i2c/scl_generator_tb.vhd +++ b/tb/i2c/scl_generator_tb.vhd @@ -161,7 +161,7 @@ begin -- architecture tb stable_cannot_comply <= '0'; wait_delay(1); - check_equal(cannot_comply, '1'); + check_equal(cannot_comply, '1', "Wrong cannot comply"); elsif run("falling_rising_scl_low") then req_falling; wait_delay(DELAY); @@ -170,7 +170,7 @@ begin -- architecture tb stable_cannot_comply <= '0'; wait_delay(DELAY); - check_equal(cannot_comply, '1'); + check_equal(cannot_comply, '1', "Wrong cannot comply"); check_equal(scl_enable, '0'); wait_delay(DELAY); check_equal(scl_enable, '0'); diff --git a/tb/i2c/tb_i2c_pkg.vhd b/tb/i2c/tb_i2c_pkg.vhd index 096bbc6..3d2da66 100644 --- a/tb/i2c/tb_i2c_pkg.vhd +++ b/tb/i2c/tb_i2c_pkg.vhd @@ -41,6 +41,24 @@ package tb_i2c_pkg is constant exp_data : in std_logic_vector(7 downto 0); signal rx_confirm_read : inout std_logic); + procedure wait_for_start_condition ( + constant timeout : in time; + signal scl : in std_logic; + signal sda : in std_logic); + + procedure wait_for_stop_condition ( + constant timeout : in time; + signal scl : in std_logic; + signal sda : in std_logic); + + procedure wait_for_scl_rise ( + constant timeout : in time; + signal scl : in std_logic); + + procedure wait_for_scl_fall ( + constant timeout : in time; + signal scl : in std_logic); + end package tb_i2c_pkg; package body tb_i2c_pkg is @@ -128,4 +146,42 @@ package body tb_i2c_pkg is rx_confirm_read <= '0'; end procedure rx_read_data; + procedure wait_for_start_condition ( + constant timeout : in time; + signal scl : in std_logic; + signal sda : in std_logic) is + begin + wait until falling_edge(sda) and scl = 'H' for timeout; + check(sda = '0' and scl = 'H', "Did not get start condition in time."); + end procedure wait_for_start_condition; + + procedure wait_for_stop_condition ( + constant timeout : in time; + signal scl : in std_logic; + signal sda : in std_logic) is + begin + wait until rising_edge(sda) and scl = 'H' for timeout; + check(sda = '1' and scl = 'H', "Did not get stop condition in time."); + end procedure wait_for_stop_condition; + + procedure wait_for_scl_rise ( + constant timeout : in time; + signal scl : in std_logic) is + begin + wait until scl = 'H' for timeout; + check_equal(scl, 'H', "Did not get rising scl in time."); + wait until falling_edge(clk); + end procedure wait_for_scl_rise; + + + procedure wait_for_scl_fall ( + constant timeout : in time; + signal scl : in std_logic) is + begin + wait until scl = '0' for timeout; + check_equal(scl, '0', "Did not get falling scl in time."); + wait until falling_edge(clk); + end procedure wait_for_scl_fall; + + end package body tb_i2c_pkg; diff --git a/tb/i2c/tb_i2c_slave_pkg.vhd b/tb/i2c/tb_i2c_slave_pkg.vhd new file mode 100644 index 0000000..0897cea --- /dev/null +++ b/tb/i2c/tb_i2c_slave_pkg.vhd @@ -0,0 +1,138 @@ +library ieee; +use ieee.std_logic_1164.all; + +library vunit_lib; +-- use vunit_lib.check_pkg.all; +context vunit_lib.vunit_context; + +use work.tb_pkg.all; +use work.tb_i2c_pkg.all; + +package tb_i2c_slave_pkg is + + procedure i2c_slave_check_start ( + constant address : in std_logic_vector(6 downto 0); + constant rw : in std_logic; + constant timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic; + constant ack : in std_logic := '1'); + + procedure i2c_slave_check_stop ( + constant timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic); + + procedure i2c_slave_transmit ( + constant data : in std_logic_vector(7 downto 0); + constant scl_timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic; + constant exp_ack : in std_logic := '1'); + + procedure i2c_slave_receive ( + constant exp_data : in std_logic_vector(7 downto 0); + constant scl_timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic; + constant ack : in std_logic := '1'); + +end package tb_i2c_slave_pkg; + +package body tb_i2c_slave_pkg is + + procedure i2c_slave_check_start ( + constant address : in std_logic_vector(6 downto 0); + constant rw : in std_logic; + constant timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic; + constant ack : in std_logic := '1') is + begin + wait_for_start_condition(timeout, scl, sda); + i2c_slave_receive(address & rw, timeout, scl, sda, ack); + end procedure i2c_slave_check_start; + + procedure i2c_slave_check_stop ( + constant timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic) is + begin + wait_for_stop_condition(timeout, scl, sda); + end procedure i2c_slave_check_stop; + + procedure i2c_slave_transmit ( + constant data : in std_logic_vector(7 downto 0); + constant scl_timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic; + constant exp_ack : in std_logic := '1') is + + begin -- procedure transmit + report "Start slave transmit"; + if scl = 'H' then + wait_for_scl_fall(scl_timeout, scl); + end if; + + -- data + for i in 7 downto 0 loop + report "Data " & integer'image(i) & " scl is: " & std_logic'image(scl); + wait until falling_edge(clk); + sda <= '0' when data(i) = '0' else 'Z'; + wait_for_scl_rise(scl_timeout, scl); + wait_for_scl_fall(scl_timeout, scl); + end loop; -- i + + wait until falling_edge(clk); + sda <= 'Z'; + wait_for_scl_rise(scl_timeout, scl); + + report "Ack slave transmit"; + if exp_ack = '1' then + check_equal(sda, '0', "No acknowledge"); + elsif exp_ack = '0' then + check_equal(sda, 'H', "There was acknowledge even though there shouldn't have been"); + end if; + + -- TODO consider removing this? + wait_for_scl_fall(scl_timeout, scl); + wait until falling_edge(clk); + sda <= 'Z'; + end procedure i2c_slave_transmit; + + procedure i2c_slave_receive ( + constant exp_data : in std_logic_vector(7 downto 0); + constant scl_timeout : in time; + signal scl : inout std_logic; + signal sda : inout std_logic; + constant ack : in std_logic := '1') is + + begin -- procedure transmit + if scl = 'H' then + wait_for_scl_fall(scl_timeout, scl); + end if; + + sda <= 'Z'; + + -- data + for i in 7 downto 0 loop + wait_for_scl_rise(scl_timeout, scl); + if exp_data(i) = '1' then + check(sda = '1' or sda = 'H', result("Received data (sda) not as expected.")); + else + check(sda = '0' or sda = 'L', result("Received data (sda) not as expected.")); + end if; + wait_for_scl_fall(scl_timeout, scl); + end loop; -- i + + if ack = '1' then + sda <= '0'; + end if; + + wait_for_scl_rise(scl_timeout, scl); + wait_for_scl_fall(scl_timeout, scl); + + sda <= 'Z'; + end procedure i2c_slave_receive; + +end package body tb_i2c_slave_pkg; -- 2.49.0