Pages

Friday, October 23, 2015

How to write generalized code in Verilog - parameter and defparam

The ability to write generic codes is an asset for any language. And Verilog has tools which lets you do that easily.

So, what do I mean by generic codes? Say, you have a big design with lot of adder modules, which have different input sizes and output sizes. Now, the functionality of all the adders are the same - just add two numbers and output the sum. Normally you would have to write N number of separate modules to get this done. But Verilog allows you to specify the size of inputs or outputs or other generic features through a keyword named parameter.

Using parameter you have to write only one single adder module with generalized functionality. You can use this single module to instantiate multiple adders with different sizes. parameter is only used before the compilation. You cannot use it during run time. This means, once the code is running, we cannot change the size of the adder, or say a module.

To explain the concept, I have written a Verilog module with 4 inputs A,B,C and D and 2 outputs Sum and Product. The names are self explanatory. The sizes of all the inputs are defined with a parameter, which means they can be changed at the time of instantiation. The size of outputs depends on the size of inputs, so you don't have to change it explicitly.

Example Module:

//Verilog module - Example for Parameter
module arith(
    A,
    B,
    C,
    D,
    Sum,
    Product
    );

    //Define parameters for the size of adder and multiplier.
    parameter size_adder = 4;
    parameter size_mult = 4;
    
    //input ports and their sizes defined through parameter keyword.
    input [size_adder-1 : 0] A,B;
     input [size_mult-1 : 0] C,D;
    //output ports and their size defined through parameter keyword.
    output [size_adder : 0] Sum;
    output [size_mult*2-1 : 0] Product;
    //Internal variables
    wire [size_adder : 0] Sum;
    wire [size_mult*2-1 : 0] Product;  
    //Adding the inputs to get the sum.
    assign Sum = A+B;
    //multiplying the inputs to get the product.
    assign Product = C*D;
    
endmodule

Instantiation method 1: module instance parameter value assignment

In this method the parameter value is changed using a '#' operator at the time of instantiation. For this example we have multiple parameters. So the values get assigned in the same order as how the parameters are defined in the instantiated module.

module tb_arith;

    // Inputs
    reg [7:0] A,B;
    reg [5:0] C,D;
    // Outputs
    wire [8:0] Sum;
    wire [11:0] Product;

    // Instantiate the Unit Under Test (UUT)
    //The parameter values are passed through "module instance parameter value assignment"
    //Value '8' will be passed to the first parameter defined which is size_adder.
    //Value '6' will be passed to the second parameter defined which is size_mult.
    arith #(8,6) uut1 (
        .A(A), 
        .B(B), 
        .C(C),
        .D(D),
        .Sum(Sum),
        .Product(Product)
    );

    initial begin
        //Apply a set of inputs and wait for 100 ns.
        A = 34;     B = 45;     C = 10;     D = 50;     #100;
        A = 255;        B = 255;        C = 20;     D = 60;     #100;
        A = 121;        B = 13;     C = 30;     D = 10;     #100;
        A = 200;        B = 200;        C = 40;     D = 20;     #100;
    end
      
endmodule

Instantiation method 2: Using defparam keyword

In the second method, we use a second keyword named defparam to get the job done. Using defparam we can specify the new value of the parameter with its hierarchical path. Lets take a look at the example:

module tb_arith;

    // Inputs
    reg [7:0] A,B;
    reg [5:0] C,D;
    // Outputs
    wire [8:0] Sum;
    wire [11:0] Product;

    // Instantiate the Unit Under Test (UUT)
    //Nothing about the parameters are mentioned here.
    arith uut1 (
        .A(A), 
        .B(B), 
        .C(C),
        .D(D),
        .Sum(Sum),
        .Product(Product)
    );
    
    //pass new values to the parameter here. 
    //uut1 is the name of the instantiated module.
    defparam uut1.size_adder = 8;
    defparam uut1.size_mult = 6;

    initial begin
        //Apply a set of inputs and wait for 100 ns.
        A = 34;     B = 45;     C = 10;     D = 50;     #100;
        A = 255;        B = 255;        C = 20;     D = 60;     #100;
        A = 121;        B = 13;     C = 30;     D = 10;     #100;
        A = 200;        B = 200;        C = 40;     D = 20;     #100;
    end
      
endmodule


Simulated Waveform:

Both the testbenches would result the same waveforms when simulated.




No comments:

Post a Comment