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 case and priority case check
  • How a default item interacts with each check
  • How the qualifiers replace the old full_case and parallel_case synthesis 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).

QualifierViolation if more than one item matchesViolation if no item matches and there is no defaultItem that runs
uniqueYesYesThe matching item; with overlap, the first match
unique0YesNoThe matching item; with overlap, the first match
priorityNoYesThe 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 for unique only.

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
Output
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
Output
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
Output
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:

QualifierSynthesis meaningEquivalent pragmas
uniqueParallel logic; unlisted values are don't-carefull_case parallel_case
unique0Parallel logic onlyparallel_case
priorityOrder kept; unlisted values are don't-carefull_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 case checks that items never overlap and that some item, or the default, always matches
  • unique0 case checks only for overlap
  • priority case keeps source order and checks only that something matches
  • A default satisfies the "no match" check, never the overlap check
  • The qualifiers replace the full_case and parallel_case pragmas, with the advantage of being checked in simulation
  • The qualifiers work with casez and casex too, where overlapping wildcard patterns are common
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5