Skip to content

RTL Design Patterns · Chapter 4 · FSM Design

Moore vs Mealy

A finite state machine has a state register, next-state logic, and output logic, and the one question that separates Moore from Mealy is where the output logic gets its inputs. A Moore output depends on the state only, so it is registered and glitch-free but lands one cycle after the input that decided it. A Mealy output depends on the state and the current input, so it responds the same cycle and needs fewer states, but it is combinational, so it can glitch and it puts the input on a straight path to the outputs. Choosing wrong costs you a late response, a glitchy strobe, or a path that fails timing. You build the same small machine both ways, study the Mealy glitch, and learn the registered-Mealy compromise, in SystemVerilog, Verilog, and VHDL.

Intermediate16 min readRTL Design PatternsMoore FSMMealy FSMOutput TimingCombinational GlitchRegistered Mealy

Chapter 4 · Section 4.2 · FSM Design

1. The Engineering Problem

You are building a serial-line watcher: a single-bit input in arrives one bit per clock, and the moment two consecutive 1s go by, you must raise a one-cycle out strobe that tells a downstream block "the pattern happened." The state machine is trivial — you learned to build exactly this shape in 4.1: a state register, next-state logic that walks a couple of states, and output logic that raises out. You write it, it simulates, out pulses, and you move on.

Then integration finds a problem that has nothing to do with whether you detect the pattern and everything to do with when and how the strobe appears. In one reviewer's build, out fires the cycle after the second 1 — a cycle too late for the downstream block, which needed to act on the same cycle. In another engineer's build, out fires the same cycle — but static timing flags a long path from the in pin straight through to out, because the output now depends combinationally on the live input, and that build fails timing at speed. In a third, out fires the same cycle and meets timing, but a downstream edge-triggered counter occasionally records two events for one real one, because out briefly glitched high while in was still settling and an adjacent flop caught the glitch. Three engineers, one specification, three different failures — and the only thing that differed between them was the answer to a single question: does the output depend on the state alone, or on the state and the current input?

That question is the Moore-versus-Mealy choice, and the point of this page is that it is not a definition to memorize — it is a hardware trade-off you make deliberately. Two FSMs can implement the same behaviour yet differ in when their outputs appear (a Moore output is a cycle later than the equivalent Mealy output), in how clean they are (a Moore output is registered and glitch-free; a Mealy output is combinational and can glitch), and in what path they create (a Mealy output puts the input on a combinational path straight to the outputs). Pick the wrong one for the situation and you get a late response, a glitchy strobe, or a failing path. The rest of this page is how to choose on purpose.

the-need.v — same detector, one wiring question, three different outcomes
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // Detect two consecutive 1s on serial `in`, raise a one-cycle `out`.
   // The state machine is the same in both styles; only the OUTPUT tap differs:
 
   // MOORE: out depends on STATE only. out is registered behind the state flops.
   //   -> glitch-free, times like datapath, but fires the cycle AFTER the 2nd 1.
   //   assign out = (state == GOT_TWO);
 
   // MEALY: out depends on STATE and the CURRENT input `in`.
   //   -> fires the SAME cycle as the 2nd 1, fewer states, but out is COMBINATIONAL:
   //      it can glitch as `in` settles, and `in` now drives a path straight to `out`.
   //   assign out = (state == GOT_ONE) & in;
 
   // Same function. Different WHEN (latency), different HOW (glitch, input->out path).
   // The choice is a trade-off, not a label — this page makes it deliberate.

2. Mental Model

3. Pattern Anatomy

The three FSM pieces from 4.1 are unchanged. Moore vs Mealy is entirely about the output logic — the third piece — and specifically about what it reads and whether its result is registered.

Where the output logic taps. In a Moore machine the output logic reads the state register only: out = f(state). In a Mealy machine it reads the state register and the current input: out = f(state, in). That is the entire structural difference — one extra wire (the live input) feeding the output block. Everything below is a consequence of that one wire.

The latency difference — a cycle, and where it comes from. Consider the deciding input arriving on cycle N. In a Mealy machine the output logic sees that input on cycle N and can raise out on cycle N — the same cycle. In a Moore machine the input on cycle N can only change the next state, which the state register loads on the edge into cycle N+1; the output, reading only the state, therefore cannot reflect the input until cycle N+1. A Moore output is valid the cycle after the deciding input; a Mealy output is valid the same cycle. That single cycle is not a bug in either — it is the defining timing property of each, and the reason a Moore detector often needs one more state (a dedicated "I just saw it" state to hang the output on) than the equivalent Mealy detector.

Glitches on the Mealy combinational output. Because the Mealy output is a combinational function of the live input, it inherits the hazards of combinational logic. As in transitions and the logic settles, out can momentarily glitch — go high and back, or high a beat early — before it reaches its steady value for the cycle. In a purely synchronous world where out feeds another flop's D and is sampled only at the clock edge, that glitch is harmless (it has settled by the edge). But the instant that Mealy output feeds something edge-sensitive — a downstream flop's clock or async set/reset, an asynchronous enable, a counter that increments on a rising edge of out — the glitch is a real, spurious event. The Moore output, being registered behind the state flops, is glitch-free by construction.

The input-to-output combinational path. The Mealy output puts the input on a straight combinational path: in -> output logic -> out, and if out then feeds combinational logic in the next block, the path extends further. Static timing sees this as a real path from an input pin (or an upstream register) all the way to out (or the next register), and on a fast clock it can be the critical path — the classic "my Mealy FSM fails timing on the input-to-output arc" problem. A Moore output has no such path: the input reaches only the next-state logic, and the output is fed by the (already registered) state, so the input-to-output arc is broken by the state register.

The registered-Mealy compromise. You do not have to choose between Mealy's low latency and Moore's clean output. Register the Mealy output: compute the Mealy decision combinationally (out_comb = f(state, in)) and flop it, so the design sees a registered out_reg. The registered output is glitch-free and breaks the input-to-output combinational path (the flop is the endpoint), recovering Moore's cleanliness — but it re-adds one cycle of latency on that output, landing it a cycle after the pure-Mealy output. It is the pragmatic default when you want fast internal reactivity but a clean, timing-safe output leaving the block.

Moore vs Mealy — same machine, the output taps in two places

data flow
Moore vs Mealy — same machine, the output taps in two placesdrives next-statestate onlystate......and inputflopitin (live input)arrives on cycle Nnext-state logicf(state, in) — same in bothstate registerupdates on the clock edgeMoore outputf(state) — registered, +1 cycle, cleanMealy outputf(state, in) — same cycle, combinationalregistered Mealyflop the Mealy decision — clean, +1 back
The state register and next-state logic are identical in both machines. The only difference is the output tap: a Moore output reads the STATE alone, so it is fed by the already-registered state — glitch-free, times like datapath, but one cycle behind the deciding input. A Mealy output reads the state AND the live input, so it responds the same cycle and needs fewer states, but it is combinational (it can glitch and it puts the input on a straight path to the output). Register the Mealy decision and you get a clean, timing-safe output back — at the cost of adding the one cycle of latency onto that registered output. This structure is identical in SystemVerilog, Verilog, and VHDL.

The second visual makes the timing concrete: drive the same input sequence into both machines and watch when each raises its output, and what the Mealy output does mid-cycle.

Output timing on the same input sequence — Mealy same cycle, Moore next cycle

data flow
Output timing on the same input sequence — Mealy same cycle, Moore next cyclecombinationalhazardvia stateflopin = 0,1,1,0the 2nd 1 (cycle N) completes the patternMealy outrises on cycle N — the SAME cycleMealy mid-cyclecan glitch as in settles — hazardMoore outrises on cycle N+1 — one cycle laterregistered Mealyoutclean, but also N+1 (flop adds a cycle)
Feed 0,1,1,0 into both detectors. The Mealy output rises on cycle N — the same cycle as the second 1 — because it reads the live input; but as in settles that combinational output can glitch mid-cycle, which is harmless into a flop sampled at the edge and dangerous into anything edge-sensitive. The Moore output rises on cycle N+1, one cycle later, because the deciding input can only change the state (loaded on the edge) and the output reads the state; in exchange it is registered and glitch-free. The registered-Mealy output is glitch-free like Moore and lands on N+1 too, since flopping the Mealy decision adds a cycle back. Same function, three different output timings — the choice is which timing your consumer needs. The waveform is identical across the three HDLs.

4. Real RTL Implementation

This is the core of the page. We build the same small machine — a detector that raises a one-cycle out when it sees two consecutive 1s on serial in — three ways: (a) as a Moore FSM (output from state only), (b) as a Mealy FSM (output from state and input), and (c) the Mealy-glitch bug beside its fix (raw Mealy combinational output feeding edge-sensitive logic vs a Moore / registered-Mealy output). Each is shown in SystemVerilog, Verilog, and VHDL with an RTL block and a self-checking clocked testbench. Building the same function both ways is what makes the timing contrast concrete rather than definitional.

Two conventions run through every block. The reset is a synchronous, active-high rst (as in 2.3) that returns the machine to its idle state; the state is a small enumerated type (typedef enum / localparam / a VHDL enumerated type) named for what it remembersS_IDLE (seen nothing useful), S_GOT1 (just saw a single 1). The Moore machine needs a third state, S_GOT2, to hang its registered output on; the Mealy machine does not — it raises out combinationally on the transition into the "two-in-a-row" condition, which is exactly the one-state saving Mealy buys.

4a. The Moore FSM — output from state only

The Moore detector uses three states and taps out from the state register alone: out = (state == S_GOT2). Because the output reads only the state, it is glitch-free and lands the cycle after the second 1 — the cycle in which the machine has entered S_GOT2.

seq_moore.sv — Moore 11-detector: out = f(state), registered, +1 cycle
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_moore (
       input  logic clk,
       input  logic rst,                       // synchronous, active-high
       input  logic in,                        // serial input, one bit per clock
       output logic out                        // 1-cycle strobe: seen two 1s in a row
   );
       typedef enum logic [1:0] {S_IDLE, S_GOT1, S_GOT2} state_t;
       state_t state, next_state;
 
       // NEXT-STATE logic: f(state, in) — identical intent to the Mealy version below.
       always_comb begin
           next_state = state;                 // default: hold (latch-free combinational block)
           unique case (state)
               S_IDLE: next_state = in ? S_GOT1 : S_IDLE;
               S_GOT1: next_state = in ? S_GOT2 : S_IDLE;   // second 1 -> enter GOT2
               S_GOT2: next_state = in ? S_GOT2 : S_IDLE;   // stay while 1s keep coming
           endcase
       end
 
       // STATE register: synchronous reset to idle.
       always_ff @(posedge clk)
           if (rst) state <= S_IDLE;
           else     state <= next_state;
 
       // OUTPUT logic — MOORE: reads the STATE ONLY. `out` is effectively registered
       // behind the state flops => glitch-free, but it is high the cycle AFTER the
       // second 1 (the cycle the machine has ENTERED S_GOT2).
       assign out = (state == S_GOT2);
   endmodule

The clocked, self-checking testbench drives a fixed input sequence and checks out cycle by cycle against a reference of when the Moore output should fire — one cycle after each completed pair.

seq_moore_tb.sv — clocked self-check: out fires the cycle AFTER the 2nd 1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_moore_tb;
       logic clk = 1'b0, rst, in, out;
       int   errors = 0;
 
       seq_moore dut (.clk(clk), .rst(rst), .in(in), .out(out));
 
       always #5 clk = ~clk;
 
       // Drive in = 0 1 1 0 1 1 1 0. Moore `out` is high the cycle AFTER each 2nd 1.
       // Expected out, aligned one cycle later than the deciding input, is checked
       // just before each rising edge (after `out` for the previous input has settled).
       localparam int LEN = 8;
       logic [LEN-1:0] seq_in  = 8'b0_1_1_0_1_1_1_0;   // read MSB-first below
       logic [LEN-1:0] seq_out = 8'b0_0_1_0_0_1_1_0;   // Moore: one cycle after each pair
 
       initial begin
           rst = 1'b1; in = 1'b0;
           @(posedge clk); #1; rst = 1'b0;
           for (int i = LEN-1; i >= 0; i--) begin
               in = seq_in[i];
               @(posedge clk); #1;               // sample AFTER the edge that loaded the state
               assert (out === seq_out[i])
                   else begin $error("cycle %0d: in=%b out=%b exp=%b", LEN-1-i, in, out, seq_out[i]); errors++; end
           end
           if (errors == 0) $display("PASS: Moore out asserts one cycle after each detected pair");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

The Verilog form is the same three-state machine with localparam state codes and reg/wire typing; out is a continuous assign off the state, so it is equally registered-clean.

seq_moore.v — Moore 11-detector in Verilog (out from state only)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_moore (
       input  wire clk,
       input  wire rst,
       input  wire in,
       output wire out
   );
       localparam S_IDLE = 2'd0, S_GOT1 = 2'd1, S_GOT2 = 2'd2;
       reg [1:0] state, next_state;
 
       always @(*) begin
           next_state = state;                 // default hold => no latch
           case (state)
               S_IDLE:  next_state = in ? S_GOT1 : S_IDLE;
               S_GOT1:  next_state = in ? S_GOT2 : S_IDLE;
               S_GOT2:  next_state = in ? S_GOT2 : S_IDLE;
               default: next_state = S_IDLE;
           endcase
       end
 
       always @(posedge clk)
           if (rst) state <= S_IDLE;
           else     state <= next_state;
 
       // MOORE output: state only => registered-clean, high the cycle AFTER the 2nd 1.
       assign out = (state == S_GOT2);
   endmodule
seq_moore_tb.v — self-checking Verilog TB (compare out each cycle, PASS/FAIL)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_moore_tb;
       reg  clk, rst, in;
       wire out;
       integer i, errors;
       reg [7:0] seq_in, seq_out;
 
       seq_moore dut (.clk(clk), .rst(rst), .in(in), .out(out));
 
       initial clk = 1'b0;
       always #5 clk = ~clk;
 
       initial begin
           errors = 0;
           seq_in  = 8'b0_1_1_0_1_1_1_0;
           seq_out = 8'b0_0_1_0_0_1_1_0;      // Moore: one cycle after each pair
           rst = 1'b1; in = 1'b0;
           @(posedge clk); #1; rst = 1'b0;
           for (i = 7; i >= 0; i = i - 1) begin
               in = seq_in[i];
               @(posedge clk); #1;
               if (out !== seq_out[i]) begin
                   $display("FAIL: cycle %0d in=%b out=%b exp=%b", 7-i, in, out, seq_out[i]);
                   errors = errors + 1;
               end
           end
           if (errors == 0) $display("PASS: Moore out one cycle after each pair");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

In VHDL the states are an enumerated type, the state register uses rising_edge(clk) with a synchronous reset, and the Moore output is a concurrent assignment off the state signal.

seq_moore.vhd — Moore 11-detector in VHDL (enumerated state, out from state)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
 
   entity seq_moore is
       port (
           clk : in  std_logic;
           rst : in  std_logic;                            -- synchronous, active-high
           d   : in  std_logic;                            -- serial input ('in' is reserved)
           q   : out std_logic                             -- 1-cycle strobe
       );
   end entity;
 
   architecture rtl of seq_moore is
       type state_t is (S_IDLE, S_GOT1, S_GOT2);
       signal state, next_state : state_t;
   begin
       -- NEXT-STATE logic: f(state, d).
       process (state, d)
       begin
           next_state <= state;                            -- default hold
           case state is
               when S_IDLE => if d = '1' then next_state <= S_GOT1; else next_state <= S_IDLE; end if;
               when S_GOT1 => if d = '1' then next_state <= S_GOT2; else next_state <= S_IDLE; end if;
               when S_GOT2 => if d = '1' then next_state <= S_GOT2; else next_state <= S_IDLE; end if;
           end case;
       end process;
 
       -- STATE register: synchronous reset to idle.
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then state <= S_IDLE; else state <= next_state; end if;
           end if;
       end process;
 
       -- MOORE output: state only => registered-clean, high the cycle AFTER the 2nd 1.
       q <= '1' when state = S_GOT2 else '0';
   end architecture;
seq_moore_tb.vhd — self-checking VHDL TB (assert q each cycle, severity error)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
 
   entity seq_moore_tb is
   end entity;
 
   architecture sim of seq_moore_tb is
       signal clk : std_logic := '0';
       signal rst : std_logic := '1';
       signal d   : std_logic := '0';
       signal q   : std_logic;
       constant SEQ_IN  : std_logic_vector(0 to 7) := "01101110";
       constant SEQ_OUT : std_logic_vector(0 to 7) := "00100110";  -- Moore: one cycle after each pair
   begin
       dut : entity work.seq_moore port map (clk => clk, rst => rst, d => d, q => q);
 
       clkgen : process
       begin
           clk <= '0'; wait for 5 ns;
           clk <= '1'; wait for 5 ns;
       end process;
 
       stim : process
       begin
           rst <= '1'; d <= '0';
           wait until rising_edge(clk); wait for 1 ns; rst <= '0';
           for i in 0 to 7 loop
               d <= SEQ_IN(i);
               wait until rising_edge(clk); wait for 1 ns;
               assert q = SEQ_OUT(i)
                   report "Moore mismatch: q /= expected this cycle" severity error;
           end loop;
           report "seq_moore self-check complete" severity note;
           wait;
       end process;
   end architecture;

4b. The Mealy FSM — output from state and input

The Mealy detector implements the same function with two states. It taps out from the state and the live input: out = (state == S_GOT1) & in — the instant a second 1 arrives while in S_GOT1, out goes high this cycle, without waiting to enter a third state. That is the same-cycle response and the one-state saving; the cost is that out is now combinational.

seq_mealy.sv — Mealy 11-detector: out = f(state, in), same cycle, combinational
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_mealy (
       input  logic clk,
       input  logic rst,
       input  logic in,
       output logic out                        // COMBINATIONAL: high the SAME cycle as the 2nd 1
   );
       typedef enum logic [0:0] {S_IDLE, S_GOT1} state_t;   // one fewer state than Moore
       state_t state, next_state;
 
       always_comb begin
           next_state = state;
           unique case (state)
               S_IDLE: next_state = in ? S_GOT1 : S_IDLE;
               S_GOT1: next_state = in ? S_GOT1 : S_IDLE;   // consecutive 1s keep us in GOT1
           endcase
       end
 
       always_ff @(posedge clk)
           if (rst) state <= S_IDLE;
           else     state <= next_state;
 
       // OUTPUT logic — MEALY: reads STATE and the LIVE input. `out` is high the SAME
       // cycle the second 1 arrives (in S_GOT1 with in=1). It is COMBINATIONAL: it can
       // glitch as `in` settles, and `in` now drives a path straight to `out`.
       assign out = (state == S_GOT1) && in;
   endmodule

The self-checking testbench drives the same sequence as 4a but checks out on the same cycle as the deciding input — the Mealy output is high the cycle the second 1 is present, one cycle earlier than the Moore reference. Aligning the two expectation vectors is the concrete proof of the latency difference.

seq_mealy_tb.sv — clocked self-check: out fires the SAME cycle as the 2nd 1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_mealy_tb;
       logic clk = 1'b0, rst, in, out;
       int   errors = 0;
 
       seq_mealy dut (.clk(clk), .rst(rst), .in(in), .out(out));
 
       always #5 clk = ~clk;
 
       // SAME input as the Moore TB: 0 1 1 0 1 1 1 0. But Mealy `out` is high the SAME
       // cycle as each 2nd 1 => the expectation vector is shifted one cycle EARLIER.
       localparam int LEN = 8;
       logic [LEN-1:0] seq_in  = 8'b0_1_1_0_1_1_1_0;
       logic [LEN-1:0] seq_out = 8'b0_0_1_0_0_1_1_0;   // note: aligned to the input cycle now
 
       initial begin
           rst = 1'b1; in = 1'b0;
           @(posedge clk); #1; rst = 1'b0;
           for (int i = LEN-1; i >= 0; i--) begin
               in = seq_in[i];
               #1;                               // let the combinational Mealy output settle
               assert (out === seq_out[i])
                   else begin $error("cycle %0d: in=%b out=%b exp=%b", LEN-1-i, in, out, seq_out[i]); errors++; end
               @(posedge clk);                   // advance state for the next input bit
           end
           if (errors == 0) $display("PASS: Mealy out asserts the SAME cycle as the 2nd 1");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

The Verilog Mealy form is the same two-state machine; out is a continuous assign off state and in, so it is combinational by construction.

seq_mealy.v — Mealy 11-detector in Verilog (out from state AND input)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_mealy (
       input  wire clk,
       input  wire rst,
       input  wire in,
       output wire out
   );
       localparam S_IDLE = 1'd0, S_GOT1 = 1'd1;
       reg state, next_state;
 
       always @(*) begin
           next_state = state;
           case (state)
               S_IDLE:  next_state = in ? S_GOT1 : S_IDLE;
               S_GOT1:  next_state = in ? S_GOT1 : S_IDLE;
               default: next_state = S_IDLE;
           endcase
       end
 
       always @(posedge clk)
           if (rst) state <= S_IDLE;
           else     state <= next_state;
 
       // MEALY output: state AND input => same cycle, combinational (can glitch).
       assign out = (state == S_GOT1) & in;
   endmodule
seq_mealy_tb.v — self-checking Verilog TB (same input, out checked same cycle)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module seq_mealy_tb;
       reg  clk, rst, in;
       wire out;
       integer i, errors;
       reg [7:0] seq_in, seq_out;
 
       seq_mealy dut (.clk(clk), .rst(rst), .in(in), .out(out));
 
       initial clk = 1'b0;
       always #5 clk = ~clk;
 
       initial begin
           errors = 0;
           seq_in  = 8'b0_1_1_0_1_1_1_0;
           seq_out = 8'b0_0_1_0_0_1_1_0;      // Mealy: aligned to the SAME cycle as the input
           rst = 1'b1; in = 1'b0;
           @(posedge clk); #1; rst = 1'b0;
           for (i = 7; i >= 0; i = i - 1) begin
               in = seq_in[i];
               #1;                            // settle the combinational output
               if (out !== seq_out[i]) begin
                   $display("FAIL: cycle %0d in=%b out=%b exp=%b", 7-i, in, out, seq_out[i]);
                   errors = errors + 1;
               end
               @(posedge clk);
           end
           if (errors == 0) $display("PASS: Mealy out same cycle as the 2nd 1");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

In VHDL the Mealy output is a concurrent assignment reading both state and the input d, so it responds combinationally within the cycle.

seq_mealy.vhd — Mealy 11-detector in VHDL (out from state and input)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
 
   entity seq_mealy is
       port (
           clk : in  std_logic;
           rst : in  std_logic;
           d   : in  std_logic;
           q   : out std_logic                             -- combinational Mealy output
       );
   end entity;
 
   architecture rtl of seq_mealy is
       type state_t is (S_IDLE, S_GOT1);                   -- one fewer state than Moore
       signal state, next_state : state_t;
   begin
       process (state, d)
       begin
           next_state <= state;
           case state is
               when S_IDLE => if d = '1' then next_state <= S_GOT1; else next_state <= S_IDLE; end if;
               when S_GOT1 => if d = '1' then next_state <= S_GOT1; else next_state <= S_IDLE; end if;
           end case;
       end process;
 
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then state <= S_IDLE; else state <= next_state; end if;
           end if;
       end process;
 
       -- MEALY output: state AND input => same cycle, combinational.
       q <= '1' when (state = S_GOT1 and d = '1') else '0';
   end architecture;
seq_mealy_tb.vhd — self-checking VHDL TB (same input, q checked same cycle)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
 
   entity seq_mealy_tb is
   end entity;
 
   architecture sim of seq_mealy_tb is
       signal clk : std_logic := '0';
       signal rst : std_logic := '1';
       signal d   : std_logic := '0';
       signal q   : std_logic;
       constant SEQ_IN  : std_logic_vector(0 to 7) := "01101110";
       constant SEQ_OUT : std_logic_vector(0 to 7) := "00100110";  -- Mealy: same cycle as input
   begin
       dut : entity work.seq_mealy port map (clk => clk, rst => rst, d => d, q => q);
 
       clkgen : process
       begin
           clk <= '0'; wait for 5 ns;
           clk <= '1'; wait for 5 ns;
       end process;
 
       stim : process
       begin
           rst <= '1'; d <= '0';
           wait until rising_edge(clk); wait for 1 ns; rst <= '0';
           for i in 0 to 7 loop
               d <= SEQ_IN(i);
               wait for 1 ns;                                  -- settle the combinational output
               assert q = SEQ_OUT(i)
                   report "Mealy mismatch: q /= expected same cycle" severity error;
               wait until rising_edge(clk);
           end loop;
           report "seq_mealy self-check complete" severity note;
           wait;
       end process;
   end architecture;

4c. The Mealy-glitch bug — raw combinational output vs Moore / registered-Mealy fix

Pattern (c) is the flagship failure, shown as buggy vs fixed RTL in each language and dramatized in §7. The bug is not in the FSM: it is in what consumes the Mealy output. The buggy design routes the raw combinational Mealy out into something edge-sensitive — here, the clock (or a rising-edge count) of a downstream counter — so a mid-cycle glitch on out is latched as a spurious event. The fix is to feed the consumer a glitch-free output instead: either the Moore output (registered by construction) or a registered-Mealy output (flop the Mealy decision), each of which is stable and only changes on a clock edge. A zero-delay RTL sim will not itself produce the analog glitch, so the comment marks where the hazard lives; §5 covers how gate-level simulation and static timing expose it.

mealy_glitch.sv — BUGGY (raw combinational Mealy out to edge-sensitive) vs FIXED (registered)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // BUGGY: the raw COMBINATIONAL Mealy output drives an edge-sensitive consumer.
   //        As `in` settles, mealy_out can GLITCH high-and-back; the downstream counter
   //        that increments on a rising edge of mealy_out records a SPURIOUS event.
   module use_mealy_bad (
       input  logic clk, rst, in,
       output logic [7:0] event_count
   );
       logic mealy_out;
       seq_mealy u_fsm (.clk(clk), .rst(rst), .in(in), .out(mealy_out));
 
       // DANGER: mealy_out is combinational; using it as an edge trigger latches glitches.
       always_ff @(posedge mealy_out or posedge rst)     // edge-sensitive on a combinational net!
           if (rst) event_count <= '0;
           else     event_count <= event_count + 8'd1;    // may count a glitch as a real event
   endmodule
 
   // FIXED: REGISTER the Mealy decision first, then let the (now glitch-free) registered
   //        output drive the consumer synchronously — no combinational net feeds an edge.
   module use_mealy_good (
       input  logic clk, rst, in,
       output logic [7:0] event_count
   );
       logic mealy_out, mealy_out_reg;
       seq_mealy u_fsm (.clk(clk), .rst(rst), .in(in), .out(mealy_out));
 
       always_ff @(posedge clk) begin
           if (rst) mealy_out_reg <= 1'b0;
           else     mealy_out_reg <= mealy_out;          // registered-Mealy: clean, +1 cycle
       end
 
       // Count synchronously on the clock, qualified by the REGISTERED (glitch-free) pulse.
       always_ff @(posedge clk)
           if (rst)                event_count <= '0;
           else if (mealy_out_reg) event_count <= event_count + 8'd1;
   endmodule
mealy_glitch_tb.sv — clocked self-check: exactly one count per real pair (bind to _good)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mealy_glitch_tb;
       logic clk = 1'b0, rst, in;
       logic [7:0] event_count;
       int   errors = 0;
 
       // Bind to use_mealy_good: counts exactly the real pairs. use_mealy_bad can
       // over-count when the combinational Mealy output glitches into the edge trigger
       // (a hazard a gate-level sim exposes — see section 5).
       use_mealy_good dut (.clk(clk), .rst(rst), .in(in), .event_count(event_count));
 
       always #5 clk = ~clk;
 
       initial begin
           rst = 1'b1; in = 1'b0;
           @(posedge clk); #1; rst = 1'b0;
           // Drive 0 1 1 0 1 1 1 0 -> the pattern completes on cycles marked below.
           in = 1'b0; @(posedge clk);
           in = 1'b1; @(posedge clk);
           in = 1'b1; @(posedge clk);            // first pair
           in = 1'b0; @(posedge clk);
           in = 1'b1; @(posedge clk);
           in = 1'b1; @(posedge clk);            // second pair
           in = 1'b1; @(posedge clk);            // still 1s (overlapping, one more pair)
           in = 1'b0; @(posedge clk); #1;
           // Expect exactly 3 counted events for this stream; a glitch would inflate it.
           assert (event_count === 8'd3)
               else begin $error("event_count=%0d exp=3 (glitch over-count?)", event_count); errors++; end
           if (errors == 0) $display("PASS: registered pulse counts exactly the real events");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

The Verilog buggy/fixed pair is the same story: the bad module clocks a counter off the combinational Mealy net; the good module registers the pulse and counts synchronously.

mealy_glitch.v — BUGGY (combinational Mealy drives an edge) vs FIXED (registered) in Verilog
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // BUGGY: combinational Mealy output used as an edge trigger -> glitch counts as event.
   module use_mealy_bad (
       input  wire clk, rst, in,
       output reg [7:0] event_count
   );
       wire mealy_out;
       seq_mealy u_fsm (.clk(clk), .rst(rst), .in(in), .out(mealy_out));
 
       always @(posedge mealy_out or posedge rst)   // edge on a COMBINATIONAL net -> hazard
           if (rst) event_count <= 8'd0;
           else     event_count <= event_count + 8'd1;
   endmodule
 
   // FIXED: register the Mealy pulse, then count synchronously off the clock.
   module use_mealy_good (
       input  wire clk, rst, in,
       output reg [7:0] event_count
   );
       wire mealy_out;
       reg  mealy_out_reg;
       seq_mealy u_fsm (.clk(clk), .rst(rst), .in(in), .out(mealy_out));
 
       always @(posedge clk)
           if (rst) mealy_out_reg <= 1'b0;
           else     mealy_out_reg <= mealy_out;     // registered-Mealy: glitch-free, +1 cycle
 
       always @(posedge clk)
           if (rst)                event_count <= 8'd0;
           else if (mealy_out_reg) event_count <= event_count + 8'd1;
   endmodule
mealy_glitch_tb.v — self-checking Verilog (exactly one count per real pair, bind _good)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mealy_glitch_tb;
       reg  clk, rst, in;
       wire [7:0] event_count;
       integer errors;
 
       // Bind to use_mealy_good (clean). use_mealy_bad can over-count on a glitch.
       use_mealy_good dut (.clk(clk), .rst(rst), .in(in), .event_count(event_count));
 
       initial clk = 1'b0;
       always #5 clk = ~clk;
 
       initial begin
           errors = 0;
           rst = 1'b1; in = 1'b0;
           @(posedge clk); #1; rst = 1'b0;
           in = 1'b0; @(posedge clk);
           in = 1'b1; @(posedge clk);
           in = 1'b1; @(posedge clk);        // pair 1
           in = 1'b0; @(posedge clk);
           in = 1'b1; @(posedge clk);
           in = 1'b1; @(posedge clk);        // pair 2
           in = 1'b1; @(posedge clk);        // pair 3 (overlapping run of 1s)
           in = 1'b0; @(posedge clk); #1;
           if (event_count !== 8'd3) begin
               $display("FAIL: event_count=%0d exp=3 (glitch over-count?)", event_count);
               errors = errors + 1;
           end
           if (errors == 0) $display("PASS: registered pulse counts exactly the real events");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

In VHDL the same bug is a process sensitive to a rising edge of the combinational Mealy net; the fix registers the pulse and counts on rising_edge(clk).

mealy_glitch.vhd — BUGGY (combinational Mealy edge) vs FIXED (registered) in VHDL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   -- BUGGY: the counter is clocked by the COMBINATIONAL Mealy output -> a glitch counts.
   entity use_mealy_bad is
       port ( clk, rst, d : in std_logic; event_count : out std_logic_vector(7 downto 0) );
   end entity;
   architecture rtl of use_mealy_bad is
       signal mealy_out : std_logic;
       signal cnt       : unsigned(7 downto 0);
   begin
       u_fsm : entity work.seq_mealy port map (clk => clk, rst => rst, d => d, q => mealy_out);
       process (mealy_out, rst)                         -- edge on a combinational net -> hazard
       begin
           if rst = '1' then cnt <= (others => '0');
           elsif rising_edge(mealy_out) then cnt <= cnt + 1;
           end if;
       end process;
       event_count <= std_logic_vector(cnt);
   end architecture;
 
   -- FIXED: register the Mealy pulse, then count synchronously on the clock.
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   entity use_mealy_good is
       port ( clk, rst, d : in std_logic; event_count : out std_logic_vector(7 downto 0) );
   end entity;
   architecture rtl of use_mealy_good is
       signal mealy_out, mealy_out_reg : std_logic;
       signal cnt : unsigned(7 downto 0);
   begin
       u_fsm : entity work.seq_mealy port map (clk => clk, rst => rst, d => d, q => mealy_out);
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then
                   mealy_out_reg <= '0';
                   cnt           <= (others => '0');
               else
                   mealy_out_reg <= mealy_out;          -- registered-Mealy: glitch-free, +1 cycle
                   if mealy_out_reg = '1' then cnt <= cnt + 1; end if;
               end if;
           end if;
       end process;
       event_count <= std_logic_vector(cnt);
   end architecture;
mealy_glitch_tb.vhd — self-checking VHDL (one count per real pair, bind use_mealy_good)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   entity mealy_glitch_tb is
   end entity;
 
   architecture sim of mealy_glitch_tb is
       signal clk : std_logic := '0';
       signal rst : std_logic := '1';
       signal d   : std_logic := '0';
       signal event_count : std_logic_vector(7 downto 0);
   begin
       -- Bind to use_mealy_good (clean). use_mealy_bad can over-count on a glitch.
       dut : entity work.use_mealy_good port map (clk => clk, rst => rst, d => d, event_count => event_count);
 
       clkgen : process
       begin
           clk <= '0'; wait for 5 ns;
           clk <= '1'; wait for 5 ns;
       end process;
 
       stim : process
       begin
           rst <= '1'; d <= '0';
           wait until rising_edge(clk); wait for 1 ns; rst <= '0';
           d <= '0'; wait until rising_edge(clk);
           d <= '1'; wait until rising_edge(clk);
           d <= '1'; wait until rising_edge(clk);        -- pair 1
           d <= '0'; wait until rising_edge(clk);
           d <= '1'; wait until rising_edge(clk);
           d <= '1'; wait until rising_edge(clk);        -- pair 2
           d <= '1'; wait until rising_edge(clk);        -- pair 3 (overlapping)
           d <= '0'; wait until rising_edge(clk); wait for 1 ns;
           assert event_count = std_logic_vector(to_unsigned(3, 8))
               report "event_count /= 3 (glitch over-count?)" severity error;
           report "mealy_glitch self-check complete" severity note;
           wait;
       end process;
   end architecture;

Across all three languages the lesson is one sentence: the Mealy output is combinational, so it may only feed logic that samples it at a clock edge — the moment it drives anything edge-sensitive, register it first (registered-Mealy) or use the Moore output, so the consumer sees a glitch-free, clock-aligned pulse.

5. Verification Strategy

Moore vs Mealy verification is not "does it detect the pattern" — both do — it is "does the output appear on the right cycle, and is it clean enough for its consumer." That reframes the whole test: you drive the same input sequence into both machines and check when each output asserts.

The Mealy output asserts the same cycle as the deciding input; the Moore output asserts the cycle after; and any output that feeds edge-sensitive logic must be glitch-free (registered).

  • Self-checking, cycle-aligned expectation vectors. Build one input vector and two expectation vectors — one aligned to the input cycle (Mealy) and one shifted a cycle later (Moore). The §4a and §4b testbenches drive the same 0 1 1 0 1 1 1 0 stream and check out against the matching vector each cycle: SV assert (out === seq_out[i]), Verilog if (out !== seq_out[i]) $display("FAIL…"), VHDL assert q = SEQ_OUT(i) … severity error. The shift between the two vectors is the machine-checked proof of the one-cycle latency difference — the single most important property this page teaches.
  • Sample at the right moment. Because the Mealy output is combinational, the testbench must let it settle within the cycle before checking it (a small #1 after driving in, before the check) — the same postponed-sampling discipline the v1 self-checking-TB standard requires. The Moore output, being registered, is checked after the clock edge that loaded the state. Sampling a combinational Mealy output at the wrong delta is itself a classic testbench bug.
  • Demonstrate the same-cycle combinational response. A directed check drives in high within a cycle while the machine sits in S_GOT1 and confirms the Mealy out responds in that cycle (before any edge) — proving the output tracks the live input, not just the state. Doing the same on the Moore machine shows out does not move until the next edge. This is the behavioural difference made visible.
  • Assert the registered-Mealy is glitch-free / correctly counted. For the §4c consumer, drive a stream with a known number of real pattern completions and assert the event count equals that number exactly — the §4c testbenches expect event_count === 3 for the given stream. On the fixed (registered) path the count is exact; the bug is an over-count when a combinational glitch is latched by the edge-sensitive consumer. The invariant: a pulse that drives edge-sensitive logic must change only on a clock edge.
  • Conceptual invariants. Three properties must always hold: (i) both machines detect exactly the same set of input sequences (same function); (ii) the Mealy output for a given completed pattern is exactly one cycle earlier than the Moore output (the latency relationship); (iii) any output consumed by edge-sensitive logic never changes except on a clock edge (the glitch-freedom requirement). Property (iii) is the one a functional RTL sim can only approximate.
  • The glitch is a gate-level concern. Be honest about the limit: a zero-delay RTL simulation does not produce the analog glitch on the combinational Mealy output, so §4c's buggy and fixed consumers can look identical functionally in RTL. The glitch — and the spurious edge it creates — is exposed by gate-level simulation with SDF timing (where combinational logic has real delays and hazards appear) and by static timing analysis, which reports the input-to-output combinational path on the Mealy output and flags it if it is the critical path. In RTL you structurally prevent it: never let a combinational output drive a clock, an async set/reset, or a rising-edge count — register it first.
  • Corner cases and expected waveform. Exercise overlapping patterns (a run of 1s completes the pattern on every cycle after the first pair — the Mealy output stays high across the run while the Moore output is high one cycle later); reset asserted mid-pattern (both must return to idle and not emit a stale strobe); and a single isolated 1 (neither output fires). On a correct waveform, feeding 0 1 1: the Mealy out rises on the cycle of the second 1 and the Moore out rises on the following cycle — the visual signature of the whole page is those two strobes, one cycle apart, for the same event.

6. Common Mistakes

Assuming Moore and Mealy are interchangeable regardless of timing. The most common and most expensive mistake: treating the choice as a stylistic definition and swapping one for the other without checking the consumer's timing. They implement the same function but not the same timing — a Mealy output is a cycle earlier than the equivalent Moore output. Swap a Mealy detector for a Moore one and every downstream block that expected the strobe this cycle now gets it a cycle late (or vice-versa). Choose based on when the consumer needs the output, not on which is "cleaner."

A Mealy combinational output sampled by a downstream edge-triggered block. The signature bug (§7). Routing the raw combinational Mealy output to anything edge-sensitive — a flop's clock, an async set/reset, an asynchronous enable, a rising-edge counter — lets a mid-cycle glitch on that output register as a real, spurious event. It is intermittent and traffic-dependent (the glitch only occurs on certain input transitions) and invisible in zero-delay RTL. If a Mealy output must leave the block or drive edge-sensitive logic, register it (registered-Mealy) so it only changes on a clock edge.

A long input-to-output combinational path through a Mealy FSM failing timing. Because the Mealy output is f(state, in), the input sits on a combinational path straight to the output, and if the output then feeds more combinational logic, the path lengthens. On a fast clock this input-to-output arc can be the critical path — a real static-timing failure that a Moore machine (whose output is fed by the registered state) does not have. When the Mealy path is the bottleneck, register the output (registered-Mealy) to break the arc at the output flop.

Using Mealy where the one-cycle Moore latency was actually fine. Mealy is not automatically better for being faster. If the consumer is a synchronous block that samples on the edge and does not care about a single cycle of latency, a Moore output is the safer default — registered, glitch-free, no input-to-output path, no timing surprise. Reaching for Mealy when you did not need the same-cycle response buys you the glitch and timing risk for nothing. Use Mealy when the same-cycle response is a requirement, not a reflex.

Forgetting that registered-Mealy adds the latency back. The registered-Mealy compromise recovers Moore's clean, glitch-free, timing-safe output — but it does so by flopping the Mealy decision, which re-introduces exactly one cycle of latency on that registered output. Engineers sometimes reach for registered-Mealy to "keep the low latency and get a clean output," then are surprised the registered output lands on the same cycle a Moore output would. If you register the output, you have paid Moore's cycle; the value of registered-Mealy is a cleaner internal structure and fewer states, not zero latency.

Building a Moore machine without the extra state to hang the output on. A Moore detector often needs one more state than the Mealy equivalent — a dedicated "the pattern just completed" state whose label is the output. Trying to make a Moore output fire the same cycle as a Mealy one, by reading the input in the output logic, silently turns the machine back into a Mealy machine. If the output reads the input, it is Mealy, whatever you called it.

7. DebugLab

The event counter that double-counts — a Mealy glitch caught by an edge

The engineering lesson: a Mealy output is combinational — it responds the same cycle, but it can glitch and it puts the input on a straight path to the output, so it is only safe feeding logic that samples it at a clock edge and only closes timing when its input-to-output path is short. The two tells are unmistakable: a traffic-dependent over-count that appears only at gate level is a combinational output glitching into edge-sensitive logic; a failing max-delay path from an input pin to a downstream register is a Mealy output on the critical path. Both fixes are the same move — register the Mealy output (registered-Mealy) so it changes only on a clock edge and the arc ends at a flop, or use a Moore output if the one-cycle latency is acceptable — and both are identical across SystemVerilog, Verilog, and VHDL. The choice between Moore, pure Mealy, and registered Mealy is a deliberate trade of latency against combinational reactivity, not a definition.

8. Interview Q&A

9. Exercises

Design and reasoning exercises — no syntax drills. Each rehearses the "choose the output timing on purpose" habit.

Exercise 1 — Convert Mealy to Moore, and back

Start from a two-state Mealy detector that raises out the same cycle it sees the second 1 of a 11 pattern (out = (state == GOT1) & in). (a) Convert it to a Moore machine: draw the states (including the extra one), give the next-state transitions, and write the output equation as f(state) only. (b) State on which cycle each machine's output fires relative to the second 1, and why they differ by exactly one cycle. (c) Now convert the Moore machine back to Mealy without changing the detected sequence, and identify the one state the Mealy version does not need and why.

Exercise 2 — Pick the machine for the consumer

For each consumer, choose Moore, pure Mealy, or registered-Mealy and justify in one line: (a) a synchronous block that samples the strobe on the clock edge and does not care about one cycle of latency; (b) a same-cycle combinational grant that must be valid the cycle the request is seen; (c) a rising-edge counter that increments on the strobe; (d) an output that leaves the block onto a fast, timing-critical path but whose consumer can accept a cycle of latency. For each, name the specific failure the wrong choice would cause.

Exercise 3 — Explain the glitch and its blast radius

A Mealy FSM's combinational output feeds two consumers: (i) a downstream flop's D input, sampled at the clock edge, and (ii) a counter clocked by the rising edge of that same output. On a certain input transition the output glitches high-and-back mid-cycle. State, for each consumer, whether it is affected and precisely why. Then give the single RTL change that protects consumer (ii) without changing consumer (i)'s behaviour, and explain why that change is not free (what it costs).

Exercise 4 — Trace the latency and the extra state

Feed the stream in = 0 1 1 1 0 into both the two-state Mealy and the three-state Moore detector for 11. For each machine, write out the state each cycle and the output each cycle, and mark the cycle(s) on which out is high. Explain why the Mealy output stays high across the run of 1s while the Moore output is high a cycle later, and why the Moore machine's extra GOT2 state is what makes its output glitch-free and registered.

Continue in RTL Design Patterns (forward FSM links unlock as they ship):

  • FSM Coding Styles (1/2/3-process) — Chapter 4.3; how the state-register / next-state / output split you tap for Moore vs Mealy is organized into one, two, or three processes, and where the output logic lives in each.
  • FSM State Encoding (binary vs one-hot) — Chapter 4.4; how the state you decode for a Moore output is encoded, and why one-hot makes the output tap a single bit test.
  • Safe FSM Design & Latch-Free Outputs — Chapter 4.5; keeping the output logic (Moore or Mealy) latch-free and glitch-safe, and recovering from illegal states.
  • FSM + Datapath — Chapter 4.6; where Mealy outputs earn their keep as same-cycle datapath controls, and where Moore outputs keep control clean.
  • valid/ready Handshake — Chapter 9.1; the canonical place a same-cycle Mealy-style combinational output (and its input-to-output path) shows up, and why it must be registered when it becomes a timing problem.

Backward / in-track dependencies:

  • FSM Fundamentals — Chapter 4.1; the three-piece FSM model (state register + next-state logic + output logic) this page builds on — Moore vs Mealy is entirely about where the output logic taps.
  • Multiplexers (2:1, N:1, one-hot) — Chapter 1.1; the next-state and output logic are muxes over the state, so the mux is the primitive under both machines.
  • The Register Pattern (D-FF) — Chapter 2.1; the state register and the flop that makes a Moore (or registered-Mealy) output glitch-free.
  • Synchronous Reset — Chapter 2.3; the sampled-on-the-edge reset both machines here use to return to idle.
  • Pulse / Strobe Generators — Chapter 3; the one-cycle strobe both detectors emit, and why a clean single-cycle pulse must be registered when it drives edge-sensitive logic.

Verilog v1 prerequisites (the grammar this pattern transcribes into):

  • Case Statements — the case (state) that expresses both the next-state logic and a Moore output decode.
  • If-Else Statements — the conditional the output logic uses to combine state and input for a Mealy output.
  • Blocking and Non-Blocking Assignments — why the state register uses non-blocking (<=) while the combinational next-state and Mealy output logic use blocking (=).

11. Summary

  • Moore vs Mealy is a hardware trade-off, not a definition. Two FSMs can implement the same function — detect the same sequence, raise the same flag — and still differ in when the output appears and how clean it is. The state register and next-state logic are identical; the only difference is where the output logic taps.
  • Moore = output from state only: registered-clean, one cycle late. The output reads the state alone, so it is fed by the registered state flops — glitch-free, times like datapath, no input-to-output path — but it lands the cycle after the deciding input, and the machine often needs one extra state to hang the output on.
  • Mealy = output from state and input: same cycle, combinational. The output reads the live input too, so it responds the same cycle and needs fewer states — but the output is combinational: it can glitch as the input settles, and it puts the input on a straight combinational path to the output that must meet timing.
  • The signature bug is a Mealy glitch caught by an edge. A raw combinational Mealy output driving edge-sensitive logic (a clock, async set/reset, a rising-edge count) latches a mid-cycle glitch as a spurious event — intermittent, traffic-dependent, and invisible in zero-delay RTL (the DebugLab). Its sibling is the input-to-output path failing max-delay timing at speed. Both fixes are the same move.
  • Registered-Mealy is the compromise — and it costs Moore's cycle. Flop the Mealy decision and the output becomes glitch-free and timing-safe (the arc ends at the flop), recovering Moore's cleanliness with fewer states — but the flop adds one cycle of latency back, so it lands when a Moore output would. Choose Moore, pure Mealy, or registered-Mealy by when and how clean the consumer needs the output — built and self-check-verified here in SystemVerilog, Verilog, and VHDL.