0

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.

Amr
  • 9
  • 5
  • Is there an [on-topic](https://stackoverflow.com/help/on-topic) question here? It appears to be missing a specific programming question. – user16145658 Mar 01 '22 at 16:24
  • @user16145658 I would say yes. The question is, "How do I structure my testbench to handle different size configurations of a design instance?" – Jim Lewis Mar 01 '22 at 16:45
  • @JimLewis Yes even given your unedited change to the question there's no specific programming question. You could have asked for clarification, here why two distinct component instantiations were used, presumably in a testbench. – user16145658 Mar 01 '22 at 21:55
  • Thanks for your replies and I have edited the question by adding more clarifications. – Amr Mar 02 '22 at 12:37
  • @Amr How many test cases are required to test a single width of your SPI project? – Jim Lewis Mar 03 '22 at 15:31
  • @JimLewis I use OSVVM lib for generating random test data and for every single width I loop 6 times. – Amr Mar 03 '22 at 17:39
  • @Amr - If you have just one test and that one test has just 6 sent-get operations, then create a single test harness. Use only one instance of the VC and design. Use a configuration to map the different sizes to the design. – Jim Lewis Mar 03 '22 at 17:55
  • @JimLewis I got your idea. Thank you so much for your help! – Amr Mar 03 '22 at 21:18
  • @JimLewis Do you have any idea how the SPI master's mode could be detected in my testbench for verification purposes? – Amr Mar 03 '22 at 22:11
  • @Amr You can make it a setting in your SPI Peripheral verification component. Generally speaking, if you create a command set that your VC reacts to, one of the commands can be set polarity. – Jim Lewis Mar 03 '22 at 22:59

1 Answers1

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

Jim Lewis
  • 3,601
  • 10
  • 20
  • A non-specific answer to a non-specific question with a bit of [self promoted product placement](https://meta.stackexchange.com/questions/57497/limits-for-self-promotion-in-answers/59302#59302) thrown in. See the answer to [How can I link to an external resource in a community-friendly way?](https://meta.stackexchange.com/questions/94022/how-can-i-link-to-an-external-resource-in-a-community-friendly-way/94027#94027) – user16145658 Mar 01 '22 at 21:55
  • @user16145658 It is an open source project with examples that provides further illumination of the implementation of the approach I discussed in my answer - I will allow it. As such, it seems to meeting the guidelines proposed in your links above. – Jim Lewis Mar 01 '22 at 22:07
  • A 'discussion' without foundation in the question. Should the OP accept your answer no future readers can benefit from why or how it was applied. You couldn't manage to articulate "..., how to solve the problem of having to test multiple data widths." without specifics. You didn't identify yourself or the other contributor to the resource nor is it clear how "**• someone could benefit from the answer without reading the linked item at all**". (For future readers as someone and the github linked item(s) in you answer.) – user16145658 Mar 02 '22 at 00:08
  • @user16145658 The only reasonable assumption to be made about having a SPI model with configurable widths (that is synthesizable) is that it has generics. Generics can either be mapped in the test harness in the generic instance or in a configuration. My solution suggests that if you use configurations, you end up with a huge number. OTOH, if you map them in the component instance in the test harness, and have multiple test harnesses, then you can solve it without having a huge number of instances. – Jim Lewis Mar 02 '22 at 04:21