I want to create an interface that can be used for modules connection (using modport) and for driving DUT from testbench (with clocking blocks). It seems to me that it should be trivial thing to do, but I could do it without warnings. I guess I don’t quite understand how to use clocking block within interface.
So far for interface I have:
`timescale 10ns/1ns
interface AXIS_if #(
parameter TDATA_WIDTH = 32,
parameter TUSER_WIDTH = 32
) (
input logic CLK
);
logic TVALID;
logic TREADY;
logic TLAST;
logic [TDATA_WIDTH-1:0] TDATA;
logic [TUSER_WIDTH-1:0] TUSER;
clocking m_cb @ (posedge CLK);
input TREADY;
output TVALID;
output TLAST;
output TDATA;
output TUSER;
endclocking
clocking s_cb @ (posedge CLK);
output TREADY;
input TVALID;
input TLAST;
input TDATA;
input TUSER;
endclocking
modport master (
input TREADY,
output TVALID,
output TLAST,
output TDATA,
output TUSER
);
modport slave (
output TREADY,
input TVALID,
input TLAST,
input TDATA,
input TUSER
);
modport master_drv (
clocking m_cb
);
modport slave_drv (
clocking s_cb
);
endinterface
for testbench driver:
`timescale 10ns/1ns
package axis_pkg;
class axis_m_driver #(parameter int TDATA_WIDTH = 8, parameter int TUSER_WIDTH = 8);
virtual AXIS_if#(.TDATA_WIDTH(TDATA_WIDTH), .TUSER_WIDTH(TUSER_WIDTH)).master_drv axis_vif;
function new(virtual AXIS_if#(.TDATA_WIDTH(TDATA_WIDTH), .TUSER_WIDTH(TUSER_WIDTH)) axis_vif);
this.axis_vif = axis_vif;
endfunction
task send_pkt (ref logic [TDATA_WIDTH-1:0] data_buf[], ref logic [TUSER_WIDTH-1:0] user_buf[]);
logic last = 1'b0;
int buf_idx = 0;
int buf_size = data_buf.size();
while (1) begin
if (buf_idx == buf_size) begin
axis_vif.m_cb.TVALID <= 1'b0;
break;
end
last = (buf_idx == buf_size - 1) ? 1'b1 : 1'b0;
axis_vif.m_cb.TVALID <= 1'b1;
axis_vif.m_cb.TLAST <= last;
axis_vif.m_cb.TDATA <= data_buf[buf_idx];
axis_vif.m_cb.TUSER <= user_buf[buf_idx];
if (axis_vif.m_cb.TREADY == 1'b1) begin
buf_idx++;
end
@ (axis_vif.m_cb);
end
endtask
endclass
endpackage
for testbench:
`timescale 10ns/1ns
module tb_top ();
import axis_pkg::*;
localparam CLK_PERIOD = 10;
localparam CLK_AFTER_RST = 5;
logic clk = 0;
logic rst_n;
AXIS_if #(
.TDATA_WIDTH (8),
.TUSER_WIDTH (8)
) axis_from_dut_if (
.CLK (clk)
);
AXIS_if #(
.TDATA_WIDTH (8),
.TUSER_WIDTH (8)
) axis_to_dut_if (
.CLK (clk)
);
// Instantiate the DUT
dut dut_inst (
//
.CLK (clk),
.RST_N (rst_n),
//
.AXIS_S_PORT_IF (axis_to_dut_if),
.AXIS_M_PORT_IF (axis_from_dut_if)
);
always #(CLK_PERIOD/2) clk=~clk;
initial begin
rst_n = 0;
repeat (CLK_AFTER_RST) @(posedge clk);
rst_n = 1;
end
assign axis_from_dut_if.TREADY = 1'b1;
initial begin
// Testbench stimulus here
automatic axis_m_driver #(.TDATA_WIDTH (8), .TUSER_WIDTH (8)) axis_to_dut_drv;
automatic logic [7:0] tdata_array [];
automatic logic [7:0] tuser_array [];
axis_to_dut_drv = new(axis_to_dut_if);
tdata_array = new[10];
tuser_array = new[10];
for (int i = 0; i < tdata_array.size(); i++) begin
tdata_array[i] = 8'(i);
tuser_array[i] = 8'(i + 10);
end
wait (rst_n == 1'b1);
@(posedge clk);
axis_to_dut_drv.send_pkt(tdata_array, tuser_array);
repeat (20) @(posedge clk);
$stop;
end
endmodule
and for DUT:
`timescale 10ns/1ns
module dut (
// Clock and reset
input logic CLK,
input logic RST_N,
AXIS_if.slave AXIS_S_PORT_IF,
AXIS_if.master AXIS_M_PORT_IF
);
assign AXIS_S_PORT_IF.TREADY = !AXIS_M_PORT_IF.TVALID | AXIS_M_PORT_IF.TREADY;
always_ff @(posedge CLK) begin
if (RST_N == 1'b0) begin
AXIS_M_PORT_IF.TVALID <= 1'b0;
end else begin
if (AXIS_S_PORT_IF.TVALID == 1'b1 && AXIS_S_PORT_IF.TREADY == 1'b1) begin
AXIS_M_PORT_IF.TVALID <= 1'b1;
end else if (AXIS_M_PORT_IF.TREADY == 1'b1) begin
AXIS_M_PORT_IF.TVALID <= 1'b0;
end
end
begin
if (AXIS_S_PORT_IF.TVALID == 1'b1 && AXIS_S_PORT_IF.TREADY == 1'b1) begin
AXIS_M_PORT_IF.TDATA <= AXIS_S_PORT_IF.TDATA;
AXIS_M_PORT_IF.TUSER <= AXIS_S_PORT_IF.TUSER;
AXIS_M_PORT_IF.TLAST <= AXIS_S_PORT_IF.TLAST;
end
end
end
endmodule
It behaves as expected, but I get warnings:
# ** Warning: (vsim-3838) D:/PRJ/SBR/rtl/src/test/dut.sv(12): Variable '/tb_top/axis_to_dut_if/TREADY' written by continuous and procedural assignments.
# One of the assignments is implicit. See D:/PRJ/SBR/FPGA_tools/tb_common/AXIS_if.sv(12).
# Time: 0 ns Iteration: 0 Instance: /tb_top/dut_inst File: D:/PRJ/SBR/rtl/src/test/dut.sv
# ** Warning: (vsim-3838) D:/PRJ/SBR/rtl/tbs/test_tb/tb_top.sv(47): Variable '/tb_top/axis_from_dut_if/TREADY' written by continuous and procedural assignments.
# One of the assignments is implicit. See D:/PRJ/SBR/FPGA_tools/tb_common/AXIS_if.sv(12).
# Time: 0 ns Iteration: 0 Instance: /tb_top File: D:/PRJ/SBR/rtl/tbs/test_tb/tb_top.sv
As far as I understand procedural assignment in clocking block don’t want to work with continuous assignments in DUT, but I don’t know how to fix this problem. Am I misunderstanding something? Is it possible to get rid of this warnings?