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.

The stream 1011011 through two detectorsOne bit is sampled at each edge, from edge 2. The top pair restarts from S0 after a match; the bottom pair keeps the tail of the stream that can start the next match.
0123456clkdinst_restartS1S10S101S0S1match_restartst_overlapS1S10S101S1S10S101S1match_overlap
  1. Cycle 0

    Edge 2 samples a 1. Both detectors move from S0 to S1: they have seen "1", the first bit of 1011.

  2. Cycle 2

    Edge 4 samples the second 1. Both have seen "101" and sit in S101, one bit short of a match.

  3. 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.

  4. Cycle 5

    Edge 7: the overlapping detector has seen "101" again. The restarting one has seen only "1" since its restart.

  5. 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.

The overlapping 1011 detectorEach arrow is a transition at a clock edge, labelled with the bit sampled there. Two self-loops are listed in the node text: S0 stays on 0 and S1 stays on 1.
The overlapping 1011 detectorEach arrow is a transition at a clock edge, labelled with the bit sampled there. Two self-loops are listed in the node text: S0 stays on 0 and S1 stays on 1. component S0 seen nothing; stayson 0 component S1 seen "1"; stays on 1 component S10 seen "10" component S101 seen "101"; a 1 nowcompletes 1011 10101: match0
  1. 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
    1. data sampled bit: S0 → S1
    2. data sampled bit: S1 → S10
    3. data sampled bit: S10 → S101
  2. 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
    1. data sampled bit: S101 → S1
  3. 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
    1. 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)BitSeen nowLongest tail that starts 1011Next 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 matchS10 / 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.

DetectorCellsFlopsLevels of logic
1011, FSM as written (2-bit enum)1353
1011, overlapping FSM with (* fsm_encoding = "none" *)732
1011, shift register742
16-bit pattern, FSM re-encoded one-hot49174
16-bit pattern, FSM kept binary5156
16-bit pattern, shift register31164

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

Design brief
PortDirectionWidthMeaning
clkinput1clock
rst_ninput1reset, asynchronous, active-low
dininput1the serial bit
matchoutput11 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, match 0
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
  1. 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.

  2. 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.

  3. 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.

RTLIcarus Verilog · in your browser · Lint: Verilator service · Synthesize: Yosys service
Edit the design, then Run, Lint or Synthesize
Expected: 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 11

Original 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

Predict the outputThe overlapping detector reads the stream 10110111011, one bit per edge. The bench prints its match count. What is the line?
`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 = 0;                         // 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(11'b10110111011, 11);
    $display("matches=%0d", nd);
    $finish(0);                             // quiet: no "$finish called" line
  end
endmodule
// det1011: match is 1 for one cycle after the input stream ends in 1011.
// One input bit per clock edge. Matches may overlap: 1011011 holds two.
module det1011 (
  input  logic clk,
  input  logic rst_n,      // active-low, asynchronous
  input  logic din,
  output logic match
);
  // Each state names the part of 1011 seen so far.
  typedef enum logic [1:0] {S0, S1, S10, S101} state_e;
  state_e st_q, st_d;

  always_comb begin
    st_d = st_q;
    unique case (st_q)
      S0:      if (din) st_d = S1;   else st_d = S0;
      S1:      if (din) st_d = S1;   else st_d = S10;
      S10:     if (din) st_d = S101; else st_d = S0;
      S101:    if (din) st_d = S1;   else st_d = S10;    // keep the tail: "1" or "10"
      default: st_d = S0;
    endcase
  end

  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n) begin
      st_q  <= S0;
      match <= 1'b0;
    end else begin
      st_q  <= st_d;
      match <= (st_q == S101) && din;                     // 1011 just completed
    end
endmodule
Spot the bugEach option is the S101 arm of the overlapping detector. The bench feeds 1010110111011 and compares the match count with the model. Which arm fails?
ConceptAn overlapping 1011 detector sits in S101 and samples a 1. It raises match. Which state does it enter?
ConceptThe detector's state is a 2-bit enum with four values, and match is one more register. Synthesize reports 5 flops. Why five?
Read the waveformThe overlapping detector reads 1011011, one bit per edge from edge 2. At which edge does it report the second match?
The overlapping detector on 1011011
0123456dinstS1S10S101S1S10S101S1match

Values after edges 2 to 8, left to right.

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_encoding says "none", so measure flops.