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

AboutContactPrivacyTerms
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
    • Top-Level Integration Hub
      • Top-down vs bottom-up integration
      • Port list discipline
      • Parameter propagation across hierarchy
    • 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

‹Top-Level Integration Hub
  1. Topics
  2. RTL Design
  3. Advanced RTL
  4. Top-Level Integration Hub
  5. Parameter propagation across hierarchy

Part 8 · Advanced · Advanced

Parameter propagation across hierarchy

One source of dimensions at the top, named passing down two levels, and the width contract.

On this page

    Why this matters

    A leaf that hard-codes a 32-bit width behind 16-bit wires still passes a data round trip, because zero-extension hides the mismatch. Only a non-default test plus width lint catches it.

    After this lesson you can: pass one set of dimensions from the top through two levels by name, and check that each descendant received the value that was requested.

    One source of dimensions

    Key dimensions flow from the top level to the leaves using a single source of truth. Hard-coded leaf constants diverge from integration intent.

    Each level passes values by name. The top owns DATA_W and FIFO_D, the fabric maps them onto DATA_W and DEPTH, and the fifo receives WIDTH and DEPTH.

    The values the testbench requests, 16 and 8, travel from soc_top through u_fabric to u_cmd_fifo; the names change at two boundaries but the values do not

    Read the boxes from the outside in and follow one value, such as 8, through every level.

    • 8 changes name once. It is FIFO_D in the top and DEPTH from the fabric down, because the fabric's override is .DEPTH(FIFO_D).

    • 16 changes name once. It is DATA_W down to the fabric and WIDTH in the fifo, through .WIDTH(DATA_W).

    • The value never changes. The bench reads dut.u_fabric.u_cmd_fifo.WIDTH and DEPTH and requires 16 and 8.

    Level

    Parameter

    Arrives as

    soc_top

    DATA_W, FIFO_D

    Owned here

    fabric

    DATA_W, DEPTH

    Named passing from the top

    param_fifo

    WIDTH, DEPTH

    Named passing from the fabric

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // snippet
    // the legacy named-passing shape, kept with real ports
    fabric #(
      .DATA_W (DATA_W),
      .DEPTH  (FIFO_D)
    ) u_fabric (
      .clk_i   (clk_i),
      .rst_ni  (rst_ni),
      ...
    );

    The width contract

    Exact connection sizing is enforced by lint. A $bits(wdata_i) != DATA_W check inside the module is tautological: the declaration already uses DATA_W. A 16-bit unsigned wire driving a 24-bit input is zero-extended; a wider output driving a narrower receiver is truncated. Signed input extension depends on the connected expression. Treat port-width lint diagnostics as errors. The bench separately checks actual descendant parameter values, depth behavior, and preservation of selected words; data round trips alone can miss a 32-bit leaf behind 16-bit wires.

    The fifo here is a power-of-two template: DEPTH must be at least 2 and a power of two, and g_depth_check rejects anything else at elaboration. For other depths, use the modulo-wrap fifo in the avoid-defparam-overrides lesson of the parameterization and generate chapter, whose pointers wrap at DEPTH - 1 for any depth.

    Depth is checked functionally: with FIFO_D=8, the bench requires space before each write and full only after the eighth accepted write. Exact descendant parameter checks provide separate configuration evidence.

    Common mistakes

    Common pitfall

    Copying a dimension into each leaf as a literal creates two sources of truth. The copies agree on the day they are written and diverge on every later edit. Pass the top-level parameter by name instead.

    Interview

    Likely question: how do you check parameter propagation? Use non-default values, compare actual fabric and leaf parameters with the top's requested values, require width lint, then check full after exactly FIFO_D accepted writes and selected word round trips. These are complementary checks.

    Check yourself The top sets DATA_W to 16 but the leaf was also edited to 24. What catches it?

    Lint, not elaboration: connecting a 16-bit signal to a 24-bit port only warns and pads the port in simulators, so run lint with width warnings as errors. A padded port can still round-trip the narrower word, so the simulation passes and only lint flags it.

    Check yourself Why does the testbench use 16 and 8 instead of the default 32 and 16?

    Non-default requests expose missing forwarding in the descendant-parameter checks. Behavior alone need not fail: a default 32-bit leaf can preserve all tested 16-bit words. That counterexample is source reasoning, not an executed mutation.

    Testbench

    Two hierarchy levels with non-default dimensions. The finite bench checks exact descendant configuration separately from selected data preservation and full/empty behavior. Connection lint remains a separate obligation.

    param_fifo.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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    module param_fifo #(
      parameter int WIDTH = 32,
      parameter int DEPTH = 16
    ) (
      input  logic             clk_i,
      input  logic             rst_ni,
      input  logic             wr_i,
      input  logic [WIDTH-1:0] wdata_i,
      input  logic             rd_i,
      output logic [WIDTH-1:0] rdata_o,
      output logic             full_o,
      output logic             empty_o
    );
      localparam int AW = $clog2(DEPTH);
      logic [WIDTH-1:0] mem [DEPTH];
      logic [AW-1:0] wptr, rptr;
      logic [AW:0] count;
    
      // elaboration-time contract: power of two, DEPTH >= 2 (this template wraps pointers by overflow)
      if (DEPTH < 2 || DEPTH != (1 << AW)) begin : g_depth_check
        $error("param_fifo: DEPTH must be a power of two and at least 2");
      end
    
      assign full_o = (count == DEPTH);
      assign empty_o = (count == '0);
    
      always_ff @(posedge clk_i or negedge rst_ni) begin
        if (!rst_ni) begin
          wptr <= '0;
          rptr <= '0;
          count <= '0;
          rdata_o <= '0;
        end else begin
          case ({wr_i && !full_o, rd_i && !empty_o})
            2'b10: begin
              mem[wptr] <= wdata_i;
              wptr <= wptr + 1'b1;
              count <= count + 1'b1;
            end
            2'b01: begin
              rdata_o <= mem[rptr];
              rptr <= rptr + 1'b1;
              count <= count - 1'b1;
            end
            2'b11: begin
              mem[wptr] <= wdata_i;
              rdata_o <= mem[rptr];
              wptr <= wptr + 1'b1;
              rptr <= rptr + 1'b1;
            end
            default: begin end
          endcase
        end
      end
    endmodule
    
    module fabric #(
      parameter int DATA_W = 32,
      parameter int DEPTH = 16
    ) (
      input  logic              clk_i,
      input  logic              rst_ni,
      input  logic              wr_i,
      input  logic [DATA_W-1:0] wdata_i,
      input  logic              rd_i,
      output logic [DATA_W-1:0] rdata_o,
      output logic              full_o,
      output logic              empty_o
    );
      param_fifo #(
        .WIDTH (DATA_W),
        .DEPTH (DEPTH)
      ) u_cmd_fifo (
        .clk_i   (clk_i),
        .rst_ni  (rst_ni),
        .wr_i    (wr_i),
        .wdata_i (wdata_i),
        .rd_i    (rd_i),
        .rdata_o (rdata_o),
        .full_o  (full_o),
        .empty_o (empty_o)
      );
    endmodule
    
    module soc_top #(
      parameter int DATA_W = 32,
      parameter int FIFO_D = 16
    ) (
      input  logic              clk_i,
      input  logic              rst_ni,
      input  logic              wr_i,
      input  logic [DATA_W-1:0] wdata_i,
      input  logic              rd_i,
      output logic [DATA_W-1:0] rdata_o,
      output logic              full_o,
      output logic              empty_o
    );
      fabric #(
        .DATA_W (DATA_W),
        .DEPTH  (FIFO_D)
      ) u_fabric (
        .clk_i   (clk_i),
        .rst_ni  (rst_ni),
        .wr_i    (wr_i),
        .wdata_i (wdata_i),
        .rd_i    (rd_i),
        .rdata_o (rdata_o),
        .full_o  (full_o),
        .empty_o (empty_o)
      );
    endmodule
    

    Remember

    • Keep one source of truth for each dimension at the top, and pass it by name down every level.

    • A narrower-to-wider connection is zero-extended and a wider-to-narrower one is truncated; treat width lint as an error.

    • Test with non-default values and compare each descendant's actual parameters.

    Rule of thumb: a copy of a dimension in a leaf agrees on the day it is written and diverges on the next edit.

    How this was checked

    Testbench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It checks exact descendant configuration separately from selected data preservation and full and empty behaviour, with FIFO_D equal to 8. It is a finite digital check. The default-width counterexample in the check-yourself answer is reasoning from the source, not a run mutation.

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Top-down vs bottom-up integration

    • Port list discipline

    Previous

    251 / 350

    Next: Synthesis Intent Hub

    Up next

    Up nextSynthesis Intent Hub

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Top-down vs bottom-up integration