Passing parameters between modules in Verilog - verilog

I am trying to learn how to pass parameters in Verilog. So far I've learned that the declaration looks like this:
module funct #(parameter n = 32)
(input clk, input [n-1:0]in, input reset, input L,
input load, input shift, output reg[n-1:0] out);
I instantiate another module within that module that depends on the parameter, so I defined the parameter there as well.
module funct2 #(parameter n = 32) (
input clk,
input [n-1:0] in,
input rst,
input L,
output [n-1:0] out
);
My question is how to call funct2 within funct?
I was also wondering how I can instantiate funct within the testbench folder. Without the parameter, it looked like this
funct uut(.clk(clk),.in(in), .reset(reset),.L(L), .load(load), .shift(shift), .out(out));

The following syntax is used to instantiate funct2 within funct, passing the parameter into the module:
funct2 #(.n(n)) i1 (.clk(clk), .in(in), .rst(reset), .L(L), .out(out));
Refer to IEEE Std 1800-2017, section 23.3.2 Module instantiation syntax.
The syntax you used to instantiate funct inside the testbench is valid, as you've discovered. The default value of 32 is used in this case. Optionally, you could use the #(.n(n)) syntax to explicitly show that you are passing a parameter.

Related

Verilog [dot] meaning?

What does this " .depth_log2(7) " and .i_wclk mean in Verilog code?
asynch_fifo #(.depth_log2(7),
.data_width(22),
.rd_flop1_megedge(1'b1),
) USB2_ASYNCH_FIFO (
.i_wclk(i_usb2_sieclockin_ip),
);
I'm not able to understand what that .depth_log2 and .rd_flop1_megedge means
When you instantiate a module, such module might have some parameters. You can leave them at default, or you can initialize them at the values you prefer. In your example you are setting the depth at 7, the data width at 22 etc..
In general, if you have a verilog module like this:
module my_module
#( parameter P1 = 2,
parameter P2 = 0)
( input clk,
output reg [P1-1:0] out);
// Module logic
endmodule
You can instantiate it with the dot notation
wire wire_clk;
wire [2-1:0] wire_out;
my_module #(.P1(2),
.P2(3) ) u0
( .clk(wire_clk),
.out(wire_out);
This is called instantiation. Using this "." notation you are basically saying that you want to connect a constant 7 to depth_log2 parameter of your component asynch_fifo.

How to use regs to modify wires?

I am kind of new to Verilog and was wondering how I can modify wires. I know that you cannot modify wires inside always blocks.
I've seen something like this where you can declare some regs and assign the wire to those regs (which you can then modify the reg to modify the wire?)
module something
#(parameter D_W = 8)
(
input wire clk,
input wire rst,
output wire valid,
output wire [D_W-1:0] data,
);
reg valid_rg = 0;
reg [D_W-1:0] data_rg = 0;
assign valid = valid_rg;
assign data = data_rg;
I was wondering how to do something like that for a wire like:
wire [7:0] wire_a [7:0];
Initially my guess would be to have something like this
reg [7:0] wire_a_rg [7:0];
assign wire_a[7:0] = wire_a_rg[7:0];
But I have a feeling it might be wrong. How could I approach this?
There's no need to use wires in SystemVerilog unless you need to model bi-directional buses, or any kind of circuitry with multiple drivers. You can write
module something
#(parameter D_W = 8)
(
input logic clk,
input logic rst,
output logic valid,
output logic [D_W-1:0] data,
);
And then you can make procedural assigmemnts to valid/data in an always block, or a continuous assign statement (but not both).
BTW, SystemVerilog prefers the use of logic keyword over synonym reg.
You should read my post about the difference between nets and variables.

Passing bus array to another module via port mapping

I have below code inside SV module where I instantiate another SV module and pass 5-bit bus to it to check for X and Z's as coded below:
input [4:0] analdo_trim;
cds_XZ_checker XZ_check_analdo_trim (.in(analdo_trim),.in_ok(analdo_trim_ok));
Here is module definition for cds_XZ_checker:
module cds_XZ_checker(in,in_ok);
input in;
output bit in_ok;
always_comb begin //Asynchronous assertion check block
asynch_XZ_check: assert (!($isunknown(in))) in_ok=1'b1;
else begin
$warning ("WARNING (%M) digital signal in=%b is undefined at time %t",in,$time);
in_ok=1'b0;
end//else
end
endmodule
The issue is when I read 5-bit analdo_trim in above module via in port, it only reads LSB of analdo_trim because it doesn't have bus width in declaration.
Module cds_XZ_checker is generic module which is instantiated inside several other modules. Hence, I can't declare 'in' as [4:0] as some other modules might pass bus with different bit width. Is there a way I can parameterize this so that it will work for any bit width?
You can use a parameter to accommodate different input bus widths:
module cds_XZ_checker #(parameter WIDTH=5) (in,in_ok);
input [WIDTH-1:0] in;
output bit in_ok;
always_comb begin //Asynchronous assertion check block
asynch_XZ_check: assert (!($isunknown(in))) in_ok=1'b1;
else begin
$warning ("WARNING (%M) digital signal in=%b is undefined at time %t",in,$time);
in_ok=1'b0;
end//else
end
endmodule
module tb;
logic a,b;
logic [4:0] c;
logic d;
cds_XZ_checker #(.WIDTH(1)) i0 (a, b);
cds_XZ_checker i1 (c, d);
endmodule
The tb module shows how you would parameterize each instance of the checker module. The default width is 5. If your checker input is 5-bit, then passing the parameter is optional.
Demo on edaplayground

How to change input signal to parameter in systemverilog?

I have an input logic sequence and I would like to convert it to a parameter in order to add it elsewhere in my program.
For example,
module myModule(input logic[7:0] SW, output logic[7:0] LEDR);
parameter shift = SW;
assign LEDR = SW[shift + 1: shift];
endmodule
I know that's not correct syntax, I just wanted to get the main idea.
Parameters are by definition compile time constants. That means you can not change their value based on an expression that can change over time.
What you can do is change the way you model so it does not require a parameter. For example , you could write your code as
module myModule(input logic[7:0] SW, output logic[7:0] LEDR);
assign LEDR = SW[SW +: 2];
endmodule
You cannot convert a variable to parameter. The value of the parameter will be locked after elaborations. A variable will not have a value until simulation.
Part-select (sometimes called range-slice) should do what you need. See
Indexing vectors and arrays with +: for more info.
Having SW slice itself does make sense since the resulting value would always be 0. Here is better example:
module myModule(input [8:0] IN, input [2:0] SW, output [1:0] LEDR);
assign LEDR = IN[SW +: 2];
endmodule

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)
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.
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.
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
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.
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.

Resources