Quick Start
- 下载并安装 Vivado 2026.1(或更高版本),确保已添加目标器件库(示例:xc7a35tcsg324-1)。
- 创建一个空工程,选择 RTL 项目,添加本文提供的三个状态机 RTL 文件(one-hot、binary、gray)。
- 为每个状态机模块分别创建顶层 wrapper,例化并连接时钟(100 MHz)、复位(异步高有效)及 8-bit 输出总线。
- 运行综合(synth_1),保持默认策略(Vivado Synthesis Defaults)。
- 综合完成后,打开“Report Utilization”,分别记录每个状态机模块的 LUT、FF、Slice 数量。
- 对比三个模块的资源占用,观察 one-hot 编码是否比 binary 消耗更多 LUT,但 FF 更少(或相反)。
- (可选)运行实现(impl_1)并打开“Report Timing Summary”,记录各状态机的 Fmax,验证 one-hot 是否通常获得更高 Fmax。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a35tcsg324-1) | 主流低成本 FPGA,资源有限,面积差异明显 | Intel Cyclone V / Lattice ECP5(需调整约束) |
| EDA 版本 | Vivado 2026.1 | 2026年Q2最新稳定版,综合策略稳定 | Vivado 2025.x / 2024.x(结果趋势一致) |
| 仿真器 | Vivado Simulator (xsim) | 用于功能验证,确保状态机行为正确 | ModelSim / Questa / VCS |
| 时钟/复位 | 时钟 100 MHz,异步高有效复位 | 标准同步设计,便于对比时序 | 其它频率(50-200 MHz 均可) |
| 接口依赖 | 8-bit 输出总线,无外部输入 | 状态机内部自循环,避免 IO 影响面积 | 可增加输入信号(面积变化趋势不变) |
| 约束文件 | create_clock -period 10.000 [get_ports clk] | 仅时钟约束,无需 I/O 约束 | 可添加 set_false_path 对复位 |
目标与验收标准
- 功能点:三个状态机模块实现相同的 8 状态序列(S0→S1→…→S7→S0),每个状态输出唯一 8-bit 值。
- 面积指标:记录各模块的 LUT 数、FF 数、Slice 数。预期 one-hot 比 binary 多 20%-50% LUT,但 FF 少 30%-50%(因 one-hot 需更多组合逻辑解码,但每个状态只需 1 个 FF)。
- 性能指标:记录各模块的 Fmax(WNS 负值越小越好)。预期 one-hot 比 binary 高 10%-30%(因组合逻辑深度更浅)。
- 验收方式:打开 Vivado 的“Report Utilization”和“Report Timing Summary”,截图保存;仿真波形确认状态跳转正确。
实施步骤
1. 工程结构与 RTL 编写
创建三个独立的 Verilog 模块:fsm_onehot、fsm_binary、fsm_gray。每个模块包含 8 个状态,状态跳转逻辑相同(循环移位),输出为当前状态对应的 8-bit 常量。
// fsm_onehot.v
module fsm_onehot (
input wire clk,
input wire rst_n,
output reg [7:0] out
);
localparam S0 = 8'b0000_0001;
localparam S1 = 8'b0000_0010;
localparam S2 = 8'b0000_0100;
localparam S3 = 8'b0000_1000;
localparam S4 = 8'b0001_0000;
localparam S5 = 8'b0010_0000;
localparam S6 = 8'b0100_0000;
localparam S7 = 8'b1000_0000;
reg [7:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= S0;
else
state <= next_state;
end
always @(*) begin
case (state)
S0: next_state = S1;
S1: next_state = S2;
S2: next_state = S3;
S3: next_state = S4;
S4: next_state = S5;
S5: next_state = S6;
S6: next_state = S7;
S7: next_state = S0;
default: next_state = S0;
endcase
end
always @(*) begin
case (state)
S0: out = 8'h01;
S1: out = 8'h02;
S2: out = 8'h04;
S3: out = 8'h08;
S4: out = 8'h10;
S5: out = 8'h20;
S6: out = 8'h40;
S7: out = 8'h80;
default: out = 8'h00;
endcase
end
endmodule逐行说明(fsm_onehot)
- 第 1-5 行:模块声明,时钟 clk、复位 rst_n(低有效)、8-bit 输出 out。
- 第 7-14 行:使用 localparam 定义 8 个 one-hot 编码状态,每个状态只有 1 bit 为 1,其余为 0。这是 one-hot 的核心特征。
- 第 16 行:声明 state(当前状态)和 next_state(次态)为 8-bit 寄存器。
- 第 18-22 行:时序逻辑,在时钟上升沿或复位下降沿更新 state。复位时赋初值 S0。
- 第 24-33 行:组合逻辑,根据当前 state 计算 next_state。case 语句覆盖所有状态,default 防止锁存。
- 第 35-44 行:组合逻辑,根据当前 state 输出对应数值。输出与状态一一对应,无额外编码。
// fsm_binary.v
module fsm_binary (
input wire clk,
input wire rst_n,
output reg [7:0] out
);
localparam S0 = 3'd0;
localparam S1 = 3'd1;
localparam S2 = 3'd2;
localparam S3 = 3'd3;
localparam S4 = 3'd4;
localparam S5 = 3'd5;
localparam S6 = 3'd6;
localparam S7 = 3'd7;
reg [2:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= S0;
else
state <= next_state;
end
always @(*) begin
case (state)
S0: next_state = S1;
S1: next_state = S2;
S2: next_state = S3;
S3: next_state = S4;
S4: next_state = S5;
S5: next_state = S6;
S6: next_state = S7;
S7: next_state = S0;
default: next_state = S0;
endcase
end
always @(*) begin
case (state)
S0: out = 8'h01;
S1: out = 8'h02;
S2: out = 8'h04;
S3: out = 8'h08;
S4: out = 8'h10;
S5: out = 8'h20;
S6: out = 8'h40;
S7: out = 8'h80;
default: out = 8'h00;
endcase
end
endmodule逐行说明(fsm_binary)
- 第 1-5 行:与 one-hot 相同,模块接口一致。
- 第 7-14 行:使用 3-bit 二进制编码(0-7),状态寄存器宽度仅 3 bit,比 one-hot 的 8 bit 少。
- 第 16 行:state 和 next_state 为 3-bit 寄存器,节省 FF 资源。
- 第 18-22 行:时序逻辑,与 one-hot 相同。
- 第 24-33 行:组合逻辑,case 分支数相同(8 个),但综合工具需将 3-bit 值解码为跳转逻辑,组合逻辑深度通常比 one-hot 深。
- 第 35-44 行:输出逻辑,与 one-hot 相同(输出 8-bit 独热码,仅用于对比,实际应用中输出可能不同)。
// fsm_gray.v
module fsm_gray (
input wire clk,
input wire rst_n,
output reg [7:0] out
);
localparam S0 = 3'b000;
localparam S1 = 3'b001;
localparam S2 = 3'b011;
localparam S3 = 3'b010;
localparam S4 = 3'b110;
localparam S5 = 3'b111;
localparam S6 = 3'b101;
localparam S7 = 3'b100;
reg [2:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= S0;
else
state <= next_state;
end
always @(*) begin
case (state)
S0: next_state = S1;
S1: next_state = S2;
S2: next_state = S3;
S3: next_state = S4;
S4: next_state = S5;
S5: next_state = S6;
S6: next_state = S7;
S7: next_state = S0;
default: next_state = S0;
endcase
end
always @(*) begin
case (state)
S0: out = 8'h01;
S1: out = 8'h02;
S2: out = 8'h04;
S3: out = 8'h08;
S4: out = 8'h10;
S5: out = 8'h20;
S6: out = 8'h40;
S7: out = 8'h80;
default: out = 8'h00;
endcase
end
endmodule逐行说明(fsm_gray)
- 第 1-5 行:模块接口与 one-hot、binary 一致。
- 第 7-14 行:使用格雷码(相邻状态仅 1 bit 变化),宽度仍为 3 bit。
- 第 16 行:3-bit 寄存器,与 binary 相同。
- 第 18-22 行:时序逻辑,复位到 S0(格雷码 000)。
- 第 24-33 行:跳转逻辑,格雷码顺序不是自然二进制顺序,但综合工具会优化为等效逻辑。
- 第 35-44 行:输出逻辑,与 one-hot 相同。
2. 综合与实现
在 Vivado 中分别对三个模块运行综合。注意:为避免工具优化掉未使用的逻辑,确保每个模块的顶层有输出端口连接到顶层端口(例如 out 连接到 LED 或 GPIO)。
3. 面积与性能报告解读
打开“Report Utilization”,关注以下指标:
- Slice LUTs:组合逻辑消耗。
- Slice Registers:触发器消耗。
- Slice:整体逻辑单元。
打开“Report Timing Summary”,记录 WNS(最差负时序裕量),计算 Fmax = 1 / (Tclk – WNS)。
常见坑与排查
- 坑 1:综合工具自动优化编码。Vivado 默认会尝试将状态机重新编码为 one-hot 或 binary。为公平对比,需在综合设置中关闭自动 FSM 编码:
set_property STEPS.SYNTH_DESIGN.ARGS.FSM_EXTRACTION off [get_runs synth_1](或在 GUI 中取消“FSM Extraction”)。 - 坑 2:输出逻辑影响面积。本文输出逻辑相同,但若输出编码不同,面积差异可能被掩盖。建议保持输出逻辑一致。
原理与设计说明
为什么 one-hot 消耗更多 LUT? one-hot 状态机中,每个状态由独立的 FF 表示,跳转逻辑只需检查当前状态是否激活(1 bit 比较),组合逻辑深度浅。但状态数量多时,case 语句的分支数量不变,但每个分支的条件更简单(仅需判断 1 bit)。然而,综合工具会将 one-hot 编码的 case 语句实现为大量 AND-OR 逻辑,导致 LUT 数量增多。例如,8 状态 one-hot 需要 8 个 FF,但次态逻辑需要将 8 个输入(当前状态位)映射到 8 个输出(次态位),每个输出是一个 8 输入 OR 树,消耗较多 LUT。
为什么 binary 消耗更少 LUT? binary 编码仅需 log2(N) 个 FF,次态逻辑只需对 3-bit 值进行加 1 运算(或 case 解码),组合逻辑可被综合工具优化为简单的加法器或查找表,LUT 数量较少。但加法器链可能较长,导致 Fmax 降低。
格雷码的权衡:格雷码在跨时钟域场景中优势明显(相邻状态仅 1 bit 变化,降低亚稳态概率),但在单时钟域中,其面积和 Fmax 介于 binary 和 one-hot 之间。格雷码的跳转逻辑需要将 3-bit 值映射到下一个格雷码,综合工具通常实现为 ROM 或 LUT 树,面积略大于 binary。
关键 trade-off 总结:
| 编码风格 | FF 数 | LUT 数 | Fmax | 适用场景 |
|---|---|---|---|---|
| One-hot | N | 高 | 高 | 高速、大状态数(>16) |
| Binary | log2(N) | 低 | 中 | 资源受限、状态数少 |
| Gray | log2(N) | 中 | 中 | 跨时钟域、异步 FIFO |
验证与结果
以下数据基于 Vivado 2026.1,器件 xc7a35tcsg324-1,关闭 FSM 自动提取,综合策略默认。结果以实际工程为准,此处为示例值。
| 模块 | Slice LUTs | Slice Registers | Slice | Fmax (MHz) |
|---|---|---|---|---|
| fsm_onehot | 32 | 8 | 12 | 450 |
| fsm_binary | 18 | 3 | 7 | 380 |
| fsm_gray | 22 | 3 | 9 | 410 |
测量条件:时钟约束 10 ns(100 MHz),实际 Fmax 通过 WNS 换算。仿真验证所有模块在 100 MHz 下功能正确,状态跳转顺序一致。
故障排查(Troubleshooting)
- 现象:综合后面积差异很小(<10%)。
原因:Vivado 自动 FSM 提取未关闭,工具将三种编码统一优化为同一种实现。
解决:按照“常见坑与排查”中的方法关闭 FSM 自动提取。
- 现象:综合后 Fmax 差异不明显。
原因:时钟频率较低(如 100 MHz),未达到状态机的极限频率。可尝试提高时钟频率(如 500 MHz)以暴露时序差异。
- 现象:仿真时状态跳转错误。
原因:复位极性或状态编码定义错误。检查 rst_n 是否为低有效,以及 localparam 值是否与预期一致。
扩展与进阶
本指南仅针对 8 状态简单状态机。实际应用中,状态数可能达到数十甚至上百,此时 one-hot 的面积劣势会更加明显(FF 数线性增长),但 Fmax 优势也更突出。建议在项目初期根据状态数和时序要求进行预评估,选择最合适的编码风格。此外,现代综合工具(如 Vivado)支持自动 FSM 编码选择,但理解底层原理有助于在关键路径上手动干预。
参考
- Vivado Design Suite User Guide: Synthesis (UG901)
- Xilinx WP231: HDL Coding Practices to Accelerate Design Performance
- Clifford E. Cummings, “State Machine Coding Styles for Synthesis”, SNUG 2002
附录:完整 RTL 代码
本文所有 RTL 代码已在实施步骤中完整给出,可直接复制使用。如需顶层 wrapper 示例,请参考以下代码:
module top_onehot (
input wire clk,
input wire rst_n,
output wire [7:0] led
);
fsm_onehot u_fsm (
.clk(clk),
.rst_n(rst_n),
.out(led)
);
endmodule类似地,可创建 top_binary 和 top_gray 模块。

评论 0