Altera® AXI4 Bus Functional Model User Guides

ID 838773
Date 5/19/2025
Public
Document Table of Contents

2.4.4. Altera® AXI4 Streaming BFM RTL Example

The following top_tb.sv testbench example is uses the axi4st_tx_bfm and axi4st_rx_bfm BFMs generated from the Quartus® Prime Pro Edition software IP Catalog. For more information about creating simulation models and setup scripts, refer to Quartus® Prime Pro Edition User Guide: Third-party Simulation.

// top_tb.sv
`timescale 1 ps / 1 ps

import altera_lnsim_ver.axi4_stream_bfm_types_pkg::*;
import altera_lnsim_ver.axi4_stream_bytes_class_pkg::*;
import altera_lnsim_ver.axi4_stream_transfer_class_pkg::*;
import altera_lnsim_ver.axi4_stream_packet_class_pkg::*;

// For simplicity, defining hierarchical names of manager and subordinate BFMs
//<bfm IP inst>.axi4_stream_bfm_<transmitter/receiver>_0.axi4_stream_<tx/rx>_bfm 
`define  bfm_tx tx_bfm_inst.axi4_stream_bfm_transmitter_0.axi4_stream_tx_bfm
`define  bfm_rx rx_bfm_inst.axi4_stream_bfm_receiver_0.axi4_stream_rx_bfm

module top_tb ();

localparam real CLK_PERIOD = 10;

localparam DATA_BUS_WIDTH = 4;  // Data Bus Width in bytes. 
localparam TID_WIDTH = 8;       // TID width in bits. 
localparam TDEST_WIDTH = 4;     // TDEST width in bits. 
localparam TUSER_WIDTH = 12;    // TUSER width in bits. 

bit clk, rstn;

wire [0:0] tvalid;   
wire [0:0] tlast; 
wire [ 0:0] tready;
wire [DATA_BUS_WIDTH*8-1:0] tdata;
wire [DATA_BUS_WIDTH-1:0] tstrb;
wire [DATA_BUS_WIDTH-1:0] tkeep;
wire [TID_WIDTH-1:0] tid;
wire [TUSER_WIDTH*DATA_BUS_WIDTH-1:0] tuser;
wire [TDEST_WIDTH-1:0] tdest;

// start toggling clock
always begin
	#(CLK_PERIOD/2) clk = ~clk;
end

initial begin
	rstn = 0;
	#10;
	repeat (1) @(posedge clk);
	rstn = 1;

end

axi_st_tx_bfm tx_bfm_inst (
.tvalid (tvalid), // output, width = 1, altera_axi4_stream_transmit_if.tvalid
.tlast  (tlast),  // output, width = 1,                               .tlast
.tready (tready), // input,  width = 1,                               .tready
.tdata  (tdata),  // output, width = 32,                              .tdata
.tstrb  (tstrb),  // output, width = 4,                               .tstrb
.tkeep  (tkeep),  // output, width = 4,                               .tkeep
.tid    (tid),    // output, width = 8,                               .tid
.tuser  (tuser),  // output, width = 48,                              .tuser
.tdest  (tdest),  // output, width = 4,                               .tdest
.clk    (clk),    // input,  width = 1,                     clock_sink.clk
.rst_n  (rstn)    // input,  width = 1,                     reset_sink.reset_n
);

axi_st_rx_bfm rx_bfm_inst  (
.tvalid (tvalid),// input,  width = 1, altera_axi4_stream_receive_if.tvalid
.tlast  (tlast), // input,  width = 1,                              .tlast
.tready (tready),// output, width = 1,                              .tready
.tdata  (tdata), // input,  width = 32,                             .tdata
.tstrb  (tstrb), // input,  width = 4,                              .tstrb
.tkeep  (tkeep), // input,  width = 4,                              .tkeep
.tid    (tid),   // input,  width = 8,                              .tid
.tuser  (tuser), // input,  width = 48,                             .tuser
.tdest  (tdest), // input,  width = 4,                              .tdest
.clk    (clk),   // input,  width = 1,                    clock_sink.clk
.rst_n  (rstn)   // input,  width = 1,                    reset_sink.reset_n
);


// Transfers
Axi4StreamTransfer#(.AXI4_STREAMING_DATA_BUS_WIDTH(DATA_BUS_WIDTH),
.AXI4_STREAMING_TID_WIDTH(TID_WIDTH),
.AXI4_STREAMING_TDEST_WIDTH(TDEST_WIDTH),
.AXI4_STREAMING_TUSER_WIDTH(TUSER_WIDTH)) tx_tr, rx_tr;

// Transfer Queues - Stores tx and rx transfers for comparison at simulation end
Axi4StreamTransfer#(.AXI4_STREAMING_DATA_BUS_WIDTH(DATA_BUS_WIDTH),
.AXI4_STREAMING_TID_WIDTH(TID_WIDTH),
.AXI4_STREAMING_TDEST_WIDTH(TDEST_WIDTH),
.AXI4_STREAMING_TUSER_WIDTH(TUSER_WIDTH)) trq_tx[$], trq_rx[$]; //History Queue

// Packet handles.
Axi4StreamPacket#(.AXI4_STREAMING_DATA_BUS_WIDTH(DATA_BUS_WIDTH),
.AXI4_STREAMING_TID_WIDTH(TID_WIDTH),
.AXI4_STREAMING_TDEST_WIDTH(TDEST_WIDTH),
.AXI4_STREAMING_TUSER_WIDTH(TUSER_WIDTH)) p;	

byte_t byte_buf [];

Axi4StreamBytes#(TUSER_WIDTH)		    asb; //Super Class
Axi4StreamBytesData#(TUSER_WIDTH)		asbd;
Axi4StreamBytesPosition#(TUSER_WIDTH)	asbp;
Axi4StreamBytesNull#(TUSER_WIDTH)		asbn;
Axi4StreamBytes#(TUSER_WIDTH)		    ba[];  // Byte array
Axi4StreamBytes#(TUSER_WIDTH)		    q[$];  // Queue

int i;

string message;
string formatted_number;


initial begin

// Clear transfer queues 
trq_tx.delete();
trq_rx.delete();

// Transfer 1 - load from buffer
byte_buf = '{8'h00, 8'h11, 8'h22, 8'h33, 8'h44, 8'h55, 8'h66, 8'h77,
	8'h88, 8'h99, 8'hAA, 8'hBB, 8'hCC, 8'hDD, 8'hEE, 8'hFF,
	8'h10, 8'h11, 8'h12, 8'h13, 8'h14, 8'h45, 8'h16, 8'h17,
	8'h18, 8'h19, 8'h1A, 8'h1B, 8'h1C, 8'h1D, 8'h1E, 8'h1F};
		
	tx_tr = new();
	tx_tr.set_payload_from_data(byte_buf);
	tx_tr.set_gap(5);
	tx_tr.print_payload();
	
	// Load transfer into queue to compare at end of simulation
	trq_tx.push_back(tx_tr);
	
	// Create Packet
	p = new();
	// Add transfer to packet
	p.add_transfer_to_packet(tx_tr);

// Transfer 2 - load using queue
	q.delete();
	asbd = new(8'h00);
	asbd.set_tuser_value('hbcd);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h11);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h22);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h33);
	asb  = asbd;
	q.push_back(asb);	
	asbd = new(8'h44);
	asbd.set_tuser_value('h123);	
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h55);
	asbd.set_tuser_value('h456);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h66);
	asbd.set_tuser_value('h789);
	asb  = asbd;
	q.push_back(asb);	
	asbd = new(8'h77);
	asbd.set_tuser_value('habc);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h11);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h12);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h13);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h14);
	asb  = asbd;
	q.push_back(asb);
	//Position Byte
	asbp = new();
	asb  = asbp;
	q.push_back(asb);
	//Position Byte
	asbn = new();
	asb  = asbn;
	q.push_back(asb);
    asbd = new(8'h17);
	asb  = asbd;
	q.push_back(asb);
	asbd = new(8'h18);
	asbd.set_tuser_value(0);
	asb  = asbd;
	q.push_back(asb);

// transfer queue to byte array
	ba = new[q.size()];
	for(i=0; i<q.size(); i++)
	begin
		ba[i] = q[i];	
	end

	tx_tr = new();
	tx_tr.set_tdest('h7);
	tx_tr.set_tid('h14);
	tx_tr.set_payload_from_bytes(ba);
    tx_tr.print_transfer_short();
	
// Load transfer into queue to compare at end of simulation
	trq_tx.push_back(tx_tr);

// Add transfer to packet
	p.add_transfer_to_packet(tx_tr);
	
// Transmit packet
	`bfm_tx.transmit_bfm.put_packet_for_transmit(p);


	while (`bfm_rx.receive_bfm.get_number_of_transfers_in_queue() != trq_tx.size())
	begin
		@(posedge clk);
	end
		
	formatted_number = num_formatter(`bfm_rx.receive_bfm.get_number_of_transfers_in_queue(), "d");
	message = {"  Number of transfers in receive queue: ", formatted_number};
	$display("*****");
	$display("%s", message);
	$display("*****");

	while (`bfm_rx.receive_bfm.transfer_available())
	begin
		rx_tr = `bfm_rx.receive_bfm.get_transfer_from_receive();
		rx_tr.print_transfer_long();
		trq_rx.push_back(rx_tr);
	end
	  

  foreach(trq_rx[i])
  begin
	 if (trq_rx[i].is_not_equal(trq_tx[i]))
	 begin
		formatted_number = num_formatter(i, "d");
		message = {" Transaction Failed: ", formatted_number};
		$display("*****");
		$display("%s", message);
		$display("*****");
	 end
	 else
	 begin
		formatted_number = num_formatter(i, "d");
		message = {" Transaction Passed: ", formatted_number};
		$display("*****");
		$display("%s", message);
		$display("*****");
	 end
  end
end

endmodule

The following shows the simulation output from this example:

Figure 14. Streaming BFM RTL Example Simulation Output