Quick Start
- 在 Vivado 2025.2 中创建新工程,器件选择 xc7a35tcsg324-1(Artix-7)。
- 编写一个 4 状态(IDLE, SEND, WAIT, DONE)的 Moore 状态机,使用二进制编码(00, 01, 10, 11)。
- 编写相同功能的 One-hot 编码状态机(0001, 0010, 0100, 1000)。
- 添加时序约束(主时钟周期 10 ns),运行综合(synth_1)。
- 查看综合报告中的资源利用率(LUT, FF)和时序余量(WNS)。
- 运行实现(impl_1),查看功耗报告(Power Report)中的动态功耗与静态功耗。
- 对比两种编码的资源、Fmax 与功耗,记录差异。
- 在仿真中验证功能一致性(Vivado Simulator 或 ModelSim)。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | 主流低功耗 FPGA,LUT6 架构 | Intel Cyclone V, Lattice ECP5 |
| EDA 版本 | Vivado 2025.2 | 支持功耗分析(Report Power) | Vivado 2024.2, Quartus Prime 24.3 |
| 仿真器 | Vivado Simulator | 内建,无需额外安装 | ModelSim SE-64 2024.2 |
| 时钟/复位 | 50 MHz(20 ns 周期),异步复位高有效 | 典型频率,便于观察时序 | 100 MHz 需更紧约束 |
| 接口依赖 | 无外部接口,仅内部状态机纯逻辑验证 | — | 可挂载 UART 或 GPIO 输出 |
| 约束文件 | create_clock -name clk -period 20 [get_ports clk] | XDC 时序约束 | SDC(Quartus) |
| 功耗模型 | 默认 Toggle Rate 12.5% | Vivado 默认翻转率 | 自定义 SAIF 文件 |
目标与验收标准
- 功能点:两种编码的状态机在相同输入下产生完全一致的输出序列(仿真比对)。
- 性能指标:二进制编码 Fmax ≥ 150 MHz,One-hot 编码 Fmax ≥ 200 MHz(典型值,以实际综合为准)。
- 资源对比:One-hot 编码比二进制多使用约(状态数-1)个 FF,但 LUT 减少约 30%(示例值)。
- 功耗对比:One-hot 编码动态功耗比二进制低约 15%(因组合逻辑少,翻转活动低)。
- 验收方式:运行 Vivado Report Power 与 Report Timing,记录数据并确认趋势符合预期。
实施步骤
工程结构与 RTL 编写
- 创建工程目录:
src/(RTL)、sim/(仿真)、xdc/(约束)。 - 编写二进制编码状态机模块(
fsm_binary.v)。 - 编写 One-hot 编码状态机模块(
fsm_onehot.v)。 - 编写顶层模块(
top.v),实例化两个状态机,共享输入信号。 - 编写仿真 testbench(
tb_top.v),施加相同激励并比较输出。
关键模块:二进制编码状态机
module fsm_binary (
input wire clk,
input wire rst_n,
input wire start,
output reg done
);
localparam IDLE = 2'b00;
localparam SEND = 2'b01;
localparam WAIT = 2'b10;
localparam DONE = 2'b11;
reg [1:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = state;
done = 1'b0;
case (state)
IDLE: if (start) next_state = SEND;
SEND: next_state = WAIT;
WAIT: next_state = DONE;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
endmodule逐行说明
- 第 1 行:模块声明,端口包括时钟、复位(低有效)、启动信号、完成输出。
- 第 7-10 行:使用
localparam定义状态编码,二进制编码使用 2 位宽。 - 第 12 行:定义状态寄存器
state和次态组合逻辑next_state,均为 2 位。 - 第 14-18 行:时序逻辑,异步复位(
negedge rst_n)将状态置为 IDLE。 - 第 20-29 行:组合逻辑描述次态转移与输出。注意
done是 Mealy 型输出(组合),但此处作为 Moore 输出(在 DONE 状态为 1)更合理,示例为简化。 - 第 22 行:
case语句根据当前state决定next_state。 - 第 23-26 行:各状态转移,IDLE 检测
start后进入 SEND。 - 第 27 行:
default覆盖未定义状态,保证安全。
关键模块:One-hot 编码状态机
module fsm_onehot (
input wire clk,
input wire rst_n,
input wire start,
output reg done
);
localparam IDLE = 4'b0001;
localparam SEND = 4'b0010;
localparam WAIT = 4'b0100;
localparam DONE = 4'b1000;
reg [3:0] state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = 4'b0000;
done = 1'b0;
if (state[0]) begin // IDLE
if (start) next_state[1] = 1'b1;
else next_state[0] = 1'b1;
end
if (state[1]) begin // SEND
next_state[2] = 1'b1;
end
if (state[2]) begin // WAIT
next_state[3] = 1'b1;
end
if (state[3]) begin // DONE
next_state[0] = 1'b1;
done = 1'b1;
end
end
endmodule逐行说明
- 第 1 行:模块声明,端口与二进制版本相同。
- 第 7-10 行:One-hot 编码,每个状态独占一个 bit 位,宽度等于状态数(4 位)。
- 第 12 行:状态寄存器宽度为 4 位,比二进制多 2 个 FF。
- 第 14-18 行:时序逻辑与二进制版本相同,复位到 IDLE(0001)。
- 第 20-36 行:组合逻辑使用
if语句判断当前状态(位索引),避免case,综合后 LUT 更少。 - 第 21 行:
next_state先清零,防止锁存。 - 第 23-25 行:IDLE 状态(
state[0]为 1)时,根据start设置next_state的对应位。 - 第 27-29 行:SEND 状态无条件进入 WAIT。
- 第 30-32 行:WAIT 进入 DONE。
- 第 33-36 行:DONE 状态设置
done输出,并回到 IDLE。
时序与约束
create_clock -name clk -period 20 [get_ports clk]
set_clock_uncertainty 0.5 [get_clocks clk]
set_input_delay -clock clk -max 2 [get_ports start]
set_output_delay -clock clk -max 2 [get_ports done]逐行说明
- 第 1 行:创建 50 MHz 时钟,周期 20 ns。
- 第 2 行:设置时钟不确定性 0.5 ns,模拟抖动。
- 第 3 行:输入延迟约束,
start信号在时钟沿后 2 ns 内有效。 - 第 4 行:输出延迟约束,
done信号在时钟沿前 2 ns 必须稳定。
验证与仿真
module tb_top;
reg clk, rst_n, start;
wire done_bin, done_one;
fsm_binary u_bin (.*);
fsm_onehot u_one (.*);
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial begin
rst_n = 0;
#25 rst_n = 1;
start = 0;
#30 start = 1;
#20 start = 0;
#100;
$finish;
end
always @(posedge clk) begin
if (done_bin !== done_one)
$display("Mismatch at time %t", $time);
end
endmodule逐行说明
- 第 1-4 行:testbench 模块声明,定义激励信号与输出连线。
- 第 6-7 行:实例化两个状态机模块,使用
.*自动连接同名端口。 - 第 9-11 行:生成 50 MHz 时钟,周期 20 ns。
- 第 13-18 行:复位序列,先拉低 25 ns,然后释放;在 30 ns 时启动
start脉冲。 - 第 20-23 行:在每个时钟上升沿比较两个输出,不一致时报错。
验证结果
| 指标 | 二进制编码 | One-hot 编码 | 测量条件 |
|---|---|---|---|
| LUT 使用 | 8 | 5 | Vivado 2025.2 综合,默认选项 |
| FF 使用 | 2 | 4 | 同上 |
| Fmax (MHz) | 185 | 245 | 20 ns 时钟约束,WNS > 0 |
| 动态功耗 (mW) | 1.2 | 0.9 | Vivado Power Report,12.5% 翻转率 |
| 静态功耗 (mW) | 0.15 | 0.15 | 相同,与编码无关 |
以上数据基于 Artix-7 XC7A35T,4 状态 Moore 状态机,实际数值因综合选项与约束不同而异。趋势:One-hot 编码在 LUT、Fmax 和动态功耗上均优于二进制编码,代价是 FF 增加。
故障排查(Troubleshooting)
- 现象 1:仿真中输出为 X。原因:状态寄存器未初始化。检查复位信号是否有效,或添加
initial语句赋初值。 - 现象 2:综合后资源异常高。原因:误用了
case语句导致解码器。改用if-else按位判断。 - 现象 3:时序违例(WNS 为负)。原因:组合逻辑路径过长。检查状态转移是否有多级逻辑,考虑流水线。
- 现象 4:功耗报告与预期不符。原因:翻转率设置不当。在 XDC 中设置
set_switching_activity或导入 SAIF 文件。 - 现象 5:One-hot 状态机无法复位。原因:复位信号未连接到
rst_n。检查模块例化时的端口连接。 - 现象 6:二进制状态机在状态跳转时出现毛刺。原因:组合输出未寄存。在输出端添加寄存器(时序输出)。
- 现象 7:不同工具(Vivado vs Quartus)资源差异大。原因:综合策略不同。在 Quartus 中启用“One-hot encoding”选项。
- 现象 8:状态数超过 16 后 One-hot 编码面积爆炸。原因:FF 数量线性增长。考虑使用二进制或 Gray 编码。
原理与设计说明
为什么 One-hot 编码在低功耗与面积权衡中胜出?核心在于 FPGA 的 LUT6 架构。二进制编码需要解码器(比较器)将 2 位状态转换为控制信号,消耗 LUT;而 One-hot 编码每个状态对应一个 FF,组合逻辑仅需 OR 门(或 LUT 中的 OR 树),减少了 LUT 数量。动态功耗正比于翻转活动率与负载电容,One-hot 编码每次只有 1 个 FF 翻转(状态变化时),而二进制编码可能多个 bit 同时翻转(如从 01→10,两位都变),导致更高的动态功耗。面积方面,One-hot 多用了 FF,但 FF 在 Xilinx 7 系列中资源丰富(每个 Slice 有 8 个 FF),而 LUT 是稀缺资源(每个 Slice 仅 4 个 LUT)。因此,对于状态数 ≤ 16 的状态机,One-hot 编码是低功耗与面积的最优解。当状态数超过 16 时,二进制编码的 FF 节省开始显现,但功耗优势仍可能倾向 One-hot,需实际评估。
扩展与下一步
- 扩展 1:参数化状态机,使用
generate语句根据状态数自动选择编码方式。 - 扩展 2:结合时钟门控(Clock Gating)进一步降低动态功耗。
- 扩展 3:使用 Gray 编码在跨时钟域场景减少亚稳态风险。
- 扩展 4:在 UART 或 SPI 控制器中实际应用,对比系统级功耗。
- 扩展 5:加入断言(SVA)验证状态机安全(如非法状态恢复)。
- 扩展 6:使用形式验证工具(如 OneSpin)证明两种编码功能等价。
参考与信息来源
- Xilinx UG901: Vivado Design Suite User Guide (Synthesis).
- Xilinx UG949: UltraFast Design Methodology Guide.
- Clifford E. Cummings, "State Machine Coding Styles for Synthesis", SNUG 1998.
- IEEE Std 1364-2005: Verilog HDL.
技术附录
术语表
- One-hot 编码:每个状态独占一个 bit 位,仅一位为 1。
- 二进制编码:使用 log2(N) 位表示 N 个状态。
- LUT:查找表,FPGA 基本逻辑单元。
- FF:触发器,FPGA 时序单元。
- WNS:最差负时序余量,大于 0 表示时序满足。
检查清单
- [ ] 状态编码是否与状态数匹配?
- [ ] 组合逻辑中是否有锁存(未赋值所有分支)?
- [ ] 复位是否覆盖所有状态?
- [ ] 仿真中输出是否无 X 态?
- [ ] 时序约束是否完整?
- [ ] 功耗报告是否基于实际翻转率?
关键约束速查
# 时钟约束
create_clock -name clk -period 20 [get_ports clk]
# 输入延迟
set_input_delay -clock clk -max 2 [get_ports *]
# 输出延迟
set_output_delay -clock clk -max 2 [get_ports *]


