Quick Start(快速上手)
下载并安装 Vivado 2024.2(或更高版本),确保包含 Vitis 和 ModelSim/QuestaSim 仿真器。克隆或创建示例工程:一个 4 位计数器 + LED 闪烁,验证工具链可用。编写 Testbench,运行行为仿真,确认波形正确。综合并实现(Synthesis & Implementation),查看资源利用率报告。生成比特流并下载到开发板(如 Xilinx Artix-7),观察 LED 按预期频率闪烁。记录 Fmax(最大时钟频率)和资源占用,作为后续优化基线。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T(典型校招开发板) | Altera Cyclone V 或 Lattice iCE40 |
| EDA 版本 | Vivado 2024.2(含 Vitis) | ISE 14.7(旧器件)或 Quartus Prime 23.2 |
| 仿真器 | QuestaSim 2024.2(Vivado 自带) | ModelSim SE / VCS / Verilator |
| 时钟/复位 | 板载 50 MHz 晶振,按键复位(低有效) | PLL 分频或外部时钟源 |
| 接口依赖 | JTAG(USB 下载线) | SPI Flash 或以太网配置 |
| 约束文件 | XDC(Vivado)或 SDC(Quartus) | UCF(旧版) |
目标与验收标准
- 功能点:实现一个 4 位计数器,在 50 MHz 时钟下以 1 Hz 翻转,驱动 LED 显示二进制值。
- 性能指标:Fmax ≥ 100 MHz(典型值,以实际综合报告为准),资源占用 LUT ≤ 50,FF ≤ 40。
- 验收方式:仿真波形显示计数器递增;上板后 LED 按 1 Hz 闪烁;时序分析无 setup/hold 违例。
实施步骤
阶段一:工程结构与顶层模块
创建 Vivado 工程,添加顶层 RTL 文件 led_counter_top.v,包含时钟、复位、计数器和 LED 输出。
module led_counter_top (
input wire clk, // 50 MHz 板载时钟
input wire rst_n, // 低有效复位
output wire [3:0] led // 4 位 LED 输出
);
reg [25:0] cnt; // 计数寄存器,26 位
reg [3:0] led_reg; // LED 寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 26'd0;
led_reg <= 4'd0;
end else begin
if (cnt == 26'd49_999_999) begin // 50M / 2 = 25M 周期翻转一次
cnt <= 26'd0;
led_reg <= led_reg + 1'b1;
end else begin
cnt <= cnt + 1'b1;
end
end
end
assign led = led_reg;
endmodule逐行说明
- 第 1 行:模块声明,端口列表。
- 第 2-4 行:输入时钟
clk(50 MHz)、复位rst_n(低有效)、输出led(4 位)。 - 第 6 行:内部计数器
cnt,26 位宽,最大计数值 67,108,863,用于分频。 - 第 7 行:LED 寄存器
led_reg,存储当前 LED 值。 - 第 9 行:always 块,敏感列表为
clk上升沿或rst_n下降沿(异步复位)。 - 第 10-11 行:复位时,
cnt和led_reg清零。 - 第 12 行:非复位时,进入计数逻辑。
- 第 13 行:判断
cnt是否等于 49,999,999(即 50,000,000 - 1,因为从 0 开始计数)。 - 第 14-15 行:达到最大值时,
cnt清零,led_reg加 1(相当于每 50M 周期翻转一次)。 - 第 16-17 行:否则
cnt递增。 - 第 20 行:将
led_reg赋值给输出led。
阶段二:时序与约束
创建 XDC 约束文件 led_counter_top.xdc,指定时钟周期和 I/O 引脚。
# 时钟约束:50 MHz,周期 20 ns
create_clock -period 20.000 [get_ports clk]
# 复位信号:异步,低有效
set_property PACKAGE_PIN L16 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property PACKAGE_PIN M18 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN J15 [get_ports {led[0]}]
set_property PACKAGE_PIN J16 [get_ports {led[1]}]
set_property PACKAGE_PIN H15 [get_ports {led[2]}]
set_property PACKAGE_PIN H16 [get_ports {led[3]}]逐行说明
- 第 1 行:注释,说明时钟频率。
- 第 2 行:创建时钟约束,周期 20 ns(对应 50 MHz),作用于
clk端口。 - 第 4 行:设置
clk引脚位置(以 Artix-7 为例,实际需查开发板原理图)。 - 第 5 行:设置
clk的 I/O 标准为 LVCMOS33(3.3V)。 - 第 6-7 行:复位引脚
rst_n的引脚与电平标准。 - 第 8-11 行:4 个 LED 输出引脚分配,使用花括号语法访问总线中的每一位。
阶段三:验证与仿真
编写 Testbench tb_led_counter_top.v,验证计数器行为。
`timescale 1ns / 1ps
module tb_led_counter_top;
reg clk;
reg rst_n;
wire [3:0] led;
led_counter_top uut (
.clk(clk),
.rst_n(rst_n),
.led(led)
);
initial begin
clk = 0;
forever #10 clk = ~clk; // 50 MHz: 周期 20 ns, 每 10 ns 翻转
end
initial begin
rst_n = 0;
#100;
rst_n = 1;
#500_000_000; // 仿真 500 ms
$finish;
end
endmodule逐行说明
- 第 1 行:时间尺度,1 ns 精度,1 ps 分辨率。
- 第 3 行:Testbench 模块声明,无端口。
- 第 5-7 行:声明激励信号
clk、rst_n和观测信号led。 - 第 9-13 行:实例化待测模块(UUT),端口连接。
- 第 15-17 行:initial 块生成时钟,每 10 ns 翻转一次,周期 20 ns。
- 第 19-23 行:复位逻辑,先拉低 100 ns,然后释放,仿真 500 ms 后结束。
验证结果
| 指标 | 实测值(示例) | 测量条件 |
|---|---|---|
| Fmax | 125 MHz | Vivado 2024.2,Artix-7 -1 速度等级,无时序违例 |
| LUT 占用 | 32 | 综合后报告 |
| FF 占用 | 30 | 综合后报告 |
| 仿真波形 | led 每 500 ms 翻转一次 | QuestaSim 仿真 500 ms |
注意:以上数值为典型配置下的示例,实际结果取决于器件型号、约束和综合选项。建议在 Vivado 中运行 report_timing_summary 和 report_utilization 获取精确数据。
故障排查(Troubleshooting)
- 现象:仿真中 led 不变化。→ 原因:复位未释放或时钟未启动。→ 检查:rst_n 是否在 100 ns 后拉高;clk 是否有波形。→ 修复:修正 initial 块中的时序。
- 现象:上板后 LED 常亮或常灭。→ 原因:引脚分配错误或 IOSTANDARD 不匹配。→ 检查:XDC 中的 PACKAGE_PIN 是否与原理图一致。→ 修复:对照原理图修正引脚。
- 现象:综合报错“Clock not found”。→ 原因:XDC 中 get_ports clk 名称与 RTL 不匹配。→ 检查:RTL 中端口名是否与 XDC 一致。→ 修复:统一命名。
- 现象:时序分析有 setup 违例。→ 原因:逻辑路径过长或时钟频率过高。→ 检查:report_timing 查看违例路径。→ 修复:插入流水线或降低时钟频率。
- 现象:仿真结果与上板不一致。→ 原因:仿真未包含时序信息(后仿)。→ 检查:运行后仿(SDF 反标)。→ 修复:在 Vivado 中生成后仿网表并仿真。
- 现象:Vivado 综合时内存不足。→ 原因:工程过大或系统资源不足。→ 检查:任务管理器查看内存使用。→ 修复:关闭其他程序,或增加虚拟内存。
- 现象:下载比特流失败。→ 原因:JTAG 驱动未安装或硬件连接问题。→ 检查:设备管理器是否识别 USB 下载器。→ 修复:重新安装驱动或更换 USB 线。
- 现象:LED 闪烁频率不对。→ 原因:分频系数计算错误。→ 检查:cnt 最大值是否等于 50,000,000 - 1。→ 修复:重新计算并修改代码。
原理与设计说明
为什么使用 26 位计数器?
因为 50 MHz 时钟下,要得到 1 Hz 信号,需要计数 50,000,000 个周期。2^25 = 33,554,432 不够,2^26 = 67,108,863 足够,因此选择 26 位。这是资源与精度的权衡:更宽的计数器消耗更多触发器,但能实现更精确的分频。
异步复位 vs 同步复位
本设计使用异步复位(敏感列表包含 negedge rst_n),因为复位信号通常来自按键,异步复位能立即将电路置为已知状态,避免同步复位可能引入的亚稳态。但异步复位需要保证释放时序满足 recovery/removal 时间,否则可能引起 metastability。在 XDC 中,Vivado 会自动处理异步复位约束。
为什么 LED 以 1 Hz 闪烁?
人眼视觉暂留约为 0.1 秒,低于 10 Hz 的闪烁能被感知。1 Hz 是典型的演示频率,便于观察。实际项目中,分频系数应根据应用需求调整,如 UART 波特率生成需要更精确的分频。
扩展与下一步
- 参数化设计:使用 parameter 定义分频系数和位宽,提高代码复用性。
- 增加 PWM 功能:用计数器生成 PWM 信号,控制 LED 亮度,学习占空比调节。
- 跨时钟域(CDC):引入两个不同频率的时钟,学习同步器设计(双触发器打拍)。
- 加入断言(SVA):在 Testbench 中使用 SystemVerilog 断言检查计数器溢出条件。
- 形式验证:使用 JasperGold 或 VC Formal 验证计数器是否在所有条件下正确复位。
- 上板调试:使用 Vivado ILA(集成逻辑分析仪)捕获内部信号,验证实际波形。
参考与信息来源
- Xilinx UG903: Vivado Design Suite User Guide: Using Constraints
- Xilinx UG949: Vivado Design Suite User Guide: Methodology
- IEEE Std 1364-2005: Verilog Hardware Description Language
- Clifford E. Cummings, “Synthesis and Scripting Techniques for Designing Multi-Asynchronous Clock Designs”, SNUG 2001
- 成电国芯 FPGA 培训内部教材(2025 版)
技术附录
术语表
- Fmax:最大时钟频率,由最差路径的 setup 时间决定。
- LUT:查找表,FPGA 基本逻辑单元。
- FF:触发器,用于存储状态。
- CDC:跨时钟域,处理不同时钟域间的信号同步。
- SVA:SystemVerilog Assertion,用于形式验证和仿真断言。
检查清单
- RTL 代码无语法错误(Vivado 综合通过)。
- Testbench 覆盖正常、复位、边界条件。
- XDC 约束完整(时钟、I/O、时序例外)。
- 时序分析无违例(setup/hold 裕度 ≥ 0)。
- 上板测试结果与仿真一致。
关键约束速查
| 约束类型 | 命令示例 |
|---|---|
| 主时钟 | create_clock -period 20 [get_ports clk] |
| 生成时钟 | create_generated_clock -source [get_ports clk] -divide_by 2 [get_pins pll/clk_out] |
| 输入延迟 | set_input_delay -clock clk 5 [get_ports data_in] |
| 输出延迟 | set_output_delay -clock clk 5 [get_ports data_out] |
| 异步时钟组 | set_clock_groups -asynchronous -group [get_clocks clk1] -group [get_clocks clk2] |




