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
      • The Solution-Space Mental Model
      • Bidirectional Solving
      • randc Internals & Limits
      • Solver Performance
      • Solver FAQ: Determinism & Vendor Differences
    • Debugging Randomization Failures
    • 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

‹How the Constraint Solver Works
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. How the Constraint Solver Works
  5. The Solution-Space Mental Model

Part 3 · Constraint Randomization · Intermediate

The Solution-Space Mental Model

Legal tuples form the solution space, and the standard asks for a uniform pick over them.

On this page

    Why this matters

    A reviewer asked why an unweighted field came out zero a third of the time. Nobody had counted the legal tuples: the uniform pick was over pairs, not over that field.

    After this lesson you can: count a solution space, predict each field's share from it, and list everything that joins one solve.

    The tuple-set model

    Take every rand field in one randomize() call and form the tuple of their values. Each active constraint removes the tuples that break it. What remains is the solution space, and the solver returns one tuple from it.

    Two 3-bit fields give 8 × 8 = 64 tuples. The rule x + y <= 4 leaves 15 of them, and x != y removes three more on the diagonal, leaving 12.

    Twelve legal tuples.

    A uniform pick over tuples is not a uniform pick per field. A value that appears in many legal tuples comes up more often: here x = 0 should come up four times as often as x = 4, with no dist anywhere.

    Predict first For x + y <= 4; x != y; on two 3-bit fields, what share of results should have x = 0 under the standard's uniform rule? And x = 4?

    4 of 12 and 1 of 12. Count the legal tuples in each column: 4, 3, 2, 2 and 1.

    Check yourself x < y; y < 10; on 4-bit fields. Why is x = 0 far more likely than x = 8?

    x = 0 pairs with nine values of y, x = 8 with one. Of 45 legal tuples, nine have x = 0, so the uniform share is 9/45 against 1/45. Verilator gave 1079 and 56 out of 4500, more skewed than uniform but in the same order.

    Order does not matter

    Constraints combine into one system, so their order inside a block, and the order of blocks, changes nothing. { x < y; y < 10; } and { y < 10; x < y; } describe the same 45 tuples.

    Common pitfall

    Reading constraints top to bottom as assignments predicts uniform fields. The x < y triangle disproves it: uniformity is over tuples, so each field's shares follow the tuple counts.

    What joins the set

    One randomize() call combines more than the class text. All of these go into the same system:

    • Active blocks of the object and its parents. A child block with the same name replaces the parent's.

    • Constraints of rand child objects, which are solved together with the parent.

    • The inline with clause, for that call only.

    • Plain fields and frozen rand fields, read as constants at the moment of the call.

    • Each field's own type range, and the randc cycle state.

    The bench below checks both halves of this lesson. It solves the 12-tuple class 3000 times, asserts every result is legal and that the shares follow the tuple counts, and then solves a child class whose replaced block meets an inline with.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class two_var;
      rand bit [2:0] x, y;                      // 8 x 8 = 64 tuples
      constraint c { x + y <= 4; x != y; }      // 12 tuples survive
    endclass
    
    class base_txn;
      rand bit [7:0] len;
      constraint c_len { len inside {[1:64]}; }
    endclass
    
    class small_txn extends base_txn;
      constraint c_len { len inside {[1:8]}; }  // same name: replaces the parent block
    endclass
    
    Terminal
    x=0: 4 tuples, uniform pick 1000 of 3000, measured 1104
    x=1: 3 tuples, uniform pick 750 of 3000, measured 811
    x=2: 2 tuples, uniform pick 500 of 3000, measured 452
    x=3: 2 tuples, uniform pick 500 of 3000, measured 398
    x=4: 1 tuples, uniform pick 250 of 3000, measured 235
    joint set {1,2,3,5,6,7,8}: 7 values seen
    PASS: every tuple legal, x shares ordered by tuple count, and the joint set is exactly 7 values

    Notice what the bench asserts and what it only prints. Legality and the order of the shares are asserted. The exact counts are printed beside the uniform numbers, because no tool promises them: Verilator's x = 3 came out about 20% under the uniform share.

    Interview

    Draw the grid for any toy constraint, mark the legal cells, and read probabilities off the picture. Every follow-up about bias or ordering then becomes counting.

    Check yourself randomize() returns 0. What happened to the fields, and what must the caller do?

    The space was empty, so the fields kept their previous values. The caller checks the return value instead of sending stale data.

    Remember

    • The solver picks one legal tuple; the standard asks for a uniform pick over tuples.

    • A field's share follows how many legal tuples hold each value.

    • Inherited blocks, rand children, the inline with and state values all join one solve.

    Rule of thumb: assert legality on every result; compare shares only against a band.

    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.

    Over 3000 calls of the 12-tuple class, Verilator gave 1104, 811, 452, 398 and 235 for x = 0 to 4, against 1000, 750, 500, 500 and 250 for a uniform pick.

    Related topics

    • Bidirectional Solving

    Previous

    83 / 259

    Next: Bidirectional Solving

    Up next

    Up nextBidirectional Solving

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • randc Internals & Limits
    • Solver Performance
    • Solver FAQ: Determinism & Vendor Differences