Quick Start
- 打开Vivado 2024.2(或更新版本),创建一个空工程,器件选择Xilinx Artix-7 XC7A35T-1CSG324C(示例)。
- 编写一个包含两个异步时钟域(clk_a 50MHz, clk_b 75MHz)的简单设计,两个域之间只有一个寄存器级跨时钟域(CDC)同步器。
- 在约束文件(.xdc)中,先定义两个主时钟:
create_clock -period 20.000 -name clk_a [get_ports clk_a] 和 create_clock -period 13.333 -name clk_b [get_ports clk_b]。 - 添加
set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b]。 - 运行综合(Synthesis),打开综合后的时序报告(Report Timing Summary),确认没有跨时钟域的路径被分析(即报告只显示同域路径)。
- 运行实现(Implementation),检查实现后的时序报告,确认无跨时钟域违例(WNS、TNS均为正)。
- 可选:在仿真中注入跨时钟域数据,观察同步器输出稳定无亚稳态传播。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T-1CSG324C | 入门级FPGA,支持多时钟域 | 任何Xilinx 7系列或UltraScale+;Intel Cyclone V也可(需对应工具) |
| EDA版本 | Vivado 2024.2 | 支持set_clock_groups完整语法 | Vivado 2019.1+;Intel Quartus Prime 18.0+(对应derive_pll_clocks) |
| 仿真器 | Vivado Simulator 或 ModelSim | 用于功能验证和CDC行为检查 | VCS、Questa、Xsim |
| 时钟/复位 | 两个独立时钟源(板级晶振或PLL) | clk_a 50MHz, clk_b 75MHz(示例) | 任意频率差>10%的异步时钟 |
| 接口依赖 | 无外部接口要求 | 仅内部寄存器CDC | 可扩展至AXI跨时钟域 |
| 约束文件 | 一个.xdc文件包含主时钟定义和set_clock_groups | 可拆分为多个.xdc,但需注意优先级 | — |
目标与验收标准
- 功能点:设计包含两个异步时钟域之间的单比特CDC同步器(两级寄存器)。
- 性能指标:综合/实现后,时序报告中不应出现跨时钟域路径(即所有跨域路径被正确忽略)。
- 资源/Fmax:不额外增加LUT/FF;Fmax由每个域内最差路径决定,不受跨域影响。
- 验收方式:在Vivado中运行
report_timing_summary,检查“Inter-Clock Paths”部分应显示“0 paths analyzed”。 - 运行
report_clock_interaction,确认clk_a和clk_b之间无交互(Interaction = None)。 - 仿真中,跨时钟域数据经过同步器后无毛刺或亚稳态传播。
实施步骤
1. 工程结构与RTL设计
- 创建顶层模块
top,例化两个子模块:clk_a_domain和clk_b_domain。 clk_a_domain中生成一个单比特信号data_a(例如计数器溢出),通过两级寄存器同步到clk_b_domain。- 同步器代码:
always @(posedge clk_b) begin sync1 <= data_a; sync2 <= sync1; end
module top (
input wire clk_a,
input wire clk_b,
input wire rst_n,
output wire data_out
);
reg [7:0] counter_a;
reg data_a;
reg sync1, sync2;
// clk_a domain: generate data_a
always @(posedge clk_a or negedge rst_n) begin
if (!rst_n) begin
counter_a <= 8'd0;
data_a <= 1'b0;
end else begin
counter_a <= counter_a + 1'b1;
if (counter_a == 8'd255)
data_a <= 1'b1;
else
data_a <= 1'b0;
end
end
// CDC: synchronize data_a to clk_b domain
always @(posedge clk_b or negedge rst_n) begin
if (!rst_n) begin
sync1 <= 1'b0;
sync2 <= 1'b0;
end else begin
sync1 <= data_a;
sync2 <= sync1;
end
end
assign data_out = sync2;
endmodule
逐行说明
- 第1行:声明顶层模块
top,包含时钟、复位和输出端口。 - 第2行:输入端口
clk_a,50MHz时钟。 - 第3行:输入端口
clk_b,75MHz时钟。 - 第4行:输入端口
rst_n,低电平有效复位。 - 第5行:输出端口
data_out,同步后的数据。 - 第7行:寄存器
counter_a,8位计数器,用于产生脉冲。 - 第8行:寄存器
data_a,在clk_a域中生成的单比特信号。 - 第9行:寄存器
sync1和sync2,构成两级同步器。 - 第12行:clk_a域的逻辑块,敏感列表为clk_a上升沿或rst_n下降沿。
- 第13-14行:复位时清零计数器和data_a。
- 第15-20行:非复位时,计数器递增;当计数到255时,data_a置1,否则置0。
- 第23行:CDC同步逻辑块,敏感列表为clk_b上升沿或rst_n下降沿。
- 第24-25行:复位时清零同步器寄存器。
- 第26-28行:非复位时,将data_a传递到sync1,再将sync1传递到sync2。
- 第31行:将sync2赋值给输出data_out。
2. 约束文件编写
- 创建一个.xdc文件(例如
top.xdc),添加以下内容。
create_clock -period 20.000 -name clk_a [get_ports clk_a]
create_clock -period 13.333 -name clk_b [get_ports clk_b]
set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b]
逐行说明
- 第1行:定义主时钟clk_a,周期20ns(50MHz),绑定到端口clk_a。
- 第2行:定义主时钟clk_b,周期13.333ns(75MHz),绑定到端口clk_b。
- 第3行:将clk_a和clk_b设为异步时钟组,指示工具不分析它们之间的跨时钟域路径。
3. 综合与实现
- 在Vivado中运行综合(Synthesis)。
- 综合完成后,打开Report Timing Summary,在“Inter-Clock Paths”部分应看到“0 paths analyzed”。
- 运行实现(Implementation),再次检查时序报告,确认WNS(最差负slack)和TNS(总负slack)均为正数。
- 运行
report_clock_interaction,确认clk_a和clk_b之间的交互状态为“None”。
4. 仿真验证(可选)
- 编写testbench,驱动clk_a和clk_b,并观察data_out。
- 注入跨时钟域数据后,检查同步器输出是否稳定,无毛刺或亚稳态传播。
验证结果
- 综合后时序报告显示“Inter-Clock Paths: 0 paths analyzed”,符合预期。
- 实现后WNS和TNS均为正,无跨时钟域违例。
report_clock_interaction显示clk_a和clk_b之间无交互。- 仿真中,data_out在clk_b域中稳定输出,无亚稳态现象。
排障指南
- 问题1:时序报告中仍显示跨时钟域路径。
原因:set_clock_groups未正确应用或时钟定义有误。
解决:检查.xdc文件中时钟名称是否与create_clock定义一致;确认set_clock_groups语法正确,且未被其他约束覆盖。 - 问题2:实现后WNS为负。
原因:域内路径本身时序紧张,或跨域路径未被完全忽略。
解决:先检查同域路径是否满足时序;若跨域路径仍被分析,尝试使用set_false_path作为补充。 - 问题3:仿真中出现亚稳态。
原因:同步器寄存器数量不足或复位不同步。
解决:使用两级同步器(必要时三级);确保复位信号同步到目标时钟域。
扩展应用
- 多时钟域设计:可将
set_clock_groups扩展至三个或更多异步时钟组,例如-group [get_clocks clk_a] -group [get_clocks clk_b] -group [get_clocks clk_c]。 - 结合PLL:若时钟由PLL生成,使用
derive_pll_clocks后,再应用set_clock_groups。 - 复杂CDC:对于多比特或握手协议,需结合
set_max_delay或set_bus_skew约束。
参考
- Xilinx UG903: Vivado Design Suite User Guide: Using Constraints
- Xilinx UG949: Vivado Design Suite User Guide: Methodology
- IEEE Std 1364-2001 Verilog HDL
附录:完整约束文件示例
# top.xdc
create_clock -period 20.000 -name clk_a [get_ports clk_a]
create_clock -period 13.333 -name clk_b [get_ports clk_b]
set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b]
逐行说明
- 第1行:定义clk_a主时钟,周期20ns。
- 第2行:定义clk_b主时钟,周期13.333ns。
- 第3行:设置异步时钟组,忽略clk_a和clk_b之间的路径。