Part 8 · Senior & Interview Prep · Intermediate

Q&A: Constraints Quick-Fire

Rapid classic constraint questions with crisp answers: even values, dist semantics, soft, solve-before, inline with, randc.

How to use this page

These are the constraint questions interviewers fire in rapid succession to gauge fluency — each deserves a ten-second answer with one line of code. Depth (solver mechanics, distribution math, constraint debugging) lives in the dedicated Part 3 randomization and interview modules; this page is the revision sprint.


Q: Constrain a variable to even values only.

Constrain the LSB to zero — or use the modulo form. The LSB form is solver-friendlier and shows you think in bits.

systemverilog
rand bit [7:0] addr;
constraint c_even { addr[0] == 1'b0; }     // preferred: bit-level
// or: constraint c_even { addr % 2 == 0; }

// follow-up variant — multiple of 4 within a range:
constraint c_a4 { addr inside {[16:128]}; addr[1:0] == 2'b00; }

Follow-up: "Odd values? Multiple of 8?" — addr[0] == 1 and addr[2:0] == 0. The pattern generalizes: multiples of 2^N have N low bits zero.

Junior vs senior: the modulo answer is fine; the bit-slice answer plus the 2^N generalization signals hardware thinking.


Q: What is the difference between := and :/ in a dist?

:= assigns the weight to each value in a range; :/ divides the weight across the range. Over [1:4], := 40 gives each of the four values weight 40 (total 160); :/ 40 gives each value weight 10 (total 40).

systemverilog
rand int len;
constraint c_len {
  len dist { 1 := 50, [2:9] := 5  };   // each of 2..9 has weight 5
  // len dist { 1 := 50, [2:9] :/ 40 }; // 2..9 SHARE 40 → 5 each
}
// dist also implies set membership: len can ONLY be 1..9 here

Follow-up: "Does dist constrain the legal set or just the probabilities?" — Both: a value with no dist entry (or zero weight) cannot be generated. dist is inside-plus-weights, not weights alone.

Junior vs senior: juniors know per-value vs shared. Seniors add that dist defines the legal set too — the zero-weight exclusion surprises people.


Q: What does soft do, and when does a soft constraint lose?

A soft constraint is a default that the solver satisfies unless any hard constraint (including an inline with) contradicts it — then it is silently dropped, no error. It is the mechanism for "sane defaults that tests can override without knowing the base class internals."

systemverilog
class txn;
  rand int len;
  constraint c_len_dflt { soft len inside {[1:8]}; }  // gentle default
endclass

txn t = new();
assert(t.randomize());                          // len in 1..8
assert(t.randomize() with { len == 100; });     // hard wins, soft drops — no conflict error

Follow-up: "Two contradicting soft constraints?" — Priority by declaration order: later-declared (and derived-class) soft constraints beat earlier ones. With hard constraints the same contradiction is a randomize failure.

Junior vs senior: juniors say "soft can be overridden." Seniors say by whom (any hard constraint), how silently (no error), and the soft-vs-soft priority rule.


Q: What does solve a before b change — the legal set or the distribution?

Only the distribution. The solution set is identical with or without it; what changes is probability. Default solving picks uniformly over (a,b) pairs, so a variable enabled by a rare flag is almost never exercised. solve a before b picks a first (uniform over its own values), then b given a — equalizing the branch probabilities.

systemverilog
rand bit       err;
rand bit [7:0] code;
constraint c { err == 0 -> code == 0; solve err before code; }
// without solve-before: 257 pairs, err==1 in 256 of them → err≈99.6%... 
//   or with err==0 dominating in other shapes — distribution skew either way
// with solve-before: err is 50/50 first, then code chosen given err

Follow-up: "Can solve-before cause a failure that wasn't there?" — No. It cannot change satisfiability, only the order of choice and therefore the weighting. (Cyclic solve-befores are illegal, though.)

Junior vs senior: the senior marker is the crisp sentence: same solution set, different probability — plus a real example of the skew it fixes.


Q: What does randomize() with do, and what scope do its names use?

It adds temporary hard constraints for that one call — layered on top of (never replacing) the class constraints. Names inside the with block resolve to the object being randomized first, which creates the classic shadowing trap when a local variable has the same name as a class property.

systemverilog
int len = 4;                       // local in the test
txn t = new();
assert(t.randomize() with { len == 8; });
// 'len' here is t.len — the CLASS property, not the local!
// to use the local explicitly:
assert(t.randomize() with { len == local::len; });   // t.len == 4

Follow-up: "What if the with clause contradicts a class constraint?" — randomize() returns 0 (failure) — with adds constraints, it does not override hard ones. Overridable defaults are what soft is for.

Junior vs senior: juniors use with daily. Seniors know object-scope-first name resolution, local::, and that with cannot beat a hard class constraint.


Q: randc vs rand — what is the contract?

randc is random-cyclic : it permutes through every legal value exactly once before repeating, with a new permutation each cycle. rand is independent sampling — repeats any time. randc is the cheap exhaustive iterator for small fields: opcode sets, channel IDs, mode registers.

systemverilog
rand  bit [1:0] a;   // may repeat: 2,2,0,3,2...
randc bit [1:0] b;   // 3,0,2,1 | 1,3,0,2 | ...  (each value once per cycle)

// constraints shrink the cycle to the legal set:
randc bit [3:0] op;
constraint c_op { op inside {[0:9]}; }   // cycles through 10 values

Follow-up: "Limits of randc?" — Width limits (tools guarantee modest sizes — keep it small), no dist on randc, solved before rand variables, and the cycle restarts if constraints change between calls. For one-permutation-of-many-fields, shuffle a queue instead.

Junior vs senior: juniors define cyclic. Seniors add the width/dist limits and the constraint-change cycle reset — the practical gotchas.

Key takeaways

  • Even/aligned values: zero the low bits — multiples of 2^N have N low zeros.

  • := weights each value; :/ shares across the range; dist also defines the legal set.

  • soft loses silently to any hard constraint — that is its job, not a bug.

  • solve-before changes probability only — never satisfiability.

  • with adds one-call hard constraints; names resolve object-first (local:: for the test scope).

  • randc cycles exhaustively — small fields only, no dist, solved first.

Common pitfalls

  • Expecting with { } to override a hard class constraint — randomize fails instead.

  • Forgetting dist excludes unlisted values — the "why does it never generate 10" bug.

  • Shadowed names in with clauses — constraining t.len when you meant the local len.