In programming languages,
switch) statements are used as
a conditional statement in which a selection is made based on
different values of a particular variable or expression. A general
discussion of these statements can be found
In hardware description languages (HDL) such as VHDL and
case statements are also available.
case sel is -- VHDL when "00" => y <= a; when "01" => y <= b; when "10" => y <= c; when "11" => y <= d; end case;
case (sel) // (System)Verilog "00" : y = a; "01" : y = b; "10" : y = c; "11" : y = d; endcase
The above code fragments demonstrate the use of a
case statement to
describe a 4-to-1 multiplexer, a common case where a case statement is
case in VHDL has the advantage that the language
guarantees that all cases are covered. Any choice not covered in
case statement will lead to a compilation error. As a
case statement is preferred over an
But what if a number of cases trigger the same action? Code duplication should be avoided, because that makes the code harder to maintain. Fortunately, VHDL case statements can handle ranges and lists of values.
variable bmi: natural range 0 to 100; -- code omitted case bmi is when 18 downto 0 => verdict <= "too low "; -- range when 20 => verdict <= "perfect "; -- single value when 19 | 21 | 22 | 23 | 24 => verdict <= "good "; -- list of values when 25 to 29 => verdict <= "a bit high"; -- range when 30 to 100 => verdict <= "too high "; -- range end case;
SystemVerilog (but not Verilog) has a
case inside statement with similar functionality:
logic [3:0] sel; // code omitted case (sel) inside [4'h0:4'h7] : y = a; // range [4'h9:4'hD] : y = b; // range 4'h8, 4'hE : y = c; // list of values 4'hF : y = d; // single value endcase
casez statements, in which some bits of the
selection pattern can be marked as don’t care. These statements should
be avoided for range matching. The wildcard match could hide an
undefined or illegal signal value of
x, i.e. it could hide a design
flaw. Priority encoding is one example where
casez is a good fit:
casez (sel) // priority encoder 4'b???1 : prio = 0; 4'b??10 : prio = 1; 4'b?100 : prio = 2; 4'b1000 : prio = 3; default : prio = 0; //no match endcase
Finally, a default action may be added to the case statement using
others (VHDL) or
clause. This removes the requirement to enumerate every option in the
case statement. For clarity, the default should be the last clause
of the case statement.
logic [3:0] sel; // code omitted case (sel) inside [4'h0:4'h7] : y = a; // particular values [4'h9:4'hD] : y = b; 4'hF : y = d; default : y = c; // default value, in this case `8` and `E` endcase
The example below shows one of the most common uses of case statements, namely a finite state machine (FSM).
type t_state is (SIT, STAND, WALK, RUN, FLY); signal state: t_state; -- code omitted p_state : process(clk) is begin if rising_edge(clk) then if rst = '1' then state <= SIT; else case state is when SIT => if start_moving then state <= STAND; end if; when STAND => state <= WALK; when WALK => state <= RUN; when RUN => if cleared_for_takeoff then state <= FLY; end if; when others => -- default, includes state FLY state <= SIT; end case; end if; end if; end process p_state;
A discussion exists
default clause is recommended if all possible
cases have been enumerated in the case statement. We believe that it
should be up to the designer to make that decision, taking the
following considerations into account:
If the code is meant for simulation only, the presence or absence of a default clause will not affect the design. For clarity, it may be better to leave it out.
If the selection variable may be extended (e.g. more bits, more enumeration literals), a default clause will ensure that the design will still compile with the extension. This may be convenient, but there is a risk that the designer may forget to add specific selections.
A synthesis tool may add hardware to protect the circuit against illegal patterns. This may happen in particular if the selection variable is an enumerated type. If the enumerated type has a number of values that is not a power of 2, or if one-hot encoding is used, some bit patterns of the enumeration value don’t correspond to an enumerated value. In that case, a default value may be used by the synthesis tool to determine what should happen if an illegal selection value is seen, e.g. raise an alarm, or bring a finite state machine (FSM) to a safe state. Depending on the synthesis tool and nature of the circuit, it may be desirable or not to add a protective circuit.
In (System)Verilog, it is not required to cover all possible options of a
casestatement. Combinations of
'z'would typically be omitted, potentially hiding the propagation of uninitialized data and leading to synthesis-simulation mismatch. Adding a
'x'-es on its outputs will reveal potential design flaws in simulation and will help debug the design.
case in HDL is a powerful conditional control
statement. Case statements are easy to understand and maintain. Use
the most suitable variant in your design case for optimal design
quality and maintainability.
Sigasi Studio offers case statement templates and configurable validations to improve designers' productivity.
- Actual? Formal? What do they mean? (blog post)
- Wildcards in sensitivity lists in VHDL and Verilog (blog post)
- Prefix all signals in an instantiation (blog post)
- Checking module instantiations (screencast)
- Recovering Verilog and SystemVerilog Parser (blog post)