Verilog parameters are essential for making your Verilog designs more flexible and reusable. By using parameters, you can easily change the behavior of a module without rewriting the code. This allows you to create more adaptable and efficient designs. In this article, we will explain what Verilog parameters are, how to use them, and their different types.
What Are Verilog Parameters?
Parameters in Verilog are constants that you define within a module. They allow you to adjust the functionality of a module at compile time, making your code reusable with different specifications. For example, a 4-bit adder can be modified to become an 8-bit or 16-bit adder by simply changing the parameter values. Parameters are similar to function arguments, passed in when the module is instantiated.
Here are a few examples of Verilog parameters:
parameter MSB = 7; // MSB has a constant value of 7
parameter REAL = 4.5; // REAL holds a real number
parameter FIFO_DEPTH = 256,
MAX_WIDTH = 32; // Two parameters declared together
parameter [7:0] f_const = 2'b3; // f_const is an 8-bit value
Note: Parameters are constants and cannot be modified during runtime. It is illegal to redeclare a parameter name if it already exists as a net, variable, or another parameter.
Types of Verilog Parameters
There are three main types of parameters in Verilog:
- Module Parameters
- Specify Parameters
- Local Parameters
We’ll dive deeper into these types below.
1. Module Parameters
Module parameters are used to make a module flexible. You can pass new values for parameters when you instantiate a module. This allows you to customize the module’s behavior at compile time.
Example of Module Parameters
Here’s an example of a module that uses parameters for bus width, data width, and FIFO depth:
module design_ip (
input addr,
input wdata,
input write,
input sel,
output rdata
);
parameter BUS_WIDTH = 32, // Default value for BUS_WIDTH
DATA_WIDTH = 64, // Default value for DATA_WIDTH
FIFO_DEPTH = 512; // Default value for FIFO_DEPTH
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 this example:
BUS_WIDTH
,DATA_WIDTH
, andFIFO_DEPTH
are parameters with default values.- These parameters can be overridden when the module is instantiated or by using the
defparam
statement.
Verilog 2001 ANSI Style Declaration
You can also use the more modern ANSI style for parameter declaration like this:
module design_ip
#(parameter BUS_WIDTH = 32,
parameter DATA_WIDTH = 64) (
input [BUS_WIDTH-1:0] addr,
// Other port declarations
);
2. Overriding Parameters
You can override parameters when instantiating a module by passing new values inside the #()
syntax. Here’s how:
Example 1: Overriding Parameters during Instantiation
module tb;
// Override parameters in instantiation
design_ip #(BUS_WIDTH = 64, DATA_WIDTH = 128) d0 (
// port list
);
endmodule
Example 2: Using defparam
to Override Parameters
module tb;
// Using defparam to override parameters
design_ip d0 (
// port list
);
// Override FIFO_DEPTH using defparam
defparam d0.FIFO_DEPTH = 128;
endmodule
In this case:
- The first method (using
#()
) is commonly used in RTL designs. - The second method (using
defparam
) is often used in testbenches for quick modifications during simulation.
3. Local Parameters
Local parameters are similar to regular parameters, but their values cannot be changed or overridden from outside the module. They are defined using the localparam
keyword and provide a way to protect certain design values from accidental changes.
Example of Local Parameters
module adder #(parameter WIDTH = 8) (
input wire [WIDTH-1:0] a,
input wire [WIDTH-1:0] b,
output wire [WIDTH-1:0] sum
);
// Local parameter, cannot be modified outside the module
localparam LENGTH = 4;
assign sum = a + b;
endmodule
Attempting to Override a Local Parameter
If you try to override a localparam
, you’ll get an error. For example:
module tb;
// This will cause an error, since LENGTH is a localparam
// adder #(.WIDTH(16), .LENGTH(5)) add_instance2 (...);
endmodule
However, you can assign a localparam
based on regular parameters:
module adder #(parameter WIDTH = 8, PARAM_LENGTH = 4) (
input wire [WIDTH-1:0] a,
input wire [WIDTH-1:0] b,
output wire [WIDTH-1:0] sum
);
// Local parameter derived from a regular parameter
localparam LENGTH = 2 + PARAM_LENGTH;
assign sum = a + b;
endmodule
Specify Parameters
Specify parameters are used to define timing and delay values in Verilog. They are declared using the specparam
keyword and are typically used within a specify
block to model the timing characteristics of a module.
Example of Specify Block
specify
specparam t_rise = 200, t_fall = 150;
specparam clk_to_q = 70, d_to_q = 100;
endspecify
You can also declare specify parameters inside the 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 with specparam | Declared with parameter |
Can be declared in specify block | Declared within the main module |
Can be overridden with SDF | Overridden with module instance parameters or defparam |
Conclusion
Verilog parameters make your designs flexible and reusable, allowing you to adjust values at compile time. Whether you’re using module parameters, local parameters, or specify parameters, understanding how to properly implement them will help you create more efficient and scalable designs. By following the guidelines and examples in this article, you can leverage the power of Verilog parameters in your own projects.