Sequence detectors and overlap
Design a sequence detector whose states remember how much of the pattern has been seen. Choose the right state after a match and after a wrong bit, and compare it with a shift-register detector in gates.
- Lesson
- R3.4
- Time
- 25 min
- Runs on
- Your browser
- Check
- 5 questions
A detector for the pattern 1011 counted one match in the stream 1011011. The specification counted two, because the last 1 of the first match also starts the second. After its match the detector went back to the state that had seen nothing, and threw away the 1 it needed.
- Cycle 0
Edge 2 samples a 1. Both detectors move from S0 to S1: they have seen "1", the first bit of 1011.
- Cycle 2
Edge 4 samples the second 1. Both have seen "101" and sit in S101, one bit short of a match.
- Cycle 3
Edge 5 samples a 1: both report a match. The restarting detector goes to S0. The other goes to S1, because the 1 it just took is also the first bit of 1011.
- Cycle 5
Edge 7: the overlapping detector has seen "101" again. The restarting one has seen only "1" since its restart.
- Cycle 6
Edge 8 completes the second 1011. Only the overlapping detector reports it: two matches against one.
Measured on the lesson's bench with a probe that also prints the state, edges 2 to 8. The match output is a register, so it shows the bit that the same edge sampled.
What each state remembers
A sequence detector reads one bit per clock edge and reports when the last few bits spell a pattern. Its state is a memory of progress: how much of the pattern the most recent bits already spell. For 1011 that needs four states. Name each state after the bits it has seen: S0 (nothing useful), S1, S10 and S101.
- Climb the pattern
Each expected bit moves the machine one state to the right: 1, then 0, then 1 reaches S101.
Follow the calls, values and waits
- data sampled bit: S0 → S1
- data sampled bit: S1 → S10
- data sampled bit: S10 → S101
- A match keeps its last 1
From S101 a 1 completes 1011 and raises match. The stream now ends in "11", and its longest tail that starts 1011 is "1", so the next state is S1.
Follow the calls, values and waits
- data sampled bit: S101 → S1
- A miss keeps what it can
From S101 a 0 makes "1010". Its tail "10" still starts 1011, so the next state is S10. From S10 a 0 makes "100", and no tail starts 1011: back to S0.
Follow the calls, values and waits
- data sampled bit: S101 → S10
Read the diagram as text
- S0: seen nothing; stays on 0
- S1: seen "1"; stays on 1
- S10: seen "10"
- S101: seen "101"; a 1 now completes 1011
- S0 → S1: 1
- S1 → S10: 0
- S10 → S101: 1
- S10 → S0: 0
- S101 → S1: 1: match
- S101 → S10: 0
The restarting detector of the hook has both arrows out of S101 pointing to S0.
The rule for every arrow is the same. Append the new bit to what the state has seen. Then find the longest tail of that string that is also a beginning of the pattern. That tail names the next state. Text search uses the same idea, under the name failure function.
| State (seen) | Bit | Seen now | Longest tail that starts 1011 | Next state |
|---|---|---|---|---|
| S0 ("") | 0 / 1 | "0" / "1" | "" / "1" | S0 / S1 |
| S1 ("1") | 0 / 1 | "10" / "11" | "10" / "1" | S10 / S1 |
| S10 ("10") | 0 / 1 | "100" / "101" | "" / "101" | S0 / S101 |
| S101 ("101") | 0 / 1 | "1010" / "1011" | "10" / "1" after the match | S10 / S1, match |
The whole design method in one table. A non-overlapping detector differs only in the last row's match arc, which goes to S0.
A shift register detects the same pattern
There is a second way to build a detector. Keep the last three bits in a shift register and compare them, with the new bit, against 1011. Every match is found, overlapping or not, because the register never forgets a bit early. The cost is one flop per pattern bit.
State machine
S101: if (din) st_d = S1;
else st_d = S10;
...
match <= (st_q == S101) && din;
Four states. Every arc must be designed for overlap, and a wrong arc misses matches.
Shift register
last_q <= {last_q[1:0], din};
match <= ({last_q, din} == 4'b1011);
Three bits of history and one compare. Overlap needs no thought.
Which is smaller? A binary state register for N states needs about log2 N flops, so the FSM should win for long patterns. Synthesize says otherwise on this flow. Yosys 0.70 recognizes the state register and re-encodes it one-hot: one flop per state.
| Detector | Cells | Flops | Levels of logic |
|---|---|---|---|
| 1011, FSM as written (2-bit enum) | 13 | 5 | 3 |
1011, overlapping FSM with (* fsm_encoding = "none" *) | 7 | 3 | 2 |
| 1011, shift register | 7 | 4 | 2 |
| 16-bit pattern, FSM re-encoded one-hot | 49 | 17 | 4 |
| 16-bit pattern, FSM kept binary | 51 | 5 | 6 |
| 16-bit pattern, shift register | 31 | 16 | 4 |
Measured with Yosys 0.70 on this flow. Flops include the registered match. The 16-bit pattern is 1011001110001011.
Read the table twice. The attribute keeps the encoding you wrote: 3 flops instead of 5 for 1011. For the 16-bit pattern the binary FSM needs 11 fewer flops than the shift register, but 20 more cells and two more levels of logic. No version wins every column, so the brief says which column matters, and Synthesize settles it.
The lab: make the detector overlap
| Port | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | clock |
| rst_n | input | 1 | reset, asynchronous, active-low |
| din | input | 1 | the serial bit |
| match | output | 1 | 1 for one cycle after the edge that samples the last bit of 1011; a register, so it is clean for the whole cycle |
- Reset
- state S0,
match0 - Corner cases
- a match that starts inside the last one; a 0 after 101; long runs of 1s. Overlap: every match counts: 1011011 holds two, 10110111011 holds three
The lab ships the restarting detector. Run it: one match in 1011011 where the model counts two, and 6 against 11 in a 48-bit stream. Fix the two arcs out of S101 and run again; both lines agree. Then replace the state machine with the shift register and compare the Gates tab.
Asked in interviews
Design a detector for 1011 that counts overlapping matches.
Four states for the prefixes "", "1", "10" and "101". The 1 from S101 raises match and goes to S1; the 0 from S101 goes to S10. Register match so it is clean.
Where does the machine go after a match?
To the state for the longest tail of the input that is also a beginning of the pattern. For 1011 that tail is "1", so S1. A non-overlapping detector goes to S0 instead.
FSM or shift register for a pattern detector?
The shift register is simple and overlaps for free, at one flop per pattern bit. The FSM needs fewer flops only if its encoding stays binary. Measure both on the real flow.
Try it
The first seven lines feed 1011011 and show one match, at edge 5. The model compares the last four bits with 1011 and counts two. The 48-bit stream shows the gap is not rare: 6 matches against 11.
Change the S101 arcs to if (din) st_d = S1; else st_d = S10; and run. Edge 8 now shows match=1, and both summary lines agree: 2 and 2, 11 and 11. Synthesize still reports 13 cells. Then replace the state machine with the shift register from the compare block. The log is the same, and the Gates tab drops to 7 cells, 4 flops, 2 levels of logic.
Synthesize the design to measure its cells and inspect the mapped gates.
e2 din=1 match=0 e3 din=0 match=0 e4 din=1 match=0 e5 din=1 match=1 e6 din=0 match=0 e7 din=1 match=0 e8 din=1 match=0 1011011: design 1, model 2 48-bit stream: design 6, model 11Original design · 13 cells · 5 flip-flops · 0 latches · 3 logic levels. Synthesis uses Yosys; counts depend on the toolchain.
Read included file: tb.sv
`timescale 1ns/1ps
module tb;
logic clk = 0;
always #5 clk = ~clk; // rising edges at 5, 15, 25, ...
logic rst_n = 1; // falls at 1 ns: a real edge, so an asynchronous reset acts before any clock
int edge_n = -1;
always @(posedge clk) edge_n = edge_n + 1;
logic din = 0;
logic match;
bit show = 1; // print one line per edge
logic [3:0] win = 0; // the model: the last four bits fed
int nd = 0, nm = 0; // matches counted: design, model
// Feed one bit: drive it after an edge, let the next edge sample it, count both answers.
task automatic feed(input logic b);
din = b; tick();
win = {win[2:0], b};
nd += match;
nm += (win == 4'b1011);
endtask
// Feed the n low bits of v, leftmost first, so 7'b1011011 arrives as 1, 0, 1, 1, 0, 1, 1.
task automatic feed_bits(input logic [63:0] v, input int n);
for (int i = n - 1; i >= 0; i--) feed(v[i]);
endtask
det1011 dut (.*); // ports connect to the signals of the same name
// Advance to the next rising edge, wait for the design to settle, then print one line.
task automatic tick();
@(posedge clk); #1;
if (show && rst_n) $display("e%0d din=%b match=%b", edge_n, din, match);
endtask
initial begin
#1 rst_n = 0; tick(); tick(); rst_n = 1; // asserted at 1 ns, edges 0 and 1 in reset, released 1 ns after edge 1
feed_bits(7'b1011011, 7);
$display("1011011: design %0d, model %0d", nd, nm);
show = 0;
feed_bits(4'b0000, 4); // four zeros: both forget the past
nd = 0; nm = 0;
feed_bits(48'b1011_0110_1110_1011_0101_1011_0001_0110_1101_1101_0111_1011, 48);
$display("48-bit stream: design %0d, model %0d", nd, nm);
$finish(0); // quiet: no "$finish called" line
end
endmodule
Check yourself
Remember
- A sequence detector's state records how much of the pattern the latest bits already spell: S0, S1, S10, S101 for 1011.
- Every arc goes to the longest tail of the input that is also a beginning of the pattern; after a match of 1011 that is S1, after 1010 it is S10.
- A detector that restarts from S0 after a match finds only non-overlapping matches; the brief must say which kind it wants.
- A shift register of the last bits overlaps for free at one flop per bit; Yosys 0.70 re-encodes an FSM one-hot unless
fsm_encodingsays "none", so measure flops.