Sequential case statement

Some care is needed with the Verilog version of this code. Verilog case statements are essentially identical to if-else statements; each case item is "executed" in turn, and the first match wins. There is no requirement that case items do not overlap. This implies a priority-encoder structure, rather than a wide-input multiplexer. There is, additionally, no requirement for a default clause.

The interpretation of a Verilog case statement as a multiplexer is a synthesiser convention, rather than a language feature (contrast with VHDL, in which the simulation semantics of the case statement - in other words, the language itself - actually does have "multiplexer" behaviour). In order for a Verilog synthesiser to recognise and extract a multiplexer, the programmer has to ensure that two conditions are met:

  1. no case items overlap (the case statement is parallel). In this example, the case items are simply the integers 0 through 6, and the items are clearly all different, with no overlap.
  2. All possible values of the case expression (SEL) are covered by the case items (the case statement is full). If the case statement is not full, then a latch will be inferred, for simulation, synthesis, or both.

There are two ways to ensure that a case statement is full:

  1. explicitly list all possible values of the case expression (SEL). For a simulator, this means all 4-state values. For a synthesiser, this means all 2-state values (SEL has 64 possible 4-state values, and 8 possible 2-state values).
  2. use a default clause; this covers all values which are not explicitly listed.

Historically, it was common to add a synthesis directive (/* synopsys full_case parallel_case */) to tell the synthesiser that the case statement met these two conditions. This is not, however, a good idea; it gives the synthesiser information which is not available to the simulator, and potentially leads to simulation mismatches. It is safer, and simpler, to manually ensure that the case items do not overlap, and that you have a default clause.

There are two possibilities for the coding of the default clause, depending on whether or not you want to propagate metavalues. This applies to both the Verilog and VHDL models. If you're not interested in propagating metavalues, you can simply replace the last "real" (2-state) selector value with a default; both the examples below do this. If, on the other hand, you wish to propagate metavalues, all the "real" selector values should be explicitly listed, with an additional default. This default branch normally includes error-handling code (reporting an error, for example), but should be protected from synthesis:

    case (SEL)
      7:       O = I[7];
/* synthesis off */
      default: $display("no case matched");
/* synthesis on */

Note also that blocking assignments (=) may be used in the Verilog code, since this is combinatorial code.


module MUX8TO1(
    input [2:0] SEL, 
    input [7:0] I, 
    output reg  O);

  always @(SEL, I) begin
      0:        O = I[0];
      1:        O = I[1];
      2:        O = I[2];
      3:        O = I[3];
      4:        O = I[4];
      5:        O = I[5];
      6:        O = I[6];
      default:  O = I[7];


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;               -- unsigned and to_integer

entity MUX8TO1 is
  port (
    SEL : in  unsigned(2 downto 0);
    I   : in  std_logic_vector(7 downto 0);
    O   : out std_logic);
end entity MUX8TO1;

architecture A of MUX8TO1 is
  process(SEL, I) is
    -- could instead use to_integer(SEL) here; see mux8to1_a3.vhd  
    case SEL is 
      when "000"  => O <= I(0);
      when "001"  => O <= I(1);
      when "010"  => O <= I(2);
      when "011"  => O <= I(3);
      when "100"  => O <= I(4);
      when "101"  => O <= I(5);
      when "110"  => O <= I(6);
      when others => O <= I(7);
    end case;
  end process;
end architecture A;