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
  • Senior & Interview76
    • Low-Power RTL Hub
    • DFT and Scan Hub
    • Physical Awareness Hub
    • Waveform Debug Hub
    • STA and ECO Hub
    • RTL-to-Implementation Closure
    • DFT-Ready RTL
    • Capstone: Resilient Streaming Peripheral
    • Principal Design Review
    • Senior RTL Interview Questions & Model Answers
      • Interview: Combinational and Arithmetic
        • The latch inference trap
        • Signed arithmetic and width truncation
        • Parameterized RTL and carry-chain guards
        • Glitches on enables and combinational hazards
      • Interview: Sequential and Pipelines
      • Interview: FSM and Control
      • Interview: Timing and STA
      • Interview: CDC and Reset
      • Interview: Memories and Buses
      • Interview: Low-Power Design
      • Interview: Debug and ECO
      • Interview: Coding and Whiteboard
  • Glossary and Notation
Loading lesson

‹Interview: Combinational and Arithmetic
  1. Topics
  2. RTL Design
  3. Senior & Interview
  4. Senior RTL Interview Questions & Model Answers
  5. Interview: Combinational and Arithmetic
  6. The latch inference trap

Part 9 · Senior · Senior

The latch inference trap

Why a missing default builds a latch, and the defaults-first style that keeps combinational logic latch-free.

On this page

    Why this matters

    An if without an else, or a case without a default, makes synthesis hold the old value in a latch. The simulation looks right, and the damage shows up later in timing closure and scan shift.

    After this lesson you can: spot every path that leaves an output unassigned, fix it with defaults first, and say how lint and a select sweep catch the rest.

    The question

    The interviewer shows an always_comb block with an if without an else and a case without a default, then asks whether it infers a latch and how to fix it.

    The question tests complete assignment. Every combinational output must be assigned on every path, or synthesis holds the old value in a latch.

    Model answer

    A latch is inferred whenever a combinational output keeps its previous value on some path. The fix is defaults first: assign every output at the top of the block, then override.

    A default branch alone is not enough. It covers the case but not an earlier incomplete if, while defaults cover both.

    Shape

    Result

    if without else, no prior default

    Latch on the skipped path

    case without default, missing values

    Latch on the missing values

    Defaults first, then if and full case

    Clean combinational logic

    Single-condition cone

    assign needs no defaults at all

    grant_b from the bad example below: it is assigned only when sel is 1 or 2, so every other select must return the old value, which is a latch. A default of 0 gives the mux a real second input.

    Read each panel as the logic synthesis must build for grant_b. The only difference is what drives the mux's 0 input.

    • Left: nothing is assigned for sel 0 or 3, so the 0 input is the output itself. A loop with no clock is a latch.

    • Right: the default gives the 0 input a value, so the loop disappears.

    Interview

    “Defaults first, full case, and a sweep of every select in the testbench.”

    Follow-ups

    • How is it checked? A synthesis log latch report plus lint, and a testbench that sweeps every select value so a held value fails.

    • Defaults first or a `default` branch? Defaults first. A default branch covers only the case, while defaults also cover incomplete if statements above it.

    • What about an intentional latch? It is coded explicitly as a storage element, never inferred by omission. Accidental inference is always a bug.

    Common wrong answers

    “It simulates correctly, so synthesis is fine” misses the point. Simulation shows the held value working, while timing closure and scan shift break on silicon.

    “A default branch fixes everything” is incomplete. It leaves earlier partial if statements uncovered.

    grant_bad.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // bad-example
    module grant_bad (
      input  logic       req_a, req_b,
      input  logic [1:0] sel,
      output logic       grant_a, grant_b
    );
      always_comb begin
        case (sel)
          2'd0: grant_a = req_a;
          2'd1: grant_b = req_b;
          2'd2: begin grant_a = req_a; grant_b = req_b; end
        endcase
      end
    endmodule

    Common pitfall

    At sel == 0, grant_b is unassigned; at sel == 1, grant_a is unassigned; at sel == 3, both are unassigned. Each missing assignment retains previous state and requires a latch. Test transitions between selects, not only the missing default value.

    Check yourself The case above gains a default branch that clears both grants. Is the block latch-free now?

    No. The existing select-0 branch still leaves grant_b unassigned and select-1 still leaves grant_a unassigned. Assign both defaults before the case, then override the selected grants, or assign both outputs in every branch.

    Solution and testbench

    The latch-free grant block with defaults, plus a testbench that sweeps all 4 selects against all 4 request combinations.

    grant_logic.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    module grant_logic (
      input  logic       req_a, req_b,
      input  logic [1:0] sel,
      output logic       grant_a, grant_b,
      output logic [7:0] y
    );
      always_comb begin
        grant_a = 1'b0;
        grant_b = 1'b0;
        y = 8'h00;
        case (sel)
          2'd0: begin grant_a = req_a; y = 8'hA1; end
          2'd1: begin grant_b = req_b; y = 8'hB2; end
          2'd2: begin grant_a = req_a; grant_b = req_b; y = 8'hC3; end
          default: y = 8'hD4;
        endcase
      end
    endmodule
    

    Remember

    • A latch is inferred whenever a combinational output keeps its previous value on some path.

    • Defaults first covers incomplete if statements as well as the case; a default branch alone does not.

    • An intentional latch is coded as storage, never inferred by omission.

    Rule of thumb: assign every output at the top of the block, then override.

    Keep going

    • Practice: Level-sensitive latch: when it is risky and how to code it safely

    • Interview: run a mock interview on this topic

    Related topics

    • Complete assignment pattern

    • Why accidental latches happen

    Previous

    313 / 350

    Next: Signed arithmetic and width truncation

    Up next

    Up nextSigned arithmetic and width truncation

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Parameterized RTL and carry-chain guards
    • Glitches on enables and combinational hazards