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

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
    • Randomization Basics: rand, randc & randomize()
    • Constraint Operators & Expressions
    • Dynamic Constraint Control
    • Randomizing Arrays and Queues
    • How the Constraint Solver Works
    • Debugging Randomization Failures
      • Randomization Failure Taxonomy
      • Finding Contradictions
      • Wrong Distribution Debugging
      • The Randomization Debug Checklist
    • Interview Drills — Constraints and Randomization Q&A
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Debugging Randomization Failures
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. Debugging Randomization Failures
  5. Randomization Failure Taxonomy

Part 3 · Constraint Randomization · Intermediate

Randomization Failure Taxonomy

The four failure modes and the triage flowchart that picks the right debug tool.

On this page

    Why this matters

    A scoreboard reported duplicate packets for two days. The real fault was upstream: a bare randomize() call had failed, and the stale packet went out again.

    After this lesson you can: sort a randomization problem into one of four modes from its symptom, and pick the right first tool for it.

    Two symptoms, four modes

    From the caller side, randomize can only return 0 or return 1 with values. The underlying failure modes split into four categories, and each demands a different debugging tool.

    The critical first question is always whether randomize returned 0 or returned 1 with wrong values. Those two branches share almost no debugging steps.

    Mode

    Symptom

    Debug with

    Hard contradiction

    Returns 0 every time or only in some states

    Constraint block binary search

    Silent skip

    Returns 1, but a field never changes

    Null-handle and rand_mode audit

    Wrong distribution

    Returns 1, values legal but skewed

    Histogram or covergroup on rand fields

    Irreproducibility

    Failure appears once and cannot be replayed

    Seed capture and stability rules

    The triage flowchart

    Run the symptom through this chart before opening any constraint file. Reproduction sits at the bottom because it is orthogonal to the other three modes.

    Triage flowchart for randomization failures.
    Triage flowchart: check, classify, reproduce, then debug.

    A hard contradiction that only fires on one seed must first be made deterministic. Experienced engineers lock down the seed before touching any constraints.

    Bidirectional solving in sixty seconds

    The solver gathers every active constraint into one simultaneous system: class constraints up the inheritance chain, nested-object constraints, inline with constraints, and each variable's type and width. Solving is bidirectional, so a constraint written as a check can act as a generator.

    State variables enter as constants at their current values. If the conjunction has no solution, randomize returns 0 and every rand field keeps its previous value.

    top.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
    class pkt;
      rand bit [7:0] len;
      rand bit [7:0] payload_max;
      bit            small_mode;          // state variable, NOT rand
    
      constraint len_c     { len > 0; len <= payload_max; }
      constraint mode_c    { small_mode -> len < 4; }
      constraint floor_c   { payload_max >= 16; }
    endclass
    
    module top;
      initial begin
        pkt p = new();
        p.small_mode = 1;
        // Solvable: len in [1:3], payload_max in [16:255].
        if (!p.randomize()) $fatal(1, "pkt randomize failed");
    
        // State-dependent contradiction: small_mode==1 forces len<4,
        // the inline forces len>=10, so the conjunction is empty.
        if (!p.randomize() with { len >= 10; })
          $display("randomize did not solve: len>=10 vs len<4");
        else $fatal(1, "FAIL: len>=10 should contradict small_mode");
        // p.len still holds the value from the EARLIER success.
        $display("PASS: the state-dependent contradiction failed as analyzed");
        $finish;
      end
    endmodule
    Terminal
    %Warning-UNSATCONSTR: /var/folders/41/dgs80y1569b4x8pwflv_w9jr0000gn/T/rtbtb-K1WcxI/t42.sv:7: Unsatisfied constraint: 'constraint mode_c    { small_mode -> len < 4; }'
    %Warning-UNSATCONSTR: /var/folders/41/dgs80y1569b4x8pwflv_w9jr0000gn/T/rtbtb-K1WcxI/t42.sv:20: Unsatisfied constraint: 'if (!p.randomize() with { len >= 10; })'
    randomize did not solve: len>=10 vs len<4
    PASS: the state-dependent contradiction failed as analyzed

    The inline constraint does not replace class constraints. It is ANDed with them, and the same call would succeed with small_mode equal to 0.

    Check every call

    A bare obj.randomize call discards the return value. On failure the simulation continues with stale field values, and the bug surfaces far downstream as a phantom protocol mismatch.

    Some teams avoid plain assert for this because assertions can be disabled globally. Worse, a disabled assert may skip the randomize call entirely, not just leave it unchecked. A fatal-on-failure wrapper, shown in the checklist lesson, is immune to that. The listing below shows the three levels.

    Three ways to call it, from best to worst. This fragment is an illustration, so the course checks skip it.

    top.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // snippet
    class txn;
      rand bit [7:0] len;
      constraint c_len { len inside {[1:64]}; }
    endclass
    
    module top;
      initial begin
        txn t;
        t = new();
        // Level 1, minimum: an immediate assertion. If assertions are
        // turned off, the tool may skip the randomize call entirely.
        assert (t.randomize());
        // Level 2, preferred: a plain check that dumps context and stops.
        if (!t.randomize()) begin
          $display("txn randomize failed: len=%0d (stale value)", t.len);
          $fatal(1, "txn randomize failed");
        end
        // Level 3, never: the status is thrown away.
        void'(t.randomize());
      end
    endmodule

    On Verilator 5.048, post_randomize also runs after a failed call, which the standard forbids. A post_randomize that logs or forwards the transaction would then pass stale values downstream with no warning, so the return check has to come first.

    Common pitfall

    Calling randomize bare. The failure leaves stale values, the scoreboard sees a duplicate transaction, and the debug starts three layers from the cause. Expecting inline with to override class constraints is the same class of mistake: it conjoins.

    Interview

    “What happens when randomize fails?” has three parts: it returns 0; nothing is partially assigned, so an unchecked call silently reuses stale data; and the failure means the conjunction of class, inherited, nested, and inline constraints plus state values is unsatisfiable. Naming the four failure modes unprompted signals field experience.

    Check yourself After a failed randomize, what do the rand fields hold?

    Their previous values. Nothing is partially assigned, which is why the object dump at a failure site shows stale rand values next to live state values.

    Check yourself An inline with constraint contradicts a class constraint. Which one wins?

    Neither. Both have equal authority and the call is UNSAT. Only a soft constraint creates priority, and a conflicting soft constraint is dropped entirely.

    Remember

    • Returns 0: a contradiction. Returns 1 with odd values: a skip or a skew.

    • A failed call assigns nothing; every rand field keeps its old value.

    • Fix reproduction first when the failure depends on the seed.

    Rule of thumb: check every return value before debugging anything else.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator prints a harmless WIDTHTRUNC warning for if (!obj.randomize()), because randomize() returns an int, and an UNSATCONSTR warning when a solve fails on purpose. Icarus 13 does not support constraints.

    Verilator 5.048 ran post_randomize after a failed call too, which the standard forbids.

    Related topics

    • Finding Contradictions

    • The Randomization Debug Checklist

    Previous

    88 / 259

    Next: Finding Contradictions

    Up next

    Up nextFinding Contradictions

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Wrong Distribution Debugging
    • The Randomization Debug Checklist