'How to use only one DUT for different test cases in a VHDL testbench and how to detect SPI master's mode on the slave side?
First I would like to mention that I'm still new to the VHDL World so please bear with me.
Currently, I'm working on an SPI project where I created a generic master module that can send and receive different data widths ranging from 8 to 64 bits. Everything is working fine in my testbench regarding sending and receiving data between the master and the slave.
P.S. The master is synthesizable while the slave is just for simulation purposes.
In the testbench I instantiated many DUTs from the master and the slave, for instance, I created DUT_00 for testing 8bit data width, DUT_01 for 11bit data with, etc. As shown below:
Signal declaration for DUT_00 in my testbench
---------------------------------------------------------------------------------------------------------------------
-- DUT0 Signals
---------------------------------------------------------------------------------------------------------------------
constant data_width_8bit : positive range 8 to 64 := 8; --! data width from the master to the slave
--shared variable s_data_width : positive range 8 to 64 := data_width_00; --! data width from the master to the slave
constant bw_g : positive range 8 to 24 := 24; --! width of baud rate configuration port cbr_i
constant inv_g : natural range 0 to 1 := 0; --! 0: master and slave sample on same edge, 1: master samples on opposite edge as slave
signal clk_tb : std_logic:= '0'; --! system clock
signal rst_tb : std_logic:= '0'; --! system reset synchronous, high activsen_i
signal cbr_0tb :std_logic_vector(bw_g-1 downto 0):= x"00_0001"; --! clock baud rate configuration
signal cpol_0tb :std_logic:= '0'; --! clock polarity selection
signal cpha_0tb :std_logic :='0'; --! clock phase selection
signal sen_0tb :std_logic:='0'; --! slave enable : 1 => slave enabled, 0 => slave disabled
signal sts_0tb :std_logic:='0'; --! slave transfer start (unit pulse)
signal sbusy_0tb :std_logic; --! slave status: 1 => SPI transaction ongoing, 0 => idle
signal stv_0tb :std_logic; --! slave transfer valid (unit pulse)
signal dts_0tb :std_logic_vector(data_width_8bit-1 downto 0):= (others => '0'); --! parallel data to slave
signal dfs_0tb :std_logic_vector(data_width_8bit-1 downto 0); --! parallel data from slave
signal ss_0tb :std_logic; --! slave selection
signal spi_clk_0tb :std_logic; --! master clock
signal mosi_0tb :std_logic; --! serial data to slave (Master Output Slave Input)
signal miso_0tb :std_logic; --! serial data from slave (Master Input Slave Output)
---------------------------------------------------------------------------------------------------------------------
-- Slave Signals
---------------------------------------------------------------------------------------------------------------------
signal dfm_0tb :std_logic_vector(data_width_8bit-1 downto 0); --! parallel data from master
signal dfm_val_0tb :std_logic; --! valid pulse for data from master
signal dtm_0tb :std_logic_vector(data_width_8bit-1 downto 0):= (others => '0'); --! parallel data to master
signal busy_tx_0tb :std_logic; --! slave busy,Do not write data while high
signal dtm_val_0tb :std_logic; --! valid pulse for data to the master
signal sum_0tb :std_logic_vector(data_width_8bit-1 downto 0):= (others => '0'); --! bit counter for the data to the master
DUT_00 instantiation
DUT_Gen: for i in 0 to 4 generate
--! @brief instantiation of SPI master entity
DUT_00: if i = 0 generate
master_00: entity work.SPI_MASTER(rtl_SPI_MASTER)
generic map (
nb_g => data_width_8bit,
bw_g => bw_g,
inv_g => inv_g)
-- Master Ports Mapping
port map(
-------------------------------Master I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
-- HOST IF
cbr_i => cbr_0tb,
cpol_i => cpol_0tb,
cpha_i => cpha_0tb,
-- Control
sen_i => sen_0tb,
sts_i => sts_0tb,
sbusy_o => sbusy_0tb ,
stv_o => stv_0tb,
-- Data
dts_i => dts_0tb,
dfs_o => dfs_0tb,
-- SPI IF
spi_ss_o => ss_0tb,
spi_clk_o => spi_clk_0tb,
spi_mosi_o => mosi_0tb,
spi_miso_i=> miso_0tb );
slave_00: entity work.SPI_SLAVE(beh_SPI_SLAVE)
generic map (
nb_g => data_width_8bit
-- bw_g => bw_g
)
-- Slave Ports Mapping
port map(
-------------------------------Slave I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
cpol_i => cpol_0tb,
cpha_i => cpha_0tb,
sck_i => spi_clk_0tb,
mosi_i => mosi_0tb,
miso_o => miso_0tb,
cs_i => ss_0tb,
dfm_o => dfm_0tb,
dfm_val_o => dfm_val_0tb,
dtm_i => dtm_0tb,
busy_tx_o => busy_tx_0tb,
dtm_val_o => dtm_val_0tb,
sum_o => sum_0tb
-- sck_frq_o => sck_frq_0tb
);
end generate DUT_00;
DUT_01 instanstation
--! @brief instantiation of SPI master entity
DUT_01: if i = 1 generate
master_01: entity work.SPI_MASTER(rtl_SPI_MASTER)
generic map (
nb_g => data_width_11bit,
bw_g => bw_g,
inv_g => inv_g)
-- Master Ports Mapping
port map(
-------------------------------Master I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
-- HOST IF
cbr_i => cbr_1tb,
cpol_i => cpol_1tb,
cpha_i => cpha_1tb,
-- Control
sen_i => sen_1tb,
sts_i => sts_1tb,
sbusy_o => sbusy_1tb ,
stv_o => stv_1tb,
-- Data
dts_i => dts_1tb,
dfs_o => dfs_1tb,
-- SPI IF
spi_ss_o => ss_1tb,
spi_clk_o => spi_clk_1tb,
spi_mosi_o => mosi_1tb,
spi_miso_i=> miso_1tb );
slave_01: entity work.SPI_SLAVE(beh_SPI_SLAVE)
generic map (
nb_g => data_width_11bit
-- bw_g => bw_g
)
-- Slave Ports Mapping
port map(
-------------------------------Slave I/O pins------------------------------------------------------
clk_i => clk_tb,
rst_i => rst_tb,
cpol_i => cpol_1tb,
cpha_i => cpha_1tb,
sck_i => spi_clk_1tb,
mosi_i => mosi_1tb,
miso_o => miso_1tb,
cs_i => ss_1tb,
dfm_o => dfm_1tb,
dfm_val_o => dfm_val_1tb,
dtm_i => dtm_1tb,
busy_tx_o => busy_tx_1tb,
dtm_val_o => dtm_val_1tb,
sum_o => sum_1tb
-- sck_frq_o => sck_frq_1tb
);
end generate DUT_01;
My first question is, instead of creating many DUTs for every different data width, how can I only use one DUT that can handle different data sizes also different baud rates?
Second, how can I make the slave detect the mode that has been used by the master when it sends data without telling the slave explicitly which Phase and polarity have been used?
Sorry if my question wasn't well-stated at the beginning.
Solution 1:[1]
Have you written a test plan for your SPI project? How many test cases are required to test a single data width of your SPI project? Once you determine that, you can decide how to solve the problem of having to test multiple data widths.
How many of your data widths do you want to test?
One approach is to create a testbench that instantiates a single SPI module and then use a configuration to specify the generic that sizes the SPI module. Your test cases would also need to have this generic so they could adapt what they do for different sizes.
Unfortunately when there are a large number of test cases, configurations tend to explode - meaning you end up with alot of them. Here the number of configurations you would need is #DataWidthsTested * #TestCases.
So if you restructure your testbench to separate your test cases from your test harness, you can instead create #DataWidthsTested variations of your test harness and have run each of the test cases against that.
If you would like a more detailed example of this, OSVVM has two variations of our verification components - AxiStreamTransmitter and AxiStreamTransmitterVti. They are the same inside. The only thing that is different is how the external connections are done. As a result, we create two separate directories for the test harnesses, testbench (which instantiates AxiStreamTransmitter) and testbenchVti (which instantiates AxiStreamTransmitterVti). Care is taken so that both instances have the same instance label and same port names.
The test cases are written in a way that either component can be used. There are 63 test cases. In our methodology we prefer to use configurations to run our test cases - that means we have 63 configurations. Note the configurations only select the test case to be run and not which version of the AxiStreamTransmitter to be use (it is left to default bind). We have a script that compiles and runs each test case.
At a higher level, we have a script that compiles (RunAllTests.pro vs RunAllTestsVti.pro) that calls the script to compile the test harness and then calls the script to compile and run the test cases. By putting each test harness in a separate library we are able to safely run each test case with the intended test harness.
For more see the OSVVM repository on GitHub. It starts at: https://github.com/OSVVM/OsvvmLibraries It needs to be cloned recursively as there are submodules. The AxiStream examples are in the directory OsvvmLibraries/AXI4/AxiStream. To get started with OSVVM, see the README.rst which is displayed at the bottom of the page of https://github.com/OSVVM/Documentation
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
