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 绘制规范
- 必须有初始状态:用双圈或箭头标注“复位后进入哪个状态”
- 每个状态至少一个出口:不能有“死胡同”状态
- 条件要互斥且完备:所有可能的情况都要覆盖,不能有“未定义跳转”
- 避免状态爆炸:能用计数器解决的,别用状态堆
⚠️ 我曾经踩过的坑:画状态图时漏了一个跳转条件,综合后工具自动补了默认跳转——结果芯片跑起来莫名其妙复位。从那以后,我每个状态都强制加一个 else 分支,哪怕只是跳回自身。
1.4 知识体系框架图
下面这张图,是我自己总结的状态机知识体系。你把它存下来,后面学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编码就成功了一半。