Part 1 · Language Foundations · Intermediate
unique, unique0 & priority
Case and if qualifiers, runtime violation warnings, and the full_case/parallel_case pragmas they safely replace.
What the qualifiers assert
Prefixing case or if with unique, unique0, or priority attaches a checked claim about the branch structure that both the simulator and the synthesis tool act on. The simulator verifies the claim at runtime every time the statement executes and emits a violation warning when it is false; the synthesis tool uses the same claim to pick cheaper hardware (a parallel mux instead of a priority chain). Because both tools read the same keyword, the claim cannot drift out of sync the way comment pragmas did.
QUALIFIER SEMANTICS
qualifier claim runtime violation when
───────── ──────────────────────────────── ───────────────────────────
unique exactly ONE branch matches zero match OR >1 match
unique0 at most one branch matches >1 match (no-match is OK)
priority at least one branch matches; zero match
overlaps OK, first wins
synthesis interpretation:
unique / unique0 → branches are mutually exclusive → parallel mux
priority → full coverage → no latch, keep priority chainunique claims the selector hits exactly one branch: items are mutually exclusive and collectively cover every value that occurs. unique0 relaxes the coverage half — no match is acceptable (useful when a default assignment before the case handles the no-match path). priority claims at least one branch always matches and that overlapping items are intentional, with textual order deciding the winner.
Replacing full_case / parallel_case pragmas
Before SystemVerilog, designers used the synthesis comment pragmas // synopsys full_case and // synopsys parallel_case to make the same claims. The fatal flaw: pragmas are invisible to the simulator. If the claim was wrong — two requests asserted simultaneously under parallel_case — synthesis built hardware assuming exclusivity while simulation modeled priority, and the gate-level netlist behaved differently from RTL sim. These pragma-induced mismatches caused real silicon escapes. unique and priority fix this by making the simulator police the claim continuously: every violated assumption prints a warning during regressions instead of surfacing in the lab.
// OLD (dangerous): claim visible only to synthesis
always @(*) begin
case (sel) // synopsys parallel_case full_case
2'b01: y = a;
2'b10: y = b;
endcase
end
// NEW: simulator checks the claim on every evaluation
always_comb begin
unique case (sel)
2'b01: y = a;
2'b10: y = b;
2'b00, 2'b11: y = '0;
endcase
end
// If sel ever makes two items true, or none:
// ** Warning: unique case violation at time ...
always_comb begin
priority if (irq_nmi) vec = VEC_NMI; // overlap intended,
else if (irq_timer) vec = VEC_TMR; // order = priority
else if (irq_soft) vec = VEC_SW;
else vec = VEC_NONE; // covers no-match
endViolation reporting and the glitch caveat
Violation checks run whenever the statement executes — including on intermediate combinational glitches partway through a delta cycle. A momentary two-hot selector during settling triggers a unique warning even though the settled value is legal. Simulators mitigate this by deferring the check to the end of the time step in newer LRM revisions, but tool behavior varies, so teams often filter these warnings to clocked contexts or accept some noise. The warnings are warnings, not errors by default — promote them to errors in regression flows or they scroll past unread, defeating the purpose.
Interview angle: unique vs priority
unique asserts mutual exclusivity AND full coverage — simulator flags zero or multiple matches; synthesis builds a parallel mux.
priority asserts only coverage — overlaps are intentional, first match wins, hardware keeps the priority chain.
Both replace full_case/parallel_case pragmas, with the critical improvement that simulation verifies the claim.
unique0 is unique minus the coverage claim — useful with a preceding default assignment.
CHOOSING A QUALIFIER
Are branch conditions mutually exclusive by construction?
│
┌────┴────┐
YES NO (overlap is intended, order matters)
│ │
│ └──► priority case / priority if
│
Is every selector value covered by an item?
│
┌────┴────┐
YES NO (default assignment before the case)
│ │
▼ ▼
unique unique0Key takeaways
unique = exactly one match, unique0 = at most one, priority = at least one with ordered overlap.
Qualifiers make the simulator verify what synthesis assumes — eliminating pragma-induced sim/gate mismatches.
Violations are runtime warnings; promote them to errors in regression or they go unnoticed.
Glitch-time violations on combinational paths are a known noise source — know your tool's deferral behavior.
Common pitfalls
Treating unique case as just a lint hint — it changes synthesized hardware to a parallel mux.
Using unique when two branches can legitimately overlap — every overlap fires a runtime violation.
Keeping full_case/parallel_case pragmas alongside qualifiers — conflicting claims confuse tools.
Ignoring unique-violation warnings in regression logs — they are exactly the sim/synth mismatch you wanted to catch.