Three processes, three jobs
The Digital Electronics path's sixth lesson covered Moore and Mealy machines as a concept: a state diagram, a state table, next-state and output logic derived from them. This lesson is about writing that same machine in Verilog in a way that cannot accidentally produce a latch (lesson 6) or a blocking/non-blocking mismatch (lesson 8) โ by splitting it into exactly three always blocks, each with one job.
// Process 1: state register -- the ONLY clocked part of the whole FSM.
always @(posedge clk or negedge rst_n) begin
if (!rst_n) state <= S0;
else state <= next_state;
end
// Process 2: next-state logic -- pure combinational, reads state and inputs,
// decides next_state.
always @* begin
case (state)
// ...
endcase
end
// Process 3: output logic -- pure combinational, reads state (Moore)
// or state and inputs (Mealy), decides the outputs.
always @* begin
// ...
end
The split is not cosmetic. Process 1 is the only block in the whole machine assigned with <=, because it is the only clocked block โ exactly lesson 7's flip-flop template, with state as the register. Processes 2 and 3 are both always @* blocks assigned with =, following lesson 5 and lesson 6's rules for combinational logic exactly: every branch of every case needs to be covered (a default is cheap insurance against an unreachable-looking state value becoming reachable later), and nothing in either block is clocked. Separating next-state logic (process 2) from output logic (process 3) rather than folding them together is what makes a Mealy machine's output โ which depends on the current input as well as the current state โ straightforward to add later without touching the state transition logic at all: only process 3 would need to read bit_in as well as state.
A working sequence detector
Let us build the same 1011 overlap-detecting Moore machine the Digital Electronics path designed on paper, as real, simulated Verilog.
module seq_detect_1011 (
input wire clk,
input wire rst_n,
input wire bit_in,
output reg match
);
localparam S0 = 3'd0, S1 = 3'd1, S2 = 3'd2, S3 = 3'd3, S4 = 3'd4;
reg [2:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) state <= S0;
else state <= next_state;
end
always @* begin
case (state)
S0: next_state = bit_in ? S1 : S0;
S1: next_state = bit_in ? S1 : S2;
S2: next_state = bit_in ? S3 : S0;
S3: next_state = bit_in ? S4 : S2;
S4: next_state = bit_in ? S1 : S2;
default: next_state = S0;
endcase
end
always @* begin
match = (state == S4);
end
endmodule
localparam names each state instead of using bare numbers โ this is purely for readability and carries no synthesis cost, and it means the next-state case reads exactly like the state table it was derived from. Driving the bit stream 1011011, which contains two deliberately overlapping matches of 1011 (bits 0–3, and bits 3–6, sharing bit 3), one bit per clock cycle:
bit=1 state=1 match=0
bit=0 state=2 match=0
bit=1 state=3 match=0
bit=1 state=4 match=1
bit=0 state=2 match=0
bit=1 state=3 match=0
bit=1 state=4 match=1
match goes high exactly twice, exactly where the two overlapping occurrences of 1011 actually end โ the fourth and the seventh bits of the stream. The machine reaching S4 after the first match and continuing on to a second match four bits later, rather than needing to restart from S0, is precisely the re-synchronizing behavior the Digital Electronics path's state table built in on purpose: from S4, an incoming 0 goes to S2 and an incoming 1 goes to S1, never back to S0, which is what makes overlapping matches detectable at all. Synthesizing this module produces only flip-flop cells for the 3-bit state register and ordinary combinational gates for both case statements โ no latch warning, because both case statements have a default branch and cover every state the register can hold.
Common mistakes
Coding the state register and the next-state logic in the same always block, mixing <= for state with = for some intermediate signal, is the first mistake โ lesson 8's rule against mixing styles in one block applies here exactly as it does anywhere else. The second is forgetting a default branch in the next-state case โ with a 3-bit state register encoding only 5 real states, three bit patterns (3'd5 through 3'd7) are never assigned by the design but are not actually unreachable from the hardware's point of view (power-on in an unknown state, or a single-event upset), and an unhandled case there infers a latch exactly as lesson 6 described. The third is writing a Moore machine's output logic so that it accidentally reads an input signal as well as state โ the moment process 3 reads anything besides state, the machine has quietly become Mealy, whether or not that was the intent, which is worth knowing precisely because of the timing difference the Digital Electronics path's lesson on this topic explains.
Interview questions
Why split an FSM into three always blocks instead of one? It cleanly separates the one genuinely clocked part (the state register, described with <=) from the two combinational parts (next-state logic and output logic, described with =), which keeps lesson 8's blocking/non-blocking rules unambiguous and makes a Moore-to-Mealy change a one-block edit rather than a restructuring.
What turns a three-process Moore machine into a Mealy machine? The output-logic process reading the current input, not just the current state. In the three-process template, this means process 3's sensitivity list and body change from depending on state alone to depending on state and the relevant input signals.
Why does a default branch matter in next-state logic even if every state value looks covered? A state register's bit width often has more encodings than the design uses on purpose โ an unhandled state value, even one that "shouldn't happen," still infers a latch for the next-state signal exactly as any other incomplete case would, per lesson 6.
Key takeaways
- The three-process FSM template separates the clocked state register (
<=) from the combinational next-state logic and output logic (=, each its ownalways @*block) โ applying lessons 5 through 8's rules directly rather than inventing new ones for FSMs specifically. localparamnames for states keep the next-statecasereading like the state table it was derived from, at no synthesis cost.- A sequence detector built this way and driven with a real overlapping-match bit stream correctly flags both matches, confirmed here in simulation and confirmed latch-free in synthesis.
- Whether a machine is Moore or Mealy is decided entirely by whether the output-logic process reads an input signal in addition to
stateโ splitting next-state and output logic into separate processes keeps that choice a one-block edit.