top of page

6.1. Parameter and Localparam in Verilog: Building Flexible, Reusable Designs

When designing digital circuits in Verilog, creating reusable and configurable modules is essential for efficient development. Two key constructs that enable this flexibility are parameter and localparam. Understanding when and how to use each can significantly improve your RTL design quality.

6.1.1. What are Parameters?

Parameters in Verilog act as constants that can be customized when a module is instantiated. They allow you to create generic, scalable designs without rewriting code for different configurations.

Naming Conventions

​

  • Use UPPERCASE for parameters to distinguish them from regular signals

  • Use descriptive, self-documenting names

  • Include units in the name when relevant (e.g., TIMEOUT_CYCLES, CLOCK_FREQ_MHZ)

 Verilog

// Good examples

parameter DATA_WIDTH = 32;

parameter FIFO_DEPTH = 256;

parameter TIMEOUT_CYCLES = 1000;

parameter CLOCK_FREQ_MHZ = 100;

// Poor examples

parameter w = 32;           // Too cryptic

parameter data = 32;        // Unclear what it represents

parameter timeout = 1000;   // No units specified

Parameter Declaration Syntax

​

There are two primary ways to declare parameters in Verilog:

1. Traditional Parameter Declaration:

 Verilog

module memory_controller (

    input clk,

    input rst_n,

    // other ports

);

    parameter ADDR_WIDTH = 32;

    parameter DATA_WIDTH = 64;

    parameter BURST_LENGTH = 8;

    parameter TIMING_MODE = "FAST";

    // Module implementation

endmodule

2. ANSI-Style Parameter Declaration (Recommended):

 Verilog

module memory_controller #(

    parameter ADDR_WIDTH = 32,

    parameter DATA_WIDTH = 64,

    parameter BURST_LENGTH = 8,

    parameter TIMING_MODE = "FAST"

)(

    input clk,

    input rst_n,

    input [ADDR_WIDTH-1:0] address,

    input [DATA_WIDTH-1:0] write_data,

    output reg [DATA_WIDTH-1:0] read_data

);

    // Module implementation

endmodule

The ANSI-style declaration is preferred in modern Verilog coding because it clearly separates parameters from the port list and improves code readability.

​

Key Characteristics:

​

  • Declared at module level using the parameter keyword

  • Can be overridden during module instantiation

  • Visible to parent modules

  • Ideal for making modules configurable and reusable

​

Use Parameters when:

​

  • You want users to configure the module during instantiation

  • Designing reusable IP blocks with variable widths, depths, or timing

  • Creating parameterized designs for different applications

6.1.2. Parameter Overriding Methods

Parameters can be overridden during module instantiation using three different methods:

6.1.2.1. Method 1: Defparam Statement (Not Recommended)

 Verilog

module top;

    memory_controller mem_ctrl (

        .clk(clk),

        .rst_n(rst_n)

    );

    

    defparam mem_ctrl.ADDR_WIDTH = 24;

    defparam mem_ctrl.DATA_WIDTH = 32;

endmodule

Why not recommended? The defparam statement can override parameters from anywhere in the hierarchy, making code harder to trace and maintain.

6.1.2.2. Method 2: Ordered Parameter Override

 Verilog

module top;

    memory_controller #(

        24,  // ADDR_WIDTH

        32,  // DATA_WIDTH

        16,  // BURST_LENGTH

        "SLOW" // TIMING_MODE

    ) mem_ctrl (

        .clk(clk),

        .rst_n(rst_n),

        .address(addr),

        .write_data(wdata),

        .read_data(rdata)

    );

endmodule

Drawback: You must maintain the exact order of parameters and specify all parameters even if you only want to change one.

6.1.2.3. Method 3: Named Parameter Override (Recommended)

 Verilog

module top;

    memory_controller #(

        .ADDR_WIDTH(24),

        .DATA_WIDTH(32),

        .TIMING_MODE("SLOW")

        // BURST_LENGTH uses default value of 8

    ) mem_ctrl (

        .clk(clk),

        .rst_n(rst_n),

        .address(addr),

        .write_data(wdata),

        .read_data(rdata)

    );

endmodule

Advantages: Clear, maintainable, order-independent, and allows selective overriding.

6.1.3. Parameter Data Types

​

Parameters can hold various types of values:

​

6.1.3.1. Integer Parameters:

​

parameter DATA_WIDTH = 32;

parameter FIFO_DEPTH = 256;

parameter PIPELINE_STAGES = 4;

​

6.1.3.2. Real Parameter:

​

parameter CLOCK_PERIOD = 10.5;  // For timing calculations

parameter DUTY_CYCLE = 0.5;

​

6.1.3.3. String Parameters:

​

parameter MODE = "SYNCHRONOUS";

parameter FILE_NAME = "memory_init.hex";

​

6.1.3.4. Bit Vector Parameters:

​

parameter RESET_VALUE = 8'hAA;

parameter INIT_STATE = 4'b1010;

Example: Parameterized FIFO Design

 Verilog

module sync_fifo #(

    parameter DATA_WIDTH = 8,

    parameter DEPTH = 16,

    parameter ALMOST_FULL_THRESHOLD = 14,

    parameter ALMOST_EMPTY_THRESHOLD = 2

)(

    input wire clk,

    input wire rst_n,

    input wire wr_en,

    input wire rd_en,

    input wire [DATA_WIDTH-1:0] wr_data,

    output reg [DATA_WIDTH-1:0] rd_data,

    output wire full,

    output wire empty,

    output wire almost_full,

    output wire almost_empty

);

    // Localparams for derived and internal constants

    localparam ADDR_WIDTH = $clog2(DEPTH);

    localparam PTR_WIDTH = ADDR_WIDTH + 1;  // Extra bit for full/empty detection

    // Memory array

    reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];

    // Pointers

    reg [PTR_WIDTH-1:0] wr_ptr;

    reg [PTR_WIDTH-1:0] rd_ptr;

    // Count of elements

    wire [PTR_WIDTH-1:0] count;

    assign count = wr_ptr - rd_ptr;

    // Status signals

    assign full = (count == DEPTH);

    assign empty = (count == 0);

    assign almost_full = (count >= ALMOST_FULL_THRESHOLD);

    assign almost_empty = (count <= ALMOST_EMPTY_THRESHOLD);

    // Write operation

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n) begin

            wr_ptr <= 0;

        end else if (wr_en && !full) begin

            mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data;

            wr_ptr <= wr_ptr + 1;

        end

    end 

    // Read operation

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n) begin

            rd_ptr <= 0;

            rd_data <= 0;

        end else if (rd_en && !empty) begin

            rd_data <= mem[rd_ptr[ADDR_WIDTH-1:0]];

            rd_ptr <= rd_ptr + 1;

        end

    end 

endmodule

Instantiation Example:

 Verilog

Instantiation Example:

// 32-bit wide, 64-deep FIFO

sync_fifo #(

    .DATA_WIDTH(32),

    .DEPTH(64),

    .ALMOST_FULL_THRESHOLD(60),

    .ALMOST_EMPTY_THRESHOLD(4)

) data_fifo (

    .clk(clk),

    .rst_n(rst_n),

    .wr_en(fifo_wr),

    .rd_en(fifo_rd),

    .wr_data(data_in),

    .rd_data(data_out),

    .full(fifo_full),

    .empty(fifo_empty),

    .almost_full(fifo_afull),

    .almost_empty(fifo_aempty)

);

 Verilog

module tb_sync_fifo;

    // Parameters

    parameter DATA_WIDTH = 8;

    parameter DEPTH = 16;

    parameter ALMOST_FULL_THRESHOLD = 14;

    parameter ALMOST_EMPTY_THRESHOLD = 2;

    parameter CLK_PERIOD = 10;

    

    // Signals

    reg clk;

    reg rst_n;

    reg wr_en;

    reg rd_en;

    reg [DATA_WIDTH-1:0] wr_data;

    wire [DATA_WIDTH-1:0] rd_data;

    wire full;

    wire empty;

    wire almost_full;

    wire almost_empty;

    

    // Clock generation

    always #(CLK_PERIOD/2) clk = ~clk;

    

    // Instantiate DUT

    sync_fifo #(

        .DATA_WIDTH(DATA_WIDTH),

        .DEPTH(DEPTH),

        .ALMOST_FULL_THRESHOLD(ALMOST_FULL_THRESHOLD),

        .ALMOST_EMPTY_THRESHOLD(ALMOST_EMPTY_THRESHOLD)

    ) dut (

        .clk(clk),

        .rst_n(rst_n),

        .wr_en(wr_en),

        .rd_en(rd_en),

        .wr_data(wr_data),

        .rd_data(rd_data),

        .full(full),

        .empty(empty),

        .almost_full(almost_full),

        .almost_empty(almost_empty)

    );

    

    // Test variables

    integer i, j;

    integer error_count;

    integer test_count;

    reg [DATA_WIDTH-1:0] expected_data;

    reg [DATA_WIDTH-1:0] written_data [0:DEPTH-1];

    

    // Main test sequence

    initial begin

        // Initialize

        initialize();

        

        $display("=== Synchronous FIFO Testbench ===");

        $display("FIFO Depth: %0d, Data Width: %0d bits", DEPTH, DATA_WIDTH);

        $display("Almost Full Threshold: %0d", ALMOST_FULL_THRESHOLD);

        $display("Almost Empty Threshold: %0d", ALMOST_EMPTY_THRESHOLD);

        $display("==================================");

        

        error_count = 0;

        test_count = 0;

        

        // Test 1: Reset Test

        test_reset();

        

        // Test 2: Basic Write-Read Test

        test_basic_write_read();

        

        // Test 3: Simultaneous Write-Read Test

        test_simultaneous_write_read();

        

        // Test 4: Full FIFO Test

        test_full_fifo();

        

        // Test 5: Empty FIFO Test

        test_empty_fifo();

        

        // Test 6: Almost Full/Empty Test

        test_almost_flags();

        

        // Test 7: Overflow/Underflow Test

        test_overflow_underflow();

        

        // Test 8: Random Operations Test

        test_random_operations();

        

        // Test 9: Back-to-Back Operations

        test_back_to_back();

// Summary

        $display("\n=== Test Summary ===");

        $display("Total Tests: %0d", test_count);

        $display("Errors: %0d", error_count);

        if (error_count == 0) begin

            $display("STATUS: ALL TESTS PASSED!");

        end else begin

            $display("STATUS: %0d TESTS FAILED!", error_count);

        end

        

        #100;

        $finish;

    end

    

    // Initialize signals

    task initialize;

        begin

            clk = 0;

            rst_n = 1;

            wr_en = 0;

            rd_en = 0;

            wr_data = 0;

            #100;

        end

    endtask

    

    // Test 1: Reset Test

    task test_reset;

        begin

            $display("\n--- Test 1: Reset Test ---");

            test_count = test_count + 1;

            

            // Apply reset

            rst_n = 0;

            #(CLK_PERIOD * 2);

            

            // Check all outputs after reset

            if (empty !== 1'b1) begin

                $display("ERROR: empty should be 1 after reset, got %b", empty);

                error_count = error_count + 1;

            end

            

            if (full !== 1'b0) begin

                $display("ERROR: full should be 0 after reset, got %b", full);

                error_count = error_count + 1;

            end

            

            if (almost_empty !== 1'b1) begin

                $display("ERROR: almost_empty should be 1 after reset, got %b", almost_empty);

                error_count = error_count + 1;

            end

            

            if (almost_full !== 1'b0) begin

                $display("ERROR: almost_full should be 0 after reset, got %b", almost_full);

                error_count = error_count + 1;

            end

            

            if (rd_data !== 0) begin

                $display("ERROR: rd_data should be 0 after reset, got %h", rd_data);

                error_count = error_count + 1;

            end

            

            // Release reset

            rst_n = 1;

            #(CLK_PERIOD);

            

            $display("Reset Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 2: Basic Write-Read Test

    task test_basic_write_read;

        begin

            $display("\n--- Test 2: Basic Write-Read Test ---");

            test_count = test_count + 1;

            

            // Write data

            for (i = 0; i < 8; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                wr_data = i + 8'h10;

                written_data[i] = i + 8'h10;

            end

            

            @(posedge clk);

            wr_en = 0;

            

            // Read data and verify

            for (i = 0; i < 8; i = i + 1) begin

                @(posedge clk);

                rd_en = 1;

                #1; // Wait for data to appear

                if (rd_data !== written_data[i]) begin

                    $display("ERROR: Read data mismatch at read %0d: expected %h, got %h", 

                            i, written_data[i], rd_data);

                    error_count = error_count + 1;

                end

            end

            

            @(posedge clk);

            rd_en = 0;

            

            if (empty !== 1'b1) begin

                $display("ERROR: FIFO should be empty after reading all data");

                error_count = error_count + 1;

            end

            

            $display("Basic Write-Read Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 3: Simultaneous Write-Read Test

    task test_simultaneous_write_read;

        begin

            $display("\n--- Test 3: Simultaneous Write-Read Test ---");

            test_count = test_count + 1;

            

            // Fill FIFO partially first

            for (i = 0; i < 4; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                wr_data = i + 8'h20;

                written_data[i] = i + 8'h20;

                rd_en = 0;

            end

            

            // Simultaneous write and read

            for (i = 4; i < 12; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                rd_en = 1;

                wr_data = i + 8'h20;

                written_data[i] = i + 8'h20;

                #1;

                // Verify read data matches previously written data

                if (rd_data !== written_data[i-4]) begin

                    $display("ERROR: Simultaneous op mismatch at cycle %0d: expected %h, got %h", 

                            i-4, written_data[i-4], rd_data);

                    error_count = error_count + 1;

                end

            end

            

            @(posedge clk);

            wr_en = 0;

            rd_en = 0;

            

            $display("Simultaneous Write-Read Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 4: Full FIFO Test

    task test_full_fifo;

        begin

            $display("\n--- Test 4: Full FIFO Test ---");

            test_count = test_count + 1;

            

            // Fill FIFO to full

            for (i = 0; i < DEPTH; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                wr_data = i + 8'h30;

                written_data[i] = i + 8'h30;

                rd_en = 0;

            end

            

            @(posedge clk);

            wr_en = 0;

            

            // Check full flag

            if (full !== 1'b1) begin

                $display("ERROR: FIFO should be full");

                error_count = error_count + 1;

            end

            

            // Try to write when full (should be ignored)

            @(posedge clk);

            wr_en = 1;

            wr_data = 8'hFF;

            rd_en = 0;

            #1;

            if (full !== 1'b1) begin

                $display("ERROR: FIFO should remain full when writing to full FIFO");

                error_count = error_count + 1;

            end

            

            @(posedge clk);

            wr_en = 0;

                        $display("Full FIFO Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

 // Test 5: Empty FIFO Test

    task test_empty_fifo;

        begin

            $display("\n--- Test 5: Empty FIFO Test ---");

            test_count = test_count + 1;

            

            // Empty the FIFO

            for (i = 0; i < DEPTH; i = i + 1) begin

                @(posedge clk);

                rd_en = 1;

                wr_en = 0;

            end

            

            @(posedge clk);

            rd_en = 0;

            

            // Check empty flag

            if (empty !== 1'b1) begin

                $display("ERROR: FIFO should be empty");

                error_count = error_count + 1;

            end

            

            // Try to read when empty (should be ignored)

            @(posedge clk);

            rd_en = 1;

            wr_en = 0;

            #1;

            if (empty !== 1'b1) begin

                $display("ERROR: FIFO should remain empty when reading from empty FIFO");

                error_count = error_count + 1;

            end

            

            @(posedge clk);

            rd_en = 0;

            

            $display("Empty FIFO Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 6: Almost Full/Empty Test

    task test_almost_flags;

        begin

            $display("\n--- Test 6: Almost Full/Empty Test ---");

            test_count = test_count + 1;

            

            // Reset and check almost_empty

            rst_n = 0;

            @(posedge clk);

            rst_n = 1;

            @(posedge clk);

            

            if (almost_empty !== 1'b1) begin

                $display("ERROR: almost_empty should be 1 after reset");

                error_count = error_count + 1;

            end

            

            // Write until almost_full threshold

            for (i = 0; i < ALMOST_FULL_THRESHOLD; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                wr_data = i + 8'h40;

                rd_en = 0;

            end

            

            @(posedge clk);

            wr_en = 0;

            

            if (almost_full !== 1'b1) begin

                $display("ERROR: almost_full should be 1 when count >= %0d", ALMOST_FULL_THRESHOLD);

                error_count = error_count + 1;

            end

            

            // Read until almost_empty threshold

            for (i = 0; i < (ALMOST_FULL_THRESHOLD - ALMOST_EMPTY_THRESHOLD); i = i + 1) begin

                @(posedge clk);

                rd_en = 1;

                wr_en = 0;

            end

            

            @(posedge clk);

            rd_en = 0;

            

            if (almost_empty !== 1'b1) begin

                $display("ERROR: almost_empty should be 1 when count <= %0d", ALMOST_EMPTY_THRESHOLD);

                error_count = error_count + 1;

            end

            

            $display("Almost Flags Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 7: Overflow/Underflow Test

    task test_overflow_underflow;

        begin

            $display("\n--- Test 7: Overflow/Underflow Test ---");

            test_count = test_count + 1;

            

            // Fill to full

            for (i = 0; i < DEPTH; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                wr_data = i + 8'h50;

                written_data[i] = i + 8'h50;

                rd_en = 0;

            end

            

            @(posedge clk);

            wr_en = 0;

            

            // Overflow test: write when full

            @(posedge clk);

            wr_en = 1;

            wr_data = 8'hFF;  // This should be ignored

            #1;

            // Verify FIFO still contains original data

            rd_en = 1;

            @(posedge clk);

            #1;

            if (rd_data !== written_data[0]) begin

                $display("ERROR: Overflow corrupted FIFO data");

                error_count = error_count + 1;

            end

            rd_en = 0;

            

            // Empty FIFO

            for (i = 0; i < DEPTH; i = i + 1) begin

                @(posedge clk);

                rd_en = 1;

                wr_en = 0;

            end

            

            @(posedge clk);

            rd_en = 0;

            

            // Underflow test: read when empty

            @(posedge clk);

            rd_en = 1;

            #1;

            if (rd_data !== written_data[DEPTH-1]) begin

                $display("ERROR: Underflow should not change rd_data");

                error_count = error_count + 1;

            end

            

            $display("Overflow/Underflow Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 8: Random Operations Test

    task test_random_operations;

        integer rand_wr, rand_rd;

        begin

            $display("\n--- Test 8: Random Operations Test ---");

            test_count = test_count + 1;

            

            // Reset

            rst_n = 0;

            @(posedge clk);

            rst_n = 1;

            

            // Random operations for 100 cycles

            for (i = 0; i < 100; i = i + 1) begin

                @(posedge clk);

                rand_wr = $random & 1;

                rand_rd = $random & 1;

                

                wr_en = rand_wr;

                rd_en = rand_rd;

                wr_data = $random;

                

                // Check flag consistency

                #1;

                if (full && !empty) begin

                    // OK

                end else if (empty && !full) begin

                    // OK

                end else if (full && empty) begin

                    $display("ERROR: FIFO cannot be both full and empty");

                    error_count = error_count + 1;

                end

                

                if (almost_full && !full) begin

                    // OK

                end else if (almost_full && full) begin

                    // OK

                end

                

                if (almost_empty && !empty) begin

                    // OK

                end else if (almost_empty && empty) begin

                    // OK

                end

            end

            

            @(posedge clk);

            wr_en = 0;

            rd_en = 0;

            

            $display("Random Operations Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Test 9: Back-to-Back Operations

    task test_back_to_back;

        begin

            $display("\n--- Test 9: Back-to-Back Operations Test ---");

            test_count = test_count + 1;

            

            // Reset

            rst_n = 0;

            @(posedge clk);

            rst_n = 1;

            

            // Back-to-back writes

            for (i = 0; i < 8; i = i + 1) begin

                @(posedge clk);

                wr_en = 1;

                wr_data = i + 8'h60;

                written_data[i] = i + 8'h60;

            end

            

            @(posedge clk);

            wr_en = 0;

            

            // Back-to-back reads

            for (i = 0; i < 8; i = i + 1) begin

                @(posedge clk);

                rd_en = 1;

                #1;

                if (rd_data !== written_data[i]) begin

                    $display("ERROR: Back-to-back read mismatch at %0d: expected %h, got %h", 

                            i, written_data[i], rd_data);

                    error_count = error_count + 1;

                end

            end

            

            @(posedge clk);

            rd_en = 0;

            

            $display("Back-to-Back Operations Test: %s", (error_count == 0) ? "PASS" : "FAIL");

        end

    endtask

    

    // Monitor to track FIFO status

    initial begin

        $display("\nTime\tWR_EN\tRD_EN\tWR_DATA\tRD_DATA\tFULL\tEMPTY\tA_FULL\tA_EMPTY");

        $display("-------------------------------------------------------------------");

        forever begin

            @(posedge clk);

            #1;

            $display("%0t\t%b\t%b\t%h\t%h\t%b\t%b\t%b\t%b",

                    $time, wr_en, rd_en, wr_data, rd_data, full, empty, almost_full, almost_empty);

        end

    end

    

    // VCD dump for waveform viewing

    initial begin

        $dumpfile("sync_fifo.vcd");

        $dumpvars(0, tb_sync_fifo);

    end

endmodule

6.1.4. Advanced Parameter Techniques

​

6.1.4.1. Using generate block with Parameters

​

Parameters become extremely powerful when combined with generate statements for creating scalable, parameterized hardware:

 Verilog

// Adder unit module (pipelined adder)

module adder_unit #(

    parameter WIDTH = 8

)(

    input wire clk,

    input wire [WIDTH-1:0] a,

    input wire [WIDTH-1:0] b,

    output reg [WIDTH-1:0] sum

);

    always @(posedge clk) begin

        sum <= a + b;

    end

endmodule

 Verilog

module parallel_adder #(

    parameter NUM_STAGES = 4,

    parameter DATA_WIDTH = 8

)(

    input wire clk,

    input wire [NUM_STAGES*DATA_WIDTH-1:0] a,

    input wire [NUM_STAGES*DATA_WIDTH-1:0] b,

    output wire [NUM_STAGES*DATA_WIDTH-1:0] sum

);

    localparam TOTAL_WIDTH = NUM_STAGES * DATA_WIDTH;

 

    genvar i;

    generate

        for (i = 0; i < NUM_STAGES; i = i + 1) begin : adder_stages

            wire [DATA_WIDTH-1:0] a_slice;

            wire [DATA_WIDTH-1:0] b_slice;

            wire [DATA_WIDTH-1:0] sum_slice;

            

            assign a_slice = a[i*DATA_WIDTH +: DATA_WIDTH];

            assign b_slice = b[i*DATA_WIDTH +: DATA_WIDTH];

            assign sum[i*DATA_WIDTH +: DATA_WIDTH] = sum_slice;

            

            adder_unit #(

                .WIDTH(DATA_WIDTH)

            ) adder_inst (

                .clk(clk),

                .a(a_slice),

                .b(b_slice),

                .sum(sum_slice)

            );

        end

    endgenerate

endmodule

 Verilog

​​

module tb_parallel_adder;

    // Parameters

    parameter NUM_STAGES = 4;

    parameter DATA_WIDTH = 8;

    parameter CLK_PERIOD = 10;

    

    // Signals

    reg clk;

    reg [NUM_STAGES*DATA_WIDTH-1:0] a;

    reg [NUM_STAGES*DATA_WIDTH-1:0] b;

    wire [NUM_STAGES*DATA_WIDTH-1:0] sum;

    

    // Clock generation

    always #(CLK_PERIOD/2) clk = ~clk;

    

    // Instantiate DUT

    parallel_adder #(

        .NUM_STAGES(NUM_STAGES),

        .DATA_WIDTH(DATA_WIDTH)

    ) dut (

        .clk(clk),

        .a(a),

        .b(b),

        .sum(sum)

    );

    

    // Test cases

    initial begin

        // Initialize

        clk = 0;

        a = 0;

        b = 0;

        

        // Wait for reset

        #100;

        

        $display("=== Parallel Adder Testbench ===");

        $display("Time\t Test Case\t Expected Sum\t Actual Sum\t Status");

        $display("----------------------------------------------------------------");

        

        // Test Case 1: Simple addition

        a = {8'd10, 8'd20, 8'd30, 8'd40};  // [40, 30, 20, 10]

        b = {8'd5,  8'd15, 8'd25, 8'd35};  // [35, 25, 15, 5]

        #CLK_PERIOD;

        check_result("Test 1", {8'd75, 8'd55, 8'd35, 8'd15});

        

        // Test Case 2: Maximum values

        a = {8'd255, 8'd255, 8'd255, 8'd255};

        b = {8'd1,   8'd1,   8'd1,   8'd1};

        #CLK_PERIOD;

        check_result("Test 2", {8'd0, 8'd0, 8'd0, 8'd0}); // Overflow expected

        

        // Test Case 3: Mixed values

        a = {8'd100, 8'd50, 8'd200, 8'd25};

        b = {8'd50,  8'd100, 8'd55, 8'd75};

        #CLK_PERIOD;

        check_result("Test 3", {8'd150, 8'd150, 8'd255, 8'd100});

        

        // Test Case 4: Zero values

        a = {8'd0, 8'd0, 8'd0, 8'd0};

        b = {8'd0, 8'd0, 8'd0, 8'd0};

        #CLK_PERIOD;

        check_result("Test 4", {8'd0, 8'd0, 8'd0, 8'd0});

        

        // Test Case 5: Random values

        a = {8'd123, 8'd45, 8'd67, 8'd89};

        b = {8'd77,  8'd155, 8'd33, 8'd166};

        #CLK_PERIOD;

        check_result("Test 5", {8'd200, 8'd200, 8'd100, 8'd255});

        

        // Finish simulation

        #100;

        $display("=== Simulation Complete ===");

        $finish;

    end

    

    // Task to check results

    task check_result;

        input string test_name;

        input [NUM_STAGES*DATA_WIDTH-1:0] expected;

        

        reg [DATA_WIDTH-1:0] exp_slice, act_slice;

        integer i;

        reg all_pass;

        

        begin

            all_pass = 1;

            

            $write("%0t\t %s", $time, test_name);

            

            // Check each stage

            for (i = 0; i < NUM_STAGES; i = i + 1) begin

                exp_slice = expected[i*DATA_WIDTH +: DATA_WIDTH];

                act_slice = sum[i*DATA_WIDTH +: DATA_WIDTH];

                

                if (exp_slice !== act_slice) begin

                    all_pass = 0;

                end

            end

            

            // Display results

            $write("\t ");

            for (i = NUM_STAGES-1; i >= 0; i = i - 1) begin

                $write("%0d ", expected[i*DATA_WIDTH +: DATA_WIDTH]);

            end

            

            $write("\t ");

            for (i = NUM_STAGES-1; i >= 0; i = i - 1) begin

                $write("%0d ", sum[i*DATA_WIDTH +: DATA_WIDTH]);

            end

            

            if (all_pass) begin

                $display("\t PASS");

            end else begin

                $display("\t FAIL");

                $display("ERROR: Mismatch detected!");

                for (i = 0; i < NUM_STAGES; i = i + 1) begin

                    exp_slice = expected[i*DATA_WIDTH +: DATA_WIDTH];

                    act_slice = sum[i*DATA_WIDTH +: DATA_WIDTH];

                    if (exp_slice !== act_slice) begin

                        $display("  Stage %0d: Expected %0d, Got %0d", 

                                i, exp_slice, act_slice);

                    end

                end

            end

        end

    endtask

    

    // Monitor to track signals

    initial begin

        $monitor("Time=%0t: a=[%0d %0d %0d %0d] b=[%0d %0d %0d %0d] sum=[%0d %0d %0d %0d]",

                $time,

                a[31:24], a[23:16], a[15:8], a[7:0],

                b[31:24], b[23:16], b[15:8], b[7:0],

                sum[31:24], sum[23:16], sum[15:8], sum[7:0]);

    end

    

    // VCD dump for waveform viewing

    initial begin

        $dumpfile("parallel_adder.vcd");

        $dumpvars(0, tb_parallel_adder);

    end

endmodule

6.1.4.2. Conditional compilation based on Parameters

 Verilog

module configurable_processor #(

    parameter ENABLE_MULTIPLIER = 1,

    parameter ENABLE_DIVIDER = 0,

    parameter ENABLE_CACHE = 1,

    parameter CACHE_SIZE = 1024

)(

    input wire clk,

    input wire rst_n,

    // processor interface

);

 

    // Conditional hardware instantiation

    generate

        if (ENABLE_MULTIPLIER) begin : gen_multiplier

            localparam MUL_LATENCY = 3;

            // Multiplier logic

            reg [31:0] mul_result;

            // ...

        end

        

        if (ENABLE_DIVIDER) begin : gen_divider

            localparam DIV_LATENCY = 32;

            // Divider logic

            reg [31:0] div_result;

            // ...

        end

        

        if (ENABLE_CACHE) begin : gen_cache

            localparam CACHE_LINE_SIZE = 64;

            localparam NUM_CACHE_LINES = CACHE_SIZE / CACHE_LINE_SIZE;

            localparam INDEX_BITS = $clog2(NUM_CACHE_LINES);

            

            // Cache implementation

            reg [CACHE_LINE_SIZE*8-1:0] cache_memory [0:NUM_CACHE_LINES-1];

            // ...

        end else begin : gen_no_cache

            // Direct memory access without cache

        end

    endgenerate

 

endmodule

6.1.4.3. Parameter Validation and Error Checking

 Verilog

module validated_fifo #(

    parameter DATA_WIDTH = 8,

    parameter DEPTH = 16,

    parameter ALMOST_FULL = 14

)(

    // ports

);

 

    // Compile-time parameter validation

    localparam PARAM_CHECK_1 = (DATA_WIDTH > 0) && (DATA_WIDTH <= 128);

    localparam PARAM_CHECK_2 = (DEPTH >= 2) && (DEPTH <= 4096);

    localparam PARAM_CHECK_3 = (ALMOST_FULL < DEPTH);

    localparam PARAM_CHECK_4 = (DEPTH & (DEPTH - 1)) == 0;  // Power of 2

    

    // Generate compile error if parameters are invalid

    initial begin

        if (!PARAM_CHECK_1)

            $error("DATA_WIDTH must be between 1 and 128");

        if (!PARAM_CHECK_2)

            $error("DEPTH must be between 2 and 4096");

        if (!PARAM_CHECK_3)

            $error("ALMOST_FULL must be less than DEPTH");

        if (!PARAM_CHECK_4)

            $error("DEPTH must be a power of 2");

    end

    

    localparam ADDR_WIDTH = $clog2(DEPTH);

    

    // FIFO implementation

    

endmodule

6.1.4.4. Common Use Cases and Design Patterns

​

Pattern 1: Protocol-Agnostic Bus Interface

 Verilog

module bus_interface #(

    parameter ADDR_WIDTH = 32,

    parameter DATA_WIDTH = 32,

    parameter PROTOCOL = "AXI"  // "AXI", "AHB", "APB"

)(

    input wire clk,

    input wire rst_n,

    // Generic interface ports

);

 

    localparam IS_AXI = (PROTOCOL == "AXI");

    localparam IS_AHB = (PROTOCOL == "AHB");

    localparam IS_APB = (PROTOCOL == "APB");

    

    generate

        if (IS_AXI) begin : axi_protocol

            // AXI-specific implementation

            localparam ID_WIDTH = 4;

            localparam BURST_WIDTH = 2;

            // ...

        end else if (IS_AHB) begin : ahb_protocol

            // AHB-specific implementation

            localparam TRANS_WIDTH = 2;

            localparam BURST_WIDTH = 3;

            // ...

        end else if (IS_APB) begin : apb_protocol

            // APB-specific implementation

            // ...

        end

    endgenerate

 

endmodule

Pattern 2: Configurable Pipeline Stages

 Verilog

module pipeline_multiplier #(

    parameter DATA_WIDTH = 32,

    parameter PIPELINE_STAGES = 3  // Can be 0 (combinational) to 4

)(

    input wire clk,

    input wire rst_n,

    input wire [DATA_WIDTH-1:0] a,

    input wire [DATA_WIDTH-1:0] b,

    input wire valid_in,

    output wire [2*DATA_WIDTH-1:0] product,

    output wire valid_out

);

 

    localparam PRODUCT_WIDTH = 2 * DATA_WIDTH;

    localparam HAS_PIPELINE = (PIPELINE_STAGES > 0);

    

    // Partial product calculation

    wire [PRODUCT_WIDTH-1:0] partial_product;

    assign partial_product = a * b;

    

    generate

        if (PIPELINE_STAGES == 0) begin : no_pipeline

            // Purely combinational

            assign product = partial_product;

            assign valid_out = valid_in;

        end

        else if (PIPELINE_STAGES == 1) begin : single_stage

            reg [PRODUCT_WIDTH-1:0] product_reg;

            reg valid_reg;

            

            always @(posedge clk or negedge rst_n) begin

                if (!rst_n) begin

                    product_reg <= 0;

                    valid_reg <= 0;

                end else begin

                    product_reg <= partial_product;

                    valid_reg <= valid_in;

                end

            end

            

            assign product = product_reg;

            assign valid_out = valid_reg;

        end

        else begin : multi_stage

            // Multi-stage pipeline with intermediate registers

            reg [PRODUCT_WIDTH-1:0] pipe_data [0:PIPELINE_STAGES-1];

            reg [PIPELINE_STAGES-1:0] pipe_valid;

            

            integer i;

            always @(posedge clk or negedge rst_n) begin

                if (!rst_n) begin

                    for (i = 0; i < PIPELINE_STAGES; i = i + 1) begin

                        pipe_data[i] <= 0;

                    end

                    pipe_valid <= 0;

                end else begin

                    // First stage

                    pipe_data[0] <= partial_product;

                    pipe_valid[0] <= valid_in;

                    

                    // Subsequent stages

                    for (i = 1; i < PIPELINE_STAGES; i = i + 1) begin

                        pipe_data[i] <= pipe_data[i-1];

                        pipe_valid[i] <= pipe_valid[i-1];

                    end

                end

            end

            

            assign product = pipe_data[PIPELINE_STAGES-1];

            assign valid_out = pipe_valid[PIPELINE_STAGES-1];

        end

    endgenerate

 

endmodule

Constant variable

LocalParam

© Copyright 2025 VLSI Mentor. All Rights Reserved.©

Connect with us

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
bottom of page