How Do You Design a Finite State Machine in Verilog, With a Testbench?
Draw the state diagram first, then write three pieces of Verilog: a clocked state register that uses nonblocking assignments and handles reset, a combinational next-state block with a default, and the output logic. Test it with a self-checking testbench that feeds directed and random inputs and compares the output against a simple reference model on every clock. The example here, a 1011 sequence detector, passes 1000 random cycles with zero mismatches in both Icarus Verilog and Verilator.
What you'll learn
- How to turn a problem statement into states, including the overlap case
- The three-block FSM style and why each block uses the assignment it does
- Moore vs Mealy outputs, and which one this design uses
- How to write a self-checking testbench with a reference model and random stimulus
Conventions and assumptions
Synchronous, active-high reset. One clock, rising edge. Verilog-2001 syntax, valid in SystemVerilog. Simulated in Icarus Verilog 12 and Verilator 5.036 (with --timing), and linted with Verilator -Wall, which reports no warnings for the design.
The problem
A serial input din arrives one bit per clock. Raise detect for one clock whenever the last four bits were 1, 0, 1, 1. Overlaps count: in 1011011 the final 1 of the first match and the next 011 form a second match, so detect goes high twice.
Step 1: the states
Each state remembers how much of the pattern has been seen so far. That gives five states for a Moore machine, where the output depends only on the state:
| State | Meaning | din = 0 | din = 1 | detect |
|---|---|---|---|---|
| IDLE | nothing useful | IDLE | S1 | 0 |
| S1 | seen 1 | S10 | S1 | 0 |
| S10 | seen 10 | IDLE | S101 | 0 |
| S101 | seen 101 | S10 | S1011 | 0 |
| S1011 | seen 1011 | S10 | S1 | 1 |
The overlap is in the S1011 row. After 1011, a 0 means the last two bits are 10, so go to S10, not IDLE. A 1 means the latest bits end in 11, of which only the final 1 can start a new match, so go to S1. The S101 row is the other subtle one: after 101, a 0 leaves 1010, whose last two bits are 10, so S10.
Step 2: the Verilog
Three blocks. The state register is the only clocked block and uses <=. The next-state logic is combinational, uses =, and starts with a default so that no path leaves next unassigned (which would infer a latch). The output is a plain assign because this is a Moore machine.
// Moore FSM: detect = 1 for one clock after the input has shown 1, 0, 1, 1
// (overlapping, so 1011011 detects twice). Synchronous, active-high reset.
module seq_detect_1011 (
input wire clk,
input wire rst,
input wire din,
output wire detect
);
localparam [2:0] IDLE = 3'd0, // nothing useful seen
S1 = 3'd1, // seen 1
S10 = 3'd2, // seen 10
S101 = 3'd3, // seen 101
S1011 = 3'd4; // seen 1011: output high
reg [2:0] state, next;
// 1. State register: the only clocked block, non-blocking.
always @(posedge clk) begin
if (rst) state <= IDLE;
else state <= next;
end
// 2. Next-state logic: combinational, blocking, with a default.
always @(*) begin
next = state;
case (state)
IDLE: next = din ? S1 : IDLE;
S1: next = din ? S1 : S10;
S10: next = din ? S101 : IDLE;
S101: next = din ? S1011 : S10;
S1011: next = din ? S1 : S10;
default: next = IDLE;
endcase
end
// 3. Output logic: Moore, so it depends on the state alone.
assign detect = (state == S1011);
endmodule
Moore or Mealy?
In a Mealy machine the output depends on the state and the current input, so detect could go high in the same clock that the final 1 arrives, with one fewer state (S101 plus din = 1). The cost is that the output can glitch with the input and passes combinational logic straight from din to detect. The Moore version here is one clock later but its output comes straight from the state register's decode, which is easier to time and to reason about.
Step 3: a self-checking testbench
Rather than reading waveforms, the testbench keeps the last four input bits in a shift register and works out what detect should be. After every clock it compares the design against that model and counts mismatches. It runs a directed sequence first (1011011, expecting two detections) and then 1000 random bits.
`timescale 1ns/1ps
module tb_seq_detect_1011;
reg clk = 0, rst = 1, din = 0;
wire detect;
reg [3:0] history = 4'b0000; // the last four inputs, newest in bit 0
reg expected = 0;
integer i, errors = 0, hits = 0;
seq_detect_1011 dut (.clk(clk), .rst(rst), .din(din), .detect(detect));
always #5 clk = ~clk;
// Reference model: after each edge, detect should be 1 exactly when the
// last four inputs sampled were 1, 0, 1, 1.
always @(posedge clk) begin
if (rst) begin
history <= 4'b0000;
expected <= 1'b0;
end else begin
history <= {history[2:0], din};
expected <= ({history[2:0], din} == 4'b1011);
end
end
task check;
begin
if (detect !== expected) begin
errors = errors + 1;
$display("MISMATCH at %0t: din history %b, detect %b, expected %b", $time, history, detect, expected);
end
if (detect) hits = hits + 1;
end
endtask
initial begin
// Hold reset for two edges, then a directed sequence with an overlap.
@(negedge clk); @(negedge clk); rst = 0;
// 1011011 should detect twice (overlapping).
for (i = 6; i >= 0; i = i - 1) begin
din = (7'b1011011 >> i) & 1'b1;
@(negedge clk); check;
end
$display("directed: %0d detections (expect 2)", hits);
// Then 1000 random bits, checked against the model on every clock.
for (i = 0; i < 1000; i = i + 1) begin
din = $random;
@(negedge clk); check;
end
$display("random: %0d errors in 1000 cycles, %0d detections in total", errors, hits);
if (errors == 0) $display("PASS"); else $display("FAIL");
$finish;
end
endmodule
What the simulation shows
Both simulators print "directed: 2 detections (expect 2)", then "random: 0 errors in 1000 cycles" and PASS. The number of random detections depends on the simulator's random sequence; it should be close to 1000/16 = 62.5, since any four random bits are 1011 with probability 1/16. Icarus Verilog gave 62 in total including the two directed ones.
Why each block uses what it does
- State register: <=, because it is clocked (see blocking vs nonblocking).
- Next-state logic: =, with next = state as the first line, so every path assigns next and no latch is inferred.
- A default branch in the case: if the state register ever holds an unused code (5, 6 or 7 here), the machine goes back to IDLE rather than getting stuck.
- Named states with localparam: the code reads like the table, and you can change the encoding in one place.
Common mistakes
- Going to IDLE on every mismatch, which misses overlapping matches such as 1011011.
- Leaving next unassigned on some path in the combinational block, which infers a latch.
- Putting the output in the clocked block by accident, which delays it by one more clock.
- A testbench that only prints values, so nobody notices when they are wrong. Compare against a model and count errors.
Check your understanding
- Q1. The input is 10111011. How many times does detect go high? Answer: twice, after the fourth bit and after the eighth.
- Q2. Which state does the machine go to from S10 on din = 0, and why? Answer: IDLE. The last two bits are 00, and no suffix of 100 starts the pattern.
- Q3. How many states does a Mealy version need? Answer: four: IDLE, S1, S10 and S101. The output is high while in S101 with din = 1, so S1011 is not needed.
Related
Sources
Last verified
Design and testbench simulated in Icarus Verilog 12 and Verilator 5.036 on 14 November 2026, both passing with zero mismatches; the design lints cleanly with Verilator -Wall.