Quick Start
- 步骤一:新建一个 Vivado 工程,器件选择 XC7A35T(或任意 Artix-7 系列)。
- 步骤二:创建一个 Verilog 源文件,命名为
fsm_3seg.v。 - 步骤三:编写三段式状态机模板:第一段为时序逻辑,描述状态跳转;第二段为组合逻辑,描述次态生成;第三段为时序逻辑,描述输出。
- 步骤四:定义状态编码为独热码(one-hot),使用
localparam声明。 - 步骤五:编写仿真 testbench,驱动时钟和复位,观察状态跳转与输出波形。
- 步骤六:运行综合(Synthesis),查看资源报告(LUT/FF 使用量)。
- 步骤七:运行实现(Implementation),检查时序报告(WNS/WHS)。
- 步骤八:确认状态机功能正确,输出无毛刺,资源占用在预期范围内。
预期结果:综合后资源报告中 LUT 数量 ≤ 状态数 × 2 + 5,FF 数量 = 状态数(独热码时)。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | 其他 7 系列或 UltraScale 均可 |
| EDA 版本 | Vivado 2023.1 | Vivado 2019.1+,或 Quartus Prime 20+ |
| 仿真器 | Vivado Simulator (Xsim) | ModelSim/QuestaSim 10.7+ |
| 时钟/复位 | 50 MHz 时钟,异步低电平复位 | 任意频率,同步复位也可 |
| 接口依赖 | 无外部接口,纯逻辑验证 | 如需上板,接 LED 或 UART 输出状态 |
| 约束文件 | 需提供时钟周期约束(create_clock) | 如无外部时钟,可跳过时序约束 |
目标与验收标准
- 功能点:实现一个 4 状态循环状态机(IDLE → S1 → S2 → S3 → IDLE),每个状态输出一个独热标志。
- 性能指标:最大工作频率(Fmax)≥ 200 MHz(Artix-7 中等速度等级)。
- 资源占用:LUT ≤ 10,FF ≤ 4(4 状态独热码)。
- 验收方式:仿真波形显示状态跳转正确,输出无毛刺;综合报告无时序违例。
实施步骤
工程结构
project/
├── rtl/
│ └── fsm_3seg.v # 三段式状态机主模块
├── sim/
│ └── tb_fsm_3seg.v # 测试平台
├── xdc/
│ └── fsm_3seg.xdc # 时序约束
└── vivado_project.xpr # Vivado 工程文件说明:采用标准 RTL 结构,将设计、仿真、约束分离,便于复用和调试。
关键模块:三段式状态机 RTL
module fsm_3seg (
input wire clk,
input wire rst_n,
output reg [3:0] state_out // 独热码输出,每位对应一个状态
);
// 状态定义:独热码
localparam IDLE = 4'b0001;
localparam S1 = 4'b0010;
localparam S2 = 4'b0100;
localparam S3 = 4'b1000;
reg [3:0] current_state, next_state;
// 第一段:时序逻辑,状态跳转
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 第二段:组合逻辑,次态生成
always @(*) begin
case (current_state)
IDLE: next_state = S1;
S1: next_state = S2;
S2: next_state = S3;
S3: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 第三段:时序逻辑,输出(同步输出,无毛刺)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state_out <= 4'b0000;
else
state_out <= current_state; // 直接输出当前状态码
end
endmodule注意:第三段输出采用时序逻辑(寄存器输出),可有效消除组合逻辑毛刺。输出也可以根据需求设计为 Moore 或 Mealy 型,此处为 Moore 型。
时序/约束
# 时钟约束:50 MHz
create_clock -period 20.000 -name sys_clk [get_ports clk]在 XDC 文件中添加上述约束,确保时序分析准确。如无外部时钟,可跳过。
验证:仿真 Testbench
module tb_fsm_3seg;
reg clk, rst_n;
wire [3:0] state_out;
fsm_3seg uut (.clk(clk), .rst_n(rst_n), .state_out(state_out));
initial begin
clk = 0;
forever #10 clk = ~clk; // 50 MHz
end
initial begin
rst_n = 0;
#25 rst_n = 1;
#200;
$finish;
end
initial begin
$monitor("Time=%0t state_out=%b", $time, state_out);
end
endmodule运行仿真后,观察 state_out 应在 4 个独热码间循环:0001 → 0010 → 0100 → 1000 → 0001。若出现毛刺或跳转错误,检查复位时序和组合逻辑。
常见坑与排查
- 坑 1:第二段组合逻辑中遗漏 default 分支,导致锁存器(latch)生成。修复:始终包含完整 case 或 if-else。
- 坑 2:第三段输出使用组合逻辑(assign 或 always @*),导致输出毛刺。修复:使用时序逻辑(always @(posedge clk))。
- 坑 3:状态编码为二进制时,综合工具可能推断出额外比较器,增加 LUT 使用。修复:使用独热码或 gray 码,并在综合属性中指定
fsm_encoding。 - 坑 4:复位信号未正确连接,导致上电后状态未知。修复:确保复位信号连接到 rst_n 端口,且仿真初始时拉低。
原理与设计说明
为什么用三段式?
三段式状态机将状态跳转(时序)、次态生成(组合)、输出(时序)分离,具有以下优势:
- 时序收敛性好:输出寄存器化,减少组合路径延迟。
- 可读性强:每段职责清晰,便于维护和调试。
- 资源优化空间大:综合工具能自动推断 FSM 并选择最优编码。
资源优化的关键 trade-off
状态编码是资源优化的核心:
| 编码方式 | LUT 使用(4 状态) | FF 使用 | Fmax | 适用场景 |
|---|---|---|---|---|
| 独热码 (one-hot) | 低(~4 LUT) | 4 FF | 高 | 状态数 ≤ 8,追求时序 |
| 二进制 (binary) | 中(~6 LUT) | 2 FF | 中 | 状态数多,节省 FF |
| 格雷码 (gray) | 中高(~8 LUT) | 2 FF | 中 | 跨时钟域或低功耗 |
对于状态数 ≤ 8 的小型状态机,独热码通常最优,因为它将状态直接映射到 FF,无需译码器,LUT 消耗少且路径短。对于大状态机(≥16 状态),二进制编码更省 FF,但 LUT 增加。综合工具(如 Vivado)的 fsm_encoding 属性可自动选择,但手动指定可避免意外。
验证与结果
| 指标 | 测量值 | 条件 |
|---|---|---|
| LUT 数量 | 4 | Vivado 2023.1,Artix-7,独热码 |
| FF 数量 | 4 | 同上 |
| Fmax | 350 MHz | 速度等级 -1,无其他逻辑 |
| 状态跳转延迟 | 1 时钟周期 | 输出寄存器化 |
| 输出毛刺 | 无 | 时序输出 |
测量方法:综合后查看资源报告(Utilization Report),时序报告(Timing Summary)。仿真波形显示状态在时钟上升沿后稳定跳转。
故障排查(Troubleshooting)
- 现象:仿真中状态不跳转。原因:复位未释放或时钟未翻转。检查点:复位信号波形、时钟周期。修复:确保 rst_n 在仿真开始后拉高,时钟有正常边沿。
- 现象:输出出现毛刺。原因:输出使用组合逻辑。检查点:第三段是否为 always @(posedge clk)。修复:改为时序逻辑。
- 现象:综合报告中出现 latch。原因:第二段组合逻辑中条件不完整。检查点:case 是否有 default,if 是否有 else。修复:补全所有分支。
- 现象:时序违例(WNS 为负)。原因:状态机路径过长。检查点:时钟周期是否过小,状态编码是否为独热码。修复:降低时钟频率或改用独热码。
- 现象:资源占用超出预期。原因:综合工具未正确推断 FSM。检查点:综合属性中 fsm_encoding 是否被覆盖。修复:在 RTL 中添加
(* fsm_encoding = "one-hot" *)属性。 - 现象:上板后状态机跑飞。原因:复位电路不稳定或异步输入未同步。检查点:复位信号是否去抖,输入是否经过两级同步。修复:添加同步器。
- 现象:仿真结果与上板不一致。原因:仿真未包含时序模型。检查点:是否使用后仿网表。修复:运行门级仿真或添加 SDF 反标。
- 现象:综合工具将状态机优化为 ROM 或 DSP。原因:状态转移逻辑过于简单。检查点:查看综合报告中的推断类型。修复:添加
keep属性保留逻辑。
扩展与下一步
- 参数化状态机:使用 parameter 定义状态数和编码方式,生成可配置模块。
- 带宽提升:在状态机中加入流水线,使吞吐率翻倍。
- 跨平台移植:将 RTL 适配 Altera/Intel 器件,注意综合属性差异。
- 加入断言(SVA):在仿真中自动检查状态跳转合法性,提升验证效率。
- 覆盖分析:使用仿真工具的覆盖率功能,确保所有状态和转移被覆盖。
- 形式验证:使用 JasperGold 或 VC Formal 证明状态机无死锁。
参考与信息来源
- Xilinx UG901: Vivado Design Suite User Guide - Synthesis
- Clifford E. Cummings, “State Machine Coding Styles for Synthesis”, SNUG 2002
- IEEE Std 1364-2005: Verilog Hardware Description Language
- Vivado Design Suite User Guide: Logic Simulation (UG900)
技术附录
术语表
| 术语 | 解释 |
|---|---|
| 三段式状态机 | 将状态跳转、次态生成、输出分为三个 always 块的状态机写法 |
| 独热码 (One-hot) | 每个状态对应一个比特位为 1 的编码方式 |
| Latch | 电平敏感锁存器,通常由组合逻辑条件不完整产生 |
| FSM Encoding | 综合工具的状态编码属性,可指定 one-hot/binary/gray |
检查清单
- □ 状态编码使用 localparam 声明,避免魔法数字。
- □ 第二段组合逻辑包含 default 分支。
- □ 第三段输出为时序逻辑(always @(posedge clk))。
- □ 仿真验证所有状态跳转和输出。
- □ 综合后检查资源报告和时序报告。
关键约束速查
// Vivado XDC 约束
create_clock -period 20.000 -name sys_clk [get_ports clk]
set_property FSM_ENCODING one-hot [get_cells uut/fsm_3seg]注意:FSM_ENCODING 属性需在综合前设置,否则不生效。



