I want to create an interface for AXI-stream with tasks for testbench for sending and receiving data. As far as I understand I have some issues with task visibility in the interface.
My AXI-stream interface so far looks like this, tb_axis.sv:
`timescale 1ns/1ns
import tb_axis_pkg::*;
interface AXIS #(
parameter integer TDATA_WIDTH = 32,
parameter integer TUSER_WIDTH = 32
) (
input logic CLK
);
logic TVALID;
logic TREADY;
logic TLAST;
logic [TDATA_WIDTH-1:0] TDATA;
logic [TUSER_WIDTH-1:0] TUSER;
modport slave (
input TVALID,
output TREADY,
input TLAST,
input TDATA,
input TUSER
);
modport master (
output TVALID,
input TREADY,
output TLAST,
output TDATA,
output TUSER
);
task automatic get_packet();
@ (posedge CLK);
TREADY <= 1'b1;
// write data to data_buffer object here
endtask
task automatic send(const ref data_buffer_c #(TDATA_WIDTH, TUSER_WIDTH) data_buffer);
for (int i = 0; i < data_buffer.size(); ++i) begin
@ (posedge CLK);
TVALID <= 1'b1;
TDATA <= data_buffer.axis_packet[i].tdata;
TUSER <= data_buffer.axis_packet[i].tuser;
TLAST <= (i == data_buffer.size() - 1) ? 1'b1 : 1'b0;
while (TREADY == 1'b0) @ (posedge CLK);
end
TVALID <= 1'b0;
endtask
endinterface
I have slave
and master
modports for different directions of the interface, and tasks get_packet
and send
.
Usage of the interface in a testbench, test_tb.sv:
`timescale 1ns/1ns
import tb_axis_pkg::*;
module test_tb ();
localparam int CLK_PERIOD = 5;
localparam int WAIT_AFTER_RESET = 10;
localparam int TEST_QTY = 1;
localparam int TDATA_WIDTH = 16;
localparam int TUSER_WIDTH = 16;
logic clk = 1'b0;
logic reset = 1'b1;
event new_iteration;
AXIS #(
.TDATA_WIDTH (TDATA_WIDTH),
.TUSER_WIDTH (TUSER_WIDTH)
) dut_in (clk);
AXIS #(
.TDATA_WIDTH (TDATA_WIDTH),
.TUSER_WIDTH (TUSER_WIDTH)
) dut_out (clk);
test_module DUT (
.clk (clk),
.reset (reset),
.AXIS_S (dut_in),
.AXIS_M (dut_out)
);
initial begin
forever begin
#(CLK_PERIOD);
clk <= ~clk;
end
end
//
initial begin
reset = 1'b1;
#(17);
reset = 1'b0;
repeat (WAIT_AFTER_RESET) @ (posedge clk);
repeat (TEST_QTY) begin
-> new_iteration;
@ (posedge clk);
end
end
// Slave
initial begin
data_buffer_c #(TDATA_WIDTH, TUSER_WIDTH) data_buffer;
forever begin
wait (new_iteration.triggered);
data_buffer = new();
for (int i = 0; i < 10; ++i) begin
data_buffer.push_back(i, 0);
end
dut_in.send(data_buffer);
end
end
// Master
initial begin
forever begin
wait (new_iteration.triggered);
dut_out.get_packet();
@ (posedge clk);
end
end
endmodule
to reproduce this example you also need
test_module.sv:
`timescale 1ns/1ns
module test_module (
//
input logic clk,
input logic reset,
//
AXIS.slave AXIS_S,
AXIS.master AXIS_M
);
assign AXIS_S.TREADY = !AXIS_M.TVALID | AXIS_M.TREADY;
always_ff @(clk) begin
if (reset == 1'b1) begin
AXIS_M.TVALID <= 1'b0;
AXIS_M.TLAST <= 1'b0;
end else begin
if (AXIS_S.TVALID == 1'b1 && AXIS_S.TREADY == 1'b1) begin
AXIS_M.TVALID <= 1'b1;
end else if (AXIS_M.TREADY == 1'b1) begin
AXIS_M.TVALID <= 1'b0;
end
if (AXIS_S.TVALID == 1'b1 && AXIS_S.TREADY == 1'b1) begin
AXIS_M.TLAST <= AXIS_S.TLAST;
end
end
begin
if (AXIS_S.TVALID == 1'b1 && AXIS_S.TREADY == 1'b1) begin
AXIS_M.TDATA <= AXIS_S.TDATA;
AXIS_M.TUSER <= AXIS_S.TUSER;
end
end
end
endmodule
and tb_axis_pkg.sv:
package tb_axis_pkg;
class data_buffer_c #(int TDATA_WIDTH = 0, int TUSER_WIDTH = 0);
typedef struct packed{
logic [TDATA_WIDTH-1:0] tdata;
logic [TUSER_WIDTH-1:0] tuser;
} axis_word_t;
axis_word_t axis_packet[$];
//
function automatic void push_back (input logic [TDATA_WIDTH-1:0] tdata, input logic[TUSER_WIDTH-1:0] tuser);
axis_word_t new_word;
new_word.tdata = tdata;
new_word.tuser = tuser;
this.axis_packet.push_back(new_word);
endfunction
//
function automatic int size();
return this.axis_packet.size();
endfunction
endclass
endpackage
During simulation I get an error:
# ** Error (suppressible): (vsim-12003) D:/Projects/SBR/Firmware/Fpga/source/test/test.sv(12): Variable '/test_tb/dut_in/TREADY' written by continuous and procedural assignments. See D:/Projects/SBR/Firmware/Fpga/source/library/tb_lib/tb_axis.sv(41).
# Time: 0 ns Iteration: 0 Instance: /test_tb/DUT File: D:/Projects/SBR/Firmware/Fpga/source/test/test.sv
As far as I understand, the reason for this error is the task get_packet
in dut_in
interface. So is there a way to specify task for modport
, so get_packet
was available only for dut_out
?
I get a similar compile error on a different simulator (Cadence):
assign AXIS_S.TREADY = !AXIS_M.TVALID | AXIS_M.TREADY;
|
xmelab: *E,ICDPAV: Illegal combination of driver and procedural assignment to variable
TREADY detected (procedural assignment found in task/function get_packet )
I don't think this error is related to a task
.
The error goes away when I change:
assign AXIS_S.TREADY = !AXIS_M.TVALID | AXIS_M.TREADY;
to:
always_comb AXIS_S.TREADY = !AXIS_M.TVALID | AXIS_M.TREADY;