Quick Start
- 打开 Vivado 2025.1(或更高版本),创建新工程,目标器件选择 Xilinx Artix-7 XC7A35T(示例)。
- 编写顶层模块,实例化一个双触发器同步器(2-FF synchronizer),用于单比特跨时钟域信号。
- 编写 testbench,生成两个异步时钟(例如 clk_a = 50 MHz, clk_b = 75 MHz),驱动输入信号在 clk_a 域变化。
- 运行行为仿真,观察同步器输出在 clk_b 域稳定后的波形,确认毛刺被消除、信号正确传递。
- 添加 XDC 约束:对同步器寄存器设置 ASYNC_REG = TRUE,禁止工具做时序优化或重定时。
- 运行综合与实现,检查时序报告——同步器路径应被标记为“False Path”或“Async Path”。
- 上板测试:用逻辑分析仪(ILA)观察 clk_b 域输出,确认无亚稳态传播。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T | 主流低成本FPGA,支持 ASYNC_REG 属性 | Intel Cyclone V / Lattice ECP5 |
| EDA 版本 | Vivado 2025.1 | 2026年最新稳定版,支持 CDC 约束与报告 | Vivado 2024.2 / Quartus Prime Pro 24.3 |
| 仿真器 | Vivado Simulator 或 ModelSim SE-64 2025.1 | 支持 Tcl 脚本与波形查看 | QuestaSim / VCS |
| 时钟/复位 | 两个独立时钟源(板载晶振或 PLL 生成) | 异步时钟域,频率比任意 | 同源但不同分频(仍属同步,不推荐) |
| 接口依赖 | 无特殊接口,使用 GPIO 或内部逻辑 | 单比特信号跨时钟域 | 多比特需配合握手或 FIFO |
| 约束文件 | XDC 文件必须包含 ASYNC_REG 与 set_false_path | — | SDC 文件(Quartus) |
目标与验收标准
- 功能点:单比特信号从 clk_a 域正确传递到 clk_b 域,无亚稳态传播,输出在 2~3 个 clk_b 周期后稳定。
- 性能指标:同步器引入 2 个 clk_b 周期的延迟(latency),无额外抖动。
- 资源/Fmax:同步器仅消耗 2 个寄存器(FF),不降低系统 Fmax(路径被设为 false path)。
- 关键波形/日志:仿真中 clk_b 域输出信号在输入变化后 2~3 个 clk_b 周期内稳定;综合报告无 setup/hold 违规;实现后时序报告显示同步器路径被忽略。
实施步骤
工程结构与 RTL 编写
创建工程目录结构:src/(RTL文件)、sim/(testbench)、constr/(XDC约束)。
// sync_2ff.v
// 双触发器同步器,用于单比特异步信号
module sync_2ff (
input wire clk_dst, // 目标时钟域时钟
input wire rst_n, // 异步复位,低有效
input wire async_in, // 异步输入(来自源时钟域)
output wire sync_out // 同步后输出
);
(* ASYNC_REG = "TRUE" *) reg sync_ff1, sync_ff2;
always @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
sync_ff1 <= 1'b0;
sync_ff2 <= 1'b0;
end else begin
sync_ff1 <= async_in;
sync_ff2 <= sync_ff1;
end
end
assign sync_out = sync_ff2;
endmodule逐行说明
- 第 1 行:注释,标明文件名。
- 第 2 行:注释,说明模块功能。
- 第 3 行:模块声明开始,模块名为
sync_2ff。 - 第 4 行:输入端口
clk_dst,目标时钟域的时钟信号。 - 第 5 行:输入端口
rst_n,异步复位,低电平有效。 - 第 6 行:输入端口
async_in,来自源时钟域的异步输入信号。 - 第 7 行:输出端口
sync_out,同步后的输出信号。 - 第 9 行:声明两个寄存器
sync_ff1和sync_ff2,并添加综合属性ASYNC_REG = "TRUE",指示工具这些寄存器用于跨时钟域同步,避免时序优化。 - 第 11 行:always 块开始,敏感列表为
clk_dst上升沿或rst_n下降沿。 - 第 12 行:复位条件判断,若
rst_n为低电平(复位有效)。 - 第 13 行:复位时,
sync_ff1赋值为 0。 - 第 14 行:复位时,
sync_ff2赋值为 0。 - 第 15 行:非复位条件下(时钟上升沿触发)。
- 第 16 行:第一级寄存器
sync_ff1采样异步输入async_in。 - 第 17 行:第二级寄存器
sync_ff2采样第一级输出sync_ff1,完成两级同步。 - 第 19 行:组合逻辑,将
sync_ff2赋值给输出sync_out。 - 第 21 行:模块结束。
约束文件编写
在 constr/ 目录下创建 sync_2ff.xdc,内容如下:
# 对同步器寄存器设置 ASYNC_REG 属性(已在 RTL 中通过 (* ASYNC_REG = "TRUE" *) 声明,此处可选重复)
# 设置 false path,禁止工具分析跨时钟域路径
set_false_path -from [get_cells -hier -filter {NAME =~ *sync_ff1_reg*}] -to [get_cells -hier -filter {NAME =~ *sync_ff2_reg*}]
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]逐行说明
- 第 1 行:注释,说明 ASYNC_REG 属性已在 RTL 中声明,XDC 中可重复但非必需。
- 第 2 行:注释,说明后续命令用于设置 false path。
- 第 3 行:
set_false_path命令,禁止工具分析从sync_ff1_reg到sync_ff2_reg的时序路径,避免不必要的时序违规。 - 第 4 行:
set_false_path命令,禁止工具分析从时钟域clk_a到clk_b的所有路径,确保跨时钟域路径不被时序引擎检查。
仿真验证
编写 testbench 文件 tb_sync_2ff.v,生成两个异步时钟并驱动输入信号。以下为关键代码片段:
// tb_sync_2ff.v (关键部分)
reg clk_a, clk_b;
reg rst_n;
reg async_in;
wire sync_out;
// 生成 clk_a: 50 MHz (周期 20 ns)
always #10 clk_a = ~clk_a;
// 生成 clk_b: 75 MHz (周期 13.333 ns)
always #6.666 clk_b = ~clk_b;
initial begin
clk_a = 0; clk_b = 0; rst_n = 0; async_in = 0;
#100 rst_n = 1; // 释放复位
#30 async_in = 1; // 在 clk_a 域产生上升沿
#200 async_in = 0; // 产生下降沿
#300 $finish;
end
sync_2ff u_sync (
.clk_dst(clk_b),
.rst_n(rst_n),
.async_in(async_in),
.sync_out(sync_out)
);逐行说明
- 第 1 行:注释,标明 testbench 文件名。
- 第 2 行:声明时钟信号
clk_a和clk_b。 - 第 3 行:声明复位信号
rst_n。 - 第 4 行:声明异步输入信号
async_in。 - 第 5 行:声明同步输出线网
sync_out。 - 第 7 行:生成
clk_a,周期 20 ns(50 MHz),每 10 ns 翻转一次。 - 第 8 行:生成
clk_b,周期约 13.333 ns(75 MHz),每 6.666 ns 翻转一次。 - 第 10 行:initial 块开始,初始化信号。
- 第 11 行:初始赋值,所有信号为 0。
- 第 12 行:延迟 100 ns 后释放复位(
rst_n拉高)。 - 第 13 行:延迟 30 ns 后,
async_in置为 1(模拟输入上升沿)。 - 第 14 行:延迟 200 ns 后,
async_in置为 0(模拟输入下降沿)。 - 第 15 行:延迟 300 ns 后结束仿真。
- 第 17 行:实例化
sync_2ff模块,连接端口。 - 第 18 行:
clk_dst连接clk_b。 - 第 19 行:
rst_n连接rst_n。 - 第 20 行:
async_in连接async_in。 - 第 21 行:
sync_out连接sync_out。 - 第 22 行:模块实例化结束。
综合与实现
在 Vivado 中运行综合(Synthesis)和实现(Implementation),然后打开时序报告。关键检查点:
- 确认同步器路径被标记为“False Path”或“Async Path”。
- 确认无 setup/hold 违规。
- 确认资源使用报告显示仅消耗 2 个寄存器。
上板测试
使用 Vivado 的 ILA(Integrated Logic Analyzer)IP 核,将 sync_out 信号连接到 ILA 探针,设置触发条件为 sync_out 上升沿。上电后观察波形,确认输出在 clk_b 域稳定,无毛刺或亚稳态传播。
验证结果
仿真波形显示:async_in 在 clk_a 域变化后,sync_out 在 2 个 clk_b 周期后稳定跟随,无毛刺。时序报告确认同步器路径被忽略,无违规。上板测试通过,ILA 捕获信号干净。
排障指南
- 问题:仿真中输出不稳定——检查复位时序,确保复位释放后至少等待一个时钟周期再驱动输入。
- 问题:综合报告显示同步器路径有 setup 违规——确认 XDC 中
set_false_path已正确设置,且 ASYNC_REG 属性已应用。 - 问题:上板后输出毛刺——检查 ILA 采样时钟是否为 clk_b,避免跨时钟域采样引入混淆。
- 问题:多比特信号同步失败——单比特同步器不适用于多比特信号,需改用握手协议或异步 FIFO。
扩展:多比特同步与握手协议
对于多比特信号跨时钟域,单比特同步器无法保证所有比特同时稳定。推荐使用以下方法:
- 握手协议:源域发送数据并置位请求信号,目标域同步请求后采样数据,再返回应答信号。适用于低速、少量数据。异步 FIFO:使用格雷码指针和双端口 RAM,适用于高速、连续数据流。DMUX 同步器:对控制信号同步后,再通过多路选择器选择数据,适用于数据变化较慢的场景。
参考
- Xilinx UG949: Vivado Design Suite User Guide — MethodologyXilinx UG906: Vivado Design Suite User Guide — Design Analysis and Closure TechniquesIEEE Std 1364-2005: Verilog HDL Language Reference ManualClifford E. Cummings, "Synthesis and Scripting Techniques for Designing Multi-Asynchronous Clock Designs", SNUG 2001
附录:完整工程文件清单
src/sync_2ff.v — 双触发器同步器 RTLsim/tb_sync_2ff.v — 测试平台constr/sync_2ff.xdc — 时序约束文件vivado_project.tcl — 工程创建脚本(可选)




