vunit
vunit copied to clipboard
Axis bug with slave verification component

There is a problem with the verification component. When I try to push_axi_stream and pop_axi_stream the m_axis_tready_s goes low for no reason. I want to know is this is a bug from vunit verification component or if there is something wrong with the testbench or ip. I also put here the codes. That basically receive data and output this data on the next clock cycle following the axi stream protocol. The problem can be solved if I put 2 wait until rising_edge(clock_s) after the first time i push one data. And that is something that i dont understand. If you need more information please ask for it, i really want to know what is happening here.
---------------------------------------------------------- IP CODE---------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
entity axis_proba is generic ( -- Parameters of Axi Slave Bus Interface s_axis S_AXIS_DATA_WIDTH_G : integer; --:= 24; -- Parameters of Axi Master Bus Interface m_axis M_AXIS_DATA_WIDTH_G : integer --:= 24 ); port ( -- clock and reset clk_i : in std_logic; reset_i : in std_logic;
-- Ports of Axi Slave Bus Interface
s_axis_tvalid_i : in std_logic;
s_axis_tready_o : out std_logic;
s_axis_tlast_i : in std_logic;
s_axis_tdata_i : in std_logic_vector(S_AXIS_DATA_WIDTH_G-1 downto 0);
s_axis_tkeep_i : in std_logic_vector(S_AXIS_DATA_WIDTH_G/8-1 downto 0);
-- Ports of Axi Master Bus Interface
m_axis_tvalid_o : out std_logic;
m_axis_tready_i : in std_logic;
m_axis_tlast_o : out std_logic;
m_axis_tdata_o : out std_logic_vector(M_AXIS_DATA_WIDTH_G-1 downto 0);
m_axis_tkeep_o : out std_logic_vector(M_AXIS_DATA_WIDTH_G/8-1 downto 0)
);
end entity axis_proba;
architecture arch_imp of axis_proba is
signal s_axis_tready_s : std_logic := '1';
signal s_axis_tdata_old_s : std_logic_vector(S_AXIS_DATA_WIDTH_G-1 downto 0) := (others => '0');
signal m_axis_tvalid_s : std_logic := '0';
signal m_axis_tlast_s : std_logic := '1';
signal m_axis_tdata_s : std_logic_vector(M_AXIS_DATA_WIDTH_G-1 downto 0) := (others => '0');
signal m_axis_tkeep_s : std_logic_vector(M_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
begin
p_main : process(clk_i) is
begin
if rising_edge(clk_i) then
if (reset_i = '1') then
m_axis_tvalid_s <= '0';
m_axis_tlast_s <= '1';
m_axis_tdata_s <= (others => '0');
m_axis_tkeep_s <= (others => '1');
else
if (m_axis_tready_i = '1') then
-- reads the input in order to output it
m_axis_tvalid_s <= s_axis_tvalid_i;
m_axis_tlast_s <= s_axis_tlast_i;
m_axis_tdata_s <= s_axis_tdata_i;
m_axis_tkeep_s <= s_axis_tkeep_i;
end if;
end if;
end if;
end process p_main;
-- Outputs
m_axis_tvalid_o <= m_axis_tvalid_s;
s_axis_tready_o <= s_axis_tready_s;
m_axis_tlast_o <= m_axis_tlast_s;
m_axis_tdata_o <= m_axis_tdata_s;
m_axis_tkeep_o <= m_axis_tkeep_s;
end architecture arch_imp;
------------------------------------------------------------------TB_CODE-------------------------------------------------------------
-- Library instantiated, and then a declaration of files and functions to be use from it library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all;
-- A reference to VUnit (and related) set of libraries and packages (define library name first, or just reference it with "work") library vunit_lib; context vunit_lib.vunit_context; context vunit_lib.com_context; context vunit_lib.data_types_context; -- Library for AXI STREAM use vunit_lib.axi_stream_pkg.all; use vunit_lib.stream_master_pkg.all; use vunit_lib.stream_slave_pkg.all;
-- For VUnit approach, two generics are necessary in the testbench top entity (their names cannot be changed): -- encoded_tb_cfg: To make the encoded configuration accessible -- runner_cfg: Allows to controls the testbench from python script
entity tb_axis_proba is generic ( encoded_tb_cfg : string; runner_cfg : string -- := runner_cfg_default (if the VUnit testbench is used standalone [without Python]) ); end tb_axis_proba;
architecture Behavioral of tb_axis_proba is ----------------------------------------------------------------------------------------------- -- Record type definition to pack all configurable signals (from Python script) together --- ----------------------------------------------------------------------------------------------- type tb_cfg_t is record S_AXIS_DATA_WIDTH_G : integer; -- Parameters of Axi Slave Bus Interface s_axis M_AXIS_DATA_WIDTH_G : integer; -- Parameters of Axi Master Bus Interface m_axis NUM_PACKETS_G : integer; end record tb_cfg_t; -- Arrays declaration type array_slv_t is array (natural range <>) of std_logic_vector;
-----------------------------------------------------------------------------------------------
-- Function to decode the Python signals and connect them into the VHDL testbench ---
-----------------------------------------------------------------------------------------------
-- Decodes the Python signals and connect them into the VHDL testbench ---
impure function decode(encoded_tb_cfg : string) return tb_cfg_t is
begin
return (
S_AXIS_DATA_WIDTH_G => integer'value(get(encoded_tb_cfg, "S_AXIS_DATA_WIDTH_PY")),
M_AXIS_DATA_WIDTH_G => integer'value(get(encoded_tb_cfg, "M_AXIS_DATA_WIDTH_PY")),
NUM_PACKETS_G => integer'value(get(encoded_tb_cfg, "NUM_PACKETS_PY"))
);
end function decode;
-- Constant declaration of type "tb_cfg_t" and initialized by callind "decode()" function
constant tb_cfg : tb_cfg_t := decode(encoded_tb_cfg);
-- Constant handle for the AXI STREAM Master (VUnit) module
constant AXIS_MASTER_CONFIG_C : axi_stream_master_t := new_axi_stream_master(
data_length => tb_cfg.S_AXIS_DATA_WIDTH_G, -- Parameterize USING PYTHON
id_length => 1,
dest_length => 1,
user_length => 1,
logger => get_logger("master"),
actor => new_actor("master")
-- stall_config => master_stall_config,
-- monitor => default_axi_stream_monitor,
-- protocol_checker => default_axi_stream_protocol_checker
);
-- Constant handle for the AXI STREAM slave (VUnit) module
constant AXIS_SLAVE_CONFIG_C : axi_stream_slave_t := new_axi_stream_slave(
data_length => tb_cfg.M_AXIS_DATA_WIDTH_G, -- Parameterize USING PYTHON
id_length => 1,
dest_length => 1,
user_length => 1,
logger => get_logger("slave"),
actor => new_actor("slave")
-- stall_config => slave_stall_config
-- monitor => default_axi_stream_monitor,
-- protocol_checker => default_axi_stream_protocol_checker
);
-- Signals
signal clock_s : std_logic := '0';
signal reset_s : std_logic := '1';
signal s_axis_tvalid_s : std_logic := '0';
signal s_axis_tready_s : std_logic := '0';
signal s_axis_tlast_s : std_logic := '1';
signal s_axis_tdata_s : std_logic_vector(tb_cfg.S_AXIS_DATA_WIDTH_G-1 downto 0) := (others => '0');
signal s_axis_tkeep_s : std_logic_vector(tb_cfg.S_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
signal m_axis_tvalid_s : std_logic := '0';
signal m_axis_tready_s : std_logic := '0';
signal m_axis_tlast_s : std_logic := '1';
signal m_axis_tdata_s : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G-1 downto 0) := (others => '0');
signal m_axis_tkeep_s : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
--shared variables
shared variable data_arr_sv : array_slv_t(0 to tb_cfg.NUM_PACKETS_G-1)(tb_cfg.S_AXIS_DATA_WIDTH_G-1 downto 0) := (X"111111",X"222222",X"333333",X"444444",X"555555",X"666666",X"777777",X"888888",X"999999",X"000000");
-- Process ok flags for both the send and receive processes
signal flag_snd_start_rnd, flag_snd_done_rnd, flag_rcv_start_rnd, flag_rcv_done_rnd : boolean := false;
begin -- Clock and reset configuration clock_s <= not clock_s after 4 ns; reset_s <= '0' after 10 ns;
-----------------------------------------------------------------------------------------------
-- PROCESSES DECLARATION ---
-----------------------------------------------------------------------------------------------
-- Simulation MAIN process (if this one finishes, the whole simulation does it too, so synchronize this process with the others...
p_test_runner : process
begin
-- The process starts by setting up VUnit using the test_runner_setup procedure
test_runner_setup(runner, runner_cfg);
-- For synchronization purposes, it waits until reset signal is low
wait until falling_edge(reset_s);
-- To show even when the "check" statements have passed
-- show(get_logger(default_checker), display_handler, pass);
-- test_suite returns true and keep the while loop running as long as there are enabled test cases left to run...
while test_suite loop
if run("RANDOM") then
wait for 10 ns;
flag_snd_start_rnd <= true; wait for 1 ns; flag_snd_start_rnd <= false; -- Sending data process can start
flag_rcv_start_rnd <= true; wait for 1 ns; flag_rcv_start_rnd <= false; -- Receiving data process can start
wait until flag_rcv_done_rnd; -- Wait until sending and receiving processes have both finished
wait for 50 ns;
end if;
end loop;
-- The process ends with the test_runner_cleanup procedure which will force the simulation to stop
test_runner_cleanup(runner);
end process p_test_runner;
-- Set Watchdog with limited time
test_runner_watchdog(runner, 1 ms);
-- Generates and pushes random data
p_push_rnd : process
variable last_v : std_logic := '1';
variable keep_v : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
variable strb_v : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
variable id_v : std_logic_vector(0 downto 0) := (others => '0');
variable dest_v : std_logic_vector(0 downto 0) := (others => '0');
variable user_v : std_logic_vector(0 downto 0) := (others => '0');
begin
wait until flag_snd_start_rnd;
if (running_test_case = "RANDOM") then
info(to_string(running_test_case) & ": " & "SENDING PROCESS STARTS");
for i in 0 to tb_cfg.NUM_PACKETS_G-1 loop
-- push_axi_stream(net, AXIS_MASTER_CONFIG_C, data_arr_sv(i), last_v);
push_axi_stream(net, AXIS_MASTER_CONFIG_C, data_arr_sv(i), last_v, keep_v, strb_v, id_v, dest_v, user_v);
info(to_string(running_test_case) & ": " & " SND: AXI Stream request " & " sent out!");
end loop;
info(to_string(running_test_case) & ": " & "SENDING PROCESS FINISHED");
flag_snd_done_rnd <= true; wait for 1 ns; flag_snd_done_rnd <= false;
end if;
end process p_push_rnd;
-- Receives and checks random data
p_check_rnd : process
variable last_v : std_logic := '1';
variable data_v : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G-1 downto 0) := (others => '0');
variable keep_v : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
variable strb_v : std_logic_vector(tb_cfg.M_AXIS_DATA_WIDTH_G/8-1 downto 0) := (others => '1');
variable id_v : std_logic_vector(0 downto 0) := (others => '0');
variable dest_v : std_logic_vector(0 downto 0) := (others => '0');
variable user_v : std_logic_vector(0 downto 0) := (others => '0');
begin
wait until flag_rcv_start_rnd;
info(to_string(running_test_case) & ": " & "RECEIVING PROCESS STARTS");
-- Receives all packets
for i in 0 to tb_cfg.NUM_PACKETS_G-2 loop
-- Blocking function, until valid data is received from the UUT
pop_axi_stream(net, AXIS_SLAVE_CONFIG_C, data_v, last_v, keep_v, strb_v, id_v, dest_v, user_v);
end loop;
info(to_string(running_test_case) & ": " & "RECEIVING PROCESS FINISHED");
flag_rcv_done_rnd <= true; wait for 1 ns; flag_rcv_done_rnd <= false;
end process p_check_rnd;
-----------------------------------------------------------------------------------------------
-- IPs INSTANTIATION ---
-----------------------------------------------------------------------------------------------
i_axis_master_vc : entity vunit_lib.axi_stream_master
generic map(
master => AXIS_MASTER_CONFIG_C
)
port map(
aclk => clock_s,
areset_n => not reset_s,
tvalid => s_axis_tvalid_s,
tready => s_axis_tready_s,
tdata => s_axis_tdata_s,
tlast => s_axis_tlast_s,
tkeep => s_axis_tkeep_s
--tstrb => (others => '0'),
--tid => (others => '0'),
--tuser => (others => '0'),
--tdest => (others => '0')
);
i_UUT : entity vunit_lib.axis_proba
generic map(
S_AXIS_DATA_WIDTH_G => tb_cfg.S_AXIS_DATA_WIDTH_G,
M_AXIS_DATA_WIDTH_G => tb_cfg.M_AXIS_DATA_WIDTH_G
)
port map(
clk_i => clock_s,
reset_i => reset_s,
s_axis_tvalid_i => s_axis_tvalid_s,
s_axis_tready_o => s_axis_tready_s,
s_axis_tlast_i => s_axis_tlast_s,
s_axis_tdata_i => s_axis_tdata_s,
s_axis_tkeep_i => s_axis_tkeep_s,
m_axis_tvalid_o => m_axis_tvalid_s,
m_axis_tready_i => m_axis_tready_s,
m_axis_tlast_o => m_axis_tlast_s,
m_axis_tdata_o => m_axis_tdata_s,
m_axis_tkeep_o => m_axis_tkeep_s
);
i_axis_slave_vc : entity vunit_lib.axi_stream_slave
generic map(
slave => AXIS_SLAVE_CONFIG_C
)
port map(
aclk => clock_s,
areset_n => not reset_s,
tvalid => m_axis_tvalid_s,
tready => m_axis_tready_s,
tdata => m_axis_tdata_s,
tlast => m_axis_tlast_s,
tkeep => m_axis_tkeep_s
--tstrb => (others => '0'),
--tid => (others => '0'),
--tuser => (others => '0'),
--tdest => (others => '0')
);
end architecture Behavioral;
-------------------------------------------------------PYTHON CODE------------------------------------------------------
***********************************************************************************
************************* VUNIT AND LIBRARY INITIALIZATION *************************
***********************************************************************************
Operation system and regular expression interfaces
import os, re
To know the current path and work with it
from os.path import join, dirname
The public interface of VUnit
from vunit import VUnit
Computes the cartesian product of input iterables
from itertools import product
Returns the directory name where the present file (run.py) is located
root = dirname(file)
Create VUnit instance by parsing command line arguments
vu_inst = VUnit.from_argv()
Add verification component library
vu_inst.add_verification_components()
Add communication package
vu_inst.add_com()
Creating library XPM (from Xilinx) and adding all source files (necessary for simulation with ModelSim)
xpm = vu_inst.add_library("xpm")
xpm.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/external_libs/vhdl_code/xpm/*.vhd"))
xpm.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/external_libs/vhdl_code/xpm/xpm_cdc/*.vhd"))
xpm.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/external_libs/vhdl_code/xpm/xpm_fifo/*.vhd"))
xpm.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/external_libs/vhdl_code/xpm/xpm_memory/*.vhd"))
Creating library UNISIM (from Xilinx) and adding all source files (necessary for simulation with ModelSim)
unisim = vu_inst.add_library("unisim")
unisim.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/external_libs/vhdl_code/unisim/*.vhd"))
unisim.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/external_libs/vhdl_code/unisim/primitive/*.vhd"))
Create library "vunit_lib"
vunit_lib = vu_inst.library("vunit_lib")
Add all necessary packages into VUnit library
vunit_lib.add_source_files(join(root, "../../../_packages/fpga-internal-components/sources/packages/*.vhd"))
Add all necessary testbenches into VUnit library
vunit_lib.add_source_files(join(root, "tb_axis_proba.vhd"))
Add all necessary sources into VUnit library
vunit_lib.add_source_files(join(root, "../axis_proba.vhd"))
***********************************************************************************
****************************** FUNCTIONS DECLARATION ******************************
***********************************************************************************
To encode the parameters, the script must contain the encode function
def encode(tb_cfg): return ",".join(["%s:%s" % (key, str(tb_cfg[key])) for key in tb_cfg])
A list of parameters are defined, then saved and finally encoded to be in the testbench
def gen_demodulation_core_tests(obj, s_axis_tdata_width, m_axis_tdata_width, num_packets): for s_axis_tdata_width, m_axis_tdata_width, num_packets in product(s_axis_tdata_width, m_axis_tdata_width, num_packets): tb_cfg=dict( S_AXIS_DATA_WIDTH_PY = s_axis_tdata_width, # Parameters of Axi Slave Bus Interface s_axis M_AXIS_DATA_WIDTH_PY = m_axis_tdata_width, # Parameters of Axi Master Bus Interface m_axis NUM_PACKETS_PY = num_packets # Number of inputs for the vunit testbench ) config_name=encode(tb_cfg) obj.add_config(name=config_name, generics=dict(encoded_tb_cfg=encode(tb_cfg)))
NOTE: In case parameters are sent to the testbench, VUnit requires at least 2 elements...
***********************************************************************************
****************************** GENERATE TESTBENCHES *******************************
***********************************************************************************
Execute the (previously defined in library) testbench with a specific input parameters configuration.
These parameters remain fixed for the whole testbench execution, which means they are thought to be the
GENERIC (or constants) values in the top module (and auxiliar modules) instantiation, not Testbench itself...
Everytime a new testbench is here requested (it can be the same with different parameters),
all test cases in the VHDL testbench file will be executed again.
tb_axis_proba = vunit_lib.test_bench("tb_axis_proba") for test in tb_axis_proba.get_tests(): # The complete name of the case must be written here. # More than one value as a variable can be set at a time (which means this testbench is called different times) gen_demodulation_core_tests (test, [24], [24], [10])
NOTE: For more than 1271 test cases, VUnit complains about too many arguments and dies...
***********************************************************************************
********************************** MAIN FUNCTION **********************************
***********************************************************************************
vu_inst.main()