A SystemVerilog case statement checks whether an expression matches one of several case items and branches to the matching one. On its own it behaves exactly as in Verilog. SystemVerilog adds three qualifiers, unique, unique0 and priority, which declare whether the items may overlap and whether every possible value must be covered. Simulators check that intent at run time, and synthesis tools use it to build parallel or simplified logic.
7 min read | Intermediate Level
What You'll Learn
- What
unique case,unique0 caseandpriority casecheck - How a
defaultitem interacts with each check - How the qualifiers replace the old
full_caseandparallel_casesynthesis pragmas - How to pick the right qualifier for decoders and FSMs
Syntax
unique case (expression) item1: stmt; item2: stmt; [default: stmt;] endcase
unique0 case (expression) item1: stmt; item2: stmt; [default: stmt;] endcase
priority case (expression) item1: stmt; item2: stmt; [default: stmt;] endcase
The qualifiers also work with casez and casex, for example unique casez (opcode).
| Qualifier | Violation if more than one item matches | Violation if no item matches and there is no default | Item that runs |
|---|---|---|---|
unique | Yes | Yes | The matching item; with overlap, the first match |
unique0 | Yes | No | The matching item; with overlap, the first match |
priority | No | Yes | The first matching item, in source order |
These are the same rules as for unique and priority if-else. Violations are reported at run time, usually as warnings, and simulation continues.
unique and unique0 case
unique and unique0 declare that the case items do not overlap, so they can be evaluated in parallel. They do not prevent overlap. They check for it and report a violation when it happens:
- If more than one case item matches the expression, a violation is reported and the first matching item is executed.
- If no case item matches and there is no
default, a violation is reported foruniqueonly.
unique0 does not report a violation when no item matches.
unique: No items match for given expression
module tb;
bit [1:0] abc;
initial begin
abc = 1;
// None of the case items match the value in "abc"
// A violation is reported here
unique case (abc)
0 : $display ("Found to be 0");
2 : $display ("Found to be 2");
endcase
end
endmodule
ncsim> run
ncsim: *W,NOCOND: Unique case violation: Every case item expression was false.
File: ./testbench.sv, line = 9, pos = 14
Scope: tb
Time: 0 FS + 1
ncsim: *W,RNQUIE: Simulation is complete.
abc is 1, which matches neither 0 nor 2, and there is no default. The NOCOND violation tells you a value occurred that the designer declared impossible. With unique0 case, or with a default item, nothing would be reported.
unique: More than one case item matches
module tb;
bit [1:0] abc;
initial begin
abc = 0;
// Multiple case items match the value in "abc"
// A violation is reported here
unique case (abc)
0 : $display ("Found to be 0");
0 : $display ("Again found to be 0");
2 : $display ("Found to be 2");
endcase
end
endmodule
ncsim> run
Found to be 0
ncsim: *W,MCONDE: Unique case violation: Multiple matching case item expressions at {line=10:pos=6 and line=11:pos=6}.
File: ./testbench.sv, line = 9, pos = 14
Scope: tb
Time: 0 FS + 1
ncsim: *W,RNQUIE: Simulation is complete.
Two items match the value 0. The first one runs, and MCONDE reports the overlap with the line numbers of both items. Duplicate constants like these are easy to spot, but overlaps between casez wildcard patterns are not, and that is where this check earns its keep.
Quick Check
Q: If you add default: $display("Other"); to the multiple-match example, does the MCONDE violation go away?
Show Answer
No. A default only covers the "no match" case. Two items still match 0, so the overlap violation is still reported.
priority case
priority declares that the items must be evaluated in order, with the first match winning, and that at least one item must match. Overlap is allowed and not reported. A violation is reported only when nothing matches and there is no default.
module tb;
bit [1:0] abc;
initial begin
abc = 0;
// First match is executed
priority case (abc)
0 : $display ("Found to be 0");
0 : $display ("Again found to be 0");
2 : $display ("Found to be 2");
endcase
end
endmodule
ncsim> run Found to be 0 ncsim: *W,RNQUIE: Simulation is complete.
This is the same overlapping code as before, but with priority no violation is reported. Only the first match runs. If abc were 1 or 3, nothing would match, and because there is no default, a NOCOND priority case violation would be reported.
Using unique and priority case in Digital Design
All three qualifiers are synthesizable and are widely used in RTL decoders and FSMs.
Before SystemVerilog, designers steered synthesis with comment pragmas: // synopsys parallel_case to say items never overlap, and // synopsys full_case to say every possible value is covered. Simulators ignore comments, so a wrong pragma caused silent simulation-versus-synthesis mismatches. The qualifiers express the same intent, and simulation checks it:
| Qualifier | Synthesis meaning | Equivalent pragmas |
|---|---|---|
unique | Parallel logic; unlisted values are don't-care | full_case parallel_case |
unique0 | Parallel logic only | parallel_case |
priority | Order kept; unlisted values are don't-care | full_case |
The example below decodes a 3-state FSM encoded in 2 bits. The value 2'b11 is never used, so unique lets synthesis treat it as don't-care. Simulation flags it if it ever appears.
typedef enum logic [1:0] {IDLE, READ, WRITE} state_t; // 2'b11 unused
module ctrl_decode (
input state_t state,
output logic rd_en,
output logic wr_en
);
always_comb begin
rd_en = 1'b0; // defaults: outputs always assigned
wr_en = 1'b0;
unique case (state) // items never overlap; 2'b11 is impossible
IDLE : ; // nothing to drive
READ : rd_en = 1'b1;
WRITE : wr_en = 1'b1;
endcase
end
endmodule
Synthesis trusts the qualifier, but only simulation checks it. If an illegal value such as 2'b11 could really reach this logic on silicon, the gates may behave in ways no RTL branch describes. Fix every violation you see in simulation; do not just filter it out of the log.
Where are unique and priority case used?
Instruction decoders in CPU and DSP cores use unique casez so that overlapping opcode patterns are caught early in simulation. FSM output and next-state logic commonly uses unique case on the state variable. Address decoders whose regions are deliberately nested, with a small register window inside a larger memory region, use priority casez so the more specific region wins.
Common Beginner Mistakes
Mistake #1: Violations before reset from an X case expression
A 4-state state register is X until reset is applied. An X matches no case item, so a unique case or priority case without a default reports a violation at time 0 or during reset. Why? The check sees "no item matched", which is what happened. Apply reset at time 0, or recognize these early warnings for what they are, but investigate any violation that appears after reset.
Mistake #2: Assuming default disables all checks
default satisfies only the "no match" check. Why? The overlap check of unique and unique0 is separate, and overlapping items are still reported.
Mistake #3: Using priority when the items cannot overlap
Weaker: priority case (sel) with items 0, 1, 2, 3
Better: unique case (sel)
Why? If the items are exclusive by construction, priority throws away both the overlap check and the parallel-logic optimization. Use priority only when overlap is intended and order matters.
Practice Exercise
Challenge: Opcode decoder with overlapping patterns
A 4-bit opcode selects a 2-bit unit: patterns 11?? select the branch unit (2), any other 1??? selects the ALU (1), and 0??? selects the load/store unit (0). Write the decoder with casez and the right qualifier.
Hint
11?? is a subset of 1???, so the patterns overlap, and the more specific one must win.
Solution
always_comb begin
priority casez (opcode)
4'b11?? : unit = 2'd2; // branch: most specific pattern first
4'b1??? : unit = 2'd1; // ALU: overlaps with the line above
4'b0??? : unit = 2'd0; // load/store
endcase
end
unique would report MCONDE for every opcode starting with 11, so priority is correct. The three patterns cover all 16 values, so no "no match" violation can occur.
Summary
In this tutorial, you learned:
unique casechecks that items never overlap and that some item, or thedefault, always matchesunique0 casechecks only for overlappriority casekeeps source order and checks only that something matches- A
defaultsatisfies the "no match" check, never the overlap check - The qualifiers replace the
full_caseandparallel_casepragmas, with the advantage of being checked in simulation - The qualifiers work with
casezandcasextoo, where overlapping wildcard patterns are common