Module 5: Behavioral Verilog for Synthesis ยท 5.3 Conditional and Case Statements

casez, and Why Not casex

Decoding a 4-bit opcode where the top two bits pick the class and the bottom two do not matter yet is awkward with plain case, which wants exact values. casez and casex both add don’t-care bits. One of them is an everyday tool. The other has been quietly breaking chips for decades, and they differ by one letter.

casez

In a casez item, ? or z means “either 0 or 1”:

always @(*) begin
    casez (req)
        4'b1???: grant = 2'd3;
        4'b01??: grant = 2'd2;
        4'b001?: grant = 2'd1;
        4'b0001: grant = 2'd0;
        default: grant = 2'd0;
    endcase
end

If req[3] is set the first arm matches whatever the other bits are; this is a priority encoder written as a table. It synthesises cleanly and it is everywhere in decoders. Use it freely.

casex

casex does the same and also treats x as a wildcard. That sounds like a small extension and it is not. Before reset has driven a signal, or after any uninitialised path, that signal is x in simulation. Under casex an x in the expression matches the first arm whose pattern has a wildcard there, and the block picks an arm as if the input were known. The simulation runs clean, the waveform looks plausible, and the x that case or casez would have shown you on the output has been swallowed.

In silicon there is no x; every net is 0 or 1. So the hardware takes whichever arm the real value selects, which is not the one the simulation showed, and the testbench that passed never exercised that path. That is the shape of the bugs that appear in the lab and not in regression.

? and z x
case exact exact always fine
casez wildcard exact use for wildcards
casex wildcard wildcard do not write

Never write casex in new code. Style guides forbid it and linters flag it.

A decoder

always @(*) begin
    casez (instr[6:0])
        7'b0110011: instr_type = R_TYPE;
        7'b0010011: instr_type = I_TYPE;
        7'b0000011: instr_type = LOAD;
        7'b0100011: instr_type = STORE;
        7'b1100011: instr_type = BRANCH;
        default:    instr_type = ILLEGAL;
    endcase
end

This one happens not to need a ?, since the opcode field decides the type on its own. The moment sub-formats are distinguished by some bits and not others, ? in a casez is the tool, and it stays honest through reset because an x is never a match.

Practice Exercise

Convert this to casez, and describe a reset-time scenario where the casex version reports a wrong grant that casez would not.

always @(*) begin
    casex (req)
        4'b1xxx: grant = 2'd3;
        4'b01xx: grant = 2'd2;
        4'b001x: grant = 2'd1;
        4'b0001: grant = 2'd0;
        default: grant = 2'd0;
    endcase
end
Solution
always @(*) begin
    casez (req)
        4'b1???: grant = 2'd3;
        4'b01??: grant = 2'd2;
        4'b001?: grant = 2'd1;
        4'b0001: grant = 2'd0;
        default: grant = 2'd0;
    endcase
end

Swap the keyword and the x fillers for ?. In the first nanoseconds of a simulation, before the testbench drives it, req is 4'bxxxx. Under casex the first arm matches, since every bit is x and x is a wildcard, and grant reads 3 with nothing requested. Under casez no arm matches an all-x input, the default fires, and grant is 0.

There is one more thing people write next to a case: the full_case and parallel_case pragmas, which tell the synthesiser things the simulator cannot see. The next article is about them and why current style guides say to leave them out.

โ† case full_case and parallel_case โ†’