← All guides

case, casez, casex and their cousins

Every HDL needs a way to say “pick a behaviour based on this value”. That’s one hardware idea, a multiplexer tree, but across Verilog, SystemVerilog and VHDL it grew a small family of spellings. Two of them you’ll use weekly, a few exist to make your intent checkable, and one is banned by most style guides for good reason. This page is the family portrait.

Verilog: a family of three

case is an exact, four-state comparison. Each item is checked literally, and that includes x and z: an item written 2'b1x matches only when the selector is literally 1x in simulation, which essentially never describes hardware you meant to build. In practice: plain case, real values only, and always a default (a case that assigns nothing on some path builds a latch, which has its own guide).

casez treats z in an item as “don’t care”, and since ? is a synonym for z, you get readable priority tables. Each row reads “this bit set, lower bits clear, higher bits irrelevant”:

reg [1:0] idx;

always @(*) begin
  casez (req[3:0])
    4'b???1: idx = 2'd0;   // ? = don't care: any high bits, bit 0 wins
    4'b??10: idx = 2'd1;
    4'b?100: idx = 2'd2;
    4'b1000: idx = 2'd3;
    default: idx = 2'd0;   // no requests: park at 0 (and no latch)
  endcase
end

That’s a priority encoder, live below. Try swapping two rows and watch the priority move (a small aside: the [3:0] is there because this is a bare snippet and RapidRTL has to infer the ports; the part-select is what tells the inference engine how wide req is):

Interactive schematic by RapidRTL

casez is how the table-style takes in the priority arbiter guide are written. One convention makes it safe: write ? for don’t-care, never a literal z, so a reader can’t mistake a wildcard for a tri-state comparison.

casex goes one step further and treats x as don’t-care too, in both directions, and that direction is the problem. If the selector carries an x in simulation (uninitialized register, unconnected wire), it matches the first item as if nothing were wrong. Your testbench passes; the synthesized hardware, where that x is a real 0 or 1, does something else. A construct that can hide exactly the bugs simulation exists to catch is why most style guides simply say: never casex. If you feel you need it, casez with ? almost certainly expresses what you meant.

SystemVerilog: say what you’re claiming

SystemVerilog kept the three above and added qualifiers that turn your assumptions into checked claims:

These two exist to replace the old // synopsys full_case parallel_case pragmas, which made the same promises to the synthesizer without anything ever checking them, a famous source of simulation/synthesis mismatches. The qualifiers are the honest version: same optimization, but a lie now fails in simulation instead of in hardware.

VHDL: strict by construction

VHDL’s case doesn’t negotiate: the choices must cover every possible value of the selector or the code doesn’t compile. That’s why when others appears at the end of nearly every VHDL case. The subtlety worth knowing is that VHDL checks coverage, not assignment: an arm that exists but assigns nothing (when others => null;) satisfies the compiler and still infers a latch, as covered in the VHDL latch guide.

Here’s a classic complete case, an operation selector, with every arm assigning:

Interactive schematic by RapidRTL

Two relatives round out the VHDL side:

A note on standards, since VHDL projects pin them: plain case behaves the same from VHDL-87 through 2008. case?, like process (all), needs VHDL-2008, so check what your flow is set to before reaching for either (the embed above runs with the 2008 standard for exactly that reason).

The whole family at a glance

ConstructLanguageDon’t-caresChecked for youVerdict
caseV / SVnone (x/z literal)nothingdefault choice, add default
casezV / SV?/z in itemsnothingfine for priority tables
casexV / SV?, z and x, both waysnothingdon’t use it
unique caseSVas base constructexactly one match, no overlapuse when order doesn’t matter
priority caseSVas base constructat least one matchuse when you mean priority
case insideSVranges, lists, patternssafe asymmetric matchingthe modern wildcard case
caseVHDLnonefull coverage, at compile timeassignment still your job
case?VHDL-2008'-' in choicescoverage and no overlapthe safest wildcard case here

What to actually use