Skip to content

VHDL · Chapter 2.7 · Data Types

Enumeration Types

Some signals are not numbers or bit vectors, they are one of a small set of named states such as idle, reading, or writing. An enumeration is the type for exactly that: you list the legal values by name, and VHDL's strong typing guarantees the signal is never anything else. You already use enums, since the logic type, boolean, and character are all predefined enumerations, and your own enums are how every state machine in VHDL names its states. This lesson shows how an enum literal carries no bits until synthesis encodes it into a real representation, why the leftmost value is the silent default, and the two gotchas that bite everyone: uninitialised states and incomplete case statements.

Foundation14 min readVHDLEnumerationFSMState EncodingcaseTypes

1. Intuition — a type that is a list of names

Most of what a design tracks is not a number. A controller is idle or busy; a bus is in its address or data phase; a mode is read or write. An enumeration type captures that directly: you declare the type by listing its legal values as names, and from then on a signal of that type can hold only those names — nothing else compiles.

enum_declaration.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
type state_t is (IDLE, READ, WRITE, DONE);   -- four named values, in order
signal state : state_t;                       -- can ONLY be one of those four

This is strong typing (lesson 2.1) working for you: there is no way to accidentally assign 5 or 'X' to state, because those are not values of state_t. The type is the list, and the list is the contract.

2. You already use enumerations

The core VHDL types you have met are themselves enumerations — they are just lists of named values the standard predefines:

predefined_enums.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- from std_logic_1164 (lesson 2.2):
type std_ulogic is ('U','X','0','1','Z','W','L','H','-');   -- nine named values
 
-- from the language itself:
type boolean   is (false, true);
type bit       is ('0', '1');
-- character is also an enumeration: a list of all the character literals

So std_logic's nine values, boolean's true/false, and bit's '0'/'1' are not special machinery — they are ordinary enumerations. Understanding your own enums means you already understand these.

3. Where enums belong — naming state

The dominant use of a custom enum is naming the states of a finite state machine. Instead of magic numbers (when "01" =>), you write the states' names and let the tool pick the bits:

fsm_states.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee;
use ieee.std_logic_1164.all;
 
entity ctrl is
  port ( clk, rst : in  std_logic;
         go       : in  std_logic;
         busy     : out std_logic );
end entity;
 
architecture rtl of ctrl is
  type state_t is (IDLE, READ, WRITE, DONE);
  signal state : state_t;                       -- defaults to IDLE (leftmost)
begin
  process (clk)
  begin
    if rising_edge(clk) then
      if rst = '1' then
        state <= IDLE;
      else
        case state is
          when IDLE  => if go = '1' then state <= READ; end if;
          when READ  => state <= WRITE;
          when WRITE => state <= DONE;
          when DONE  => state <= IDLE;
        end case;
      end if;
    end if;
  end process;
 
  busy <= '0' when state = IDLE else '1';
end architecture;

The code reads like the design intent — IDLE, READ, WRITE, DONE — and never mentions a single bit pattern. That readability is the whole point.

4. Hardware — names you write, bits the tool chooses

An enum literal has no inherent width. IDLE is not "00"; it is just a name. At synthesis the tool encodes the type — it assigns each literal a bit pattern and builds the state register from those bits. You influence the scheme; the tool produces the bits.

enumeration literals mapping to binary and one-hot state encodingssynthesis encodesor one-hotstate_t literalsIDLE, READ, WRITE, DONEbinary encoding00 / 01 / 10 / 11 — 2flip-flopsone-hot encoding0001 / 0010 / 0100 / 1000 —4 flip-flops12
An enumeration literal carries no bits in the source — synthesis ENCODES the type into a state register. With the default binary (sequential) encoding, four states need 2 bits: IDLE→00, READ→01, WRITE→10, DONE→11. A one-hot encoding instead gives each state its own bit (4 flip-flops, one set at a time) — larger but with simpler, faster next-state logic. You write the names; a synthesis attribute or the tool's heuristic picks the scheme. The names never change; only their bit representation does.

Because the names are decoupled from the bits, you can change the encoding (binary → one-hot for speed, gray for low-noise counters) without touching a line of your RTL — the case still says when READ =>.

5. Enums in simulation — values appear by name

A practical payoff: in a waveform viewer an enum signal shows its literal name, not a bit pattern. You read IDLE → READ → WRITE → DONE directly, which is why FSMs are so much easier to debug than hand-encoded std_logic_vector states.

state_t advancing through an FSM — the enum shows by name, not bits

10 cycles
state_t advancing through an FSM — the enum shows by name, not bitsstate defaults to IDLE (leftmost literal)state defaults to IDLE…go=1 seen → IDLE advances to READgo=1 seen → IDLE advan…DONE → IDLE: the cycle repeatsDONE → IDLE: the cycle…clkgo0011111111stateIDLEIDLEIDLEREADREADWRITEWRITEDONEDONEIDLEt0t1t2t3t4t5t6t7t8t9
The waveform shows the enum's NAMES, exactly as a simulator displays them — the readability that makes enumerated FSMs easy to follow. The state register physically holds the encoded bits (Section 4); the viewer maps those bits back to the literal for you.

6. Debugging example — the leftmost-value trap

The most common enum surprise is a state that is "stuck at the first value." An enum signal with no reset and no initial assignment powers up at its leftmost literal — and in simulation an unresolved path can even leave it there:

the silent default
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
type state_t is (IDLE, READ, WRITE, DONE);
signal state : state_t;            -- no initialiser → starts at IDLE
 
-- If your reset never asserts (or you forgot the reset branch), `state`
-- simply sits at IDLE forever and the machine "never starts".
-- There is no error — IDLE is a perfectly legal value — so the bug is silent.

The fix is to always give an FSM an explicit reset to a known state, and to make the intended start state the leftmost literal so even a missing reset fails safe:

fail-safe: reset to a known state
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
case state is
  when IDLE => ...
  -- ...
end case;
-- elsewhere, in the clocked process:
if rst = '1' then state <= IDLE; end if;   -- explicit, known start

The second classic is an incomplete case. If you omit a state and have no when others, some tools error and others infer an unwanted latch. List every literal, or close with when others => state <= IDLE;.

7. Common mistakes & what to watch for

  • No reset / no initial value. The signal sits at the leftmost literal silently. Always reset an FSM to a known state.
  • Incomplete case. Cover every enum literal, or add when others =>. Missing branches cause latches or tool errors.
  • Depending on the encoding. Never assume IDLE = "00". If you truly need specific bits, use a synthesis attribute (e.g. attribute enum_encoding) — do not hard-code patterns.
  • Comparing an enum to an integer or std_logic_vector. state = 1 does not compile; state is a state_t, not a number. Compare to literals (state = READ).
  • Reusing a literal name across two enum types in the same scope. Overlapping literals force you to qualify (state_t'(IDLE)); keep literal names distinct.

8. Engineering insight

An enumeration separates meaning from representation. You program in meaning — IDLE, READ, DONE — and let synthesis choose the representation — the actual flip-flops and bit patterns. That separation is what makes VHDL state machines readable, retargetable (flip the encoding for timing without touching logic), and self-documenting in a waveform. The same idea underlies the types you already trust: std_logic's nine values and boolean's true/false are enums whose order was chosen so the defaults behave well. When you design an enum, choosing the literals — and their order, since the leftmost is the default — is a small but real piece of hardware design.

9. Summary & next step

An enumeration type is a list of named values; strong typing then allows nothing else. std_logic, boolean, bit, and character are all predefined enums, and your own enums name FSM states and modes. Literals carry no bits until synthesis encodes the type (binary, one-hot, gray), the leftmost literal is the default and 'left, and enums display by name in simulation. Guard against the silent-default and incomplete-case traps with an explicit reset and full coverage.

You can now describe values that are names. The chapter turns next to values that are numbers with width: numeric_std's unsigned and signed — the types that let a bus actually do arithmetic — and then the conversions that move cleanly between numbers, vectors, and integers.