Skip to content

VHDL · Chapter 5.5 · Sequential Statements & Processes

The case Statement

When a decision is based on the value of one expression rather than a list of prioritized conditions, the case statement is the right tool. It selects a branch by the value of a single expression, the choices are mutually exclusive and have no priority, and they must cover every possible value, which is why VHDL requires a catch-all others branch. That makes case synthesize to a balanced multiplexer or decoder rather than a priority chain, and it is the cleanest way to write opcode decoders and, later, finite state machines. Built on the process execution model, this lesson contrasts case with the priority if, shows ranges and grouped choices, and explains why exhaustive coverage keeps your logic free of unwanted latches.

Foundation14 min readVHDLcase statementDecoderMultiplexerFSMSequential

1. Engineering intuition — decode by value, no priority

Some decisions are not a priority ladder but a clean lookup: "for this opcode do this; for that opcode do that." Every value maps to exactly one action, and no action outranks another. The case statement expresses that as a balanced decode — the selector value picks one branch directly, with all branches equal rank. Where if builds a priority chain, case builds a flat, single-layer selection, which is both clearer to read and cheaper in logic depth for value-based choices.

2. Formal explanation — exhaustive, mutually exclusive choices

case_form.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
case expr is
  when val1            => -- statements
  when val2 | val3     => -- grouped choices (val2 OR val3)
  when val4 to val7    => -- a range of choices
  when others          => -- covers EVERY remaining value (required)
end case;

The selector expr is evaluated once and compared against each when choice; the matching branch runs. Choices must be mutually exclusive and cover the entire value space of the selector — VHDL enforces this, so a when others (or an explicit full enumeration) is mandatory. As with the combinational if, every output must be assigned on every branch (or defaulted at the top) to stay latch-free.

3. Production RTL — decoders, ALUs, and state

case_patterns.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
 
-- (A) Opcode decode (balanced): each opcode selects one result.
alu : process (all)
begin
  result <= (others => '0');                 -- default → latch-free
  case opcode is
    when "00" => result <= std_logic_vector(unsigned(a) + unsigned(b));
    when "01" => result <= std_logic_vector(unsigned(a) - unsigned(b));
    when "10" => result <= a and b;
    when others => result <= a or b;          -- covers "11" and metavalues
  end case;
end process;
 
-- (B) case on an enumerated type — the FSM idiom (Module 9 builds on this).
type state_t is (IDLE, RUN, DONE);
nxt : process (all)
begin
  next_state <= state;                        -- default: hold
  case state is
    when IDLE => if go = '1' then next_state <= RUN;  end if;
    when RUN  => if fin = '1' then next_state <= DONE; end if;
    when DONE => next_state <= IDLE;
  end case;                                    -- enum fully covered → no 'others' needed
end process;

What hardware does this become? (A) a 4:1 result multiplexer driven by opcode (with the arithmetic via numeric_std); (B) the next-state combinational logic of a state machine. Note that when the selector is an enumerated type and all literals are listed, the choices are already exhaustive, so no when others is required.

4. Hardware interpretation — a balanced mux/decoder

case statement as a balanced multiplexer keyed by selector valuebalanced select (no priority)balancedselect (no…selector (one expr)evaluated oncewhen val1branch 1when val2 | val3branch 2when othersall remaining valuesoutputthe one matching branch12
A case statement synthesises to a balanced multiplexer or decoder: the selector value is matched against all choices at once and exactly one branch is taken, routing its result to the output. There is no priority chain — all choices are equal rank, so reordering the when-clauses changes nothing. Contrast the if statement, which builds a priority ladder; case is a flat, single-layer selection, which is why it is the natural form for decoders and FSM next-state logic.

5. Simulation interpretation — match once, run one branch

By the execution model, when the process wakes the selector is evaluated and matched against the choices; the single matching branch runs (zero time, signals scheduled), and the others do not. There is no ordered scan as in if — the match is a flat decode, which is why there is no priority.

case balanced decode contrasted with if priority scancasematch selector → one branch(balanced)no prioritychoices equal rank, orderirrelevantif/elsifscan conditions → firsttrue (priority)priorityorder is precedence12
case versus if at evaluation time. case evaluates the selector once and jumps to the single matching (mutually exclusive, exhaustive) branch — a balanced decode with no priority. if tests conditions top to bottom and stops at the first true one — a priority ladder. Choose case for value-based decode (opcodes, states) and if for prioritised conditions (errors over status, enables).

An ALU case: opcode value routes the selected result

8 cycles
An ALU case: opcode value routes the selected resultopcode 00 → a+bopcode 00 → a+bopcode 01 → a-b (no priority — pure value decode)opcode 01 → a-b (no pr…opcode 11 → others → a|bopcode 11 → others → a…opcode0000010110101111resulta+ba+ba-ba-ba&ba&ba|ba|bt0t1t2t3t4t5t6t7
The opcode value alone selects the result — each value maps to exactly one branch, with no ordering between them. Reordering the when-clauses would not change anything, the signature of a balanced decode. This is why case is preferred for decoders and FSM next-state logic over an equivalent if ladder.

6. Debugging example — the incomplete case

Expected: a clean value decode that compiles and synthesises to a mux. Observed: a compile error that the choices do not cover all cases, or (when adapted to a process without a default) an inferred latch. Root cause: the selector's full value space was not covered — a std_logic_vector has more values than the "expected" patterns once metavalues are counted, so the choices are incomplete. Fix: add when others (and a top-of-process default for any output not set in every branch). Engineering takeaway: case is exhaustive by design; when others both satisfies that and defines behaviour for unexpected/metavalue selectors — never omit it on a std_logic-based selector.

incomplete_then_fixed.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: only the four expected patterns → std_logic has more values → incomplete.
case sel is
  when "00" => y <= a; when "01" => y <= b;
  when "10" => y <= c; when "11" => y <= d;   -- no others → error / latch
end case;
-- FIX: 'when others' makes the choices exhaustive and metavalue-safe.
case sel is
  when "00" => y <= a; when "01" => y <= b;
  when "10" => y <= c; when others => y <= d;
end case;

7. Common mistakes & what to watch for

  • Omitting when others. A std_logic_vector selector is never fully covered by the expected patterns alone; without others it will not compile (or infers a latch in a process).
  • Using case for prioritised conditions. case is balanced; for precedence use if (lesson 5.4).
  • Forgetting to assign every output on every branch. Default at the top of the process to stay latch-free, just as with if.
  • Overlapping or duplicate choices. Choices must be mutually exclusive; duplicates are an error.
  • Assuming choice order matters. It does not — only the selector value does.

8. Engineering insight

case is the construct for value-based decisions, and its exhaustiveness requirement is a feature: it forces you to define behaviour for every selector value, eliminating the missing-case latch that an incomplete if invites. It synthesises to a flat, balanced mux/decoder — efficient and readable for opcodes, address decode, and especially the next-state and output logic of state machines, where a case on an enumerated state type is the industry-standard idiom. Pair case (balanced decode) with if (priority) and you have the complete decision toolkit for process-based logic.

9. Summary

The sequential case selects a branch by the value of one expression — a balanced, no-priority decode synthesising to a multiplexer or decoder. Choices are mutually exclusive and must be exhaustive, so when others is required for std_logic-based selectors (an enum with all literals listed is already complete). Default-assign outputs to stay latch-free. Use case for value decode, if for priority.

10. Learning continuity

if and case give a process its decisions. The next lesson adds repetition: Loops — for, while, and loop — where a for loop unrolls into replicated hardware (the process-side analog of generate), letting you describe bit-sliced logic, reductions, and array operations from a single loop body, all still governed by the execution model from the anchor.