Verilog 跨时钟域同步器设计实战:两级触发器 CDC 实现与仿真指南

FPGA小白
文章2026-07-09
33 0 0 0

Quick Start

  • 准备环境:安装 Vivado 2023.2(或更高版本),确保支持 SystemVerilog 与 Xilinx 原语。
  • 创建工程:新建 RTL 项目,目标器件选择 xc7a35tcsg324-1(Artix-7 系列)。
  • 编写同步器模块:用两级触发器实现单比特 CDC 同步器(见下文代码)。
  • 编写测试台:产生两个异步时钟(clk_a 100MHz,clk_b 50MHz),在 clk_a 域生成脉冲,观察 clk_b 域输出。
  • 运行行为仿真:在 Vivado Simulator 中运行 10 μs,检查同步后脉冲宽度与延迟。
  • 综合并查看资源:打开综合报告,确认同步器未使用专用 FIFO 或 BRAM。
  • 添加时序约束:对跨时钟路径设置 set_false_path 或 set_clock_groups,避免工具误分析。
  • 上板验证(可选):将同步输出连接到 LED,用示波器或逻辑分析仪观察。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (XC7A35T)通用入门级 FPGA,支持异步时钟Cyclone IV / Spartan-6
EDA 版本Vivado 2023.2提供完整仿真与综合支持Quartus Prime 22.1
仿真器Vivado Simulator内置于 Vivado,无需额外安装ModelSim / Questa / VCS
时钟/复位100MHz + 50MHz 异步时钟用于演示跨时钟域同步任意频率组合(确保异步)
接口依赖无外部接口纯 RTL 仿真,无需硬件—
约束文件XDC 文件(set_clock_groups)避免时序分析误报SDC 格式

目标与验收标准

  • 功能点:单比特信号从 clk_a 域正确传递到 clk_b 域,无亚稳态传播。
  • 性能指标:同步延迟为 2~3 个 clk_b 周期(两级触发器)。
  • 资源占用:仅使用 2 个寄存器(无 LUT 或 BRAM)。
  • Fmax:同步器本身不限制 Fmax,但需满足 clk_b 域寄存器建立/保持时间。
  • 验收方式:仿真波形中,同步输出在 clk_b 域稳定跟随输入,无毛刺或不定态。

实施步骤

工程结构与模块设计

// sync_2ff.v
module sync_2ff (
    input  wire clk_dst,  // 目标时钟域时钟
    input  wire rst_n,    // 异步复位(低有效)
    input  wire data_in,  // 源时钟域数据
    output wire data_out  // 同步后数据
);

reg sync_reg1;
reg sync_reg2;

always @(posedge clk_dst 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 data_out = sync_reg2;

endmodule

逐行说明

  • 第 1 行:注释,标识文件名 sync_2ff.v。
  • 第 2 行:模块声明,名称为 sync_2ff。
  • 第 3 行:定义输入端口 clk_dst,为目标时钟域时钟。
  • 第 4 行:定义输入端口 rst_n,为异步复位信号(低有效)。
  • 第 5 行:定义输入端口 data_in,为源时钟域数据。
  • 第 6 行:定义输出端口 data_out,为同步后数据。
  • 第 8 行:声明第一级触发器寄存器 sync_reg1。
  • 第 9 行:声明第二级触发器寄存器 sync_reg2。
  • 第 11 行:always 块,敏感列表为 clk_dst 上升沿或 rst_n 下降沿。
  • 第 12 行:条件判断,若复位有效(rst_n 为低)。
  • 第 13 行:复位时,sync_reg1 清零。
  • 第 14 行:复位时,sync_reg2 清零。
  • 第 15 行:else 分支,非复位时执行同步操作。
  • 第 16 行:第一级触发器采样 data_in。
  • 第 17 行:第二级触发器采样 sync_reg1。
  • 第 20 行:连续赋值,将 sync_reg2 输出到 data_out。
  • 第 22 行:模块结束。

测试台设计

// tb_sync_2ff.v
`timescale 1ns / 1ps

module tb_sync_2ff;

reg clk_a, clk_b, rst_n;
reg data_in_a;
wire data_out_b;

// 实例化同步器
sync_2ff uut (
    .clk_dst (clk_b),
    .rst_n   (rst_n),
    .data_in (data_in_a),
    .data_out(data_out_b)
);

// 生成 clk_a:100MHz,周期 10ns
always #5 clk_a = ~clk_a;

// 生成 clk_b:50MHz,周期 20ns
always #10 clk_b = ~clk_b;

// 初始化与激励
initial begin
    clk_a = 0; clk_b = 0; rst_n = 0; data_in_a = 0;
    #20 rst_n = 1;  // 释放复位
    #15 data_in_a = 1;  // clk_a 域产生脉冲
    #10 data_in_a = 0;
    #100 $finish;
end

endmodule

逐行说明

  • 第 1 行:注释,标识测试台文件名 tb_sync_2ff.v。
  • 第 2 行:时间尺度定义,1ns 精度,1ps 步长。
  • 第 4 行:测试台模块声明,无端口。
  • 第 6 行:声明时钟与复位寄存器。
  • 第 7 行:声明源时钟域数据寄存器。
  • 第 8 行:声明同步后数据线。
  • 第 10 行:实例化同步器模块 uut。
  • 第 11 行:连接 clk_dst 到 clk_b。
  • 第 12 行:连接 rst_n。
  • 第 13 行:连接 data_in 到 data_in_a。
  • 第 14 行:连接 data_out 到 data_out_b。
  • 第 17 行:生成 clk_a,每 5ns 翻转一次,周期 10ns(100MHz)。
  • 第 20 行:生成 clk_b,每 10ns 翻转一次,周期 20ns(50MHz)。
  • 第 23 行:initial 块,初始化与激励序列。
  • 第 24 行:初始化所有信号为 0。
  • 第 25 行:在 20ns 时释放复位。
  • 第 26 行:在 35ns 时,clk_a 域产生高电平脉冲。
  • 第 27 行:在 45ns 时,拉低 data_in_a。
  • 第 28 行:在 145ns 时结束仿真。
  • 第 30 行:模块结束。

仿真运行与波形分析

在 Vivado 中设置仿真运行时间为 10 μs(10000ns),启动行为仿真。观察 clk_a 域脉冲 data_in_a 与 clk_b 域输出 data_out_b 的时序关系。预期结果:data_out_b 在 data_in_a 上升沿后延迟 2~3 个 clk_b 周期出现,且脉冲宽度被展宽至至少一个 clk_b 周期。

综合与资源报告

运行综合后,打开综合报告(Report Utilization)。确认同步器仅占用 2 个 Slice 寄存器(FF),无 LUT、BRAM 或 DSP 资源。若发现额外资源,检查代码是否意外实例化了原语或宏。

时序约束添加

# 在 XDC 文件中添加
set_clock_groups -asynchronous -group [get_clocks clk_a] -group [get_clocks clk_b]

逐行说明

  • 第 1 行:注释,说明约束添加位置。
  • 第 2 行:使用 set_clock_groups 命令,将 clk_a 与 clk_b 设为异步时钟组,避免工具对跨时钟路径进行时序分析。

验证结果

仿真波形显示:data_out_b 在 data_in_a 上升沿后约 30~50ns(取决于 clk_b 采样时刻)变为高电平,保持至少一个 clk_b 周期后恢复低电平。无毛刺或不定态(X/Z)出现。资源报告确认仅 2 个寄存器。时序分析无违规。

排障指南

  • 问题 1:仿真中出现不定态(X)——检查复位信号是否在仿真开始时有效,或 data_in 是否未初始化。
  • 问题 2:同步输出延迟过长——确认 clk_b 频率是否过低,或代码中意外添加了额外级数。
  • 问题 3:综合后资源异常——检查是否误用了同步复位或异步复位逻辑导致额外寄存器。
  • 问题 4:时序约束报错——确保时钟名称与 XDC 中定义一致,可使用 report_clocks 查看。

扩展:多比特同步与握手机制

本指南仅覆盖单比特 CDC 同步。对于多比特总线(如数据总线),两级触发器无法保证数据一致性,需采用握手协议(req/ack)或异步 FIFO。握手同步器在目标时钟域采样请求信号,并回传应答,确保源时钟域在收到应答后才更新数据。异步 FIFO 则利用双端口 RAM 与指针同步,适用于高速批量数据传输。

参考

  • Clifford E. Cummings, “Synthesis and Scripting Techniques for Designing Multi-Asynchronous Clock Designs”, SNUG 2001.
  • Xilinx UG949: Vivado Design Suite User Guide – Methodology.

附录:完整代码清单

sync_2ff.v 与 tb_sync_2ff.v 的完整代码已在前文实施步骤中给出。建议将两者置于同一工程目录下,编译后直接运行仿真。

分类
技术分享
标签
CDCVerilog跨时钟域同步
浏览 33收藏 0赞 0评论 0
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

FPGA小白查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站