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:

  1. Module Parameters
  2. Specify Parameters
  3. 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, and FIFO_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 ParameterModule Parameter
Declared with specparamDeclared with parameter
Can be declared in specify blockDeclared within the main module
Can be overridden with SDFOverridden 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.

Scroll to Top