1. 状态机基础:概念、分类与绘制规范

大家好,我是你们的数字IC设计讲师。今天咱们聊聊状态机——这个在数字电路里几乎无处不在的东西。

说实话,我刚开始做设计那会儿,觉得状态机就是个“高级点的if-else”。后来踩了不少坑才明白,这东西要是没搞透,后面写RTL代码就是给自己挖坑。嗯,咱们今天就把基础打扎实。

1.1 什么是状态机?

状态机,全称有限状态机(FSM)。说白了,就是一个系统在有限个状态之间来回跳转,根据当前状态和输入决定下一步去哪。

你想想看,生活中的自动售货机就是典型例子:

  • 等待投币 → 收到硬币 → 选择商品 → 出货 → 回到等待

每个环节就是一个状态,投币、按键就是输入信号。

核心三要素:

  • 状态寄存器:记住当前在哪个状态
  • 次态逻辑:根据输入和当前状态,算出下一步去哪
  • 输出逻辑:根据当前状态(或加上输入),产生输出信号

我在项目中遇到过一位同事,把状态机写成了“状态大杂烩”——所有逻辑揉在一个always块里。结果仿真没问题,综合后时序一塌糊涂。所以啊,结构清晰比什么都重要。

1.2 Mealy 与 Moore 状态机

这两个概念,很多教材讲得云里雾里。我换个说法:

对比项 Moore 状态机 Mealy 状态机
输出依赖 只依赖当前状态 依赖当前状态 + 当前输入
输出时机 状态变化后立即更新 输入变化时可能立即变化
状态数 通常较多 通常较少
响应速度 慢一拍(一个时钟周期) 快(组合逻辑直接输出)
典型场景 控制逻辑、安全关键系统 高速数据通路、协议解析

我个人习惯:能用Moore就用Moore。为什么?因为输出只跟状态有关,调试的时候看状态寄存器就知道输出是什么,逻辑清晰。Mealy虽然省状态,但输出容易出毛刺,需要额外处理。

我的经验:如果你做的是SPI、I2C这类协议控制器,Mealy更合适——因为输出需要根据输入实时变化。但如果是简单的流水灯、按键消抖,Moore就够用了。

1.3 状态跳转图绘制规范

画状态跳转图,不是随便画个圈连根线就完事了。我见过太多“灵魂画手”画的图,连自己都看不懂。这里说几个硬性规范:

1.3.1 基本元素

  • 圆圈/椭圆:表示一个状态,里面写状态名(如 IDLE、SEND)
  • 箭头:表示状态跳转方向
  • 箭头上的标签:写跳转条件(如 cnt==4'd9)
  • 输出信号:Moore写在状态圈内,Mealy写在箭头旁

1.3.2 绘制规范

  1. 必须有初始状态:用双圈或箭头标注“复位后进入哪个状态”
  2. 每个状态至少一个出口:不能有“死胡同”状态
  3. 条件要互斥且完备:所有可能的情况都要覆盖,不能有“未定义跳转”
  4. 避免状态爆炸:能用计数器解决的,别用状态堆

⚠️ 我曾经踩过的坑:画状态图时漏了一个跳转条件,综合后工具自动补了默认跳转——结果芯片跑起来莫名其妙复位。从那以后,我每个状态都强制加一个 else 分支,哪怕只是跳回自身。

1.4 知识体系框架图

下面这张图,是我自己总结的状态机知识体系。你把它存下来,后面学RTL编码时随时对照:

状态机知识体系 有限状态机 (FSM) 基本概念 Mealy vs Moore 绘制规范 状态寄存器 次态逻辑 输出逻辑 输出只依赖状态 输出依赖状态+输入 初始状态 条件互斥完备 避免状态爆炸 打好基础 → 写出可综合、可维护的RTL代码

1.5 一个简单的例子

咱们用“两段式”按键消抖来感受一下。状态就三个:IDLE、COUNT、DONE。

// 状态定义(用独热码,综合效率高)
localparam IDLE  = 3'b001;
localparam COUNT = 3'b010;
localparam DONE  = 3'b100;

// 状态跳转逻辑
always @(posedge clk or negedge rst_n) begin
  if (!rst_n)
    state <= IDLE;
  else
    case (state)
      IDLE:  if (key_in) state <= COUNT;
      COUNT: if (cnt == 20'd999_999) state <= DONE;
      DONE:  state <= IDLE;
      default: state <= IDLE;
    endcase
end

小提示:写状态机时,default分支一定要有。哪怕你觉得所有情况都覆盖了,综合工具可能会给你“惊喜”。我习惯default都回到初始状态,安全第一。

好了,状态机的基础就这些。概念清楚了,后面咱们才能聊怎么用代码把它漂亮地实现出来。记住:状态图画清楚,RTL编码就成功了一半。


可可精品资料库