Part 3 · Constraint Randomization · Intermediate

Q&A: dist, inside, soft, solve-before

dist := vs :/, inside vs dist, soft constraints and discard order, what solve-before changes and does not, implication vs if-else.

Q: In dist, what is the difference between := and :/?

Direct answer: both attach weights; they differ only on range bins. := gives the weight to each value in the range; :/ divides the weight across the range. On a scalar bin they are identical. Get asked this, do the arithmetic out loud — the interviewer wants the number.

systemverilog
rand bit [7:0] x;

// := each value weighted 40 → total = 5*40 + 60 = 260
constraint c1 { x dist { [1:5] := 40, 9 := 60 }; }
// P(x==3) = 40/260 ≈ 15.4%      P(x==9) = 60/260 ≈ 23.1%

// :/ range shares 40 → each of 1..5 gets 8 → total = 40 + 60 = 100
constraint c2 { x dist { [1:5] :/ 40, 9 := 60 }; }
// P(x==3) = 8/100 = 8%          P(x==9) = 60/100 = 60%

Follow-up you should expect

“Which one keeps its meaning if the range grows?” — :/ : the bucket keeps its total share and dilutes per-value; := inflates the bucket’s total share as values are added. So spec language like “small packets 95% of the time” maps to :/ — the share is what the spec pinned, not the per-value weight.

Junior vs senior answer

  • Junior: “:= is per value, :/ is divided.” Correct rule, no numbers.

  • Senior: computes an actual probability on the spot and maps spec percentage language to :/ — showing the rule is operational, not memorized.


Q: What is the difference between inside and dist?

Direct answer: inside defines legality — set membership, with the solver uniform over the legal set. dist defines legality and shapes probability over it. Also: inside is a boolean expression usable anywhere (procedural if, implications, negation), while dist is a constraint-only construct that cannot be negated or nested in arbitrary expressions, and is illegal on randc variables.

systemverilog
// inside: all 11 values equally likely — 20 gets 1/11, not "half"
constraint c_in   { x inside {[1:10], 20}; }

// dist: same legal set, weighted — 20 now actually gets half
constraint c_dist { x dist { [1:10] :/ 50, 20 :/ 50 }; }

// inside as a reusable boolean — dist cannot do this:
constraint c_cond { (mode == FAST) -> !(x inside {[200:255]}); }

Follow-up you should expect

“Can you combine them?” — Yes, and you often should: inside (or plain relations) to legislate the legal envelope in the base class, dist in a test or subclass to bias within it. Layering legality and policy separately is what keeps base classes reusable.

Junior vs senior answer

  • Junior: “inside is a range check, dist adds weights.”

  • Senior: adds the expression-vs-constraint distinction (inside negatable and usable procedurally, dist not), the randc exclusion, and the layering pattern of inside-for-legality plus dist-for-policy.


Q: What is a soft constraint, and in what order are softs discarded?

Direct answer: a soft constraint holds only while consistent with all hard constraints and higher-priority softs; on conflict it is discarded silently instead of failing the solve. Priority: hard constraints always win; among softs, the later-defined wins — and the LRM ordering means constraints in derived classes and in inline with clauses have higher priority than softs in base classes. Lower-priority conflicting softs are dropped, not averaged.

systemverilog
class base_txn;
  rand bit [7:0] len;
  constraint c_dflt { soft len == 4; }       // default policy
endclass

class big_txn extends base_txn;
  constraint c_big { soft len inside {[64:128]}; }  // derived soft wins
endclass

initial begin
  big_txn t = new();
  void'(t.randomize());                          // len in 64..128
  void'(t.randomize() with { len == 100; });     // hard inline beats all softs
end

Follow-up you should expect

“Why soft instead of constraint_mode(0)?” — soft is self-relaxing : overriders need no knowledge of the base constraint’s existence or name; the default simply yields wherever a hard constraint speaks and still applies everywhere else. constraint_mode is an explicit, named, all-or-nothing switch the test must operate. Defaults that should melt away on contact belong in soft; constraints a test must consciously kill belong named, with constraint_mode.

Junior vs senior answer

  • Junior: “soft constraints can be overridden.”

  • Senior: states the discard (not blend) semantics, the later-defined/derived-class priority rule, and the design guidance of soft-for-defaults vs named-hard-for-policy.


Q: What does solve...before change — and what does it NOT change?

Direct answer: it changes probability distribution only . The default solver is uniform over legal solution tuples , so when one value of a variable admits many more solutions than another, that value soaks up probability. solve a before b partitions the solve: a is chosen uniformly over its legal values first, then b within that choice. It does NOT change which solutions are legal, cannot fix an infeasible set, and is not procedural sequencing.

systemverilog
class pkt;
  rand bit        err;
  rand bit [7:0]  code;
  constraint c { err  -> code inside {[1:255]};   // 255 (err,code) pairs
                 !err -> code == 0; }             // 1 pair
  // Default: P(err) = 255/256.  With ordering: P(err) = 1/2.
  constraint c_ord { solve err before code; }
endclass

Follow-up you should expect

“If the constraints were contradictory, would solve...before help?” — No. Legality is fixed by the constraint set; ordering only reshapes how probability falls across the same legal set. Saying “solve-before never affects feasibility” crisply is the checkmark the interviewer is waiting for. Bonus: randc variables are implicitly solved before rand ones — you cannot solve a rand before a randc.

Junior vs senior answer

  • Junior: “it makes the solver solve one variable first.” — sounds procedural, which invites the trap.

  • Senior: leads with “probability only, legality never”, explains uniform-over-tuples as the cause of skew, and notes the implicit randc ordering rule.


Q: Implication (->) vs if-else in constraints — any real difference?

Direct answer: a -> b is logical implication — exactly equivalent to !a || b, and if (a) b; else c; is exactly (a -> b) plus (!a -> c). Same solver, same semantics — the practical difference is completeness pressure: if-else forces you to look at the else arm, while a lone implication makes it easy to forget that when the antecedent is false, the consequent variable is completely unconstrained .

systemverilog
// Incomplete — when kind != READ, addr is ANYTHING:
constraint c_half { (kind == READ) -> addr inside {[0:16'h3FFF]}; }

// Complete, either spelling:
constraint c_full {
  if (kind == READ) addr inside {[16'h0000:16'h3FFF]};
  else              addr inside {[16'h8000:16'hFFFF]};
}

Follow-up you should expect

“Is the implication bidirectional?” — The implication is one-directional as a logical statement, but the solver still solves both sides jointly: constraining addr into ROM space makes kind == READ more likely, because the solver is uniform over legal pairs. Distinguishing logical direction from solving direction is precisely the senior-level point — and the bridge to the solver-semantics round.

Junior vs senior answer

  • Junior: “they’re the same thing, different syntax.” True and incomplete.

  • Senior: gives the !a||b desugaring, names the unconstrained-else hazard, and separates one-way logic from two-way solving with the distribution consequence.

Key takeaways

  • := weights per value; :/ shares across the range — spec percentages map to :/.

  • inside legislates the set; dist shapes probability over it; layer them base-vs-test.

  • soft discards on conflict with later-defined and derived softs winning; hard always beats soft.

  • solve...before reshapes probability only — legality and feasibility are untouched.

  • Implication and if-else desugar identically; the hazard is the forgotten else arm.

Common pitfalls

  • Reading dist weights as percentages without checking the total — only ratios are guaranteed.

  • Negating dist or using it procedurally — it is constraint-only; inside is the reusable boolean.

  • Expecting conflicting softs to blend — losers are discarded whole.

  • Reaching for solve...before to “fix” a randomize() failure — ordering cannot create solutions.