4、状态表设计:状态转移表、输出表、状态编码
状态表设计,说白了就是把状态机那张抽象的状态图,翻译成硬件能看懂的真值表。我刚开始做FPGA那会儿,总觉得画个状态图就完事了,结果综合出来一堆莫名其妙的latch。后来才明白——状态表才是状态机落地的关键一步。
这一节我们重点聊三张表:状态转移表、输出表,以及状态编码的三种常用方式。这三张表搞定,你的状态机基本就成型了。
4.1 状态转移表
状态转移表,就是把状态图中的每个箭头,一行一行列出来。每一行代表:当前在哪个状态,来了什么输入,下一步跳到哪个状态。
举个例子,一个简单的交通灯控制器,三个状态:RED、GREEN、YELLOW。输入是一个定时器超时信号 timer_done。
| 当前状态 | 输入条件 | 下一状态 |
|---|---|---|
| RED | timer_done == 0 | RED |
| RED | timer_done == 1 | GREEN |
| GREEN | timer_done == 0 | GREEN |
| GREEN | timer_done == 1 | YELLOW |
| YELLOW | timer_done == 0 | YELLOW |
| YELLOW | timer_done == 1 | RED |
嗯,这里要注意:每个状态都必须覆盖所有输入组合。如果某个输入组合没写,综合工具就会推断出latch。我曾经在一个项目里漏掉了一个else分支,结果仿真怎么都对,上板就乱跳。查了两天才发现是状态转移表不完整。
4.2 输出表
输出表分两种:Moore型和Mealy型。Moore型的输出只跟当前状态有关,Mealy型的输出还跟输入有关。
还是拿交通灯举例。Moore型输出表长这样:
| 当前状态 | 输出(红灯亮) | 输出(绿灯亮) | 输出(黄灯亮) |
|---|---|---|---|
| RED | 1 | 0 | 0 |
| GREEN | 0 | 1 | 0 |
| YELLOW | 0 | 0 | 1 |
如果是Mealy型,输出表里还要多一列输入条件。比如一个序列检测器,检测到"101"时输出高电平,输出就依赖于当前状态和当前输入。
4.3 状态编码
状态编码,就是把状态名(比如RED、GREEN)映射成二进制数。编码方式直接影响电路的面积、速度和功耗。常用的有三种:二进制编码、格雷码和独热码。
4.3.1 二进制编码
二进制编码最直观。比如4个状态,用2位二进制数表示:
| 状态 | 二进制编码 |
|---|---|
| S0 | 00 |
| S1 | 01 |
| S2 | 10 |
| S3 | 11 |
优点:用的寄存器最少。n个状态只需要log2(n)个触发器。
缺点:状态跳转时可能有多位同时变化,容易产生毛刺。而且译码逻辑复杂,组合路径长。
我个人习惯:状态数少于8个时,优先用二进制编码。寄存器省,面积小。
4.3.2 格雷码
格雷码的特点是:相邻状态之间只有1位不同。比如:
| 状态 | 格雷码 |
|---|---|
| S0 | 00 |
| S1 | 01 |
| S2 | 11 |
| S3 | 10 |
你看,S1到S2,只有第二位从0变1,第一位不变。这样在状态连续跳转时,毛刺风险大大降低。
格雷码特别适合顺序执行的状态机,比如指令流水线、计数器型控制器。我在一个高速ADC的接口控制器里用过格雷码,状态跳转频率很高,用格雷码后时序收敛轻松了不少。
4.3.3 独热码
独热码,每个状态对应一个独立的寄存器位。n个状态需要n个触发器,但任何时候只有1位为1。
| 状态 | 独热码 |
|---|---|
| S0 | 0001 |
| S1 | 0010 |
| S2 | 0100 |
| S3 | 1000 |
优点:译码逻辑极其简单,速度最快。每个状态只需要一个与门就能判断。
缺点:寄存器用量大。状态数一多,面积就上去了。
我记得在FPGA上做过一个状态机,状态数有16个。用二进制编码时序跑不到200MHz,换成独热码直接飙到350MHz。FPGA的寄存器资源相对丰富,独热码在FPGA上特别吃香。
4.4 三种编码对比
| 特性 | 二进制编码 | 格雷码 | 独热码 |
|---|---|---|---|
| 寄存器用量 | 最少(log2(n)) | 最少(log2(n)) | 最多(n) |
| 组合逻辑复杂度 | 高 | 中 | 低 |
| 速度 | 慢 | 中 | 快 |
| 毛刺风险 | 高 | 低(顺序跳转) | 低 |
| 适用场景 | 状态少、面积敏感 | 顺序跳转、低功耗 | FPGA、高速设计 |
4.5 状态编码选择策略
你可能会问:到底选哪种?我的经验是:
- 状态数 ≤ 4:二进制编码,省寄存器。
- 状态数 5~16:看跳转方式。顺序跳转多选格雷码,随机跳转多选独热码。
- 状态数 > 16:优先二进制编码,或者考虑分层状态机。
- FPGA平台:独热码优先,因为FPGA寄存器多,LUT资源相对紧张。
- ASIC平台:二进制编码优先,因为ASIC的寄存器成本高。
4.6 本章知识体系
下面这张图把状态表设计的核心脉络串起来了:
状态表设计是状态机从"想法"到"电路"的桥梁。状态转移表定义行为,输出表定义结果,状态编码决定硬件实现方式。这三张表搞清楚了,写RTL代码就是体力活了。