The structure separates memory from decisions. One process holds the state register, and another decides the next state and outputs.
Process | Type | Job |
|---|---|---|
State register |
|
|
Next-state + outputs |
| Decide |
Follow the loop once per clock: state goes up into the decision block, next comes back down, and the edge copies it into state.
The only storage is the state register. Everything the combinational block produces is recomputed from state and the inputs every cycle.
Outputs leave from the combinational block, so they change as soon as state changes after an edge.
The code below is this picture: the always_ff box is the three-line clocked process, and the always_comb box is the case statement.
// snippet
typedef enum logic [1:0] {IDLE, 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
// 1. defaults: every output and 'next' gets a value at the top
next = state; load = 1'b0; sub_a = 1'b0; sub_b = 1'b0; done = 1'b0;
// 2. then override per state
unique case (state)
IDLE: if (start) begin load = 1'b1; next = RUN; end
RUN: if (a_eq_b) next = DONE; else if (a_gt_b) sub_a = 1'b1; else sub_b = 1'b1;
DONE: begin done = 1'b1; next = IDLE; end
default: next = IDLE;
endcase
endNo latches: defaults at the top mean every path assigns every signal.
Clear reading: the table “in state S, with inputs I, set outputs O and go to N” is the code.
Easy to change: adding a state touches the enum and one case branch.
Easy to review: the clocked block has only one assignment, so reset and clocking cannot hide a bug.
Style | Verdict |
|---|---|
One process (state and outputs in one | Registers assigned outputs; latency depends on whether expressions describe the current or following transition and when the consumer samples |
Three processes (state, next, outputs separately) | Separate decode can clarify ownership and review; keep defaults and state/output contracts consistent |
| Unreadable for more than two states |
Two processes are this course's preferred template, not a latency rule. A clocked done <= (next == DONE) and a combinational done = (state == DONE) can both assert after the edge entering DONE; clocking done <= (state == DONE) instead delays that indication. Specify the observation edge before comparing styles.
state <= next, plus reset state <= IDLE. Other correct templates may also register outputs or controller bookkeeping; process count alone does not set their latency.