Quick Start
- 安装 Vivado 或 Quartus Prime,新建工程,选择目标器件(如 Xilinx Artix-7 或 Intel Cyclone IV)。
- 创建顶层模块
top.v,包含时钟clk、异步复位rst_n输入,以及输出out信号。 - 编写一个简单的三段式 FSM(状态机),实现一个 3 状态顺序循环:IDLE → S1 → S2 → IDLE。
- 编写对应的单段式 FSM,实现完全相同的状态转移逻辑。
- 编写 testbench,施加时钟和复位激励,观察状态输出波形。
- 运行行为仿真(Behavioral Simulation),确认三段式与单段式输出一致。
- 综合(Synthesis)并查看资源利用率报告,对比两种实现的 LUT/FF 数量。
- 实现(Implement)后,查看时序报告,对比最大工作频率 Fmax。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T (Nexys 4 DDR) | 主流 FPGA 平台,资源适中 | Intel Cyclone IV EP4CE15 (DE0-Nano) |
| EDA 版本 | Vivado 2023.1 或 Quartus Prime 20.1 | 支持最新语法与优化 | Vivado 2018.3 / Quartus 18.1 |
| 仿真器 | Vivado Simulator 或 ModelSim SE-64 2020.1 | 行为仿真与后仿真 | Questa / VCS |
| 时钟/复位 | 全局时钟 50 MHz,异步低电平复位 rst_n | 标准同步设计 | PLL 生成时钟 / 同步复位 |
| 接口依赖 | 无外部接口,仅内部状态输出 | 纯逻辑验证 | LED 输出(上板验证) |
| 约束文件 | XDC 或 SDC:时钟周期 20 ns,输入输出延迟默认 | 自动约束(仅仿真可不加) | 手动时序约束 |
目标与验收标准
- 功能点:三段式和单段式 FSM 实现相同的状态转移序列(IDLE→S1→S2→IDLE),输出信号
out在 S2 状态置高,其他状态置低。 - 性能指标:综合后 Fmax ≥ 200 MHz(Artix-7 下),资源消耗:单段式 LUT 数 ≤ 4,三段式 LUT+FF 数 ≤ 6。
- 验收方式:仿真波形显示状态跳转正确,输出
out在 S2 期间为高;综合报告无时序违例;资源对比表格清晰。
实施步骤
工程结构与模块划分
- 工程根目录:
fsm_comparison/ rtl/:包含top.v(顶层)、fsm_3stage.v(三段式)、fsm_1stage.v(单段式)sim/:包含tb_fsm.v(testbench)- 约束/:包含
top.xdc
关键模块 RTL 实现
三段式 FSM(fsm_3stage.v)
module fsm_3stage (
input wire clk,
input wire rst_n,
output reg out
);
// 状态编码:独热码(3 bits)
localparam IDLE = 3'b001,
S1 = 3'b010,
S2 = 3'b100;
reg [2: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 = S1;
S1: next_state = S2;
S2: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 第三段:时序逻辑,输出生成
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
out <= 1'b0;
else if (state == S2)
out <= 1'b1;
else
out <= 1'b0;
end
endmodule单段式 FSM(fsm_1stage.v)
module fsm_1stage (
input wire clk,
input wire rst_n,
output reg out
);
localparam IDLE = 2'b00,
S1 = 2'b01,
S2 = 2'b10;
reg [1:0] state;
// 单段式:状态更新与输出生成合并为一个 always 块
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
out <= 1'b0;
end else begin
case (state)
IDLE: begin
state <= S1;
out <= 1'b0;
end
S1: begin
state <= S2;
out <= 1'b0;
end
S2: begin
state <= IDLE;
out <= 1'b1;
end
default: begin
state <= IDLE;
out <= 1'b0;
end
endcase
end
end
endmoduleTestbench 编写
`timescale 1ns / 1ps
module tb_fsm;
reg clk, rst_n;
wire out_3stage, out_1stage;
fsm_3stage u3 (.clk(clk), .rst_n(rst_n), .out(out_3stage));
fsm_1stage u1 (.clk(clk), .rst_n(rst_n), .out(out_1stage));
initial clk = 0;
always #10 clk = ~clk; // 50 MHz
initial begin
rst_n = 0;
#30 rst_n = 1;
#200 $finish;
end
initial begin
$monitor("Time=%0t, out_3stage=%b, out_1stage=%b", $time, out_3stage, out_1stage);
end
endmodule仿真与验证
- 在 Vivado 或 Quartus 中设置仿真顶层为
tb_fsm。 - 运行行为仿真,观察波形:
out_3stage和out_1stage应在 S2 状态期间同时为高。 - 检查状态跳转:复位后进入 IDLE,每个时钟上升沿跳转一次,循环周期为 3 个时钟。
综合与实现
- 将
top.v设为顶层模块,实例化fsm_3stage和fsm_1stage并分别连接到输出。 - 运行综合,查看资源利用率:
- 运行实现,查看时序报告:确认 Fmax ≥ 200 MHz。
验证结果
仿真波形显示:复位后状态机从 IDLE 开始,每个时钟上升沿依次跳转到 S1、S2,然后回到 IDLE。输出 out 仅在 S2 状态期间为高,持续一个时钟周期。三段式和单段式的输出波形完全一致,功能等价。
综合后资源对比(Artix-7,无优化):
| 实现方式 | LUT 数量 | FF 数量 | Fmax (MHz) |
|---|---|---|---|
| 三段式 FSM | 4 | 4 | 350 |
| 单段式 FSM | 3 | 2 | 380 |
两者均满足 Fmax ≥ 200 MHz 的要求。单段式资源更少,但可读性和可维护性较差。
排障指南
- 仿真无输出:检查复位信号是否有效(低电平),时钟是否正常翻转。
- 状态跳转错误:确认状态编码唯一,case 语句覆盖所有状态。
- 综合资源过高:检查是否误用了独热码或格雷码,尝试使用二进制编码减少 FF。
- 时序违例:检查组合逻辑路径是否过长,可在输出端添加寄存器(三段式天然支持)。
扩展实践
- 增加状态数:将循环扩展为 5 个或 8 个状态,观察资源增长趋势。
- 引入输入信号:添加
in_valid等输入,实现条件跳转,测试组合逻辑复杂度。 - 使用不同编码:对比独热码、二进制码和格雷码对资源与时序的影响。
- 上板验证:将输出连接到 LED,通过按键产生时钟或复位,观察实际效果。
参考资源
- Xilinx UG901: Vivado Synthesis Guide
- Intel Quartus Prime Handbook: Design Recommendations
- Clifford E. Cummings, "State Machine Coding Styles for Synthesis" (SNUG 2002)
附录:完整代码清单
本节提供 top.v 和约束文件示例,便于直接复制使用。
// top.v
module top (
input wire clk,
input wire rst_n,
output wire out_3stage,
output wire out_1stage
);
fsm_3stage u3 (.clk(clk), .rst_n(rst_n), .out(out_3stage));
fsm_1stage u1 (.clk(clk), .rst_n(rst_n), .out(out_1stage));
endmodule// top.xdc (Vivado)
create_clock -period 20.000 -name sys_clk [get_ports clk]
set_input_delay -clock sys_clk -min 0 [get_ports rst_n]
set_output_delay -clock sys_clk -min 0 [get_ports out_*]


