4. 时钟域建模与约束
CDC 检查的第一步不是跑规则,而是让工具“知道”设计里到底有哪些时钟、它们之间是什么关系。SpyGlass CDC 的时钟信息主要来自 SGDC(SpyGlass Design Constraints)约束文件,其中最核心的命令就是 create_clock。如果时钟没有正确定义,后续的跨域检查要么漏报,要么产生大量无意义的噪声。
create_clock:把时钟“钉”在物理点上
create_clock 的作用是在指定的源对象上定义一个时钟,并给出周期、占空比和边沿信息。它的基本语法如下:
create_clock -name <clock_name> -period <period> \
[-waveform {rise_edge fall_edge}] \
[source_objects]
例如,一个 100 MHz 的时钟,周期为 10 ns,占空比 50%,上升沿在 0 ns、下降沿在 5 ns:
create_clock -name clk_core -period 10 \
-waveform {0 5} [get_ports clk_core]
这里 get_ports clk_core 指定了时钟的源点。对于内部生成的时钟(如 PLL 输出、分频器输出),源点应指向生成时钟的驱动单元输出引脚,而不是顶层端口。SpyGlass 会从该源点沿时钟网络传播,识别出所有被该时钟驱动的触发器。如果源点选错,时钟树可能只覆盖部分逻辑,导致部分寄存器被误判为“无时钟”或“跨域”。
对于 PLL 或 MMCM 产生的多个输出时钟,需要分别用 create_clock 定义,并可用 -add 选项在同一源点叠加多个时钟(例如同一引脚上时分复用的两个时钟)。此外,create_generated_clock 可用于描述分频、倍频或相位偏移的生成时钟,帮助工具正确推导时钟之间的相位关系。
时钟分组:告诉工具“谁和谁是一家人”
定义了时钟之后,还需要用 set_clock_groups 声明时钟之间的异步关系。默认情况下,SpyGlass 会认为所有时钟都是同步的,并检查它们之间的时序路径。如果两个时钟实际是异步的,必须显式声明为异步组,否则工具会按同步路径检查,产生大量虚假的建立/保持违例。
set_clock_groups -asynchronous \
-group {clk_core} \
-group {clk_ddr} \
-group {clk_apb}
上述约束表示 clk_core、clk_ddr、clk_apb 三个时钟两两异步。同一 group 内的时钟仍按同步关系处理。对于有确定相位关系的时钟(例如同源分频),可以放在同一 group 中,或使用 -physically_exclusive 声明物理互斥。
时钟分组是 CDC 检查的“分水岭”:分组正确,跨域路径才会被识别为 CDC 路径并交给同步器检查;分组错误,要么漏掉真正的跨域,要么把同步路径误报为违例。实际项目中,建议先画出时钟拓扑图,再逐对确认同步/异步关系,最后写成 set_clock_groups。
时钟域声明与 CDC 路径识别
SpyGlass 通过时钟传播自动为每个触发器分配时钟域。当时钟定义完整时,工具会生成时钟域报告,列出每个时钟域及其驱动的寄存器数量。CDC 路径的判定依据是:路径的发射触发器与捕获触发器属于不同时钟域,且这两个时钟域被声明为异步。
为了更精细地控制,可以使用 set_cdc_clock_domain 或 assume_path 等约束,对特定模块或信号指定时钟域归属。例如,某些黑盒内部逻辑无法被工具分析时,可以手动声明其输入输出所属的时钟域,避免工具因无法传播而误判。
一个典型的时钟域声明片段如下:
# 定义时钟
create_clock -name clk_a -period 8 [get_ports clk_a]
create_clock -name clk_b -period 12 [get_ports clk_b]
# 声明异步分组
set_clock_groups -asynchronous \
-group {clk_a} -group {clk_b}
# 对黑盒模块声明时钟域
set_cdc_clock_domain -module u_blackbox -clock clk_a \
-input data_in -output data_out
黑盒与第三方 IP 的 CDC 约束方法
黑盒(black box)和第三方 IP 是 CDC 验证中最容易出问题的部分。工具看不到内部实现,无法自动传播时钟,也无法判断内部是否有同步器。常见的处理策略有三种:
- 提供行为模型或网表:如果 IP 供应商能提供可综合的行为模型或门级网表,优先用
read_file读入,让工具正常分析。这是最可靠的方式。 - 用约束“包裹”黑盒:若无法获得内部信息,则用
set_cdc_clock_domain声明黑盒各端口所属时钟域,并用set_cdc_blackbox或assume_path告诉工具哪些路径可以忽略、哪些必须检查。例如,声明黑盒输出端口属于 clk_b 域,输入端口属于 clk_a 域,工具就会把黑盒边界上的跨域路径纳入检查。 - 对 IP 内部同步器做“信任声明”:如果 IP 内部已有同步器且供应商保证,可用
set_cdc_sync_cell或set_cdc_ignore对特定路径做豁免,但必须记录豁免理由和责任人,避免滥用。
set_cdc_blackbox 而不声明端口时钟域,工具可能直接跳过整个模块,导致真正的跨域路径被漏掉。建议对每个黑盒至少声明输入/输出端口的时钟域归属。
约束文件的组织与检查顺序
一个可维护的 SGDC 文件通常按以下顺序组织:先定义所有时钟(create_clock / create_generated_clock),再声明时钟分组(set_clock_groups),然后处理黑盒和 IP 的时钟域声明,最后添加针对特定路径的例外约束。顺序颠倒可能导致工具在时钟未定义时就尝试分组,产生警告或忽略。
约束写完后,建议先跑一次时钟域报告,确认每个时钟的覆盖范围、每个寄存器的时钟域归属、以及异步分组是否与预期一致。如果报告中出现“unclocked register”或“unknown clock domain”,说明时钟定义或传播有问题,应先修正约束再继续 CDC 检查。
最后需要强调:CDC 约束不是一次写死的。随着设计迭代、IP 版本更新、时钟频率调整,SGDC 文件必须同步维护。建议把 SGDC 纳入版本管理,并在每次 CDC 回归前检查时钟定义是否与最新时钟树一致。只有约束与设计同步,SpyGlass CDC 的结果才可信。