Part 3 · Constraint Randomization · Intermediate
Solver FAQ: Determinism & Vendor Differences
Same seed + same tool = same results, legal vendor divergence, LRM guarantees vs implementation freedom, random stability rules, and what never to depend on.
The two-sided guarantee
SystemVerilog randomization is deterministic per tool : the same simulator version, same seed, same compile, and same testbench produce the identical value sequence every run. That is what makes seed-based failure reproduction possible — and it is the half of the guarantee teams rely on daily. The other half is what the LRM deliberately does not promise: that two different simulators (or two versions of one simulator) produce the same values from the same seed. The LRM pins down legality (which solutions are permitted, cyclic behavior of randc, stability scoping) but leaves the choice among legal solutions — the solver's internal search order, RNG details, distribution shaping — to the implementation.
WHAT IS GUARANTEED WHERE
same tool, different different
same seed seed vendor/version
----------------------+-----------+------------+--------------
values satisfy | YES | YES | YES
constraints | | |
----------------------+-----------+------------+--------------
identical value | YES | no | NO —
sequence | (stability)| | legal divergence
----------------------+-----------+------------+--------------
randc full-cycle | YES | YES | YES
property | | | (order differs)
----------------------+-----------+------------+--------------
distribution exactly | n/a | no | NO — only
uniform per LRM intent | | | "intent"
Portable claims: legality, satisfiability, randc cycling.
Non-portable: the specific values, their order, exact
distribution shape, failure messages, performance.Practical consequence: a regression seed list tuned to hit corner cases on one simulator hits different stimulus on another. Coverage closure must be re-earned per tool; never archive "golden" random values as expected data.
Random stability: why unrelated edits don't (usually) change your values
The LRM's random stability rules give every thread and every class object its own private RNG . An object's RNG is seeded from the thread that constructs it, at construction time; a thread's RNG is seeded from its parent thread at spawn time. Because streams are private, adding a $display or randomizing a different object does not disturb this object's sequence. What does shift sequences: changing the order or count of object constructions in a thread, adding/removing thread spawns before the one in question, or calling extra randomize() on the same object. Two tools to re-anchor streams when edits are unavoidable: srandom(seed) (manually reseed an object or process) and get_randstate/set_randstate (checkpoint and restore an RNG mid-run).
class txn;
rand bit [7:0] x;
endclass
module stability_demo;
initial begin
txn a = new(); // a's RNG seeded from this process here
txn b = new(); // b's RNG seeded next — INSERTING a new
// construction between these lines would
// shift b's stream (and everything after)
void'(a.randomize()); // uses a's private stream
void'(b.randomize()); // b's stream — independent of a's
// Re-anchor b regardless of surrounding edits:
b.srandom(32'hDEAD_BEEF);
void'(b.randomize()); // reproducible from the manual seed
// Checkpoint / restore a process RNG:
begin
string s = get_randstate();
void'($urandom); // perturb
set_randstate(s); // restore — next $urandom
// repeats the perturbed value
end
end
endmoduleTeams with strict repro requirements srandom key objects from a name-derived hash (e.g. a CRC of the full hierarchical name) so streams survive unrelated testbench refactors. UVM does a version of this automatically for components, seeding from type and path names.
FAQ rapid-fire
Answers to the questions that always come up
Same seed, same tool, different machine? — Same results. Stability does not depend on host hardware (absent non-deterministic testbench code like wall-clock or file ordering).
Same seed after a tool version upgrade? — No guarantee. Vendors may change solver internals between versions; re-baseline your seed lists.
Is the distribution truly uniform over solutions? — The LRM states the intent; complex constraint shapes may show vendor-specific skew. Never build checks that require exact uniformity.
Does adding $display change random values? — Not by itself; stability isolates streams. Constructing objects or spawning threads earlier in the same process does.
Can I make different vendors match? — No, and don't try. Portability target: same constraints satisfiable, same coverage REACHABLE — not same values.
What about $urandom vs randomize()? — $urandom draws from the calling process's stream; randomize() from the object's stream. Both are stable, separately.
How do I reproduce a failure from a regression? — Re-run the same tool/version/compile with the logged seed. Log the seed in every run banner; without it the failure is gone.
// Minimum reproducibility hygiene every testbench should have:
module seed_banner;
int unsigned seed;
initial begin
if (!$value$plusargs("seed=%d", seed)) seed = 1;
$display("[REPRO] tool+version logged by run script; seed=%0d", seed);
// pass the seed into the root of YOUR object hierarchy:
// env.srandom(seed); — single anchor point for the whole bench
end
endmoduleInterview angle
What interviewers probe
"Same seed, will two simulators agree?" — expected: no; the LRM fixes legality and stability per tool, not cross-vendor value sequences. Both outputs are correct.
"Why did adding one new() change my random values?" — expected: object RNGs seed from the constructing thread in order; a new construction shifts every later seeding. srandom re-anchors.
"What CAN you rely on?" — expected: constraint satisfaction, randc cyclic property, per-tool seed determinism, stability isolation between objects/threads.
"How do you make a testbench refactor-proof for repro?" — expected: srandom objects from name-derived seeds; log the master seed; one anchor point at the hierarchy root.
"Test passes on vendor A, randomize() fails on vendor B — who is wrong?" — expected: check satisfiability first; if genuinely satisfiable, B has a bug or a solver limit (e.g. randc width); if borderline (overflow, width traps) likely YOUR constraint relies on undefined behavior.
The strongest one-line summary for an interviewer: "The LRM standardizes which solutions are legal and that each tool is repeatable with itself; it deliberately does not standardize which legal solution gets picked." Everything in this FAQ unfolds from that sentence.
Key takeaways
Same tool + same seed + same compile = identical sequences; this per-tool determinism is the basis of seed repro.
Cross-vendor (and cross-version) value agreement is NOT guaranteed — only legality, satisfiability, and randc cycling are portable.
Random stability gives each object/thread a private RNG seeded at construction/spawn — unrelated code rarely shifts your stream.
Construction order, thread spawn order, and extra randomize calls on the same object DO shift streams; srandom/set_randstate re-anchor.
Log the master seed in every run and seed the hierarchy from one anchor — failures without a seed are unreproducible.
Common pitfalls
Archiving expected random values as golden data — breaks on any tool/version change; check properties, not values.
Assuming a refactor that 'only added a class' is repro-neutral — added constructions shift later RNG seedings.
Debugging cross-vendor value differences as a bug — legal divergence; verify satisfiability, not equality.
Building scoreboard logic that depends on exact solver distributions — LRM uniformity is intent, not a contract.
Forgetting to log seeds in regression banners — the one failure you need to debug becomes unreproducible.