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. Solver FAQ: Determinism & Vendor Differences

Part 3 · Constraint Randomization · Intermediate

Solver FAQ: Determinism & Vendor Differences

What one tool repeats, what travels between tools, and how to write checks that survive the move.

On this page

    Why this matters

    A bench that passed for months on one simulator failed on day one on another. Nothing was wrong with either tool; the test had leaned on one tool's kinder choices.

    After this lesson you can: say what travels between simulators, keep object streams stable, and write checks that survive a tool change.

    What travels and what does not

    Randomization is deterministic per tool: the same simulator version, seed, compile and testbench give the same values every run. That is what makes a failing seed reproducible.

    The standard fixes which assignments are legal, how randc cycles, and how streams are scoped. It does not fix the generator, so the specific legal values, and their exact shares, belong to each tool.

    Property

    Same tool, same seed

    Another seed

    Another tool or version

    Every result satisfies the constraints

    Yes

    Yes

    Yes

    The same sequence of values

    Yes

    No

    No

    randc visits every legal value per cycle

    Yes

    Yes

    Yes, in another order

    Shares match the standard's uniform rule

    Only approximately

    Only approximately

    Only approximately, and differently

    Which calls fail on a borderline rule

    Same

    May differ

    May differ

    Check yourself Two simulators take the same seed and print different values. Is one of them wrong?

    Not necessarily. Both are correct when both results satisfy the constraints. Only legality and cycling travel, never the values.

    Measured differences in this course

    This topic has collected the places where Verilator 5.048 and the standard disagree. Each one is legal behaviour a test could lean on by accident, and each one changes on the next tool. The full list is on What the Open Simulators Run.

    Case

    Standard

    Verilator 5.048

    x + y <= 4; x != y;, share of x = 3

    2 of 12

    About 20% fewer

    Starved enable bit with solve en before len

    50%

    26%

    Function call in a constraint

    Arguments solved first; most calls fail

    Pushed back; every call solved

    randc slot with data > slot

    Fails when 15 is dealt

    Never dealt 15; never failed

    b.sum() == 600 on four bytes, unwidened

    Unsatisfiable

    Solved

    Size first, with infeasible sizes in range

    Can fail

    Never failed

    Notice that Verilator is kinder in most rows. A bench developed only on Verilator can pass for months and then fail on a commercial simulator that follows the standard. The fix is the same each time: write the rule so that every tool agrees, and check properties, not values.

    Stability, measured

    Random Stability and Seeding taught the hierarchy: each object gets its own generator, seeded from its thread when new() runs. The bench below measures the parts a test depends on: later activity leaves an object's stream alone, an earlier new() shifts it, and srandom and set_randstate re-anchor streams.

    design.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
    class txn;
      rand bit [7:0] x;
    endclass
    
    module tb_stability;
      function automatic string five(txn t);       // the next five values of t
        string s = "";
        repeat (5) begin
          if (!t.randomize()) $fatal(1, "FAIL: randomize failed");
          s = {s, $sformatf("%0d ", t.x)};
        end
        return s;
      endfunction
      initial begin
        std::process p;
        txn a, b, d;
        string ref_seq, s1, s2, s3, state;
        bit [7:0] v1, v2;
        int unsigned d1, d2;
        p = std::process::self();
        p.srandom(7); a = new(); ref_seq = five(a);                    // reference run
        p.srandom(7); a = new(); void'($urandom); void'($urandom);
        b = new(); void'(b.randomize()); s1 = five(a);                 // noise after a was built
        p.srandom(7); a = new(); d = new(); void'(d.randomize());
        s2 = five(a);                                                  // another object randomized
        p.srandom(7); d = new(); a = new(); s3 = five(a);              // one extra new() before a
        $display("reference:                      %s", ref_seq);
        $display("draws and new objects after a:  %s", s1);
        $display("another object randomized:      %s", s2);
        $display("one extra new() before a:       %s", s3);
        if (s1 != ref_seq || s2 != ref_seq) $fatal(1, "FAIL: a's stream moved");
        if (s3 == ref_seq) $fatal(1, "FAIL: an earlier new() should shift a's seed");
        b.srandom(32'hDEAD_BEEF); void'(b.randomize()); v1 = b.x;
        b.srandom(32'hDEAD_BEEF); void'(b.randomize()); v2 = b.x;
        $display("b.srandom twice with one seed: %0d and %0d", v1, v2);
        if (v1 != v2) $fatal(1, "FAIL: srandom did not re-anchor b");
        state = p.get_randstate();                                     // checkpoint the thread
        d1 = $urandom;
        p.set_randstate(state);                                        // rewind it
        d2 = $urandom;
        $display("thread draw, then again after set_randstate: %0d and %0d", d1, d2);
        if (d1 != d2) $fatal(1, "FAIL: set_randstate did not rewind the thread");
        $display("PASS: a's stream ignores later activity, shifts with an earlier new(), and srandom and set_randstate re-anchor streams");
        $finish;
      end
    endmodule
    Terminal
    reference:                      236 191 232 245 251
    draws and new objects after a:  236 191 232 245 251
    another object randomized:      236 191 232 245 251
    one extra new() before a:       254 126 31 66 124
    b.srandom twice with one seed: 147 and 147
    thread draw, then again after set_randstate: 1112668803 and 1112668803
    PASS: a's stream ignores later activity, shifts with an earlier new(), and srandom and set_randstate re-anchor streams

    The bench reseeds the thread with p.srandom(7). On Verilator 5.048, once a design names std::process, $urandom(7) stopped reseeding what new() draws from, while p.srandom(7) kept working. The process form is the one the standard describes for this job, so use it.

    Checks that survive the move

    • Assert legality on every result, and assert properties such as “every length appeared” or “shares are ordered”.

    • Compare shares against a band, and print the measured numbers beside the expected ones.

    • Never store random values as expected data; store the seed and the tool version instead.

    • Log the seed in the first line of every run, and seed the whole hierarchy from that one value.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet
    int unsigned seed;
    initial begin
      if (!$value$plusargs("seed=%d", seed)) seed = 1;
      $display("[REPRO] seed=%0d", seed);   // the run script logs the tool and version
      std::process::self().srandom(seed);   // one anchor for everything built after it
    end

    Common pitfall

    Checks that depend on exact shares or stored random values break on any tool or version change. Check properties and legality, not values.

    Interview

    The one-line summary: the standard fixes which results are legal and that each tool repeats itself, but not which legal result gets picked. Everything in this lesson follows from that sentence.

    Check yourself A test passes on one simulator, and randomize() fails on another. Who is wrong?

    Check whether the set is really satisfiable first. If it is, the second tool has a bug or a limit. If the rule is borderline, as with size-first or randc beside rand, the first tool was being kind, and the rule needs rewriting.

    Remember

    • Same tool and seed: the same values. Another tool: other legal values.

    • An object's stream is seeded at new(); later activity does not move it.

    • Check properties and legality, never stored values.

    Rule of thumb: log the seed, anchor the hierarchy once, and check properties.

    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 kept an object's stream fixed through later draws and other objects, and shifted it after an earlier new(). Once std::process appeared in the design, $urandom(7) stopped reseeding object construction while process::self().srandom(7) still did.

    Related topics

    • Next topic: Debugging Randomization Failures

    Previous

    87 / 259

    Next: Debugging Randomization Failures

    Up next

    Up nextDebugging Randomization Failures

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Solution-Space Mental Model
    • Bidirectional Solving
    • randc Internals & Limits