Skip to content

Verilog · Chapter 14.5.2 · Behavioural Modeling

Case Statements in Verilog — case, casez, casex & the default Rule

The case statement is behavioural logic's parallel branch. It selects among the values of an expression, such as an opcode, an FSM state, or a mux select, with cases that are normally mutually exclusive, synthesizing to a parallel mux or decoder. Three variants exist: plain case for exact matches, casez which treats z and question mark as don't-cares and is the safe way to write priority and range patterns, and casex which treats both x and z as don't-cares and is dangerous because an x in the expression silently matches and can mask real bugs. This page drills all three, the all-important default that prevents an inferred latch, the use of casez for don't-care patterns, and why casex should be avoided in favour of casez. The case statement is the backbone of FSM state decoding and value-based selection.

Foundation14 min readVerilogcasecasezcasexdefaultFSM

Chapter 14 · Section 14.5.2 · Behavioural Modeling

1. The Engineering Problem

case selects on a value and is the natural construct for opcodes, states, and selects — but it carries the same latch hazard as if, plus a variant trap:

A case without a default infers a latch (incomplete assignment); and casex (which treats x as a don't-care) can silently mask bugs — prefer casez with ? for don't-cares.

This page drills case/casez/casex, the default rule, and the casex hazard.

2. Mental Model — Parallel Value Selection; default Completes It

3. The case Statement

case.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   always @(*)
       case (op)
           2'b00: y = a + b;
           2'b01: y = a - b;
           2'b10: y = a & b;
           2'b11: y = a | b;
           default: y = 8'h00;      // covers any unexpected value (incl. x/z)
       endcase
  • The expression op is matched against each case value (exact match); the first matching case executes.
  • The default covers all unlisted values — and even a fully-enumerated case should include a default to handle x/z on the expression and to avoid a latch.
  • case reads more clearly than a long if/else if chain for value selection, and synthesizes to a parallel structure.

Visual A — case is parallel selection; if/else is priority

case → one parallel mux; if/else → a priority cascade

data flow
case → one parallel mux; if/else → a priority cascadecase (op)all items compared against op at once→ parallel mux /decoderone select, all branches equalif / else if …conditions checked in order→ priority muxcascadeearliest true wins (deeper path)
case is not just syntax — it is a SELECTION structure. Because its items are mutually exclusive matches against one expression, it synthesizes to a single parallel mux/decoder where every branch is equal depth. An if/else chain, by contrast, is a priority cascade (earliest true wins) that builds a deeper mux chain. Choose case when selecting on one value; if/else when the conditions have a precedence.

4. The default Rule — Latch Avoidance

As with if, a combinational case must assign every output on every path. A missing default (with unlisted values, or x/z inputs) leaves an unassigned path → a latch:

case-default.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // LATCH — no default, sel=2'd3 unassigned:
   always @(*)
       case (sel)
           2'd0: y = a;
           2'd1: y = b;
           2'd2: y = c;
       endcase
 
   // FIX — default:
   always @(*)
       case (sel)
           2'd0: y = a;
           2'd1: y = b;
           2'd2: y = c;
           default: y = 0;          // covers 2'd3 and x/z → no latch
       endcase

Always include a default in a combinational case. Even when all values are enumerated, the default handles x/z on the expression and documents the intent. (Or use the default-first pattern: assign y before the case.)

5. casez and casex — Don't-Cares

casez and casex allow don't-care bits in the case values — but they differ critically:

casez-casex.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // casez — z and ? are don't-cares (the SAFE way to write patterns):
   casez (req)
       4'b1???: grant = 2'd3;       // req[3]=1, others don't-care (priority)
       4'b01??: grant = 2'd2;
       4'b001?: grant = 2'd1;
       4'b0001: grant = 2'd0;
       default: grant = 2'd0;
   endcase
 
   // casex — x AND z are don't-cares (DANGEROUS):
   casex (sel)
       2'b1x: ...                   // an x in 'sel' also matches → masks bugs
   endcase
  • casez treats z and ? (the preferred don't-care symbol) as don't-cares in the case values — useful for priority encoders and range matching (the 4'b1??? pattern). Safe and common.
  • casex treats both x and z as don't-cares — and crucially, an x in the case expression (a real unknown, often a bug) will match a casex pattern, silently hiding the unknown. This masks bugs, so casex is avoided in modern RTL; use casez (with ?) instead.

The discipline: casez with ? for don't-cares, never casex. (Note casez/casex build priority structures when patterns overlap, unlike plain case.)

Visual B — case variants

case / casez / casex

data flow
case / casez / casexcaseexact match · parallel selectcasezz / ? don't-care · SAFE patternscasexx AND z don't-care · DANGEROUS
Plain case matches exactly (parallel value selection). casez treats z and ? as don't-cares — the safe way to write patterns and priority encoders. casex treats x AND z as don't-cares, so a real x (a bug) silently matches a pattern, masking the unknown — which is why casex is avoided in favour of casez.

6. Common Mistakes

  1. Missing default — infers a latch (and mishandles x/z); always include it (§4, DebugLab 1).
  2. Using casex — its x-matching masks bugs; use casez with ? (§5, DebugLab 2).
  3. Using if/else for value selectioncase is clearer for selecting on a value (§3).
  4. Overlapping case items — plain case expects mutual exclusion; overlap makes it priority-like (§5).
  5. full_case/parallel_case pragmas — promise synthesis something the simulator ignores, creating a sim/synth mismatch; make the case genuinely complete instead (DebugLab 3, 14.5.3).

7. Debugging Lab

Three case-statement debug post-mortems

8. Interview Q&A

9. Exercises

Exercise 1 — Add the default

Add a default to a 4-state FSM case that decodes state into out, so it is latch-free.

Exercise 2 — casez vs casex

For a priority decoder with don't-care patterns, which variant do you use and why? What does the other one do with a real x input?

Exercise 3 — case or if?

For (a) selecting on a 3-bit opcode and (b) a prioritized error/interrupt decision, which construct (case or if/else) fits each?

10. Summary

The case statement is parallel value selection:

  • Parallel — mutually-exclusive cases; synthesizes to a parallel mux/decoder.
  • default — covers unlisted values and x/z; required for latch avoidance in combinational blocks.
  • casezz/? don't-cares; the safe way to write patterns/priority encoders.
  • casexx and z don't-cares; avoid (a real x matches, masking bugs).

The discipline: always a default; casez with ? for don't-cares, never casex.

The last branching sub-topic covers the synthesis pragmas and advanced patterns: Chapter 14.5.3 Multiway Advanced Techniques drills full_case/parallel_case (and their dangers), don't-care handling, and priority vs parallel structures.

  • if/else Statements — Chapter 14.5.1; the priority-branching alternative.
  • Multiway Branching — Chapter 14.5; the branching overview and latch discipline.
  • Equality Operators — Chapter 10.8; the case-equality (===) semantics behind case matching.
  • Generate case — Chapter 14.7.3; the elaboration-time counterpart — a runtime case builds all branches, a generate case builds only the selected structure.
  • Dataflow Practical Examples — Chapter 13.2; muxes and decoders at the dataflow level.