一、状态机基础:从概念到编码
状态机这东西,说白了就是数字电路里的「大脑」。我刚开始做验证那会儿,总觉得状态机很简单——不就是几个状态跳来跳去吗?后来踩的坑多了才明白,状态机设计得好不好,直接决定了芯片能不能稳定工作。
今天咱们就把状态机的基础掰开揉碎讲清楚。你想想看,一个复杂芯片里可能有几十个状态机在同时跑,任何一个出问题,整个系统就乱套了。
1.1 Moore机与Mealy机:两种基本模型
先说说最核心的分类。状态机分两种:Moore型和Mealy型。区别在哪?我直接给你结论:输出依赖什么。
Moore机:输出只取决于当前状态。
Mealy机:输出取决于当前状态 + 输入信号。
举个例子你就明白了。假设我们要设计一个「检测连续两个1」的电路:
- Moore机实现:需要3个状态(S0/S1/S2),每个状态对应固定输出。检测到连续两个1后,进入S2状态,输出1。
- Mealy机实现:只需要2个状态。在S1状态时,如果输入为1,立即输出1。
我个人习惯:控制逻辑用Moore机,数据通路用Mealy机。为什么?Moore机输出稳定,没有毛刺;Mealy机响应快,节省状态数。我在项目中遇到过用Mealy机做地址译码,结果因为组合逻辑路径太长,时序差点没收敛——后来改成Moore机就稳了。
| 对比项 | Moore机 | Mealy机 |
|---|---|---|
| 输出依赖 | 仅当前状态 | 当前状态 + 输入 |
| 状态数量 | 通常较多 | 通常较少 |
| 输出时序 | 同步,无毛刺 | 异步,可能有毛刺 |
| 响应速度 | 慢一拍 | 立即响应 |
| 典型应用 | 控制逻辑、协议解析 | 数据通路、快速判断 |
我的经验:新手先学Moore机,逻辑清晰好调试。等熟练了再用Mealy机优化面积和延迟。
1.2 状态转移图:画清楚再写代码
状态转移图,就是状态机的「地图」。我见过太多人上来就写Verilog,结果状态跳飞了都不知道。
画图时记住三个要素:圆圈(状态)、箭头(转移)、标签(条件/输出)。
下面这张图展示了一个简单的4状态Moore机,用于检测序列"101":
避坑指南:我曾经画状态图时漏了一个转移条件,结果仿真时状态机卡死在某个状态出不来。后来我养成了一个习惯——每个状态必须画全所有输入组合的转移,哪怕转移到自身也要画出来。
1.3 状态编码:二进制、格雷码、独热码
状态编码,就是给每个状态分配一个二进制编号。选哪种编码?这问题我当年纠结了很久。直接上对比:
| 编码方式 | 原理 | 状态数N所需触发器 | 优点 | 缺点 | 典型场景 |
|---|---|---|---|---|---|
| 二进制 | 自然二进制顺序 | log₂(N) | 最省触发器 | 组合逻辑复杂,可能有毛刺 | 状态数多(>16) |
| 格雷码 | 相邻状态仅1位变化 | log₂(N) | 减少毛刺,低功耗 | 译码逻辑稍复杂 | 跨时钟域、低功耗设计 |
| 独热码 | 每个状态1位为1 | N | 译码最快,无毛刺 | 最费触发器 | 状态数少(<8) |
我个人的选择原则很简单:
- 状态数 ≤ 8:用独热码。译码逻辑简单,时序好做。
- 状态数 8~32:用格雷码。特别是状态按顺序跳转时,格雷码能减少50%以上的翻转功耗。
- 状态数 > 32:用二进制码。省面积,但要注意组合逻辑的时序。
重点:独热码虽然费触发器,但译码只需要一个AND门。在FPGA中,LUT资源丰富,独热码往往能跑出更高的频率。
看个代码例子就清楚了。下面是一个4状态Moore机的三种编码实现:
// 二进制编码
parameter [1:0] S0 = 2'b00, S1 = 2'b01, S2 = 2'b10, S3 = 2'b11;
// 格雷码编码
parameter [1:0] S0 = 2'b00, S1 = 2'b01, S2 = 2'b11, S3 = 2'b10;
// 独热码编码
parameter [3:0] S0 = 4'b0001, S1 = 4'b0010, S2 = 4'b0100, S3 = 4'b1000;
// 状态机主体(以独热码为例)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= S0;
else
case (state)
S0: if (in) state <= S1;
S1: if (in) state <= S2; else state <= S0;
S2: state <= S3;
S3: state <= S0;
default: state <= S0;
endcase
end
// 输出逻辑(Moore型)
assign out = (state == S3);
我的习惯:写状态机时,一定要加default分支。我曾经吃过这个亏——仿真时状态机跑到了非法状态,因为没有default,整个逻辑就卡死了。加个default回到初始状态,这叫「安全状态机」。
1.4 状态编码的避坑指南
最后分享几个实战中容易踩的坑:
- 不要用枚举类型直接综合:有些工具不支持,老老实实写parameter。
- 独热码要检查「无状态有效」和「多状态有效」:仿真时加assertion,确保任何时候只有一个状态位为1。
- 格雷码的相邻性只在顺序跳转时有效:如果状态跳转不是顺序的,格雷码的优势就没了。
- 状态编码改变后,验证环境要重新检查覆盖率:特别是状态转移覆盖率,编码变了,覆盖点也要跟着变。
我曾经踩过的一个坑:用独热码做了个16状态的状态机,结果综合后面积爆炸。后来一查,独热码需要16个触发器,而二进制只需要4个。对于16个状态,独热码的触发器数量是二进制的4倍。所以记住:状态数超过8,慎用独热码。
好了,状态机的基础就讲到这里。记住三个核心:选对模型(Moore/Mealy)、画全转移图、选好编码方式。下一章咱们聊聊状态机的仿真验证方法,包括如何写testbench、如何检查覆盖率——这些才是实战中的硬功夫。
可可精品资料库