如何在 Verilog 中编写具有可变端口数的模块

How to write a module with variable number of ports in Verilog

我想编写一个输入数量可变的模块,即根据某些参数,结果将是:

module my_module #(LENGTH)(
    input clk,
    input rst_n,
    input [LENGTH-1:0] data_1
);
//...
endmodule

module my_module #(LENGTH)(
    input clk,
    input rst_n,
    input [LENGTH-1:0] data_1,
    input [LENGTH-1:0] data_2,
    input [LENGTH-1:0] data_3
);
//...
endmodule

是否可以在 Verilog 或 Systemverilog 中执行此操作,或者我是否必须编写一个脚本,比方说在 Python 中,以便为具有固定输入数量的特定模块生成代码? (可能超过1000个输入)

SystemVerilog 中没有可变数量的端口,但您可以使用作为参数化数组的端口。

module my_module #(int LENGTH, DEPTH)(
    input clk,
    input rst_n,
    input [LENGTH-1:0] data[DEPTH]
);
//...
endmodule

否则,您需要使用脚本来生成代码。

使用具有参数化大小的二维输入。添加了一个 generate for loop 可用于单独设置信号。虽然很多操作都可以用智能数组操作来完成。

module my_module #(SIZE, LENGTH)(
    input clk,
    input rst_n, 
    input [SIZE-1:0][LENGTH-1:0] data_in_array,
    output [SIZE-1:0][LENGTH-1:0] data_out_array
);
genvar N;
generate for (N=0; N<SIZE; N++) begin :la_coolOps
    //Do cool operations here. For example instantiate a module for every data_in
end
//...
endmodule

编辑: 正如 Mehran Torki 指出的那样:上面的语法仅适用于 SystemVerilog。 Verilog 不允许多个压缩数组。使用 input [LENGTH*SIZE-1:0] data_in_array.

正如其他人所说,没有直接的方法可以做到这一点,但另一种解决方法是使用 SystemVerilog 接口,您可以在接口定义中定义所需的所有输入,并且在模块内部仅使用对应的输入到参数。以下是示例:

module my_module #(LENGTH)(
       input clk;
       input rst_n;
       output o;
       interface i_data;
    );
    logic outValue;

    generate
        case (LENGTH) //Based on the value of LENGTH, use corresponding data
            1: outValue = i_data.data_1;
            2: outValue = i_data.data_1 + i_data.data_2;
            3: outValue = i_data.data_1 + i_data.data_2 + i_data.data_3;
        endcase 
    endgenerate

    always @(posedge clk) begin
    if (~rst_n)
        o <= '0;
    else
    begin
        o <= outValue;
    end

endmodule

如果您的输出相似,您仍然可以使用参数化数组作为数据并使用 for-generate 循环。

除了这些其他答案之外,我还要补充说端口只是电线的分组。虽然将 3 条 1 位线命名为 a、b 和 c 可能更容易阅读和理解,但在单个 3 位线 abc 之间没有 physical/logical 区别,其中 [=11] =]对应aabc[1]对应babc[2]对应c.

因此,您始终可以扩展或收缩单个(或多个)信号来获得所需的任意位数。它可能不那么整洁,但它会起作用。在接收模块中,您可以以任何您喜欢的方式 part-select 总线。因此,您可以使用一根非常长的电线来收缩或展开 (wire [(SOME_PARAM*8)-1:0] my_input_wire),或者使用 SystemVerilog 数组 (wire [7:0] my_input_wire[0:SOME_PARAM-1])

如果这只是 testbench/verification 代码,您可以在 SystemVerilog 中做的另一件事是使用动态数组

使用 System verilog,我们可以导入包而不是进行参数化,并在包中定义要在 portlist 中使用的类型。

module mymodule
  import mymodule_pkg::*;
(
    input portlist_t portlist
);
endmodule

并使用端口列表的不同变体定义包的多个副本,并编译所需的版本。例如

package mymodule_pkg;

   localparam LENGTH=5;

   typedef struct packed {
      logic [LENGTH-1:0] data_1,
      logic [LENGTH-1:0] data_2,
      logic [LENGTH-1:0] data_3
   } portlist_t;

endpackage

与接口解决方案一样,在某些情况下您会 运行 遇到问题,例如一起实例化模块的不同迭代。