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

I would like to write a module with a variable number of inputs, i.e. depending on some parameter, the result would be:

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

or

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

Would it be possible to do this in Verilog or Systemverilog or would I have to write a script, let's say in Python, in order to generate the code for a specific module with fixed number of inputs? (it might be more than 1000 inputs)



Solution 1:[1]

There are no variable number of ports in SystemVerilog, but you could use a port that is a parameterized array.

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

Otherwise, you would need to use a script to generate the code.

Solution 2:[2]

Use a two dimensional input with a parameterized size. Added a generate for loop that can be used to set signals individually. Although many operations can be done with smart array operations.

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

Edit: As Mehran Torki points out: The syntax above will work for SystemVerilog only. Verilog does not allow for multiple packed arrays. Use input [LENGTH*SIZE-1:0] data_in_array.

Solution 3:[3]

I would add to these other answers that ports are just groupings of wires. While having 3, 1-bit wires named a, b, and c might be easier to read and understand, there is no physical/logical difference between a single, 3-bit wire abc, where abc[0] corresponds to a, abc[1] corresponds to b, and abc[2] corresponds to c.

So, you can always just expand or shrink a single (or multiple) signal(s) to get however many bits you need. It may not be as neat, but it will work. In the receiving module, you can then part-select the bus in whatever manner you like. So, you could have one really long wire the shrinks or expands (wire [(SOME_PARAM*8)-1:0] my_input_wire), or with SystemVerilog an array (wire [7:0] my_input_wire[0:SOME_PARAM-1])

If this is just testbench/verification code, the other thing you could do in SystemVerilog is use a dynamic array

Solution 4:[4]

As others said, there is no direct way to do this, but another workaround is to use SystemVerilog interfaces, where you define all the inputs that you want in the interface definition and inside the module only use the ones that correspond to the parameter. Below is a sample:

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

You can still use a parameterized array for data and a for-generate loop if your outputs are similar.

Solution 5:[5]

With System verilog we can import a package instead of having parameterization and define types in the package to be used in the portlist.

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

And define multiple copies of the package with different variants of the port list and compile whichever version is required. eg

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

As with the interface solution there will be situations where you run into issues, like having different iterations of the module instantiated together.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 dave_59
Solution 2
Solution 3
Solution 4 Ari
Solution 5