1. 状态机基础:什么是状态机、状态机的应用场景、Mealy与Moore状态机区别

1.1 什么是状态机?

状态机,说白了就是一种“有记忆”的逻辑电路。

你想想看,组合逻辑只能根据当前输入决定输出。但很多场景下,电路需要记住“之前发生了什么”。比如你按一下按钮,灯亮了;再按一下,灯灭了。这个“亮”和“灭”就是两种状态。电路必须记住当前是亮还是灭,才能决定下一次按按钮时该做什么。

我个人习惯把状态机理解成一个“有限自动机”。它有三个核心要素:

  • 状态:电路当前所处的“模式”
  • 转移条件:什么情况下从一个状态跳到另一个状态
  • 输出:在当前状态下,电路对外产生什么信号

用数学语言说,状态机就是一个五元组:(S, I, O, δ, λ)。其中S是状态集合,I是输入集合,O是输出集合,δ是状态转移函数,λ是输出函数。嗯,这个定义你了解一下就行,实际写代码时我们更关心的是状态图怎么画、代码怎么写。

核心要点:状态机 = 状态寄存器 + 组合逻辑。状态寄存器记住当前状态,组合逻辑决定下一状态和输出。

1.2 状态机的应用场景

我在项目中遇到过太多需要状态机的地方了。可以说,只要你的电路需要“按步骤执行”,就离不开状态机。

常见的应用场景包括:

  • 通信协议解析:比如I2C、SPI、UART的收发控制。每个协议都有固定的时序,状态机可以清晰地描述每个时钟周期该做什么。
  • 数据通路控制:比如FIFO的读写控制、DMA传输的调度。我记得有一次做高速数据采集卡,就是用状态机来控制ADC采样、数据缓存和PCIe传输的流水线。
  • 按键消抖与检测:按键按下、弹起、长按、短按,这些都可以用状态机优雅地实现。
  • 序列检测器:比如检测输入数据流中是否出现“1011”这个序列。这是状态机的经典教学案例,但实际中也很常用。
  • 复杂控制逻辑:比如DDR内存的初始化流程、电源管理芯片的上电时序控制。

你想想看,如果没有状态机,这些逻辑用一堆if-else硬写,代码会乱成一锅粥。状态机最大的好处就是让逻辑变得清晰、可维护、可调试。

我的经验:写状态机之前,一定要先画状态转移图。哪怕是在白板上画个草图,也比直接写代码强。我见过太多人上来就写代码,结果状态漏了、转移条件错了,debug到崩溃。

1.3 Mealy与Moore状态机区别

这是状态机里最基础、也最容易混淆的概念。我刚开始学的时候也搞混过,后来用了一个简单的方法记住:看输出跟谁有关

Moore状态机

Moore机的输出只取决于当前状态。也就是说,只要状态不变,输出就不会变。输入信号只影响状态的转移,不影响输出。

举个例子:一个简单的计数器。状态是计数值,输出就是当前计数值。不管输入是什么,只要状态是5,输出就是5。

// Moore型状态机示例
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state <= IDLE;
    else
        state <= next_state;
end

always @(*) begin
    case (state)
        IDLE:   next_state = (start) ? RUN : IDLE;
        RUN:    next_state = (done)  ? IDLE : RUN;
        default: next_state = IDLE;
    endcase
end

// 输出只与当前状态有关
assign out = (state == RUN) ? 1'b1 : 1'b0;

Mealy状态机

Mealy机的输出取决于当前状态和当前输入。同样的状态,输入不同,输出可能不同。

还是那个例子:如果我们要做一个“当计数器在运行状态且计数到10时输出一个脉冲”,用Mealy机就很简单——输出 = (state==RUN && count==10)。

// Mealy型状态机示例
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state <= IDLE;
    else
        state <= next_state;
end

always @(*) begin
    case (state)
        IDLE:   next_state = (start) ? RUN : IDLE;
        RUN:    next_state = (done)  ? IDLE : RUN;
        default: next_state = IDLE;
    endcase
end

// 输出与当前状态和输入都有关
assign out = (state == RUN && count == 10) ? 1'b1 : 1'b0;

两者的对比

对比项 Moore状态机 Mealy状态机
输出依赖 仅当前状态 当前状态 + 当前输入
输出时序 比状态变化晚一个时钟周期(稳定) 输入变化后立即变化(可能产生毛刺)
状态数量 通常较多 通常较少
响应速度 慢一拍 快(组合逻辑直接输出)
抗干扰能力 强(输出经过寄存器) 弱(组合输出易受毛刺影响)

避坑指南:我曾经在一个高速接口项目里用了Mealy机,结果输出毛刺导致后级电路误触发。后来我加了一级寄存器打拍,才把问题解决。所以我的建议是:如果对时序要求严格,优先用Moore机;如果非要用Mealy机,输出一定要加寄存器同步

怎么选?

我个人习惯这样选:

  • 如果输出需要立即响应输入变化(比如握手信号),用Mealy机
  • 如果输出需要稳定、无毛刺(比如控制使能信号),用Moore机
  • 如果状态数量敏感(比如资源受限),Mealy机通常更省状态
  • 如果代码可读性优先,Moore机更容易理解和调试

嗯,其实很多实际项目中,两种风格会混着用。比如主控用Moore机保证稳定性,某些关键路径用Mealy机提高响应速度。这个没有绝对的对错,关键看你的设计需求。

一句话总结:Moore机输出靠状态,稳定但慢一拍;Mealy机输出靠状态+输入,快但容易出毛刺。选哪个,看你的应用场景。