Quick Start
- 在 Vivado 2025.2(或更高版本)中创建新工程,目标器件选择 Xilinx Artix-7 XC7A35T(或等效)。
- 编写三段式 FSM 的 Verilog 代码,包含状态寄存器(current_state, next_state)、状态转移逻辑(组合逻辑)和输出逻辑(组合或时序)。
- 使用
always @(posedge clk or negedge rst_n)定义状态寄存器更新。 - 使用
always @(*)定义下一状态组合逻辑,包含所有状态转移条件。 - 使用
always @(posedge clk or negedge rst_n)或always @(*)定义输出逻辑(推荐时序输出以消除毛刺)。 - 运行综合(Synthesis)并查看综合报告,确认 FSM 被推断为状态机(FSM Encoding: One-Hot, Gray 等)。
- 运行实现(Implementation)并检查时序报告,确保建立时间(Setup)和保持时间(Hold)满足约束。
- 在仿真中验证状态跳转和输出波形,确认功能正确。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | 主流低成本 FPGA,支持 FSM 优化 | Altera Cyclone V, Lattice ECP5 |
| EDA 版本 | Vivado 2025.2 | 支持最新综合策略和 FSM 编码选项 | Vivado 2024.x, Quartus Prime Pro 24.x |
| 仿真器 | Vivado Simulator (xsim) | 内置于 Vivado,支持混合仿真 | ModelSim/QuestaSim, Verilator |
| 时钟/复位 | 系统时钟 50MHz,异步低电平复位 | 典型配置,便于时序约束 | 100MHz 时钟,同步复位 |
| 接口依赖 | 无外部接口,纯逻辑验证 | 仅需顶层端口 clk, rst_n, input, output | AXI4-Stream 接口(如需要) |
| 约束文件 | XDC 文件:create_clock -period 20.000 [get_ports clk] | 定义时钟周期,指导综合/实现 | SDC 文件(Quartus) |
目标与验收标准
- 功能点:实现一个 4 状态 FSM(IDLE, S1, S2, S3),在输入信号 en 有效时依次跳转,输出 out 在 S3 状态拉高。
- 性能指标:在 50MHz 时钟下无时序违例,建立时间裕量 ≥ 0.2ns,保持时间裕量 ≥ 0.1ns。
- 资源指标:使用 FF 数量 ≤ 20,LUT 数量 ≤ 30(对于 4 状态 FSM)。
- 验收方式:仿真波形显示状态跳转正确;综合报告显示 FSM 类型为 One-Hot 或 Gray 编码;实现后时序报告无违例。
实施步骤
工程结构与代码框架
- 创建顶层模块
fsm_top.v,包含端口定义和 FSM 实例化。 - 创建 FSM 模块
fsm_3stage.v,实现三段式结构。 - 创建约束文件
fsm_top.xdc,定义时钟和复位。 - 创建仿真 testbench
fsm_tb.v,驱动时钟、复位和输入 en。
关键模块:三段式 FSM RTL
// fsm_3stage.v
module fsm_3stage (
input wire clk,
input wire rst_n,
input wire en,
output reg out
);
// State encoding
localparam IDLE = 2'b00,
S1 = 2'b01,
S2 = 2'b10,
S3 = 2'b11;
reg [1:0] current_state, next_state;
// 1st stage: State register
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end
// 2nd stage: Next state logic (combinational)
always @(*) begin
case (current_state)
IDLE: next_state = en ? S1 : IDLE;
S1: next_state = en ? S2 : S1;
S2: next_state = en ? S3 : S2;
S3: next_state = en ? IDLE : S3;
default: next_state = IDLE;
endcase
end
// 3rd stage: Output logic (registered output)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
out <= 1'b0;
else begin
case (next_state)
S3: out <= 1'b1;
default: out <= 1'b0;
endcase
end
end
endmodule逐行说明
- 第 1-6 行:模块端口定义,clk 和 rst_n 为全局信号,en 为输入使能,out 为输出。
- 第 8-11 行:使用 localparam 定义状态编码,采用二进制编码(2 位),便于综合工具重编码。
- 第 13 行:声明 current_state 和 next_state 为 reg 类型,宽度 2 位。
- 第 15-19 行:第一段时序逻辑,在时钟上升沿或复位下降沿更新 current_state。复位将状态置为 IDLE。
- 第 21-29 行:第二段组合逻辑,根据 current_state 和 en 计算 next_state。使用 case 语句覆盖所有状态和默认分支。
- 第 31-40 行:第三段时序逻辑,在时钟上升沿更新输出 out。基于 next_state 判断,当 next_state 为 S3 时输出高。这样输出会延迟一个时钟周期,避免组合输出毛刺。
时序与约束
# fsm_top.xdc
create_clock -period 20.000 -name sys_clk [get_ports clk]
set_property PACKAGE_PIN W5 [get_ports clk] ;# 根据板卡调整
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN U7 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]逐行说明
- 第 1 行:创建 50MHz 时钟约束,周期 20ns,命名为 sys_clk,绑定到 clk 端口。
- 第 2 行:将 clk 端口分配到物理引脚 W5(以 Artix-7 Basys3 为例),需根据实际板卡修改。
- 第 3 行:设置 clk 引脚的 I/O 标准为 3.3V LVCMOS。
- 第 4-5 行:类似地,为复位信号 rst_n 分配引脚和 I/O 标准。
验证结果
运行仿真后,波形应显示状态从 IDLE 开始,在 en 有效时依次跳转到 S1、S2、S3,然后回到 IDLE。输出 out 在进入 S3 的下一个时钟周期拉高,持续一个周期。综合报告应显示 FSM 被正确推断(如 Found state machine <fsm_3stage>),编码类型为 One-Hot 或 Gray。实现后时序报告应无建立时间和保持时间违例,裕量满足目标值。
排障指南
- 坑 1:第三段输出逻辑使用 current_state 而非 next_state,导致输出延迟一个状态。排查:检查输出时序,确保基于 next_state。
- 坑 2:组合逻辑中遗漏 default 分支,导致综合出锁存器。排查:综合报告检查 Latch 数量,确保为 0。
- 坑 3:状态编码未使用 parameter/localparam,综合工具可能无法自动优化编码。排查:使用 localparam 定义状态名。
- 坑 4:复位信号未在敏感列表中列出(对于异步复位),导致综合警告。排查:在 always 块敏感列表中加入 negedge rst_n。
扩展与进阶
对于更复杂的 FSM(如 8 状态以上),可尝试以下优化策略:
- 在 Vivado 综合设置中手动指定 FSM 编码类型(如
fsm_encoding = one_hot),观察资源与 Fmax 变化。 - 使用
syn_encoding属性(如(* syn_encoding = "gray" *))强制编码方式。 - 将输出逻辑改为组合逻辑(第三段使用
always @(*)),对比时序和毛刺表现。 - 添加 AXI4-Stream 接口,使 FSM 作为数据通路控制器,验证系统级时序。
原理与设计说明
三段式 FSM 的核心优势在于将状态寄存器、下一状态逻辑和输出逻辑分离,提高代码可读性和可维护性,同时便于综合工具优化。在 2026 年的 Vivado 2025.2 中,综合工具默认启用 FSM 提取(fsm_extraction)和重编码(fsm_encoding)优化。工具会分析状态转移图,自动选择 One-Hot、Gray 或二进制编码以平衡资源与 Fmax。例如,对于 4 状态 FSM,One-Hot 编码使用 4 个 FF,但组合逻辑更简单;二进制编码使用 2 个 FF,但组合逻辑更复杂。工具会根据时序约束自动选择最优编码。
关键 trade-off:
- 资源 vs Fmax:One-Hot 编码通常 Fmax 更高(因为组合逻辑深度浅),但占用更多 FF;二进制编码节省 FF 但可能降低 Fmax。对于小型 FSM(如 4 状态),One-Hot 编码往往在时序上更优。
- 输出逻辑选择:时序输出(基于 next_state 的寄存器输出)可消除毛刺,但引入一个时钟周期的延迟;组合输出(基于 current_state)无额外延迟,但易产生毛刺。在高速设计中,时序输出更可靠。
- 复位策略:异步复位(negedge rst_n)在敏感列表中列出,确保复位立即生效;同步复位可避免亚稳态,但需要时钟有效沿。本设计采用异步复位,符合 Xilinx 推荐做法。
风险边界:
- 若状态数超过 16,One-Hot 编码的 FF 开销可能过大(16 个 FF),此时 Gray 编码更优。
- 组合逻辑中的 default 分支必须覆盖所有未定义状态,否则综合工具可能推断出锁存器,导致功能错误。
- 时序输出虽消除毛刺,但若下游逻辑要求零延迟响应,则需改用组合输出并增加同步器。
参考
- Xilinx UG901 (Vivado Design Suite User Guide: Synthesis)
- Xilinx UG949 (Vivado Design Suite User Guide: Implementation)
- IEEE Std 1364-2001 Verilog HDL
附录
仿真 testbench 示例(fsm_tb.v):
module fsm_tb;
reg clk, rst_n, en;
wire out;
fsm_3stage uut (
.clk(clk),
.rst_n(rst_n),
.en(en),
.out(out)
);
initial begin
clk = 0;
forever #10 clk = ~clk; // 50MHz
end
initial begin
rst_n = 0;
en = 0;
#25 rst_n = 1;
#20 en = 1;
#80 en = 0;
#40 $finish;
end
initial begin
$monitor("Time=%0t, state=%b, out=%b", $time, uut.current_state, out);
end
endmodule运行仿真命令(Vivado):xvlog fsm_3stage.v fsm_tb.v && xelab -debug typical fsm_tb && xsim fsm_tb -R



