An FSM state is a label, not a number. A typedef enum names the states and gives them a checked type, so arbitrary constants can no longer reach the state register.
typedef enum logic [1:0] {
IDLE = 2'd0,
ACTIVE = 2'd1,
FINISH = 2'd2
} state_e;State storage sits in a clocked block, while next-state and output logic stay combinational. Every output gets a default, and default covers illegal states.
Typed enum for the state; no raw numbers.
next = state; and output defaults at the top of always_comb.
A default branch that sends illegal states to a safe state in RTL, checked later on the gate netlist.
A reset to a known state, plus tests that visit every transition.
Moore outputs that depend on state alone, unless Mealy behavior is deliberate.
A 3-state machine in a 2-bit register leaves one encoding unused (2'b11). In RTL simulation, forcing 2'b11 makes the next-state logic take default, and the machine is back in IDLE on the next clock edge. The testbench below does exactly that.
next a default value (next = state;) before the case?It guarantees next is assigned on every path, which prevents latch inference and makes “stay in this state” the default behavior, so each branch only has to describe transitions.
The template plus a testbench that walks IDLE to ACTIVE to FINISH and back, checks ACTIVE hold, abort alone and simultaneous abort/last_beat priority, then forces the illegal encoding 2'b11 and checks RTL recovery on the next edge. The state_e type sits outside the module so the testbench can cast to it.
typedef enum logic [1:0] {IDLE, ACTIVE, FINISH} state_e; // shared, so the testbench can name the type
module handshake_fsm (
input logic clk, rst_n,
input logic start, last_beat, abort,
output logic busy, done
);
state_e state, next;
// 1) state register
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
// 2) next-state and outputs, with defaults
always_comb begin
next = state; // default: stay
busy = 1'b0;
done = 1'b0;
unique case (state)
IDLE: if (start) next = ACTIVE;
ACTIVE: begin
busy = 1'b1;
if (abort) next = IDLE;
else if (last_beat) next = FINISH;
end
FINISH: begin
done = 1'b1;
next = IDLE;
end
default: next = IDLE; // illegal encoding: recover to a safe state
endcase
end
endmodule
The testbench is directed RTL evidence of recovery from the illegal encoding 2'b11 in simulation. It is not proof of fault recovery in the synthesised design.