The Verilog case statement compares one expression against a list of case items and runs the statements of the first item that matches. It is the standard way to describe a multiplexer or decoder in RTL. A long if-else-if chain on overlapping conditions builds priority logic, while a case on distinct constant items builds a parallel mux.
12 min read | Beginner Level
What You'll Learn
- The syntax of
case, including multiple values per item and thedefaultitem - What a
casestatement synthesizes into, and how it differs fromif-else - How
casematches x and z values, and howcasezandcasexdiffer - How to avoid latch, width and unreachable-item bugs
Syntax
A case statement starts with case and ends with endcase. Like if-else, it is a procedural statement, so it must be inside an always or initial block.
// Here 'expression' should match one of the items (item 1,2,3 or 4)
case (<expression>)
case_item1 : <single statement>
case_item2,
case_item3 : <single statement>
case_item4 : begin
<multiple statements>
end
default : <statement>
endcase
Components:
<expression>- Evaluated once, then compared against each case item in the order they are written (required)case_item- A value to compare against. Several values separated by commas share one branch, as withcase_item2, case_item3(at least one required)begin ... end- Groups multiple statements into one branchdefault- Runs when no item matches. Optional, and at most one is allowed
Rules to remember:
- Only the first matching item runs. There is no fall-through to the next item, so no
breakis needed as in C. - If no item matches and there is no
default, no statement runs, and any signal assigned in thecasekeeps its old value. casestatements can be nested inside othercaseorifbranches.
Example
This 3-input mux selects one of three 3-bit inputs with a 2-bit sel. Since sel has four possible values, the default item drives out to 0 when sel is 3.
module my_mux (input [2:0] a, b, c, // Three 3-bit inputs
input [1:0] sel, // 2-bit select signal to choose from a, b, c
output reg [2:0] out); // Output 3-bit signal
// This always block is executed whenever a, b, c or sel changes in value
always @ (a, b, c, sel) begin
case(sel)
2'b00 : out = a; // If sel=0, output is a
2'b01 : out = b; // If sel=1, output is b
2'b10 : out = c; // If sel=2, output is c
default : out = 0; // If sel is anything else, out is always 0
endcase
end
endmodule
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0b11 out=0x0 [10] a=0x5 b=0x5 c=0x5 sel=0b10 out=0x5 [20] a=0x1 b=0x5 c=0x6 sel=0b01 out=0x5 [30] a=0x5 b=0x4 c=0x1 sel=0b10 out=0x1 [40] a=0x5 b=0x2 c=0x5 sel=0b11 out=0x0 ncsim: *W,RNQUIE: Simulation is complete.
At 20 ns, sel is 1, so out takes b (5). At 30 ns, sel is 2, so out takes c (1). At 0 ns and 40 ns, sel is 3, which matches no item, so the default sets out to 0.
Hardware Schematic
The RTL elaborates into a 4 to 1 multiplexer controlled by sel. Inputs 0, 1 and 2 are connected to a, b and c, and input 3 is tied to the constant 0 from the default item.

Synthesis result: When the case items are distinct constants, no two items can match at the same time, so the order in which they are written does not matter. Synthesis builds a parallel mux in which every input has the same path length to the output, with no priority between items. Compare this with an if-else-if chain, which builds priority logic when its conditions can overlap.
Where is a case Statement Used?
Peripherals expose their control and status registers at fixed addresses. The read path is a case statement on the address bus, with one item per register, that selects which register's value is returned to the processor. A default item returns 0 for unmapped addresses, so a read from an unused location gives a defined value.
Quick Check
Q: In my_mux, what is out when sel is 2'b1x?
Show Answer
0. A case item matches only when every bit is identical, including x and z. 2'b1x is not equal to 2'b10 or 2'b11 in that comparison, so no item matches and the default runs.
x and z in the case expression
A case statement compares bit for bit using all four values: 0, 1, x and z. An x in sel matches only an x in the same position of a case item, and z matches only z. In my_mux no item contains x or z, so whenever sel has an x or z bit, the default runs and out is 0:
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x0 [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x0 [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x0 [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x0 [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x0 [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x0 [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x0 [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0 [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x0 [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x0 ncsim: *W,RNQUIE: Simulation is complete.
If the case items themselves contain x and z, sel must match those exact values for the branch to run:
module my_mux (input [2:0] a, b, c,
input [1:0] sel,
output reg [2:0] out);
// Case items have x and z and sel has to match the exact value for
// output to be assigned with the corresponding input
always @ (a, b, c, sel) begin
case(sel)
2'bxz : out = a; // Matches only when sel is exactly x then z
2'bzx : out = b; // Matches only when sel is exactly z then x
2'bxx : out = c; // Matches only when sel is exactly xx
default : out = 0; // Every other value, including zz
endcase
end
endmodule
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x1 [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x5 [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x1 [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x6 [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x5 [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x6 [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x7 [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0 [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x4 [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x5 ncsim: *W,RNQUIE: Simulation is complete.
For example, at 0 ns sel is xx, which matches the third item, so out takes c (1). At 70 ns sel is zz, which matches no item, so out is 0.
Case items with x or z are for simulation only.
Real hardware has no x or z values on a select line, so synthesis tools treat items containing x or z as items that can never match, and usually print a warning. The synthesized version of this second module would always output 0, which does not match its simulation. Use this style only to understand simulation behavior, never in RTL meant for synthesis.
casez and casex
Verilog has two variants of case that treat some bit values as "don't care" when comparing:
| Statement | Bits treated as don't care | Use in RTL |
|---|---|---|
case | None; 0, 1, x and z must all match exactly | Default choice |
casez | z or ?, in the expression or in an item | Recommended for wildcard matching |
casex | x, z or ?, in the expression or in an item | Avoid |
// casez: '?' bits in an item match anything, so one item covers many values
always @(*) begin
casez (opcode)
4'b1??? : unit = 2'd3; // Any opcode with bit 3 set
4'b01?? : unit = 2'd2; // Bit 3 clear, bit 2 set
4'b001? : unit = 2'd1;
default : unit = 2'd0;
endcase
end
With wildcard items, two items can match the same value, so the first one written wins. This is how casez describes priority logic such as a priority encoder.
Prefer casez over casex. Because casex also treats x in the expression as don't care, an uninitialized select signal can match the first item and hide a reset bug in simulation. With casez, an x in the expression still falls to the default, where it is easier to notice.
Finite State Machines
The next-state logic of a state machine is almost always a case statement on the current state. Each item describes one state: what the outputs are and which state comes next for each input condition. Protocol controllers for interfaces such as UART, SPI and I2C are written this way. See Verilog FSM.
How is a case different from if-else ?
case | if-else-if | |
|---|---|---|
| Conditions | One expression compared against many values | Each branch can test a different, arbitrary condition |
| Matching x and z | Exact 4-state match, so an x or z in the expression gives a definite result (usually the default) | A condition that evaluates to x or z is treated as false |
| Typical hardware | Parallel mux when items are distinct constants | Priority chain when conditions can overlap |
| Readability | Best for many alternatives on one signal | Best for a few conditions on different signals |
ALU Operation Select
An ALU computes several results in parallel, such as add, subtract, AND, OR and shift, and a case on the operation code selects which one reaches the output. The opcode values are distinct constants, so the select logic is a single wide parallel mux rather than a chain, which keeps it off the ALU's critical path.
Common Beginner Mistakes
Mistake #1: No default in combinational logic
Wrong: always @(*) case (sel) 2'b00: out = a; 2'b01: out = b; 2'b10: out = c; endcase
Right: Add default: out = 0;, or assign out before the case
Why? When sel is 3, no item matches, so out keeps its old value and synthesis infers a latch. A default also makes the result defined when sel is x or z in simulation. See D Latch.
Mistake #2: Case items wider or narrower than the expression
Wrong: case (sel) 3'd4: out = d; ... with a 2-bit sel
Right: Size every item to the width of the expression, such as 2'd0 to 2'd3
Why? The expression and items are extended to the same width before comparing. A 2-bit sel is zero-extended to 3 bits, so its value is at most 3 and can never equal 4. That item is dead code. Most tools give only a width warning, which is easy to miss.
Mistake #3: Duplicate or overlapping items
Wrong: 2'b01: out = b; and later 2'b01: out = d;
Right: Give each value exactly one item, and use a comma to share a branch: 2'b01, 2'b11: out = b;
Why? Only the first matching item runs, so the second one can never execute. This usually comes from a copy-paste error, and the code compiles without an error.
Mistake #4: Using casex for wildcard matching
Wrong: casex (opcode) 4'b1xxx: ...
Right: casez (opcode) 4'b1???: ...
Why? casex also ignores x bits in opcode itself. If opcode is all x after power-up, it matches the first item in simulation, so the design appears to work while the real hardware may not. casez ignores only z and ?.
Practice Exercise
Challenge: 3 to 8 Decoder with Enable
Write a module dec3to8 with a 3-bit input in, an enable en and an 8-bit output out. When en is 1, exactly one output bit is high: bit in. When en is 0, all outputs are 0. Use a case statement.
Hint
Assign out = 8'b0 first so that every path assigns it. Then, only if en is 1, use a case on in with one item per value.
Solution
module dec3to8 (
input [2:0] in,
input en,
output reg [7:0] out
);
always @(*) begin
out = 8'b0; // Default: every path assigns out, so no latch
if (en) begin
case (in)
3'd0 : out = 8'b0000_0001;
3'd1 : out = 8'b0000_0010;
3'd2 : out = 8'b0000_0100;
3'd3 : out = 8'b0000_1000;
3'd4 : out = 8'b0001_0000;
3'd5 : out = 8'b0010_0000;
3'd6 : out = 8'b0100_0000;
3'd7 : out = 8'b1000_0000;
default : out = 8'b0; // in has x or z: keep outputs off
endcase
end
end
endmodule
All eight items are distinct constants, so synthesis builds eight parallel AND gates, one per output, each decoding one value of in together with en. The same decoder can be written in one line as out = en ? (8'b1 << in) : 8'b0;, but the case version reads like the truth table.
Summary
In this tutorial, you learned:
- A
casestatement compares one expression against a list of items and runs only the first match, with no fall-through - Several values can share a branch when separated by commas, and
defaulthandles every unmatched value - A
casewith distinct constant items synthesizes into a parallel mux with no priority - Matching is exact for all four values, so x or z in the expression usually goes to
default - Case items containing x or z only work in simulation, not in synthesized hardware
caseztreats z and?as don't care; prefer it overcasex, which also hides x in the expression- In combinational logic, always include a
defaultor a prior assignment to avoid latches