Draft preview. Lessons are being written and reviewed.

Digital Design / Module 6: Finite state machines

34. Writing FSMs: encoding and style

Write FSMs that synthesize cleanly.

Lecture coming soon

See it move

The idea

There is a standard way to write FSMs, and following it keeps them readable and free of latches. Use one block for the state register and another for next-state logic, plus outputs.

typedef enum logic [1:0] {IDLE, LOAD, RUN, DONE} state_t;
state_t state, next;

always_ff @(posedge clk or negedge rst_n)
  if (!rst_n) state <= IDLE;
  else        state <= next;

always_comb begin
  next = state;        // default: stay
  busy = 1'b0;         // default output
  case (state)
    IDLE: if (start) next = LOAD;
    LOAD: begin busy = 1'b1; next = RUN; end
    RUN:  begin busy = 1'b1; if (finished) next = DONE; end
    DONE: next = IDLE;
    default: next = IDLE;
  endcase
end

The defaults at the top make latches impossible, and they keep each branch short.

State encoding

EncodingFlops for N statesStrengths
Binarylog2(N), rounded upFew flops
One-hotNSimple, fast decoding, suits FPGAs
Graylog2(N), rounded upOne bit changes for sequential paths

One-hot has more flops but simpler next-state logic: each state is a single bit, so its condition is a small expression. FPGAs have plentiful flops, so tools often pick it there. ASIC designs weigh the total area, since both flops and logic count.

Let the tool help

Using an enum makes waveforms show state names, and it lets synthesis re-encode the states if you allow it. Avoid magic numbers. State names are the best documentation an FSM can have.

Common mistake

Forgetting that unused encodings exist. Five states in three binary bits leave three illegal codes, and a default branch is the way to handle them. See the Debugging lesson for why this matters.

Key ideas

  • Separate next-state and state register blocks.
  • Binary is compact; one-hot often suits FPGAs and speed.
  • Use defaults to avoid latches.

Lab: your code, animated

Your exercise

Recode a 5-state FSM from binary to one-hot.

Find the bug

This code has one deliberate bug. Read the symptom, then click the line you think is wrong.

What goes wrong: busy goes high in RUN, but it stays high after the machine returns to IDLE.

Interview questions

coreFlops for 5 states, binary and one-hot?

basicWhy do most coding guides split an FSM into a clocked block for the state register and a combinational block for next state and outputs?

basicAn FSM has 12 states. How many flops does binary encoding need, how many does one-hot need, and how many unused codes does binary leave?

coreIn this next-state block, busy is set in LOAD and RUN but has no default. What happens in simulation and in hardware?

always @(*) begin
  next = state;
  case (state)
    IDLE: if (start) next = LOAD;
    LOAD: begin busy = 1'b1; next = RUN; end
    RUN:  begin busy = 1'b1; if (finished) next = DONE; end
    DONE: next = IDLE;
  endcase
end

coreThe FSM goes IDLE to LOAD on start, LOAD to RUN always, RUN to DONE on finished, and DONE to IDLE always. Write the one-hot next-state equations.

coreThis FSM is written in one clocked block, and busy is assigned inside it. Is busy high in the same cycles as when the state is LOAD or RUN?

always @(posedge clk)
  case (state)
    IDLE: begin busy <= 1'b0; if (start) state <= LOAD; end
    LOAD: begin busy <= 1'b1; state <= RUN; end
    RUN:  begin busy <= 1'b1; if (finished) state <= DONE; end
    DONE: begin busy <= 1'b0; state <= IDLE; end
  endcase

coreA one-hot FSM has 5 state flops. How many bit patterns are illegal, and what simple check catches all of them?

advancedYou add a default branch that sends unused state codes back to IDLE. Is the recovery guaranteed to be in the gates after synthesis?

advancedWhen would you choose one-hot, binary or Gray encoding for an FSM?

Practise these 9 questions with animations