【问题标题】:Arrays of interface instances in SystemVerilog with parametrized number of elementsSystemVerilog 中具有参数化元素数量的接口实例数组
【发布时间】:2017-07-12 13:16:19
【问题描述】:

我正在使用 SystemVerilog 进行综合。我反对这样一个事实,即接口数组在 SystemVerilog 中并不是真正的数组,索引必须是一个常量值,但是使用大量样板文件 generate forassign 语句克服了它,以克服真正的语言限制(如果我可以使用更多代码来模拟效果,那么语言本身就可以做正确的事情(tm)。

对于以下伪代码,为了清楚起见,我省略了真实代码中的大部分内容(modport、任务等)。 我有一个界面:

interface i_triplex();
logic a;              // input wire
logic b;              // output wire
logic [127:0] data;   // output wires
endinterface

我将这些接口的数组传递给一个看起来像

的模块
module roundrobin_triplex#(
    parameter int NUNITS = 8
)
(
    input wire clk,
    input wire rst,
    i_triplex t[NUNITS]
);
always_ff @(posedge clk) begin
    if (rst) begin
      // insert code to initialize the "data" member
      // in all interface instances in the array.
    end
    else begin
      // ...
    end
end
endmodule

无论NUNITS 的值如何,您希望以何种方式统一使用数组中的所有接口实例?我有一些建议,但我很想知道其他工程师能想出什么。

建议一: 使用 VHDL。

建议 2: 废弃界面并按照老式的 Verilog 风格进行操作,如

module roundrobin_triplex#(
    parameter int NUNITS = 8
)
(
    input wire clk,
    input wire rst,
    // This was once a proud i_triplex array
    input wire i_triplex_a[NUNITS],
    input wire i_triplex_b[NUNITS],
    input wire [127:0] i_triplex_data[NUNITS],
);
always_ff @(posedge clk) begin
    if (rst) begin
        for (int i = 0; i < NUNITS; i++)
            i_triplex_data[i] <= '1;
    end
    else begin
        // ...
    end
end
endmodule

建议 3: 输入线使用struct,输出线使用struct,而不是接口。

建议 4: 使用类似预处理器的系统,在进程内展开generate for 循环(无论如何语言应该做什么!),因此生成的代码看起来像(使用 NUNITS=4 预处理):

module roundrobin_triplex#(
    parameter int NUNITS = 8
)
(
    input wire clk,
    input wire rst,
    i_triplex t[NUNITS]
);
always_ff @(posedge clk) begin
    if (rst) begin
        i_triplex.data[0] <= '1;
        i_triplex.data[1] <= '1;
        i_triplex.data[2] <= '1;
        i_triplex.data[3] <= '1;
    end
    else begin
        // ...
    end
end
endmodule

建议 5: 使用generate for/assign解决方案:

module roundrobin_triplex#(
    parameter int NUNITS = 8
)
(
    input wire clk,
    input wire rst,
    i_triplex t[NUNITS]
);

wire i_triplex_a[NUNITS];
wire i_triplex_b[NUNITS];
wire [127:0] i_triplex_data[NUNITS];

generate
genvar i;
// The wires of the interface which are to be driven
// from this module are assigned here.
for (i = 0; i < NUNITS; i++) begin
    assign t[i].b = i_triplex_b[i];
    assign t[i].data = i_triplex_data[i];
end
endgenerate

always_ff @(posedge clk) begin
    if (rst) begin
        for (int i = 0; i < NUNITS; i++)
            i_triplex_data[i] <= '1;
    end
    else begin
        // ...
    end
end
endmodule

【问题讨论】:

    标签: arrays interface verilog system-verilog


    【解决方案1】:

    模块或接口实例的数组不能被视为常规数组,因为参数化、生成块和 defparam 语句可以使数组实例的元素不唯一。变量/连线数组不会发生这种情况。

    我的建议是修改您的建议 2;将变量/连线数组放在单个接口实例中。

    【讨论】:

    • 也许应该扩展语言以检查数组中的所有接口实例是否“足够相似”(或者让用户使用关键字来表示)并允许使用这样的接口实例数组常规数组。这将解决使用接口进行综合时的很多问题。
    • 我实际上有数百个模块使用该接口,其中大多数将其用作普通接口端口,但许多使用i_triplex t[NUNITS+2] 之类的东西与其他接口实例进行复用。我希望在不重写太多代码的情况下度过难关,因此我将检查您对建议 2 的修改和一些 generate for-assign 胶水代码的组合是否可以工作,因此我可以保留大多数其他模块并使用类似于i_triplex_array 接口,用于出现有益的情况。
    • 是的,我同意我们需要一些新的语法。试图根据用法提出规则会使已经过于复杂的语言变得更加复杂。
    【解决方案2】:

    建议1:VHDL 可能比较实用。然而,它似乎在行业中变得边缘化。

    建议 2:在我看来,如果您打算广泛重用接口并在其中实施验证/协议,那么接口是相关的。如果你可以像这样解压你的界面,同时保持你的理智,那么我首先看不到一个界面的理由。

    建议3:我从来没有尝试过合成struct,这可能是个好主意。

    建议 4:简单的解决方案,虽然很冗长。

    建议 5:我在我的一个项目中使用了类似的东西。但是,我编写了一种适配器模块来隐藏分配。
    实际上,当我需要类似的东西时,我会尝试编写一个在固定数量的接口上运行的原子模块。然后,我使用for generate 结构将其泛化到接口数组上。

    【讨论】:

      【解决方案3】:

      建议#6怎么样,使用参数化接口:

      interface I #(NPORTS = 8);
         logic clk;
      
         logic a[NPORTS];
         logic b[NPORTS];
         logic [127:0] data [NPORTS];
      
      endinterface // 
      
      module roundrobin#(NUMPORTS = 8) (I t);
         logic [127:0] data[NUMPORTS];
      
         always_ff @(posedge t.clk) begin
            data <= t.data;
         end
      
      endmodule // roundrobin
      

      请注意,您不需要系统 verilog 中的循环。你可以使用数组赋值:

      data <= t.data;
      

      为了方便起见,您可以向接口本身添加函数或语句,即

      interface I #(NPORTS = 8);
         logic clk;
      
         logic a[NPORTS];
         logic b[NPORTS];
         logic [127:0] data [NPORTS];
      
         function logic [127:0] getData(int n);
            return data[n];
         endfunction // getData
      
      endinterface // I
      

      并使用

       data[i] <= t.getData(i);
      

      对不起,上面的例子可能不是很有用,但它可能会给你一个想法。

      【讨论】:

        【解决方案4】:

        也许我错过了一些东西,为什么不把你所有的代码从 roundrobin_triplex 放到 generate 中(你不需要额外的分配)

        interface i_triplex();
            logic a;              // input wire
            logic b;              // output wire
            logic [127:0] data;   // output wires
            initial $fmonitor(1,data);
        endinterface
        
        module roundrobin_triplex#(parameter int NUNITS = 8)
        (
            input wire clk,
            input wire rst,
            i_triplex t[NUNITS]
        );
            genvar i;
            for( i=0; i<NUNITS; i++)begin
                always_ff @(posedge clk) begin
                    if (rst) begin
                        t[i].data <=0;
                    end
                    else begin
                      t[i].data <=t[i].data+1;
                    end
                end
            end
        endmodule
        
        module top;
            parameter P=4;
            bit clk,rst;
            i_triplex ii[P]();
            roundrobin_triplex #(P)uut(clk,rst,ii);
            initial begin
                rst=1;
                repeat(2) @(posedge clk);
                rst=0;
                repeat(10) @(posedge clk);
                $finish;
            end
            always #5 clk =~clk;
        endmodule
        

        【讨论】: