Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
    • Parameterization and Generate Hub
      • Parameters vs localparams
      • Generate loops and conditionals
      • Avoid defparam overrides
    • Top-Level Integration Hub
    • Synthesis Intent Hub
    • Style and Static Checks Hub
    • Assertions in RTL Hub
    • Formal Verification Basics Hub
    • Reusable SystemVerilog for RTL
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

←Parameterization and Generate Hub
  1. Topics
  2. RTL Design
  3. Advanced RTL
  4. Parameterization and Generate Hub
  5. Parameters vs localparams
On this page

    Part 8 of 9, lesson 1 of 28Part 8 of 9/Advanced/Lesson 1 of 28Advanced

    Parameters vs localparams

    Parameters set what callers configure; localparams derive the rest, including N-safe width math.

    A width written twice drifts: one edit changes the parameter and not the copy, and the bus silently truncates. With N equal to 1, $clog2(N) gives 0 and the select range becomes [-1:0], which is two bits, not none.

    After this lesson you can: split a module's inputs into parameters and derived localparams, override them by name, and guard the N equal to 1 case.

    Parameters set the interface

    A parameter is an input to elaboration. The instantiating parent sets it with a named override.

    Use parameters for externally configurable values like width or depth. Defaults in the declaration keep the common case short.

    Kind

    Set by

    Example

    parameter

    The parent, at instantiation

    WIDTH, LANES, DEPTH

    localparam

    Nobody: derived inside the module

    Address bits from $clog2(DEPTH)

    Localparams derive the rest

    A localparam cannot be overridden from outside. It computes dependent dimensions in one place, so bus widths cannot mismatch.

    Width math belongs here. One formula feeds every use, and the N equal to 1 case needs a guard:

    design.sv
    1
    2
    3
    4
    5
    // snippet
    // Width math with the N = 1 guard ($clog2(1) is 0, so the
    // unguarded range [$clog2(N)-1:0] would become [-1:0]).
    localparam int AW = (DEPTH > 1) ? $clog2(DEPTH) : 1;
    input logic [AW-1:0] addr;

    Common mistakes

    Passing parameters positionally, or duplicating a derived width by hand. Name every override, and let one localparam formula feed every dependent range.

    Likely question: why is a derived width a localparam instead of a second parameter?

    • Single source: the width follows its parameter by formula.

    • Override-proof: no instantiation can set it inconsistently.

    • Reviewable: the formula sits next to the parameter it derives from.

    Check yourself A parent instantiates a fifo with #(.DEPTH(4), .AW(2)) where AW is a localparam derived from DEPTH. What happens?

    The build fails at elaboration: overriding a localparam is an error (iverilog reports Cannot override localparam), not a silent ignore. Remove .AW(2) and pass only #(.DEPTH(4)); AW then recomputes from DEPTH on its own, so the address width always matches the depth.

    Check yourself A select is declared [$clog2(N)-1:0] and N is 1. What is the range, and what is the fix?

    The range is [-1:0]. Use localparam int SW = (N > 1) ? $clog2(N) : 1; and declare [SW-1:0].

    Testbench

    The lane array plus guarded DUT select ports for N=1 and N=5. The finite bench checks actual elaborated port sizes, every legal selection, and the defined zero result for spare binary codes. N and WIDTH must be positive.

    lane_array.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    module lane_array #(
      parameter int LANES = 4,
      parameter int WIDTH = 16
    ) (
      input  logic [LANES*WIDTH-1:0] in_flat,
      output logic [LANES*WIDTH-1:0] out_flat
    );
      genvar i;
      generate
        for (i = 0; i < LANES; i++) begin : GEN_LANE
          assign out_flat[i*WIDTH +: WIDTH] = in_flat[i*WIDTH +: WIDTH];
        end
      endgenerate
    endmodule
    
    module lane_select #(
      parameter int N = 1,
      parameter int WIDTH = 8,
      localparam int SW = (N > 1) ? $clog2(N) : 1
    ) (
      input logic [N*WIDTH-1:0] lanes,
      input logic [SW-1:0] sel,
      output logic [WIDTH-1:0] y
    );
      always_comb begin
        y = '0;
        if (sel < N) y = lanes[sel*WIDTH +: WIDTH];
      end
    endmodule
    
    • A parameter is an input to elaboration; a localparam derives from it and cannot be overridden.

    • Compute each dependent width once, in one localparam formula.

    • Guard $clog2 for N equal to 1, and require N and WIDTH to be positive.

    Rule of thumb: if a value follows from another, make it a localparam, never a second parameter.

    How this was checked

    Testbench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It is a finite digital check of elaborated port sizes, every legal selection and the zero result for spare codes; it is not physical signoff.

    • Code lab: Parameters and localparams

    • Interview: run a mock interview on this topic

    Related topics

    • Generate loops and conditionals

    • Avoid defparam overrides

    Previous

    246 / 350

    Next: Generate loops and conditionals

    Up next

    Up nextGenerate loops and conditionals

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Avoid defparam overrides