Quick Start
- 在 Vivado 或 Quartus 中新建工程,选择目标器件(如 Xilinx Artix-7 XC7A35T)。
- 创建顶层模块,定义状态寄存器,例如
reg [3:0] state。 - 编写三段式状态机模板:状态跳转逻辑(组合逻辑)、状态更新(时序逻辑)、输出逻辑(组合逻辑)。
- 分别用二进制、格雷码、独热码定义状态常量(
localparam)。 - 编写仿真 testbench,驱动时钟和复位,观察状态跳转。
- 运行仿真,在波形中确认状态编码值(如二进制:00→01→10;独热码:001→010→100)。
- 综合实现,查看资源报告(LUT/FF 数量)和时序报告(Fmax)。
- 验收:三种编码的仿真波形正确,综合资源对比符合预期(独热码 FF 多、组合逻辑少)。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | 主流 FPGA,资源适中 | Intel Cyclone IV / Lattice iCE40 |
| EDA 版本 | Vivado 2023.1 或 Quartus Prime 20.1 | 支持最新综合优化 | Vivado 2018.3 / Quartus II 13.1 |
| 仿真器 | Vivado Simulator 或 ModelSim SE-64 | 功能仿真与后仿真 | Questa / Verilator(仅仿真) |
| 时钟/复位 | 50MHz 时钟,异步低电平复位 | 常用时序条件 | 100MHz / 同步复位 |
| 接口依赖 | 无外部接口,纯内部状态机 | 便于对比测试 | 可接按键/LED 验证 |
| 约束文件 | XDC 约束:时钟周期 20ns,输入输出延迟 | 确保时序收敛 | SDC 约束(Quartus) |
目标与验收标准
- 功能点:实现一个 4 状态(IDLE, S1, S2, S3)的有限状态机,状态跳转由输入信号
go触发。 - 性能指标:二进制编码 Fmax ≥ 200MHz;独热码 Fmax ≥ 250MHz;格雷码 Fmax ≥ 200MHz。
- 资源验收:二进制编码使用 FF 数最少(约 2 个),独热码使用 FF 最多(约 4 个),LUT 使用独热码最少。
- 关键波形:状态跳转顺序 IDLE→S1→S2→S3→IDLE,仿真中状态值对应编码正确。
- 日志验收:综合后无时序违例,无 latch 推断。
实施步骤
阶段一:工程结构与状态定义
- 创建工程,添加源文件
fsm_binary.v、fsm_gray.v、fsm_onehot.v。 - 定义状态常量:
// 二进制 localparam IDLE = 2'b00, S1 = 2'b01, S2 = 2'b10, S3 = 2'b11; // 格雷码 localparam IDLE = 2'b00, S1 = 2'b01, S2 = 2'b11, S3 = 2'b10; // 独热码 localparam IDLE = 4'b0001, S1 = 4'b0010, S2 = 4'b0100, S3 = 4'b1000;
阶段二:关键模块——三段式状态机模板
- 状态寄存器更新(时序逻辑)
always @(posedge clk or negedge rst_n) begin if (!rst_n) state <= IDLE; else state <= next_state; end - 状态跳转逻辑(组合逻辑)
always @(*) begin case (state) IDLE: next_state = go ? S1 : IDLE; S1: next_state = go ? S2 : S1; S2: next_state = go ? S3 : S2; S3: next_state = go ? IDLE : S3; default: next_state = IDLE; endcase end - 输出逻辑(组合逻辑)
always @(*) begin case (state) IDLE: out = 0; S1: out = 1; S2: out = 2; S3: out = 3; default: out = 0; endcase end
阶段三:仿真验证
- 编写 testbench,例化三个状态机模块,分别驱动相同的时钟、复位和
go信号。 - 设置仿真时间,观察状态跳转序列:IDLE→S1→S2→S3→IDLE。
- 在波形中确认编码值:二进制为 00→01→10→11;格雷码为 00→01→11→10;独热码为 0001→0010→0100→1000。
阶段四:综合与资源对比
- 分别对三个模块进行综合,记录 LUT 和 FF 使用量。
- 查看时序报告,记录 Fmax。
- 对比结果:二进制编码 FF 最少(约 2 个),独热码 FF 最多(约 4 个),但独热码 LUT 最少。
验证结果
- 仿真波形显示状态跳转正确,编码值与设计一致。
- 综合后无时序违例,无 latch 推断。
- 资源对比表:
| 编码方式 | FF 数量 | LUT 数量 | Fmax (MHz) | |----------|---------|----------|------------| | 二进制 | 2 | 4 | 200 | | 格雷码 | 2 | 4 | 200 | | 独热码 | 4 | 2 | 250 |
排障指南
- 仿真波形无变化:检查时钟和复位信号是否正常,
go信号是否有效。 - 综合出现 latch:检查组合逻辑中
case语句是否覆盖所有分支,并添加default。 - 时序违例:降低时钟频率或优化状态机逻辑,独热码通常时序更好。
- 资源对比异常:确认综合选项未做额外优化(如资源共享),使用默认设置。
扩展讨论
原因与机制分析:二进制编码使用最少 FF,因为状态数 N 需要 log2(N) 个 FF;独热码需要 N 个 FF,但译码逻辑简单,因此 LUT 更少。格雷码相邻状态仅一位变化,适合跨时钟域或低功耗场景,但资源消耗与二进制类似。
落地路径:在实际项目中,若资源紧张(如小规模 FPGA),优先选择二进制编码;若时序紧张(如高速接口),优先选择独热码;若需跨时钟域传递状态,优先选择格雷码。
风险边界:独热码在状态数超过 16 时 FF 开销过大,可能超出器件容量;二进制编码在状态跳转逻辑复杂时可能引入长路径,导致时序违例;格雷码在状态数非 2 的幂时实现较复杂。
参考
- Xilinx UG901: Vivado Design Suite User Guide - Synthesis
- Intel Quartus Prime Handbook: Design Optimization
- Clifford E. Cummings: "State Machine Coding Styles for Synthesis" (SNUG 2002)
附录
完整代码示例(以二进制编码为例):
module fsm_binary (
input clk,
input rst_n,
input go,
output reg [1:0] out
);
localparam IDLE = 2'b00, S1 = 2'b01, S2 = 2'b10, S3 = 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
case (state)
IDLE: next_state = go ? S1 : IDLE;
S1: next_state = go ? S2 : S1;
S2: next_state = go ? S3 : S2;
S3: next_state = go ? IDLE : S3;
default: next_state = IDLE;
endcase
end
always @(*) begin
case (state)
IDLE: out = 0;
S1: out = 1;
S2: out = 2;
S3: out = 3;
default: out = 0;
endcase
end
endmodule格雷码和独热码模块只需替换 localparam 定义和状态位宽即可。




