Part 3 · Constraint Randomization · Intermediate
Unique Values & Ordering
Drills: all-unique arrays three ways, ascending and strictly ascending, exactly one maximum, permutations of 0..N-1, and derangements.
Problem 1 — All array elements unique (three ways)
Problem
“You have rand bit [7:0] arr[10];. Constrain it so no two elements are equal. Show me more than one way, and tell me whether randc helps.”
Think it through
The modern answer is the unique constraint — one keyword, exactly this semantics. The portable/legacy answer is pairwise inequality via nested foreach, comparing each pair once with i < j. The randc suggestion is the planted trap: randc cycles ONE variable across SUCCESSIVE randomize calls — it says nothing about distinct elements within a single call.
class uniq_drill;
rand bit [7:0] arr[10];
// Way 1: the unique constraint (IEEE 1800-2012+)
constraint c_uniq { unique {arr}; }
// Way 2: pairwise foreach — works on any IEEE 1800 tool
// constraint c_pair {
// foreach (arr[i])
// foreach (arr[j])
// if (i < j) arr[i] != arr[j];
// }
// Way 3 (randc): does NOT solve this problem — see below.
endclassWhy this works
unique {arr} tells the solver all listed members are pairwise distinct — it can also mix scalars: unique {arr, x, y}. The pairwise form encodes the identical fact as 45 explicit inequalities. Feasibility check worth saying aloud: 10 unique values need at least 10 distinct candidates — an 8-bit element type has 256, fine; a 3-bit type has 8 and randomize() would fail.
The randc answer — why it fails here
A randc variable permutes its value space across consecutive randomize() calls of the same object . Ten elements randomized in one call are one solve, not ten cycling draws. The legitimate randc trick is randomizing a SINGLE randc index 10 times in a loop and collecting values — valid, but it changes the code structure and you should present it as a different design, not a constraint.
Common wrong answers
arr[i] != arr[i+1] only — adjacent inequality permits arr[0] == arr[2]; uniqueness is pairwise, not neighborly.
Declaring rand randc bit [7:0] arr[10] expecting per-call uniqueness — randc semantics are across calls, and many tools reject randc on unpacked arrays anyway.
foreach pair with i != j instead of i < j — correct but doubles every inequality; fine functionally, sloppy on a whiteboard if you claim it is minimal.
Problem 2 — Ascending and strictly ascending
Problem
“Constrain the same array to be in ascending order. What is the difference between ascending and strictly ascending, and what does strict ascent give you for free?”
Think it through
Ordering is a relation between neighbors, and neighbor relations chain transitively — so constraining each adjacent pair is sufficient. Strict ascent (<) additionally forces all elements distinct, so it subsumes uniqueness.
class sort_drill;
rand bit [7:0] arr[10];
// Non-decreasing
// constraint c_asc { foreach (arr[i]) if (i > 0) arr[i] >= arr[i-1]; }
// Strictly ascending — implies all-unique for free
constraint c_strict { foreach (arr[i]) if (i > 0) arr[i] > arr[i-1]; }
endclassWhy this works
Transitivity: arr[0] < arr[1] and arr[1] < arr[2] imply arr[0] < arr[2], so adjacent constraints cover all pairs with N-1 relations instead of N(N-1)/2. Feasibility again: 10 strictly ascending 8-bit values need a span of at least 10 — plenty in 256. The guard if (i > 0) avoids the out-of-range arr[-1] term on the first iteration.
Variation — descending, or sorted only in a window
// Descending: flip the comparison
constraint c_desc { foreach (arr[i]) if (i > 0) arr[i] < arr[i-1]; }
// Sorted only in positions [2:6]:
constraint c_win { foreach (arr[i]) if (i > 2 && i <= 6) arr[i] > arr[i-1]; }Problem 3 — Exactly one maximum
Problem
“Constrain the array so exactly one element is strictly larger than every other element — a unique maximum at a random position.”
Think it through
Introduce a rand index for where the maximum lives, then state that every other element is strictly below the element at that index. The helper-index pattern is the general tool for “some element such that…” drills.
class onemax_drill;
rand bit [7:0] arr[10];
rand int unsigned max_idx;
constraint c_idx { max_idx < 10; }
constraint c_max {
foreach (arr[i])
if (i != max_idx) arr[i] < arr[max_idx];
}
endclassWhy this works
The solver chooses max_idx and the array jointly; strict inequality everywhere else guarantees the max is unique. Distribution caveat to volunteer: positions are uniform over max_idx, but the maximum’s value skews high — values near 255 admit more legal arrays beneath them.
Common wrong answers
arr.max() comparisons inside constraints — array reduction methods like max() are not supported in constraints by most solvers; sum() is the reliably supported one.
Constraining arr[9] to be the maximum — that is “maximum at a FIXED position”, an over-constraint; the position must be random.
Using <= instead of < for the others — permits ties, so the maximum is no longer unique.
Problem 4 — Permutation of 0..N-1, then a derangement
Problem
“Constrain rand int unsigned arr[8]; to be a random permutation of 0 through 7. Follow-up: now make it a derangement — a permutation where no element sits at its own index.”
Think it through
A permutation of 0..N-1 is fully characterized by two facts: every element is in [0:N-1], and all elements are distinct (pigeonhole does the rest). The derangement adds one foreach inequality against the index.
class perm_drill;
rand int unsigned arr[8];
constraint c_range { foreach (arr[i]) arr[i] < 8; }
constraint c_uniq { unique {arr}; }
// Derangement follow-up: nothing at its own position
constraint c_derange { foreach (arr[i]) arr[i] != i; }
endclassWhy this works
Eight distinct values drawn from a set of exactly eight must use each value once — that IS a permutation; no explicit “each value appears” constraint is needed. The derangement constraint removes fixed points; for N=8 that leaves 14833 of the 40320 permutations (about 1/e of them), so the solve stays easy. Mention arr.shuffle() as the procedural alternative when you don’t need constraints to interact — knowing when NOT to use the solver is a senior signal.
Common wrong answers
Only the range constraint without uniqueness — multisets like {3,3,5,...} satisfy it; not a permutation.
randc int idx as the whole answer — randc cycles across calls; within one randomize of an array it gives nothing.
For the derangement, arr[i] != arr[i] typo under pressure — constrain against the INDEX i, not the element.
Key takeaways
unique {arr} is the modern one-keyword answer; pairwise foreach with i<j is the portable one.
Ordering needs only adjacent constraints — transitivity covers the rest; strict ascent implies uniqueness.
Permutation = bounded range + all-unique; helper rand indices solve “some element such that” drills.
Common pitfalls
randc as an answer to within-one-call uniqueness — its cycling is across successive calls.
Adjacent-only inequality offered as uniqueness — arr[0] may equal arr[2].
Array reduction methods other than sum() inside constraints — max()/min() are not portably supported.
Forgetting feasibility: N unique values need at least N candidates in the element type’s range.