1. 状态机基础回顾
各位同学好,我是老李。做数字芯片验证这么多年,我见过太多因为状态机出问题导致芯片翻车的案例。今天咱们先打好基础,聊聊状态机到底是什么玩意儿。
1.1 什么是状态机
状态机,说白了就是一个有记忆的逻辑系统。它能在不同状态之间跳来跳去,每次跳转都取决于当前状态和输入信号。我刚开始学的时候,总觉得这东西很玄乎,后来发现其实就是个有限状态的自动机。
举个例子,你想想看——一个自动售货机。它可能处于“等待投币”、“已投币”、“出货中”这几个状态。你投了币,它就从“等待投币”跳到“已投币”。这不就是状态机吗?
核心要点:状态机 = 当前状态 + 输入条件 → 下一状态 + 输出
1.2 状态机分类:Moore vs Mealy
实际项目中,我们主要用两种状态机。我个人的习惯是,先搞清楚需求再选类型,别一上来就拍脑袋。
| 对比项 | Moore型 | Mealy型 |
|---|---|---|
| 输出依赖 | 仅当前状态 | 当前状态 + 输入 |
| 响应速度 | 慢一拍(时钟沿后变化) | 快(输入变化立即响应) |
| 状态数量 | 通常较多 | 通常较少 |
| 抗毛刺能力 | 强 | 弱(组合逻辑输出) |
Moore型:输出只跟当前状态有关。我在项目中遇到过,做SPI控制器时用了Moore型,调试起来特别省心——因为输出稳定,不会因为输入毛刺乱跳。
Mealy型:输出同时依赖当前状态和输入。说白了就是反应更快,但代价是容易受输入干扰。我曾经在一个高速数据通路里用了Mealy型,结果因为输入信号有毛刺,输出乱了一塌糊涂...嗯,从那以后我学会了加输出寄存器。
我的建议:新手先从Moore型入手。等你对时序有了感觉,再尝试Mealy型。别问我怎么知道的——都是泪。
1.3 状态转移图基础
画状态转移图,是设计状态机最重要的一步。你想想看,代码写错了还能改,但状态图画错了,整个设计方向就偏了。
下面这张图,是我用SVG画的一个简单状态机示例。它描述了一个两状态交替的序列检测器——检测到“101”就输出高电平。
画状态转移图时,我总结了几个要点:
- 状态要完整——别漏了任何可能的状态。我曾经漏了一个“复位后”状态,结果仿真跑了一整天都没发现问题,直到上板测试才暴露。
- 转移条件要互斥——同一个状态下,不能有两个转移条件同时成立。否则状态机就不知道往哪跳了。
- 要有默认转移——每个状态都要考虑“其他情况”去哪。我习惯加一个default转移,回到IDLE状态。
注意:状态转移图不是画完就完事了。一定要做可达性分析——检查每个状态是否都能从初始状态到达,以及是否都能回到初始状态。我曾经吃过这个亏,有个状态进去了就出不来,芯片直接死锁。
1.4 状态编码的选择
状态编码这事,看着简单,其实坑不少。我常用的编码方式有三种:
- 二进制编码:状态数少的时候用,省触发器。但跨时钟域时容易出问题。
- 格雷码:相邻状态只变1位,适合跨时钟域。我在做异步FIFO时必用。
- 独热码:每个状态一个触发器,译码逻辑简单。FPGA项目里我特别喜欢用,因为LUT资源多,触发器也多。
你想想看,如果状态机有16个状态,二进制编码只需要4个触发器,但译码逻辑复杂;独热码要16个触发器,但译码几乎不需要逻辑。怎么选?看你的资源情况。
我的经验:ASIC项目里,如果面积紧张,用二进制编码;FPGA项目里,如果时序紧张,用独热码。没有的好坏,只有合不合适。
好了,状态机基础就聊到这。记住一句话:状态机设计,七分在规划,三分在编码。把状态转移图画清楚了,代码写起来就是水到渠成的事。