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
asked Feb 16, 2021 at 8:42
\$\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.

answered Feb 16, 2021 at 16:47
\$\endgroup\$
1
  • 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\$ Commented Feb 17, 2021 at 12:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.