2
\$\begingroup\$

A Stream Data Generator which can take data from both a file or just a counter. This is needed for me as a testbench component for interfaces which works on one hand as an AXIS slave and on the other hand as Ethernet.

A struct module

`timescale 1ns / 1ps

module axis_data_generator #
(
  parameter integer                       AXIS_DATA_WIDTH = 32,
  parameter [(AXIS_DATA_WIDTH / 8) - 1:0] AXIS_TKEEP      = 4'hf,
  parameter                               INIT_FILE       = "",
  parameter integer                       BURST_SIZE      = 1024
)
(
  input  wire                                clk_i,
  input  wire                                s_rst_n_i,
  input  wire                                enable_i,                    

  output wire  [AXIS_DATA_WIDTH - 1 : 0]     m_axis_tdata_o,
  output wire  [(AXIS_DATA_WIDTH / 8) - 1:0] m_axis_tkeep_o,
  output wire                                m_axis_tvalid_o,
  output wire                                m_axis_tlast_o,
  input  wire                                m_axis_tready_i
);

  wire                           counter_terminate;
  wire                           counter_enable;
  wire [AXIS_DATA_WIDTH - 1 : 0] data;
  wire [AXIS_DATA_WIDTH - 1 : 0] counter_value;

  COUNTER_TC_MACRO #
  (
    .COUNT_BY      (48'h1              ),
    .DEVICE        ("7SERIES"         ), 
    .DIRECTION     ("UP"              ),
    .RESET_UPON_TC ("TRUE"            ),
    .TC_VALUE      (BURST_SIZE        ),
    .WIDTH_DATA    (AXIS_DATA_WIDTH   )
  ) 
  tc_counter_inst_0 
  (
    .Q             (counter_value    ),
    .TC            (counter_terminate),
    .CLK           (clk_i            ),
    .CE            (counter_enable   ),
    .RST           (!s_rst_n_i       )
  );
  
  generate
    if ("" != INIT_FILE)
      begin : init_from_file
        BRAM_SINGLE_MACRO #
        (
          .BRAM_SIZE   ("18Kb"                 ), 
          .DEVICE      ("7SERIES"              ), 
          .DO_REG      (0                      ), 
          .INIT        ({AXIS_DATA_WIDTH{1'h0}}),
          .INIT_FILE   ("NONE"                 ),
          .WRITE_WIDTH (AXIS_DATA_WIDTH        ),
          .READ_WIDTH  (AXIS_DATA_WIDTH        ),
          .SRVAL       ({AXIS_DATA_WIDTH{1'h0}}),
          .WRITE_MODE  ("NO_CHANGE"            )
        ) 
        single_bram_inst_0
        (
          .DO    (data                   ),
          .ADDR  (counter_value          ),
          .CLK   (clk_i                  ),
          .DI    ({AXIS_DATA_WIDTH{1'h0}}),
          .EN    (enable_i               ),
          .REGCE (1'h0                   ),
          .RST   (!s_rst_n_i             ),
          .WE    (1'h0                   )
        );
      end
    else
      begin : data_from_counter_buf
        IBUF #
        (
          .IBUF_LOW_PWR ("TRUE"   ),  
          .IOSTANDARD   ("DEFAULT")  
        ) 
        ibuf_inst_0
        (
         .O (data         ),    
         .I (counter_value)     
        );
      end
  endgenerate

  axis_data_generator_cntr #
  (
    .AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
    .AXIS_TKEEP      (AXIS_TKEEP     )
  )
  (
    .clk_i              (clk_i            ),
    .s_rst_n_i          (s_rst_n_i        ),
    .enable_i           (enable_i         ),                    
                       
    .m_axis_tdata_o     (m_axis_tdata_o   ),
    .m_axis_tkeep_o     (m_axis_tkeep_o   ),
    .m_axis_tvalid_o    (m_axis_tvalid_o  ),
    .m_axis_tlast_o     (m_axis_tlast_o   ),
    .m_axis_tready_i    (m_axis_tready_i  ),
                       
    .data_i             (data             ),
    .counter_terminal_i (counter_terminate),
    .counter_enable_o   (counter_enable   )
  );
  
endmodule

axi stream controller module

`timescale 1ns / 1ps

module axis_data_generator_cntr #
(
  parameter integer                        AXIS_DATA_WIDTH = 32,
  parameter [(AXIS_DATA_WIDTH / 8) - 1:0]  AXIS_TKEEP      = 'hf
)
(
  input  wire                                clk_i,
  input  wire                                s_rst_n_i,
  input  wire                                enable_i,                    

  output wire  [AXIS_DATA_WIDTH - 1 : 0]     m_axis_tdata_o,
  output wire  [(AXIS_DATA_WIDTH / 8) - 1:0] m_axis_tkeep_o,
  output wire                                m_axis_tvalid_o,
  output wire                                m_axis_tlast_o,
  input  wire                                m_axis_tready_i,
  
  input  wire  [AXIS_DATA_WIDTH - 1 : 0]     data_i,
  input  wire                                counter_terminal_i,
  output wire                                counter_enable_o
);

  localparam integer               STATE_NUM   = 3;
  localparam integer               STATE_WIDTH = $clog2(STATE_NUM);
  
  localparam [STATE_WIDTH - 1 : 0] IDLE_STATE    = 0;
  localparam [STATE_WIDTH - 1 : 0] SENDING_STATE = 1;
  localparam [STATE_WIDTH - 1 : 0] STOP_STATE    = 2;  
  
  reg [STATE_WIDTH - 1 : 0] fsm_state;
  reg [STATE_WIDTH - 1 : 0] next_fsm_state;
  
  assign m_axis_tvalid_o  = enable_i;
  assign m_axis_tkeep_o   = AXIS_TKEEP;
  assign m_axis_tlast_o   = counter_terminal_i;
  
  assign counter_enable_o = ((SENDING_STATE == fsm_state) && (1'h1 == m_axis_tready_i));
  assign m_axis_tdata_o   = data_i;
  
  always @( posedge clk_i )
    begin
      if(1'h0 == s_rst_n_i)
        begin
          fsm_state      <= IDLE_STATE;
          next_fsm_state <= IDLE_STATE;
        end
      else
        begin
          fsm_state <= next_fsm_state;
        end
    end

  always @ (*)
    begin
      next_fsm_state = fsm_state;
      
      if (1'h1 == enable_i)
       begin
        case (fsm_state)
        IDLE_STATE:
          begin
            if (1'h1 == m_axis_tready_i) 
              begin
                next_fsm_state = SENDING_STATE;
              end
            end
          SENDING_STATE:
            begin
              if (1'h1 == counter_terminal_i)
                begin
                  next_fsm_state = STOP_STATE;
                end
            end
          STOP_STATE:
            begin
              next_fsm_state = IDLE_STATE;
            end
          endcase
        end
    end

endmodule

testbench

`timescale 1ns / 1ps

module axis_data_generator_cntr_tb;

  localparam integer                  DATA_WIDTH       = 32;
  localparam integer                  CLOCK_PERIOD     = 100;
  localparam integer                  PACK_SIZE        = 1024;
  localparam integer                  PACK_NUMBER      = 1024;
  localparam integer                  ITERATION_NUMBER = PACK_SIZE * PACK_NUMBER;
  localparam [DATA_WIDTH / 8 - 1 : 0] KEEP             = 'hf;
  
  wire                          counter_enable;
  wire                          terminate;
  wire                          axis_tvalid;
  wire                          axis_tlast;
  
  wire [DATA_WIDTH - 1 : 0]     axis_tdata;
  wire [DATA_WIDTH / 8 - 1 : 0] axis_tkeep;
 
  wire [DATA_WIDTH - 1 : 0]     data;
  
  reg                      axis_tready;
  
  reg                      clk;
  reg                      rst_n;
  reg                      enable;
  
  COUNTER_TC_MACRO #
  (
    .COUNT_BY      (48'h1     ),
    .DEVICE        ("7SERIES" ), 
    .DIRECTION     ("UP"      ),
    .RESET_UPON_TC ("TRUE"    ),
    .TC_VALUE      (PACK_SIZE ),
    .WIDTH_DATA    (DATA_WIDTH)
  ) 
  COUNTER_TC_MACRO_inst_0 
  (
    .Q             (data          ),
    .TC            (terminate     ),
    .CLK           (clk           ),
    .CE            (counter_enable),
    .RST           (!rst_n        )
  );

  axis_data_generator_cntr #
  (
    .AXIS_DATA_WIDTH (DATA_WIDTH),
    .AXIS_TKEEP      (KEEP      )
  )
  axis_data_generator_cntr_dut_0
  (
    .clk_i              (clk           ),
    .s_rst_n_i          (rst_n         ),
    .enable_i           (enable        ),                    
                     
    .m_axis_tdata_o     (axis_tdata    ),
    .m_axis_tkeep_o     (axis_tkeep    ),
    .m_axis_tvalid_o    (axis_tvalid   ),
    .m_axis_tlast_o     (axis_tlast    ),
    .m_axis_tready_i    (axis_tready   ),
                     
    .data_i             (data          ),
    .counter_terminal_i (terminate     ),
    .counter_enable_o   (counter_enable)
  );
  
  task check_data;
    begin
      enable <= 1'h1;
      rst_n  <= 1'h1;
      @(posedge clk);
    
      wait (axis_tvalid);
    
      repeat(100) 
        begin 
          if ({DATA_WIDTH{1'h0}} != axis_tdata)
            begin
              $display("The ready signal error.");
              $stop();
            end
          
           @(posedge clk); 
         end
    
      repeat(ITERATION_NUMBER) 
        begin 
          axis_tready <= $urandom % 2; 
          @(posedge clk); 
        end

      enable <= 1'h0;
    end
  endtask
  
  initial
    begin
      clk = 1'h0;

      forever
        begin
          #(CLOCK_PERIOD / 2) clk = !clk;
        end 
    end    

  initial
    begin
      rst_n  = 1'h0;
      enable = 1'h0;
      
      @(posedge clk);
      check_data;

      $display("The test has finished.");  

      $stop();            
    end


endmodule
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

The layout of code the follows good practices, and you have a clean separation between design and testbench.

There is a syntax error which your compiler should have detected. An instance name is required when you instantiate a module, but your axis_data_generator_cntr instance is missing a name. You should add something like axis_data_generator_cntr_0 below:

  axis_data_generator_cntr #
  (
    .AXIS_DATA_WIDTH (AXIS_DATA_WIDTH),
    .AXIS_TKEEP      (AXIS_TKEEP     )
  )
  axis_data_generator_cntr_0
  (
    .clk_i              (clk_i            ),

You should not assign to signals from multiple always blocks. The assignment to next_fsm_state should be removed from the sequential logic block:

  next_fsm_state <= IDLE_STATE;

While this might not cause problems during simulation, you will likely get warnings from synthesis.

In your testbench, it is a good practice to use case equality operators for comparisons when operands are 4-state values (and can have x or z). For example, change != to !== in the following line:

      if ({DATA_WIDTH{1'h0}} != axis_tdata)

It is also good practice to display the simulation time for your $display messages. For example:

          $display($time, " The ready signal error.");

It doesn't matter much in the code you posted since you immediately stop the simulation after each $display, but if you start displaying messages at different times, it will be helpful for debug.


$urandom is a system task which was introduced as part of the SystemVerilog extensions to the language (IEEE Std 1800). This means that your simulator supports SV to some degree. You could also consider exploring whether your tool chain supports other SV features.


You could also review my Answer to your previous Question regarding some more minor coding style and layout preferences.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I gratful you for your notices, it will be very useful for me. I use a vivado compiler and yes it missed the error as well as me. All which you indicated was corrected and now it should be better. Unfortunately, I am not able to refuse "if (1'h1 == some_net)". It is my curse... \$\endgroup\$
    – Drakonof
    Feb 17, 2021 at 12:52
  • \$\begingroup\$ @drakonof: Glad I could help. I find it useful to run my Verilog code on multiple simulators. You can get access to a few on edaplayground.com. You need to sign up for an account, but it is free. \$\endgroup\$
    – toolic
    Feb 17, 2021 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.