FSM Verilog Code: Mealy vs Moore (1011 Detector)

A finite state machine (FSM) is the backbone of almost every control path in digital design, and writing clean, synthesizable FSM Verilog code is a skill interviewers test constantly. This tutorial gives you two complete, runnable sequence detectors for the pattern 1011 — one Moore, one Mealy — written in the industry-standard three-always-block style. Copy the code, run it in Icarus Verilog, and read the walkthrough to understand exactly why each block exists.

Mealy vs Moore in one paragraph

Both are FSMs; the difference is where the output comes from. A Moore machine’s output depends only on the current state, so it stays glitch-free and changes synchronously one cycle after the triggering input. A Mealy machine’s output depends on the current state and the current input, so it reacts one cycle earlier and usually needs fewer states — at the cost of combinational paths that can glitch if you feed them straight to another block. For a sequence detector, the Mealy version detects 1011 as the last bit arrives, while the Moore version flags it on the next clock edge.

The 3-always-block coding style

Professional RTL splits an FSM into three separate blocks. This is the pattern tools expect and reviewers look for:

  • Block 1 — state register: the only sequential (posedge clk) block. It does nothing but copy next_state into state, with a reset.
  • Block 2 — next-state logic: pure combinational (always @(*)). A case on state computes next_state from the inputs.
  • Block 3 — output logic: combinational. For Moore it reads only state; for Mealy it reads state and the inputs.

Keeping the register separate from the logic makes the design easy to read, prevents accidental latches, and gives synthesis a predictable structure.

Moore 1011 sequence detector (Verilog + testbench)

This is an overlapping detector: after a match, the trailing 1 can start the next pattern. States track how much of 1011 we have seen so far. Run it with iverilog -g2005 -o sim seq_detector_moore.v && vvp sim.

// Moore FSM: overlapping sequence detector for "1011"
// 3-always-block coding style: state register, next-state logic, output logic
module seq_detector_moore (
    input  wire clk,
    input  wire rst_n,   // active-low synchronous reset
    input  wire din,     // serial data input, MSB-first
    output reg  dout      // pulses high the cycle AFTER the 4th matching bit
);

    // State encoding (localparam keeps it readable and synthesizable)
    localparam [2:0] S_IDLE = 3'd0, // no useful bits yet
                     S_1    = 3'd1, // seen "1"
                     S_10   = 3'd2, // seen "10"
                     S_101  = 3'd3, // seen "101"
                     S_1011 = 3'd4; // seen "1011" -> match

    reg [2:0] state, next_state;

    // 1) State register: the ONLY sequential block
    always @(posedge clk) begin
        if (!rst_n)
            state <= S_IDLE;
        else
            state <= next_state;
    end

    // 2) Next-state logic: pure combinational, no memory
    always @(*) begin
        next_state = state; // safe default avoids latches
        case (state)
            S_IDLE : next_state = din ? S_1   : S_IDLE;
            S_1    : next_state = din ? S_1   : S_10;
            S_10   : next_state = din ? S_101 : S_IDLE;
            S_101  : next_state = din ? S_1011: S_10;
            S_1011 : next_state = din ? S_1   : S_10; // overlap-aware
            default: next_state = S_IDLE;
        endcase
    end

    // 3) Output logic: Moore output depends ONLY on the current state
    always @(*) begin
        dout = (state == S_1011);
    end

endmodule

module tb_seq_detector_moore;
    reg clk, rst_n, din;
    wire dout;
    integer i;

    // stimulus: 1 0 1 1 0 1 1  -> contains overlapping "1011" twice
    reg [0:6] stream = 7'b1011011;

    seq_detector_moore dut (.clk(clk), .rst_n(rst_n), .din(din), .dout(dout));

    initial clk = 1'b0;
    always #5 clk = ~clk; // 100 MHz

    initial begin
        $display("time rst din state_out match");
        rst_n = 1'b0; din = 1'b0;
        @(negedge clk); rst_n = 1'b1; // release reset

        for (i = 0; i < 7; i = i + 1) begin
            din = stream[i];
            @(posedge clk);          // din sampled here
            #1;                       // let combinational dout settle
            $display("%4t  %b   %b     ->      %b", $time, rst_n, din, dout);
            if (dout) $display("    >> MATCH: 1011 detected ending at bit index %0d", i);
        end

        din = 1'b0;
        @(posedge clk); #1;
        $display("%4t  %b   %b     ->      %b", $time, rst_n, din, dout);
        if (dout) $display("    >> MATCH: 1011 detected (trailing)");

        $display("Simulation finished.");
        $finish;
    end
endmodule
time rst din state_out match
  16  1   1     ->      0
  26  1   0     ->      0
  36  1   1     ->      0
  46  1   1     ->      1
    >> MATCH: 1011 detected ending at bit index 3
  56  1   0     ->      0
  66  1   1     ->      0
  76  1   1     ->      1
    >> MATCH: 1011 detected ending at bit index 6
  86  1   0     ->      0
Simulation finished.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpqsn2qos2/seq_detector_moore.v:79: $finish called at 86 (1s)

How the Moore machine works, block by block

  • Encoding: five states hold how much of the pattern matched so far. S_1 means the last bit seen was 1; S_101 means the last three bits were 101; S_1011 is the hit.
  • Register block: on every rising clock edge it loads next_state, or resets to S_IDLE when rst_n is low. No logic lives here — that is the whole point.
  • Next-state block: the first line, next_state = state;, is a default that guarantees every branch assigns a value, so synthesis never infers a latch. Each case arm answers one question: “given what I’ve matched and the new bit, how much do I now match?” The interesting arm is S_101: a 1 completes 1011 (go to S_1011), but a 0 drops us back to S_10 because the tail ...10 is still a partial match.
  • Overlap handling: from S_1011, a new 1 jumps to S_1 (the trailing 1 seeds the next pattern) and a 0 goes to S_10. That is what makes this an overlapping detector.
  • Output block: dout is simply state == S_1011. Because it reads only the state, the pulse is one clean cycle wide and aligned to the clock.

In the log you can see 1011011 trigger two overlapping matches — at bit index 3 and again at bit index 6.

Mealy 1011 sequence detector

The Mealy version needs only four states because the final match is signalled on the transition rather than in a dedicated state. Watch the timing difference in the log.

// Mealy FSM: overlapping sequence detector for "1011"
// 3-always-block style. Mealy output depends on state AND current input,
// so the detect pulse appears one cycle earlier than the Moore version.
module seq_detector_mealy (
    input  wire clk,
    input  wire rst_n,  // active-low synchronous reset
    input  wire din,    // serial data input, MSB-first
    output reg  dout     // high during the cycle the 4th bit arrives
);

    localparam [1:0] S_IDLE = 2'd0, // no useful bits
                     S_1    = 2'd1, // seen "1"
                     S_10   = 2'd2, // seen "10"
                     S_101  = 2'd3; // seen "101"

    reg [1:0] state, next_state;

    // 1) State register
    always @(posedge clk) begin
        if (!rst_n)
            state <= S_IDLE;
        else
            state <= next_state;
    end

    // 2) Next-state logic
    always @(*) begin
        next_state = state;
        case (state)
            S_IDLE : next_state = din ? S_1   : S_IDLE;
            S_1    : next_state = din ? S_1   : S_10;
            S_10   : next_state = din ? S_101 : S_IDLE;
            S_101  : next_state = din ? S_1   : S_10; // on the matching 1, overlap back to S_1
            default: next_state = S_IDLE;
        endcase
    end

    // 3) Output logic: Mealy output = f(state, input)
    always @(*) begin
        dout = (state == S_101) && (din == 1'b1); // last bit of 1011 seen
    end

endmodule

module tb_seq_detector_mealy;
    reg clk, rst_n, din;
    wire dout;
    integer i;

    reg [0:6] stream = 7'b1011011; // two overlapping "1011" matches

    seq_detector_mealy dut (.clk(clk), .rst_n(rst_n), .din(din), .dout(dout));

    initial clk = 1'b0;
    always #5 clk = ~clk;

    initial begin
        $display("time rst din match");
        rst_n = 1'b0; din = 1'b0;
        @(negedge clk); rst_n = 1'b1;

        for (i = 0; i < 7; i = i + 1) begin
            din = stream[i];
            #1;                 // Mealy output reacts to din within the cycle
            $display("%4t  %b   %b    %b", $time, rst_n, din, dout);
            if (dout) $display("    >> MATCH: 1011 detected as bit index %0d arrives", i);
            @(posedge clk);
        end

        $display("Simulation finished.");
        $finish;
    end
endmodule
time rst din match
  11  1   1    0
  16  1   0    0
  26  1   1    0
  36  1   1    1
    >> MATCH: 1011 detected as bit index 3 arrives
  46  1   0    0
  56  1   1    0
  66  1   1    1
    >> MATCH: 1011 detected as bit index 6 arrives
Simulation finished.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp5zrpwqbx/seq_detector_mealy.v:71: $finish called at 75 (1s)

What changed versus the Moore version

  • One fewer state: there is no S_1011. When we are in S_101 and a 1 arrives, that is the match, so we assert the output and move straight to S_1 for overlap.
  • The output line is the key: dout = (state == S_101) && (din == 1'b1);. Because din is in the expression, dout is a Mealy output — it goes high the instant the fourth bit is present, before the next clock edge.
  • Same three-block skeleton: register, next-state, output. Only the output block’s sensitivity to din distinguishes Mealy from Moore.

Mealy vs Moore: which should you use?

PropertyMooreMealy
Output depends onState onlyState and input
States for 1011 detector54
Detection timingOne cycle laterSame cycle as last bit
Output glitchesNone (registered by state)Possible on input changes
Best whenYou need safe, synchronous outputsYou need lower latency or fewer states

A common safe compromise is to build a Mealy next-state machine but register the output, giving you a Moore-like clean pulse one cycle later.

Common mistakes to avoid

  • Mixing blocking and non-blocking. Use non-blocking (<=) in the sequential block and blocking (=) in combinational blocks. Mixing them causes simulation-vs-synthesis mismatches.
  • Forgetting the default assignment. Without next_state = state; (or a full default arm), an incompletely specified case infers an unwanted latch.
  • Putting logic in the clocked block. Computing outputs or next state inside always @(posedge clk) defeats the readability of the three-block style and hides timing.
  • Ignoring overlap. Decide up front whether matches can overlap. A non-overlapping detector would return to S_IDLE after a hit instead of reusing the trailing 1.
  • Not resetting the state register. An FSM that powers up in an unknown state can lock up; always reset to a known state.
  • Using always @(posedge clk or negedge rst_n) when you meant synchronous reset. The sensitivity list decides async vs sync reset — match it to your design intent.

Quick check

Why does the Moore detector need five states but the Mealy only four?

Moore needs a dedicated “match” state (S_1011) because its output reads only the state. Mealy signals the match on the transition out of S_101 using the input, so no extra state is required.

What does the line next_state = state; at the top of the combinational block prevent?

It provides a default so every path assigns next_state. Without it, an unassigned branch would make synthesis infer a latch, creating a subtle hardware bug.

In the overlapping detector, where does the FSM go after a successful 1011 match on a following 1?

To S_1. The trailing 1 of 1011 becomes the first bit of a potential new match, which is exactly what “overlapping” means.

Why can a Mealy output glitch while a Moore output cannot?

The Mealy output is combinational on the input, so any mid-cycle change on din can momentarily change dout. The Moore output is a function of the registered state only, so it is stable for the whole cycle.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD