Parameters are Verilog constructs that allow a module to be reused with a different specification. For example, a 4-bit adder can be parameterized to accept a value for the number of bits and new parameter values can be passed in during module instantiation. So, an N-bit adder can become a 4-bit, 8-bit or 16-bit adder. They are like arguments to a function that are passed in during a function call.
parameter MSB = 7; // MSB is a parameter with a constant value 7
parameter REAL = 4.5; // REAL holds a real number
parameter FIFO_DEPTH = 256,
MAX_WIDTH = 32; // Declares two parameters
parameter [7:0] f_const = 2'b3; // 2 bit value is converted to 8 bits; 8'b3
Parameters are basically constants and hence it's illegal to modify their value at runtime. It is illegal to redeclare a name that is already used by a net, variable or another parameter.
There are two major types of parameters, module and specify and both accepts a range specification. But, they are normally made as wide as the value to be stored requires them to be and hence a range specification is not necessary.
Module parameters
Module parameters can be used to override parameter definitions within a module and this makes the module have a different set of parameters at compile time. A parameter can be modified with the defparam
statement or in the module instance statement. It is a common practice to use uppercase letters in names for the parameter to make them instantly noticeable.
The module shown below uses parameters to specify the bus width, data width and the depth of FIFO within the design, and can be overriden with new values when the module is instantiated or by using defparam
statements.
// Verilog 1995 style port declaration
module design_ip ( addr,
wdata,
write,
sel,
rdata);
parameter BUS_WIDTH = 32,
DATA_WIDTH = 64,
FIFO_DEPTH = 512;
input addr;
input wdata;
input write;
input sel;
output rdata;
wire [BUS_WIDTH-1:0] addr;
wire [DATA_WIDTH-1:0] wdata;
reg [DATA_WIDTH-1:0] rdata;
reg [7:0] fifo [FIFO_DEPTH];
// Design code goes here ...
endmodule
In the new ANSI style of Verilog port declaration, you may declare parameters as show below.
module design_ip
#(parameter BUS_WIDTH=32,
parameter DATA_WIDTH=64) (
input [BUS_WIDTH-1:0] addr,
// Other port declarations
);
Overriding parameters
Parameters can be overridden with new values during module instantiation. The first part instantiates the module called design_ip by the name d0 where new parameters are passed in within #( )
. The second part uses a Verilog construct called defparam
to set the new parameter values. The first method is the most commonly used way to pass new parameters in RTL designs. The second method is commonly used in testbench simulations to quickly update the design parameters without having to reinstantiate the module.
module tb;
// Module instantiation override
design_ip #(BUS_WIDTH = 64, DATA_WIDTH = 128) d0 ( [port list]);
// Use of defparam to override
defparam d0.FIFO_DEPTH = 128;
endmodule
Example
The module counter has two parameters N and DOWN declared to have a default value of 2 and 0 respectively. N controls the number of bits in the output effectively controlling the width of the counter. By default it is a 2-bit counter. Parameter DOWN controls whether the counter should increment or decrement. By default, the counter will decrement because the parameter is set to 0.
2-bit up-counter
module counter
#( parameter N = 2,
parameter DOWN = 0)
( input clk,
input rstn,
input en,
output reg [N-1:0] out);
always @ (posedge clk) begin
if (!rstn) begin
out <= 0;
end else begin
if (en)
if (DOWN)
out <= out - 1;
else
out <= out + 1;
else
out <= out;
end
end
endmodule
The module counter is instantiated with N as 2 even though it is not required because the default value is anyway 2. DOWN is not passed in during module instantiation and hence takes the default value of 0 making it an up-counter.
module design_top ( input clk,
input rstn,
input en,
output [1:0] out);
counter #(.N(2)) u0 ( .clk(clk),
.rstn(rstn),
.en(en));
endmodule
See that default parameters are used to implement the counter where N equals two making it a 2-bit counter and DOWN equals zero making it an up-counter. The output from counter is left unconnected at the top level.

4-bit down-counter
In this case, the module counter is instantiated with N as 4 making it a 4-bit counter. DOWN is passed a value of 1 during module instantiation and hence an down-counter is implemented.
module design_top ( input clk,
input rstn,
input en,
output [3:0] out);
counter #(.N(4), .DOWN(1))
u1 ( .clk(clk),
.rstn(rstn),
.en(en));
endmodule

Specify parameters
These are primarily used for providing timing and delay values and are declared using the specparam
keyword. It is allowed to be used both within the specify block and the main module body.
// Use of specify block
specify
specparam t_rise = 200, t_fall = 150;
specparam clk_to_q = 70, d_to_q = 100;
endspecify
// Within main module
module my_block ( ... );
specparam dhold = 2.0;
specparam ddly = 1.5;
parameter WIDTH = 32;
endmodule
Difference between specify and module parameters
Specify parameter | Module parameter |
---|---|
Declared by specparam | Declared by parameter |
Can be declared inside specify block or within main module | Can only be declared within the main module |
May be assigned specparams and parameters | May not be assigned specparams |
SDF can be used to override values | Instance declaration parameter values or defparam can be used to override |
What is a Verilog array ?
An array declaration of a net or variable can be either scalar or vector. Any number of dimensions can be created by specifying an address range after the identifier name and is called a multi-dimensional array. Arrays are allowed in Verilog for reg
, wire
, integer
and real
data types.
reg y1 [11:0]; // y is an scalar reg array of depth=12, each 1-bit wide
wire [0:7] y2 [3:0] // y is an 8-bit vector net with a depth of 4
reg [7:0] y3 [0:1][0:3]; // y is a 2D array rows=2,cols=4 each 8-bit wide
An index for every dimension has to be specified to access a particular element of an array and can be an expression of other variables. An array can be formed for any of the different data-types supported in Verilog.
Note that a memory of n 1-bit reg is not the same as an n-bit vector reg.
Array Assignment
y1 = 0; // Illegal - All elements can't be assigned in a single go
y2[0] = 8'ha2; // Assign 0xa2 to index=0
y2[2] = 8'h1c; // Assign 0x1c to index=2
y3[1][2] = 8'hdd; // Assign 0xdd to rows=1 cols=2
y3[0][0] = 8'haa; // Assign 0xaa to rows=0 cols=0
Array Example
The code shown below simply shows how different arrays can be modeled, assigned and accessed. mem1 is an 8-bit vector, mem2 is an 8-bit array with a depth of 4 (specified by the range [0:3]) and mem3 is a 16-bit vector 2D array with 4 rows and 2 columns. These variables are assigned different values and printed.
module des ();
reg [7:0] mem1; // reg vector 8-bit wide
reg [7:0] mem2 [0:3]; // 8-bit wide vector array with depth=4
reg [15:0] mem3 [0:3][0:1]; // 16-bit wide vector 2D array with rows=4,cols=2
initial begin
int i;
mem1 = 8'ha9;
$display ("mem1 = 0x%0h", mem1);
mem2[0] = 8'haa;
mem2[1] = 8'hbb;
mem2[2] = 8'hcc;
mem2[3] = 8'hdd;
for(i = 0; i < 4; i = i+1) begin
$display("mem2[%0d] = 0x%0h", i, mem2[i]);
end
for(int i = 0; i < 4; i += 1) begin
for(int j = 0; j < 2; j += 1) begin
mem3[i][j] = i + j;
$display("mem3[%0d][%0d] = 0x%0h", i, j, mem3[i][j]);
end
end
end
endmodule
ncsim> run mem1 = 0xa9 mem2[0] = 0xaa mem2[1] = 0xbb mem2[2] = 0xcc mem2[3] = 0xdd mem3[0][0] = 0x0 mem3[0][1] = 0x1 mem3[1][0] = 0x1 mem3[1][1] = 0x2 mem3[2][0] = 0x2 mem3[2][1] = 0x3 mem3[3][0] = 0x3 mem3[3][1] = 0x4 ncsim: *W,RNQUIE: Simulation is complete.
What are memories ?
Memories are digital storage elements that help store a data and information in digital circuits. RAMs and ROMs are good examples of such memory elements. Storage elements can be modeled using one-dimensional arrays of type reg
and is called a memory. Each element in the memory may represent a word and is referenced using a single array index.

Register Vector
Verilog vectors are declared using a size range on the left side of the variable name and these get realized into flops that match the size of the variable. In the code shown below, the design module accepts clock, reset and some control signals to read and write into the block.
It contains a 16-bit storage element called register which simply gets updated during writes and returns the current value during reads. The register is written when sel and wr are high on the same clock edge. It returns the current data when sel is high and wr is low.
module des ( input clk,
input rstn,
input wr,
input sel,
input [15:0] wdata,
output [15:0] rdata);
reg [15:0] register;
always @ (posedge clk) begin
if (!rstn)
register <= 0;
else begin
if (sel & wr)
register <= wdata;
else
register <= register;
end
end
assign rdata = (sel & ~wr) ? register : 0;
endmodule
The hardware schematic shows that a 16-bit flop is updated when control logic for writes are active and the current value is returned when control logic is configured for reads.

Memory Example
In this example, register is an array that has four locations with each having a width of 16-bits. The design module accepts an additional input signal which is called addr to access a particular index in the array.
module des ( input clk,
input rstn,
input [1:0] addr,
input wr,
input sel,
input [15:0] wdata,
output [15:0] rdata);
reg [15:0] register [0:3];
integer i;
always @ (posedge clk) begin
if (!rstn) begin
for (i = 0; i < 4; i = i+1) begin
register[i] <= 0;
end
end else begin
if (sel & wr)
register[addr] <= wdata;
else
register[addr] <= register[addr];
end
end
assign rdata = (sel & ~wr) ? register[addr] : 0;
endmodule
It can be seen in the hardware schematic that each index of the array is a 16-bit flop and the input address is used to access a particular set of flops.

The primary intent of data-types in the Verilog language is to represent data storage elements like bits in a flip-flop and transmission elements like wires that connect between logic gates and sequential structures.
What values do variables hold ?
Almost all data-types can only have one of the four different values as given below except for real
and event
data types.
0 | represents a logic zero, or a false condition |
1 | represents a logic one, or a true condition |
x | represents an unknown logic value (can be zero or one) |
z | represents a high-impedance state |
The following image shows how these values are represented in timing diagrams and simulation waveforms. Most simulators use this convention where red stands for X
and orange in the middle stands for high-impedance or Z
.

A structure can contain elements of different data types which can be referenced as a whole or individually by their names. This is quite different from arrays where the elements are of the same data-type.
// Normal arrays -> a collection of variables of same data type
int array [10]; // all elements are of int type
bit [7:0] mem [256]; // all elements are of bit type
// Structures -> a collection of variables of different data types
struct {
byte val1;
int val2;
string val3;
} struct_name;
Syntax
struct {
[list of variables]
} struct_name;
Unpacked Structures
A structure is unpacked by default and can be defined using the struct
keyword and a list of member declarations can be provided within the curly brackets followed by the name of the structure.
Structure Example
module tb;
// Create a structure called "st_fruit"
// which to store the fruit's name, count and expiry date in days.
// Note: this structure declaration can also be placed outside the module
struct {
string fruit;
int count;
byte expiry;
} st_fruit;
initial begin
// st_fruit is a structure variable, so let's initialize it
st_fruit = '{"apple", 4, 15};
// Display the structure variable
$display ("st_fruit = %p", st_fruit);
// Change fruit to pineapple, and expiry to 7
st_fruit.fruit = "pineapple";
st_fruit.expiry = 7;
$display ("st_fruit = %p", st_fruit);
end
endmodule
ncsim> run st_fruit = '{fruit:"apple", count:4, expiry:'hf} st_fruit = '{fruit:"pineapple", count:4, expiry:'h7} ncsim: *W,RNQUIE: Simulation is complete.
What is the need to typedef a structure ?
Only one variable was created in the example above, but if there's a need to create multiple structure variables with the same constituents, it'll be better to create a user defined data type of the structure by typedef
. Then st_fruit will become a data-type which can then be used to create variables of that type.
module tb;
// Create a structure called "st_fruit"
// which to store the fruit's name, count and expiry date in days.
// Note: this structure declaration can also be placed outside the module
typedef struct {
string fruit;
int count;
byte expiry;
} st_fruit;
initial begin
// st_fruit is a data type, so we need to declare a variable of this data type
st_fruit fruit1 = '{"apple", 4, 15};
st_fruit fruit2;
// Display the structure variable
$display ("fruit1 = %p fruit2 = %p", fruit1, fruit2);
// Assign one structure variable to another and print
// Note that contents of this variable is copied into the other
fruit2 = fruit1;
$display ("fruit1 = %p fruit2 = %p", fruit1, fruit2);
// Change fruit1 to see if fruit2 is affected
fruit1.fruit = "orange";
$display ("fruit1 = %p fruit2 = %p", fruit1, fruit2);
end
endmodule
ncsim> run fruit1 = '{fruit:"apple", count:4, expiry:'hf} fruit2 = '{fruit:"", count:0, expiry:'h0} fruit1 = '{fruit:"apple", count:4, expiry:'hf} fruit2 = '{fruit:"apple", count:4, expiry:'hf} fruit1 = '{fruit:"orange", count:4, expiry:'hf} fruit2 = '{fruit:"apple", count:4, expiry:'hf} ncsim: *W,RNQUIE: Simulation is complete.
Packed Structures
A packed structure is a mechanism for subdividing a vector into fields that can be accessed as members and are packed together in memory without gaps. The first member in the structure is the most significant and subsequent members follow in decreasing order of significance.
A structure is declared packed using the packed
keyword which by default is unsigned.
Example
// Create a "packed" structure data type which is similar to creating
// bit [7:0] ctrl_reg;
// ctrl_reg [0] represents en
// ctrl_reg [3:1] represents cfg
// ctrl_reg [7:4] represents mode
typedef struct packed {
bit [3:0] mode;
bit [2:0] cfg;
bit en;
} st_ctrl;
module tb;
st_ctrl ctrl_reg;
initial begin
// Initialize packed structure variable
ctrl_reg = '{4'ha, 3'h5, 1};
$display ("ctrl_reg = %p", ctrl_reg);
// Change packed structure member to something else
ctrl_reg.mode = 4'h3;
$display ("ctrl_reg = %p", ctrl_reg);
// Assign a packed value to the structure variable
ctrl_reg = 8'hfa;
$display ("ctrl_reg = %p", ctrl_reg);
end
endmodule
ncsim> run ctrl_reg = '{mode:'ha, cfg:'h5, en:'h1} ctrl_reg = '{mode:'h3, cfg:'h5, en:'h1} ctrl_reg = '{mode:'hf, cfg:'h5, en:'h0} ncsim: *W,RNQUIE: Simulation is complete.
Typedef
In complex testbenches some variable declarations might have a longer data-type specification or require to be used in multiple places in the testbench.
In such cases we can use a typedef
to give a user-defined name to an existing data type. The new data-type can then be used throughout the code and hence avoids the need to edit in multiple places if required.
// Normal declaration may turn out to be quite long
unsigned shortint my_data;
enum {RED, YELLOW, GREEN} e_light;
bit [7:0] my_byte;
// Declare an alias for this long definition
typedef unsigned shortint u_shorti;
typedef enum {RED, YELLOW, GREEN} e_light;
typedef bit [7:0] ubyte;
// Use these new data-types to create variables
u_shorti my_data;
e_light light1;
ubyte my_byte;
Syntax
typedef data_type type_name [range];
Example
module tb;
typedef shortint unsigned u_shorti;
typedef enum {RED, YELLOW, GREEN} e_light;
typedef bit [7:0] ubyte;
initial begin
u_shorti data = 32'hface_cafe;
e_light light = GREEN;
ubyte cnt = 8'hFF;
$display ("light=%s data=0x%0h cnt=%0d", light.name(), data, cnt);
end
endmodule
ncsim> run light=GREEN data=0xcafe cnt=255 ncsim: *W,RNQUIE: Simulation is complete.
Alias
In SystemVerilog, an alias is a named reference to a variable, signal, or instance. It provides a way to refer to a variable using a different name. Aliases can be useful in many situations, including reducing code complexity, enhancing readability, and improving simulation performance. It is also used to model a bi-directional short-circuit and can be used inside modules, interfaces and generate blocks.
Here's an example of how to create an alias in SystemVerilog:
logic [7:0] data;
alias mydata = data; // alias "mydata" for signal "data"
initial begin
mydata = 8'hFF; // assign the value to "data" using the alias "mydata"
end
In this example, the signal data is assigned the value 8'hFF using the alias mydata . The advantage of using an alias is that it allows you to refer to the same signal using different names, which can make the code more readable and easier to understand.