如何为特定modport指定专属任务?解决AXI-Stream仿真冲突
问题解决方案:为AXI-Stream Modport绑定专属任务
核心问题分析
当前接口任务全局可见,即使未误调用,也存在潜在的多驱动风险;同时任务与modport的功能对应关系模糊:
send是发送任务,需驱动TVALID/TDATA等输出信号,应属于master modportget_packet是接收任务,需驱动TREADY输出信号,应属于slave modport
原错误Variable '/test_tb/dut_in/TREADY' written by continuous and procedural assignments,本质是DUT对AXIS_S.TREADY的连续赋值,与接口任务中对该信号的过程赋值(若误调用)产生冲突。
解决方案:Modport绑定专属任务
通过在modport中声明任务,从语法层面限制任务可见性,仅允许对应modport类型的接口变量访问专属任务。
1. 修改AXIS接口,绑定任务到对应Modport
`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; // Slave Modport:用于接收数据,仅暴露get_packet任务 modport slave ( input TVALID, output TREADY, input TLAST, input TDATA, input TUSER, task get_packet ); // Master Modport:用于发送数据,仅暴露send任务 modport master ( output TVALID, input TREADY, output TLAST, output TDATA, output TUSER, task send ); // 接收任务:仅通过slave modport可见 task automatic get_packet(); @ (posedge CLK); TREADY <= 1'b1; // 补充数据读取逻辑(示例) while (!TVALID) @(posedge CLK); // 读取TDATA/TUSER/TLAST到buffer的逻辑 @(posedge CLK); TREADY <= 1'b0; endtask // 发送任务:仅通过master modport可见 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 @(posedge CLK); TVALID <= 1'b0; endtask endinterface
2. 修改测试平台,使用Modport类型接口变量
`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_if (clk); AXIS #( .TDATA_WIDTH (TDATA_WIDTH), .TUSER_WIDTH (TUSER_WIDTH) ) dut_out_if (clk); // 用Modport类型声明接口变量,仅暴露对应任务 AXIS.master dut_in = dut_in_if; // 仅能调用send任务 AXIS.slave dut_out = dut_out_if; // 仅能调用get_packet任务 test_module DUT ( .clk (clk), .reset (reset), .AXIS_S (dut_in_if), .AXIS_M (dut_out_if) ); // 时钟生成 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 // 发送数据:仅能调用send任务 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 // 接收数据:仅能调用get_packet任务 initial begin forever begin wait (new_iteration.triggered); dut_out.get_packet(); @ (posedge clk); end end endmodule
效果说明
- 语法层面限制任务可见性:
dut_in(master类型)无法访问get_packet,dut_out(slave类型)无法访问send,彻底避免误调用导致的多驱动冲突。 - 任务与modport功能对应清晰:master负责发送,slave负责接收,符合AXI-Stream接口规范。
内容的提问来源于stack exchange,提问作者Sergey Voloshchuk
相关产品推荐
相关产品推荐

