A unique constraint guarantees that a set of variables, or every element of an array, will all come out mutually distinct from one another after randomization. It's a far more concise way to express "no two of these should ever be equal" than writing out a pairwise != comparison between every possible pair by hand.
12 min read | Intermediate Level
What You'll Learn
- What
uniqueguarantees, for both an explicit list of variables and an array - Why
uniquecan makerandomize()fail outright when the legal range is too small for the number of values that need to be distinct - How
uniqueinteracts with other constraints that already pin some of the values - How to exclude a specific value from a
uniqueset using an auxiliary variable
Syntax
// A list of variables, all mutually distinct
constraint c_name { unique {var1, var2, var3}; }
// Every element of an array, all mutually distinct
constraint c_name { unique {array}; }
unique on a List of Variables
Combined with other constraints, unique narrows down exactly what's still possible for the remaining variables. Here, two of three variables sharing a 2-bit range are pinned to specific values:
class ABC;
rand bit [1:0] a, b, c;
constraint c_unique { unique {a, b, c}; }
constraint c_pin { a == 0; b == 1; }
endclass
With a fixed at 0 and b fixed at 1, unique requires c to differ from both — out of the four legal 2-bit values (0 through 3), only 2 and 3 remain available. c will always land on one of those two, chosen freely between them.
Quick Check
Q: If a third pinning constraint, c_pin2 { c == 2; }, were added to this same class, would anything about it be illegal?
Show Answer
No — c == 2 is one of the two values c was already free to take, so this simply removes the remaining choice, making the whole class fully deterministic: a=0, b=1, c=2, every single time.
unique on an Array
Applied to an array, every element must differ from every other element:
class ABC;
rand bit [1:0] arr[4];
constraint c_unique { unique {arr}; }
endclass
arr has exactly 4 elements, and a 2-bit element can only hold 4 possible values (0 through 3) — an exact match. This means arr is guaranteed to contain all four of those values, each exactly once, in some order — effectively a random permutation of {0, 1, 2, 3}.
Quick Check
Q: If arr were declared with only 3 elements instead of 4, keeping the same 2-bit element type, would every one of the 4 possible values (0–3) still appear somewhere in the array?
Show Answer
No. With only 3 elements needing to be mutually distinct out of 4 possible values, exactly one of the four values simply won't be used — unique only guarantees the elements present are distinct from each other, not that every legal value gets used.
When Uniqueness Becomes Impossible
If the number of items that need to be mutually distinct exceeds the number of values actually available, unique can never be satisfied — and randomize() fails every single time, regardless of seed:
class ABC;
rand bit a, b, c; // each is a single bit: only 0 or 1 possible
constraint c_unique { unique {a, b, c}; }
endclass
a, b, and c are each only 1 bit wide — just two possible values between them. Three variables can never be pairwise distinct when only two distinct values exist to assign — there's no seed, no amount of retrying, that makes this satisfiable. randomize() will fail on every call.
The exact same principle applies to arrays — a 5-element array of the same 2-bit type used earlier would also fail unconditionally, since only 4 distinct values exist for 5 elements that all need to differ.
Quick Check
Q: Would widening a, b, and c to 2 bits each (4 possible values) make the original three-variable example satisfiable?
Show Answer
Yes. With 4 possible values and only 3 variables needing to be mutually distinct, there's now room to spare — exactly one of the four legal values simply won't be used, the same situation as the 3-element array Quick Check above.
Excluding a Specific Value From unique
unique's own list can only hold variables, not a bare literal — so excluding one specific value from the set means adding an extra variable and pinning it to that value with its own constraint, rather than writing the literal directly inside the braces:
class ABC;
rand bit [1:0] a, b;
rand bit [1:0] excluded;
constraint c_unique { unique {a, b, excluded}; }
constraint c_exclude { excluded == 2'd2; }
endclass
Here, a and b both have to differ from excluded (pinned to 2), and from each other. Out of the four legal 2-bit values, only {0, 1, 3} remain available once 2 is excluded — a and b will always end up as two different values drawn from that remaining set of three.
Where Is This Used?
Generating a set of unique transaction IDs or tags for several outstanding, in-flight requests is a canonical use case — a protocol commonly requires that no two simultaneously active transactions share the same tag, and unique {tag_array} expresses that requirement directly, without hand-writing a pairwise comparison for every combination of outstanding requests.
Common Beginner Mistakes
Mistake #1: Assuming unique always succeeds regardless of range
As the pigeonhole example shows, it doesn't — if more distinct values are required than actually exist for the variable's type, randomize() fails every time. Why? Before reaching for unique, it's worth a quick sanity check that the type's range can actually hold enough distinct values for however many items need to be unique.
Mistake #2: Forgetting other constraints can narrow what's left for unique to work with
As the pinned-variable example shows, fixing some values ahead of time can shrink the pool unique has to draw from for the rest. Why? It's easy to reason about unique in isolation and forget that other active constraints are quietly removing options from the same shared range.
Mistake #3: Writing out pairwise != comparisons instead of using unique
unique {a, b, c} says the same thing as a != b; a != c; b != c;, just far more concisely — and it scales cleanly to arrays, where hand-writing every pairwise comparison would be impractical. Why? There's no downside to using unique whenever mutual distinctness is genuinely the requirement.
Mistake #4: Confusing unique with randc's cross-call behavior
unique only guarantees distinctness within the values produced by a single randomize() call — it says nothing about whether the same set of values might come up again on a later call. Why? That kind of no-repeat-across-calls guarantee is what randc provides instead, for a single variable cycling through its whole range before repeating — a genuinely different guarantee from what unique offers.
Practice Exercise
Challenge: Is It Satisfiable?
Given rand bit [2:0] tag[6]; and constraint c_unique { unique {tag}; }, is this satisfiable? If so, how many of the possible values would go unused? Then consider the same question if the array had 9 elements instead of 6.
Hint
Work out how many distinct values a 3-bit element can hold, and compare that against how many array elements need to be mutually distinct.
Solution
With 6 elements: satisfiable. A 3-bit element holds 8 possible values (0 through 7); with only 6 elements needing to be distinct, there's room to spare — exactly 8 - 6 = 2 values will go unused in any given result.
With 9 elements: impossible. 9 elements would need 9 mutually distinct values, but only 8 exist for a 3-bit type — the same pigeonhole failure demonstrated earlier in this article, just with different numbers. randomize() would fail on every call.
Summary
In this tutorial, you learned:
unique {var1, var2, ...}guarantees every listed variable is mutually distinct from every other;unique {array}applies the same guarantee to every element of an array- If more distinct values are required than a type's range can actually provide,
randomize()fails unconditionally, regardless of seed - Other active constraints can narrow the pool of values
uniquehas left to work with, sometimes down to just one remaining option - Excluding one specific value from a
uniqueset means adding an auxiliary variable and pinning it with its own constraint, not writing the literal directly inside the braces uniqueonly guarantees distinctness within a singlerandomize()call — it's unrelated torandc's guarantee of no repeats across multiple calls