'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