FPGA时序约束中set_clock_groups的实战设计指南

FPGA小白
文章2026-05-19
47

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_domainclk_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行:寄存器sync1sync2,构成两级同步器。
  • 第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_delayset_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之间的路径。

分类
技术分享
标签
fpga时序约束
浏览 47
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

FPGA小白查看主页

同分类阅读

文章

延伸阅读与实操

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

探索全站