verilog - Is it possible to create task within interface for specific modport? - Stack Overflow

时间: 2025-01-06 admin 业界

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 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?

Share Improve this question edited 23 hours ago toolic 61.8k19 gold badges79 silver badges126 bronze badges asked 23 hours ago Sergey VoloshchukSergey Voloshchuk 231 silver badge4 bronze badges New contributor Sergey Voloshchuk is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

1 Answer 1

Reset to default 1

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;
最新文章