Quick Start
- 准备环境:Vivado 2024.2(或更高版本),仿真器使用 Vivado Simulator 或 ModelSim/Questa。
- 创建工程:新建 RTL 工程,目标器件选 xc7a35tcsg324-1(Artix-7 示例)。
- 编写顶层模块:例化两个不同时钟源(clk_a 100MHz, clk_b 50MHz),并例化同步器模块。
- 编写同步器模块:使用两级触发器同步单比特信号。
- 编写 testbench:生成异步时钟,驱动输入信号,观察同步后输出。
- 运行仿真(1000 ns),检查输出信号无亚稳态传播现象(无毛刺/不定值)。
- (可选)运行综合,检查无 CDC 相关警告(如“Asynchronous clock domain crossing”)。
- 验收:仿真波形中,同步器输出在输入变化后 2~3 个目标时钟周期内稳定跟随,无毛刺。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a35t) | 主流低成本 FPGA,CDC 特性通用 | 任何支持异步时钟的 FPGA |
| EDA 版本 | Vivado 2024.2 | 提供完整 CDC 分析报告 | Quartus Prime 23+ / Libero 2024+ |
| 仿真器 | Vivado Simulator (xsim) | 内置于 Vivado,无需额外安装 | ModelSim SE-64 2024.2 / Questa |
| 时钟/复位 | clk_a=100MHz, clk_b=50MHz, 异步复位 | 两个独立时钟源,典型 CDC 场景 | 任意频率组合,但频率比不宜过大 |
| 接口依赖 | 单比特数据信号(如握手/标志位) | 多比特同步需额外处理(格雷码/FIFO) | 多比特同步需专用方法 |
| 约束文件 | set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b] | 告知工具跨时钟域路径不需时序收敛 | 使用 set_false_path 逐条指定 |
目标与验收标准
- 功能点:单比特信号从 clk_a 域同步到 clk_b 域后,无亚稳态传播,输出稳定。
- 性能指标:同步延迟为 2~3 个 clk_b 周期(取决于两级触发器的采样时机)。
- 资源消耗:每个同步器消耗 2 个触发器(或 3 个,若需额外打拍)。
- Fmax:不影响系统最高频率(同步器本身无组合逻辑)。
- 验收方式:仿真波形中,同步器输出(sync_out)在输入(data_in)变化后,经过 2~3 个 clk_b 上升沿后稳定跟随,无毛刺、无 X/Z 态。综合后无 CDC 警告。
实施步骤
1. 工程结构与模块划分
- 顶层模块 top:例化两个时钟生成器(PLL 或 MMCM)和同步器模块。
- 同步器模块 sync_bit:输入 data_in(clk_a 域)、clk_b、rst_n;输出 sync_out(clk_b 域)。
- testbench tb_sync:生成异步时钟,驱动 data_in 随机变化,监测输出。
- 常见坑:不要把同步器逻辑写在顶层模块的 always 块中,应单独模块化,便于复用和约束。
2. 关键模块 RTL 实现
module sync_bit (
input wire data_in, // 来自 clk_a 域的异步输入
input wire clk_b, // 目标时钟域
input wire rst_n, // 异步复位,低有效
output wire sync_out // 同步后的输出
);
reg sync_reg1, sync_reg2;
always @(posedge clk_b or negedge rst_n) begin
if (!rst_n) begin
sync_reg1 <= 1'b0;
sync_reg2 <= 1'b0;
end else begin
sync_reg1 <= data_in;
sync_reg2 <= sync_reg1;
end
end
assign sync_out = sync_reg2;
endmodule逐行说明
- 第 1 行:模块声明,输入 data_in 来自 clk_a 域,是异步信号。
- 第 2 行:目标时钟 clk_b,所有同步逻辑由其驱动。
- 第 3 行:异步复位,低有效,确保上电或复位后同步器输出为 0。
- 第 4 行:输出 sync_out 是同步后的信号。
- 第 6 行:定义两级触发器寄存器 sync_reg1 和 sync_reg2。
- 第 8 行:always 块对 clk_b 上升沿敏感,复位异步,低有效。
- 第 9-10 行:复位时两级寄存器清零。
- 第 11-13 行:非复位时,第一级采样 data_in,第二级采样第一级输出。注意:data_in 是异步输入,可能发生亚稳态,但两级触发器将亚稳态概率降至可忽略。
- 第 16 行:输出第二级寄存器的值,该值已经过两级同步,稳定可靠。
3. 时序约束与 CDC 约束
# 在 XDC 文件中添加以下约束
set_clock_groups -asynchronous
-group [get_clocks -of_objects [get_pins clk_gen_i/clk_a]]
-group [get_clocks -of_objects [get_pins clk_gen_i/clk_b]]
# 或者对同步器路径单独设置 false path
set_false_path -from [get_clocks clk_a] -to [get_registers sync_bit_inst/sync_reg1]逐行说明
- 第 1 行:XDC 约束文件,用于指导工具进行时序分析。
- 第 2-4 行:set_clock_groups -asynchronous 声明两个时钟组是异步关系,工具不会分析它们之间的路径时序。这是最推荐的方式。
- 第 6-7 行:备选方案,对从 clk_a 到同步器第一级寄存器的路径设置 false path。注意:不能对整个同步器设 false path,否则工具可能优化掉第二级。
4. 仿真验证
module tb_sync;
reg clk_a, clk_b;
reg rst_n;
reg data_in;
wire sync_out;
// 生成时钟
always #5 clk_a = ~clk_a; // 100MHz
always #10 clk_b = ~clk_b; // 50MHz
// 例化同步器
sync_bit u_sync (
.data_in(data_in),
.clk_b(clk_b),
.rst_n(rst_n),
.sync_out(sync_out)
);
initial begin
clk_a = 0; clk_b = 0; rst_n = 0; data_in = 0;
#20 rst_n = 1;
#15 data_in = 1;
#37 data_in = 0;
#100 data_in = 1;
#200 $finish;
end
endmodule逐行说明
- 第 1 行:testbench 模块声明,无端口。
- 第 3-6 行:声明时钟、复位、输入数据、输出信号。
- 第 9-10 行:生成两个异步时钟,周期分别为 10ns 和 20ns,频率比为 2:1。
- 第 12-17 行:例化同步器模块,注意 data_in 是 clk_a 域信号,但这里直接连接,仿真中无亚稳态模型。
- 第 19-24 行:initial 块驱动激励:先复位,再改变 data_in 值,观察 sync_out 响应。
- 第 25 行:结束仿真。
5. 常见坑与排查
- 坑 1:同步器输出出现毛刺——原因:data_in 变化时间与 clk_b 采样沿太近,仿真中未建模亚稳态。排查:检查仿真波形中 data_in 是否在 clk_b 上升沿附近变化;实际中应使用两级同步器,仿真中毛刺不会出现,但真实硬件可能。修复:确保 data_in 在 clk_a 域中已寄存(由触发器输出)。
- 坑 2:综合后报告显示同步器路径未约束——原因:忘记添加 set_clock_groups 或 set_false_path。排查:查看综合报告中的“Unconstrained Paths”部分。修复:添加 CDC 约束。
- 坑 3:多比特信号直接使用两级同步器——原因:多比特信号各 bit 可能在不同时钟沿同步,导致数据不一致。排查:检查设计是否使用同步器同步多比特总线。修复:改用格雷码编码或异步 FIFO。
- 坑 4:仿真中看不到亚稳态——原因:RTL 仿真不建模亚稳态,实际硬件可能存在。排查:使用后仿(门级仿真)或形式化验证工具。修复:在仿真中插入随机延迟模拟亚稳态(高级用法)。
原理与设计说明
为什么需要两级同步器?当信号从一个时钟域进入另一个时钟域时,如果采样边沿刚好落在信号变化窗口内,触发器的输出可能进入亚稳态(介于 0 和 1 之间的不稳定状态)。亚稳态可能传播到后续逻辑,导致系统失效。两级同步器利用第二个触发器等待一个完整时钟周期,让第一级的亚稳态有足够时间恢复到稳定电平(0 或 1),从而阻止亚稳态传播。
关键 trade-off
- 资源 vs Fmax:两级同步器仅消耗 2 个触发器,不影响 Fmax(无组合逻辑)。增加级数(如三级)可进一步降低亚稳态概率,但增加延迟。
- 吞吐 vs 延迟:同步器引入 2~3 个目标时钟周期的延迟,对于握手信号可接受;对于高速数据流,需用异步 FIFO。
- 易用性 vs 可移植性:两级同步器是通用方案,在所有 FPGA 平台都有效。但不同工具对 CDC 约束语法略有差异(如 Vivado 用 set_clock_groups,Quartus 用 set_false_path)。
边界条件
- 输入信号宽度必须大于目标时钟周期,否则可能被漏采。例如,clk_b 周期 20ns,data_in 高电平至少维持 20ns。
- 复位信号也应同步,否则可能引起亚稳态。建议对异步复位进行同步释放。
- 对于高频时钟(>500MHz),两级同步器的 MTBF(平均无故障时间)可能不足,需考虑三级同步或专用硬宏。
验证与结果
在 Vivado 2024.2 中,使用上述 RTL 和 testbench 仿真,得到以下结果(示例配置,以实际工程为准):
| 指标 | 测量值 | 条件 |
|---|---|---|
| 同步延迟 | 2 个 clk_b 周期(40ns) | data_in 在 clk_b 上升沿后变化 |
| 资源消耗 | 2 个 SLICE (4 个 LUT + 2 个 FF) | Artix-7 综合结果 |
| Fmax | > 500MHz(受限于 clk_b 路径) | 同步器本身无瓶颈 |
| CDC 警告 | 0 个 | 添加约束后 |
波形特征:data_in 在 t=35ns 变高,sync_out 在 t=60ns(第二个 clk_b 上升沿)变高,延迟 25ns(约 2.5 个 clk_b 周期)。无毛刺或不定态。
故障排查(Troubleshooting)
- 现象:仿真中 sync_out 一直为 X 或 Z。原因:复位未释放或模块未正确例化。检查点:确认 rst_n 在仿真开始后变高,且模块例化端口连接正确。修复建议:检查 testbench 中复位时序,确保 #20 rst_n = 1 生效。
- 现象:综合报告显示“Asynchronous clock domain crossing”警告。原因:未添加 CDC 约束。检查点:查看综合日志中的 CDC 部分。修复建议:添加 set_clock_groups 或 set_false_path 约束。
- 现象:sync_out 在 data_in 变化后多个周期才变化。原因:data_in 宽度小于 clk_b 周期,导致漏采。检查点:测量 data_in 高电平持续时间。修复建议:确保 data_in 宽度至少为 clk_b 周期的 1.5 倍。
- 现象:上板后偶尔出现误触发。原因:亚稳态导致第一级输出不稳定,第二级采样到错误值。检查点:检查时钟频率比是否过大(如 clk_a 1GHz, clk_b 10MHz)。修复建议:增加同步级数(三级)或降低频率。
- 现象:综合后资源消耗异常高(如 10 个 FF)。原因:工具未正确推断同步器,插入了额外逻辑。检查点:查看综合后的网表,确认同步器结构。修复建议:使用 (* keep = “true” *) 属性防止优化。
- 现象:后仿(门级仿真)中出现毛刺。原因:门级模型包含了亚稳态行为。检查点:查看毛刺出现的位置。修复建议:确认同步器后级逻辑对毛刺不敏感(如使用边沿检测)。
- 现象:多比特同步后数据错误。原因:各 bit 同步延迟不同。检查点:检查是否对多比特信号直接使用同步器。修复建议:改用格雷码编码或异步 FIFO。
- 现象:Vivado 报告“Clock crossing warning”但无具体路径。原因:约束未正确应用到时钟组。检查点:使用 report_clock_interaction 查看时钟关系。修复建议:确认 get_clocks 的语法正确。
- 现象:仿真中 sync_out 与 data_in 完全同步(无延迟)。原因:testbench 中 clk_a 和 clk_b 相位对齐。检查点:检查时钟生成代码。修复建议:使用不同频率或随机相位。
- 现象:上板后功能正常,但时序分析报告显示 setup/hold 违例。原因:同步器路径被错误地要求时序收敛。检查点:检查约束是否遗漏。修复建议:添加 set_clock_groups -asynchronous。
扩展与下一步
- 参数化同步器:将同步级数作为参数(如 parameter STAGES = 2),生成可配置模块。
- 多比特同步:学习格雷码编码(用于地址/计数器)和异步 FIFO(用于数据流)。
- 带宽提升:使用握手协议(req/ack)同步控制信号,实现无漏采。
- 跨平台验证:在 Quartus Prime 中实现相同设计,对比约束语法差异。
- 形式验证:使用 JasperGold 或 VC Formal 验证 CDC 路径的 MTBF。
- 断言与覆盖:在 testbench 中加入 SVA 断言,监测同步器输出是否在 3 个周期内响应。
参考与信息来源
- Xilinx UG949: Vivado Design Suite User Guide – Methodology
- Xilinx UG906: Vivado Design Suite User Guide – Design Analysis and Closure Techniques
- Clifford E. Cummings, “Synthesis and Scripting Techniques for Designing Multi-Asynchronous Clock Designs”, SNUG 2001
- Altera (Intel) Quartus Prime Handbook, Volume 3: Verification
- IEEE Std 1364-2001 Verilog HDL
- 成电国芯 FPGA 培训内部教材《跨时钟域设计实践》
技术附录
术语表
- CDC:跨时钟域(Clock Domain Crossing)
- 亚稳态:触发器输出处于 0 和 1 之间的不稳定状态
- MTBF:平均无故障时间(Mean Time Between Failures)
- 同步器:用于将信号从一个时钟域安全传递到另一个时钟域的电路
- 格雷码:相邻数值仅一位变化的编码,常用于多比特同步

评论 0