上一篇:FPGA开发入门:深入理解计数器------数字逻辑的时序基石-CSDN博客
目录
[1 引言](#1 引言)
[2 状态机基础:重新理解核心概念](#2 状态机基础:重新理解核心概念)
[3 状态机建模:Moore vs Mealy的深度解析](#3 状态机建模:Moore vs Mealy的深度解析)
[3.1 Moore状态机:沉稳的绅士](#3.1 Moore状态机:沉稳的绅士)
[3.1.1 核心特点:](#3.1.1 核心特点:)
[3.1.2 Verilog实现模式:](#3.1.2 Verilog实现模式:)
[3.2 Mealy状态机:敏捷的运动员](#3.2 Mealy状态机:敏捷的运动员)
[3.2.1 核心特点:](#3.2.1 核心特点:)
[3.2.2 Verilog实现模式:](#3.2.2 Verilog实现模式:)
[3.3 选择指南:什么时候用什么?](#3.3 选择指南:什么时候用什么?)
[3.4 FPGA设计建议:](#3.4 FPGA设计建议:)
[4 状态编码的艺术](#4 状态编码的艺术)
[4.1 顺序二进制编码](#4.1 顺序二进制编码)
[4.2 格雷码编码](#4.2 格雷码编码)
[4.3 One-Hot编码](#4.3 One-Hot编码)
[5 三段式状态机:为什么是黄金标准?](#5 三段式状态机:为什么是黄金标准?)
[6 实战案例:可乐售卖机状态机](#6 实战案例:可乐售卖机状态机)
[6.1 需求规格:](#6.1 需求规格:)
[6.2 状态机实现(Moore风格):](#6.2 状态机实现(Moore风格):)
[6.3 相同功能的Mealy风格实现:](#6.3 相同功能的Mealy风格实现:)
[7 高级技巧与最佳实践](#7 高级技巧与最佳实践)
[7.1 混合使用Moore和Mealy](#7.1 混合使用Moore和Mealy)
[7.2 状态机调试技巧](#7.2 状态机调试技巧)
[7.3 安全状态恢复](#7.3 安全状态恢复)
[8 常见陷阱与解决方案](#8 常见陷阱与解决方案)
[9 总结](#9 总结)
1 引言
在FPGA开发中,状态机是构建复杂控制逻辑的核心架构。然而,很多开发者在理解概念后,实际编写时仍会遇到各种问题。本文将从实战角度,深入解析状态机设计的各个关键环节,包括Moore与Mealy状态机的核心区别。
2 状态机基础:重新理解核心概念
状态机的本质是什么?
状态机实际上是数字电路中的"决策大脑",它通过明确的状态划分和转移规则,将复杂的控制流程分解为可管理的步骤。
状态机三要素的深层理解:
-
状态:不是简单的计数器,而是系统当前所处的"工作模式"
-
转移条件:状态变化的"触发事件",需要明确且无歧义
-
输出:在特定状态下产生的"动作响应"
3 状态机建模:Moore vs Mealy的深度解析
3.1 Moore状态机:沉稳的绅士
// 输出仅与当前状态有关
output = f(current_state)
3.1.1 核心特点:
-
输出完全由当前状态决定,与输入无关
-
输出稳定,时序简单,抗毛刺能力强
-
通常需要更多状态来实现相同功能
-
输出相对于输入变化会滞后一个时钟周期
3.1.2 Verilog实现模式:
// 第三段:输出逻辑 (Moore - 时序逻辑)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
output_val <= 1'b0;
end else begin
case (current_state)
S0: output_val <= 1'b0;
S1: output_val <= 1'b0;
S2: output_val <= 1'b0;
S3: output_val <= 1'b1; // 只要进入S3状态,输出就为1
default: output_val <= 1'b0;
endcase
end
end
3.2 Mealy状态机:敏捷的运动员
// 输出与当前状态和输入都有关
output = f(current_state, input)
3.2.1 核心特点:
-
输出由当前状态和当前输入共同决定
-
响应快速,无需等待时钟沿,理论上比Moore机快一个周期
-
状态数较少,节省寄存器资源
-
输出可能产生毛刺,对输入信号质量要求高
3.2.2 Verilog实现模式:
// 输出逻辑 (Mealy - 组合逻辑)
always @(*) begin
// 默认输出
output_val = 1'b0;
case (current_state)
S2: begin
if (data_input == 1'b1) // 输出取决于状态S2和当前输入
output_val = 1'b1;
end
// 其他状态...
endcase
end
3.3 选择指南:什么时候用什么?
| 场景 | 推荐选择 | 理由 |
|---|---|---|
| 高速响应系统 | Mealy状态机 | 比Moore机快一个时钟周期 |
| 稳定性要求高 | Moore状态机 | 输出无毛刺,时序稳定 |
| 资源受限 | Mealy状态机 | 状态数少,节省触发器 |
| 复杂控制系统 | Moore状态机 | 结构清晰,易于维护调试 |
| 输入信号有噪声 | Moore状态机 | 对输入毛刺不敏感 |
3.4 FPGA设计建议:
在大多数FPGA应用中,推荐优先使用Moore状态机,因为:
-
同步设计更符合FPGA的架构特点
-
输出寄存器化后时序更容易满足
-
代码可读性和可维护性更好
-
现代FPGA中触发器资源相对丰富
4 状态编码的艺术
常用的状态编码方式:
4.1 顺序二进制编码
localparam [2:0] IDLE = 3'b000,
START = 3'b001,
WORK = 3'b010,
DONE = 3'b011;
优点 :资源最省
缺点:多个状态位同时变化可能产生毛刺
4.2 格雷码编码
localparam [2:0] IDLE = 3'b000,
START = 3'b001,
WORK = 3'b011,
DONE = 3'b010;
优点 :状态转换时只有一位变化,功耗低、可靠性高
缺点:编码不够直观
4.3 One-Hot编码
localparam [3:0] IDLE = 4'b0001,
START = 4'b0010,
WORK = 4'b0100,
DONE = 4'b1000;
优点 :译码简单,时序性能好,在FPGA中通常是最佳选择
缺点:需要更多触发器
5 三段式状态机:为什么是黄金标准?
经典三段式模板:
// 第一段:状态寄存器(时序逻辑)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 第二段:下一状态逻辑(组合逻辑)
always @(*) begin
next_state = current_state; // 默认保持当前状态
case (current_state)
IDLE: begin
if (start_signal)
next_state = START;
end
START: begin
if (ready_signal)
next_state = WORK;
else if (timeout)
next_state = IDLE;
end
WORK: begin
if (done_signal)
next_state = DONE;
end
DONE: begin
next_state = IDLE;
end
default:
next_state = IDLE;
endcase
end
// 第三段:输出逻辑(Moore风格 - 时序逻辑)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
output_bus <= 'b0;
control_sig <= 1'b0;
end else begin
// 默认输出值
output_bus <= 'b0;
control_sig <= 1'b0;
case (current_state) // Moore:输出只依赖于当前状态
START: control_sig <= 1'b1;
WORK: begin
output_bus <= work_data;
control_sig <= 1'b1;
end
DONE: output_bus <= result_data;
endcase
end
end
为什么推荐这种结构?
-
时序清晰:状态注册和输出生成都在时钟沿完成
-
避免毛刺:组合逻辑只用于状态转移判断
-
综合友好:工具能够很好地进行时序分析和优化
6 实战案例:可乐售卖机状态机

6.1 需求规格:
-
可乐价格:2元
-
支持投币:1元或2元
-
功能:出货、找零、重置
6.2 状态机实现(Moore风格):
//该程序只做参考
module vending_machine (
input wire clk,
input wire rst_n,
input wire coin_1,
input wire coin_2,
output reg drop_can,
output reg give_change
);
// 状态定义 - 使用One-Hot编码
localparam [2:0] IDLE = 3'b001,
ONE_YUAN = 3'b010,
DELIVER = 3'b100;
reg [2:0] current_state, next_state;
// 第一段:状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
end
end
// 第二段:下一状态逻辑
always @(*) begin
next_state = current_state;
case (current_state)
IDLE: begin
if (coin_2)
next_state = DELIVER;
else if (coin_1)
next_state = ONE_YUAN;
else
next_state = next_state;
end
ONE_YUAN: begin
if (coin_2)
next_state = DELIVER;
else if (coin_1)
next_state = DELIVER;
else
next_state = next_state;
end
DELIVER: begin
next_state = IDLE;
end
default:
next_state = IDLE;
endcase
end
// 第三段:输出逻辑(Moore风格)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
drop_can <= 1'b0;
give_change <= 1'b0;
end else begin
// 默认输出
drop_can <= 1'b0;
give_change <= 1'b0;
case (current_state) // 只依赖于当前状态
DELIVER: begin
drop_can <= 1'b1;
// 在Moore机中,需要通过状态记忆是否需要找零
if (current_state == ONE_YUAN)
give_change <= 1'b1;
else
give_change <= give_change;
end
endcase
end
end
endmodule
6.3 相同功能的Mealy风格实现:
// 第三段:输出逻辑(Mealy风格 - 组合逻辑)
always @(*) begin
drop_can = 1'b0;
give_change = 1'b0;
case (current_state)
DELIVER: begin
drop_can = 1'b1;
give_change = (coin_2 && current_state == ONE_YUAN);
end
endcase
end
7 高级技巧与最佳实践
7.1 混合使用Moore和Mealy
在实际设计中,可以根据需要混合使用:
// 主要输出使用Moore风格(稳定)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
main_output <= 1'b0;
else if (current_state == WORK_STATE)
main_output <= 1'b1;
else
main_output <= 1'b0;
end
// 某些快速响应信号使用Mealy风格
assign fast_response = (current_state == CHECK_STATE) && (input_signal == 1'b1);
7.2 状态机调试技巧
// 添加调试信号
(* mark_debug = "true" *) reg [2:0] dbg_current_state;
(* mark_debug = "true" *) reg [2:0] dbg_next_state;
always @(posedge clk) begin
dbg_current_state <= current_state;
dbg_next_state <= next_state;
end
7.3 安全状态恢复
// 添加看门狗定时器
reg [15:0] state_timer;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_timer <= 0;
end else if (state_timer > 16'd50000) begin
// 状态停留时间过长,强制恢复
next_state <= IDLE;
state_timer <= 0;
end else if (current_state != next_state) begin
state_timer <= 0;
end else begin
state_timer <= state_timer + 1;
end
end
8 常见陷阱与解决方案
陷阱1:Mealy机输出毛刺
解决方案:关键Mealy输出后再加一级寄存器
陷阱2:状态转移条件重叠
解决方案:使用if-else-if结构,确保条件互斥
陷阱3:未定义状态行为
解决方案: always添加default分支,实现安全恢复
陷阱4:时序违例
解决方案:状态转移逻辑不要太复杂,必要时插入流水线
9 总结
状态机设计是FPGA开发者的核心技能。通过掌握以下几点,你能够设计出稳定可靠的状态机:
-
理解模型差异:Moore机稳定可靠,Mealy机快速灵活
-
正确选择模型:FPGA中优先选择Moore状态机
-
遵循三段式:保证代码清晰和时序稳定
-
完整条件覆盖:彻底避免锁存器生成
-
输出寄存器化:消除毛刺,提高可靠性
设计哲学:
-
Moore状态机像沉稳的绅士,一切行动按部就班,可靠稳定
-
Mealy状态机像敏捷的运动员,反应迅速但需要精心调教
在实际FPGA项目中,推荐以Moore状态机为基础,只在确实需要极速响应的特定信号上谨慎使用Mealy方式。记住:优秀的状态机不是功能最复杂的,而是最稳定、最易维护的。