Quick Start
- 打开 Vivado 2024.2(或 Quartus Prime Pro 23.4+),创建一个空工程,目标器件选 Xilinx Artix-7 XC7A35T(或 Intel Cyclone 10 GX)。
- 编写一个简单的分频模块:输入时钟 100 MHz,输出 50 MHz 时钟(通过计数器分频)。
- 在约束文件(.xdc 或 .sdc)中,先用
create_clock定义主时钟(主时钟周期 10 ns)。 - 再用
create_generated_clock定义分频时钟,指定源为主时钟,分频系数为 2。 - 运行综合(Synthesis),检查时序报告(Report Timing Summary),确认无未约束路径。
- 运行实现(Implementation),打开时序分析(Report Timing),验证分频时钟域路径满足建立/保持时间。
- 预期结果:主时钟与分频时钟均被正确约束,时序报告显示所有路径覆盖,无违例。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | 主流低功耗 FPGA,支持多时钟域 | Intel Cyclone 10 GX / Lattice ECP5 |
| EDA 版本 | Vivado 2024.2 | 2026 年稳定版,支持最新时序引擎 | Quartus Prime Pro 23.4+ / Synplify 2024.03 |
| 仿真器 | Vivado Simulator 2024.2 | 内建,支持 SDF 反标 | ModelSim SE-64 2024.1 / Questa |
| 时钟/复位 | 板上 100 MHz 差分晶振,全局复位 | 差分时钟需 IBUFDS 原语处理 | 单端 50 MHz 晶振 |
| 接口依赖 | 无外部接口(纯内部分频演示) | 仅用板上 LED 或仿真波形验证 | UART / SPI 接口(如需外设) |
| 约束文件 | 1 个 .xdc 文件 | 包含主时钟与生成时钟定义 | 多文件分层约束(.sdc 格式) |
目标与验收标准
- 功能点:RTL 中分频模块输出正确频率(50 MHz),仿真波形显示周期 20 ns。
- 时序收敛:实现后时序报告无建立时间违例(WNS ≥ 0),保持时间违例(WHS ≥ 0)。
- 资源指标:分频模块占用 ≤ 2 个 slice(Artix-7 示例),Fmax 主时钟 ≥ 200 MHz(示例值,以实际器件为准)。
- 波形验证:仿真中观察 clk_out 上升沿与 clk_in 上升沿对齐(无毛刺)。
- 日志关键行:Vivado 日志中应出现 "All constraints were processed successfully" 且无 "Unconstrained path" 警告。
实施步骤
1. 工程结构与 RTL 设计
- 创建顶层模块
top.v,例化分频器clk_divider。 - 分频器采用计数器实现,避免使用 PLL(以展示
generated_clock手动约束)。 - 输出寄存器用
always_ff(SystemVerilog)或process(VHDL)同步输出。
// clk_divider.sv
module clk_divider (
input logic clk_in,
input logic rst_n,
output logic clk_out
);
logic [3:0] cnt;
always_ff @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
cnt <= 4'd0;
clk_out <= 1'b0;
end else begin
if (cnt == 4'd1) begin
cnt <= 4'd0;
clk_out <= ~clk_out;
end else begin
cnt <= cnt + 1'b1;
end
end
end
endmodule逐行说明
- 第 1 行:定义模块
clk_divider,输入时钟clk_in、异步复位rst_n,输出分频时钟clk_out。 - 第 2 行:声明 4 位计数器
cnt,用于计数到分频边界。 - 第 3–7 行:
always_ff敏感列表为clk_in上升沿和rst_n下降沿(异步复位)。 - 第 8–11 行:复位时清零计数器和输出时钟。
- 第 12–15 行:当
cnt == 1时(即每两个时钟周期翻转一次),计数器归零并翻转clk_out,实现 2 分频。 - 第 16–18 行:否则计数器递增。
2. 约束文件编写(.xdc)
- 创建
top.xdc,先定义主时钟(物理引脚约束需结合板级原理图)。 - 再定义生成时钟,指定源为主时钟,分频系数为 2。
- 注意:生成时钟的
-source必须指向已定义的主时钟名或时钟引脚。
# top.xdc
create_clock -name sys_clk -period 10.000 [get_ports {clk_in_p}]
create_generated_clock -name gen_clk -source [get_ports {clk_in_p}] \
-divide_by 2 [get_pins {clk_divider_inst/clk_out}]逐行说明
- 第 1 行:
create_clock定义主时钟sys_clk,周期 10 ns(100 MHz),绑定到差分输入时钟的正端clk_in_p。 - 第 2–3 行:
create_generated_clock定义生成时钟gen_clk,源为主时钟引脚,分频系数-divide_by 2,输出引脚为分频器模块的clk_out引脚(层次化路径)。
3. 综合与实现
- 在 Vivado 中运行 Synthesis,检查综合日志中约束是否被识别(搜索 "create_clock" 和 "generated_clock")。
- 运行 Implementation,完成后打开 Report Timing Summary,确认所有时钟域均被分析。
- 常见坑:若生成时钟未自动传播到分频器内部路径,需手动添加
set_property CLOCK_DEDICATED_ROUTE FALSE(仅在调试时使用,生产环境应避免)。
4. 仿真验证
- 编写 testbench,激励时钟 100 MHz,复位后等待 100 ns,观察
clk_out波形。 - 验证
clk_out周期为 20 ns,占空比 50%(理想分频)。 - 检查时钟沿对齐:
clk_out上升沿应位于clk_in上升沿之后 0–1 个 LUT 延迟内(仿真中可忽略)。
5. 常见坑与排查
- 坑 1:生成时钟未定义——现象:时序报告显示分频时钟域无约束。检查:确保
create_generated_clock的-source路径正确,且主时钟已定义。修复:使用report_clocks命令列出所有时钟。 - 坑 2:分频系数错误——现象:生成时钟周期不符合预期。检查:
-divide_by或-multiply_by参数。修复:用report_timing -clock gen_clk查看实际周期。 - 坑 3:时钟路径被优化掉——现象:综合后生成时钟引脚不存在。检查:综合选项是否开启
-keep_equivalent_registers。修复:在 RTL 中添加(* keep = "true" *)属性保留寄存器。
原理与设计说明
create_clock 与 create_generated_clock 是时序约束的基石。主时钟定义外部输入时钟的周期与波形;生成时钟则描述由主时钟派生出的内部时钟(如分频、倍频、门控时钟)。正确使用生成时钟能确保时序分析工具自动计算时钟域间的相位关系,避免人为计算偏差。
关键机制:生成时钟的 -source 指定源时钟节点,工具通过追踪该节点的时钟树,自动推导生成时钟的延迟与抖动。若源节点错误(如指向非时钟引脚),工具会报错或忽略约束。
Trade-off:手动约束生成时钟(如本示例)比使用 PLL 更灵活,但需注意分频器输出寄存器可能引入额外延迟,导致跨时钟域路径时序紧张。若使用 PLL,工具自动生成约束,但灵活性降低(如无法实现非整数分频)。
边界条件:当生成时钟源为门控时钟时,需使用 -combinational 选项(Vivado)或 -divide_by 1(Quartus)以保持相位关系。对于多级生成时钟(如级联分频),每级必须单独定义,且源指向上一级生成时钟的引脚。
验证与结果
| 指标 | 示例值 | 测量条件 |
|---|---|---|
| 主时钟 Fmax | 200 MHz(示例) | Vivado 2024.2,Artix-7 -1 速度等级,无其他逻辑 |
| 分频时钟周期 | 20.000 ns | 仿真波形测量,与理论一致 |
| 建立时间余量(主→分频) | 0.123 ns(示例) | Report Timing,最差路径 |
| 保持时间余量(分频→主) | 0.045 ns(示例) | Report Timing,最差路径 |
| 资源占用 | 1 slice + 1 FF | 综合报告,仅分频模块 |
注意:以上数值为示例配置下的典型值,实际结果取决于器件型号、速度等级、布局布线随机性等,应以实际工程报告为准。
故障排查(Troubleshooting)
- 现象:Vivado 报错 "CRITICAL WARNING: [Timing 38-282] The generated clock ... source is invalid"——原因:
-source路径指向非时钟节点或不存在。检查点:用get_pins确认路径正确性。修复:修正路径,确保指向时钟引脚或寄存器输出。 - 现象:时序报告显示 "No timing paths found for clock gen_clk"——原因:生成时钟未被正确传播到下游逻辑。检查点:用
report_clocks查看gen_clk是否列出。修复:在约束中添加set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets gen_clk](仅调试)。 - 现象:仿真中 clk_out 占空比不为 50%——原因:分频逻辑中计数器边界设置错误。检查点:查看 RTL 中翻转条件。修复:确保翻转发生在计数器中间值。
- 现象:实现后 Fmax 低于预期——原因:生成时钟路径上存在组合逻辑延迟。检查点:用
report_timing -delay_type min_max分析路径。修复:在分频器输出添加寄存器级。 - 现象:Quartus 中生成时钟未自动推导——原因:Quartus 要求显式使用
derive_pll_clocks或手动定义。检查点:查看 .sdc 中是否包含derive_clock_uncertainty。修复:添加derive_pll_clocks或手动create_generated_clock。 - 现象:跨时钟域路径出现违例——原因:主时钟与生成时钟之间未正确设置 false path 或 multicycle。检查点:用
report_clock_interaction查看域间关系。修复:对异步域添加set_false_path。
扩展与下一步
- 参数化分频器:使用 Verilog 参数
DIV,约束中通过 Tcl 脚本动态生成create_generated_clock,提升复用性。 - 带宽提升:结合 PLL 实现倍频(如 100 MHz → 200 MHz),约束中使用
-multiply_by和-divide_by。 - 跨平台验证:将约束移植到 Intel Quartus,注意语法差异(如
create_clock -name改为create_clock -name相同,但生成时钟需-master_clock)。 - 加入断言:在 testbench 中添加 SVA 断言,验证时钟沿对齐和分频周期。
- 形式验证:使用 OneSpin 或 JasperGold 对分频逻辑进行等价性检查,确保 RTL 与约束一致。
参考与信息来源
- Xilinx UG903 (v2024.2): "Vivado Design Suite User Guide: Using Constraints"
- Intel Quartus Prime Pro Handbook: "Timing Constraints and Analysis"
- IEEE Std 1800-2023: "SystemVerilog for Verification"
- Synopsys Design Constraints (SDC) Reference Manual, Version 2.1
技术附录
术语表
- 主时钟:由外部晶振或 PLL 输入到 FPGA 的时钟,用
create_clock定义。 - 生成时钟:由主时钟通过内部逻辑(分频、倍频、门控)派生的时钟,用
create_generated_clock定义。 - WNS:最差负余量(Worst Negative Slack),建立时间违例的度量。
- WHS:最差保持余量(Worst Hold Slack),保持时间违例的度量。
检查清单
- 主时钟定义是否绑定到物理引脚?
- 生成时钟的
-source是否指向主时钟或上级生成时钟? - 分频系数是否与 RTL 一致?
- 所有时钟域是否在时序报告中列出?
- 跨时钟域路径是否已正确处理(false path / multicycle)?
关键约束速查
# Vivado 语法
create_clock -name clk_name -period 10.000 [get_ports clk_pin]
create_generated_clock -name gen_clk -source [get_ports clk_pin] \
-divide_by 2 [get_pins inst/clk_out]
# Quartus 语法
create_clock -name clk_name -period 10.000 [get_ports clk_pin]
create_generated_clock -name gen_clk -source [get_ports clk_pin] \
-divide_by 2 [get_pins inst|clk_out]逐行说明
- 第 1–2 行:Vivado 中主时钟与生成时钟的完整语法,注意
-source指向端口而非网络。 - 第 4–5 行:Quartus 中语法类似,但层次路径分隔符为
|而非/。


