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 the default item
  • What a case statement synthesizes into, and how it differs from if-else
  • How case matches x and z values, and how casez and casex differ
  • 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 with case_item2, case_item3 (at least one required)
  • begin ... end - Groups multiple statements into one branch
  • default - 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 break is needed as in C.
  • If no item matches and there is no default, no statement runs, and any signal assigned in the case keeps its old value.
  • case statements can be nested inside other case or if branches.

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

Synthesized schematic of my_mux: a 4 to 1 multiplexer selected by sel, with inputs a, b, c and a constant 0

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:

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

StatementBits treated as don't careUse in RTL
caseNone; 0, 1, x and z must all match exactlyDefault choice
casezz or ?, in the expression or in an itemRecommended for wildcard matching
casexx, z or ?, in the expression or in an itemAvoid

// 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 ?

caseif-else-if
ConditionsOne expression compared against many valuesEach branch can test a different, arbitrary condition
Matching x and zExact 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 hardwareParallel mux when items are distinct constantsPriority chain when conditions can overlap
ReadabilityBest for many alternatives on one signalBest 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 case statement 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 default handles every unmatched value
  • A case with 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
  • casez treats z and ? as don't care; prefer it over casex, which also hides x in the expression
  • In combinational logic, always include a default or a prior assignment to avoid latches
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5