The usual readable FSM is three processes: a flop that holds state_q, combo that computes state_d, and combo (or registered) outputs.
flowchart LR in[inputs] --> ns[next_state] sq[state_q] --> ns ns --> sd[state_d] sd --> ff[state_reg] clk[clk] --> ff ff --> sq sq --> out[outputs]
The three blocks
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) state_q <= ST_IDLE;
else state_q <= state_d;
end
always_comb begin
state_d = state_q;
unique case (state_q)
ST_IDLE: if (start) state_d = ST_RUN;
ST_RUN: if (done_i) state_d = ST_DONE;
default: state_d = ST_IDLE;
endcase
end
always_comb begin
busy = (state_q != ST_IDLE);
end
Wrong vs right
Wrong — next-state and outputs jammed into one clocked block with blocking assigns (hard to lint, easy to drop a reset path):
always_ff @(posedge clk) begin
if (start) state = ST_RUN;
busy = 1'b1;
end
Right — split register vs combo, always give default recovery.
A two-process style (flop + combined next/output) is fine if the team agrees; do not mix styles inside one module.