2. 时钟域定义与约束

在 PolarFire FPGA 中,时钟域并不是一个抽象概念,而是由时钟源、时钟网络、时序约束和寄存器触发沿共同界定的物理与逻辑边界。一个设计里往往同时存在多个时钟域:外部晶振输入、PLL 输出、分频器输出、恢复时钟、以及由时钟使能信号形成的“伪时钟域”。识别这些域,并用 SDC 约束准确描述它们,是 CDC 验证能够成立的前提。约束写错,CDC 工具看到的拓扑就是错的,后续所有分析都会失去意义。

一、如何识别一个时钟域

识别时钟域的核心判断依据是:一组寄存器是否由同一个时钟信号、同一个有效触发沿驱动。在 PolarFire 中,需要特别关注以下几类对象:

  • 主时钟输入:来自外部晶振或时钟芯片,通常接入 CCC(Clock Conditioning Circuit)或全局时钟网络,例如 REFCLK_0、CLK_50M。
  • PLL/DLL 输出:由 CCC 生成,频率和相位与输入存在确定关系,属于生成时钟。
  • 分频/倍频逻辑输出:由计数器或寄存器分频产生,若驱动寄存器时钟端,则形成新的生成时钟。
  • 时钟使能域:同一时钟下由使能信号控制的数据路径,虽然物理时钟相同,但有效采样率不同,CDC 分析中常需单独处理。
  • 恢复时钟:来自高速串行接口(SerDes)或外部协议恢复的时钟,通常与本地时钟无固定相位关系。

识别时建议从顶层端口向下追踪:先列出所有时钟输入引脚,再追踪每个 CCC 的输出,最后检查是否有寄存器输出被用作时钟。凡是驱动了寄存器 CLK 端或作为生成时钟源的信号,都应被纳入时钟域清单。

二、用 SDC 定义时钟、生成时钟与虚拟时钟

SDC 约束是 Libero SoC 时序与 CDC 分析的输入。PolarFire 中常用的约束命令包括 create_clock、create_generated_clock、create_virtual_clock 以及 set_clock_groups。下面给出一个典型示例,假设外部输入 50 MHz 晶振,经 PLL 产生 100 MHz 和 25 MHz 两路输出,另有一个来自外部器件的虚拟时钟用于 I/O 时序分析。

# 1. 主时钟:外部 50 MHz 晶振,周期 20 ns
create_clock -name clk_ref -period 20.000 [get_ports REFCLK_0]

# 2. 生成时钟:PLL 输出 100 MHz,周期 10 ns
create_generated_clock -name clk_100m \
  -source [get_pins pll_inst/REFCLK] \
  -divide_by 1 -multiply_by 2 \
  [get_pins pll_inst/OUT0]

# 3. 生成时钟:PLL 输出 25 MHz,周期 40 ns
create_generated_clock -name clk_25m \
  -source [get_pins pll_inst/REFCLK] \
  -divide_by 2 \
  [get_pins pll_inst/OUT1]

# 4. 虚拟时钟:用于外部器件接口,不驱动内部寄存器
create_clock -name vclk_ext -period 8.000

# 5. 声明异步时钟组,避免跨域路径被误约束
set_clock_groups -asynchronous \
  -group {clk_ref clk_100m} \
  -group {clk_25m} \
  -group {vclk_ext}

这里有几个关键点。第一,create_clock 定义的是主时钟,必须指定周期和端口或引脚;第二,create_generated_clock 必须通过 -source 指向其源时钟,否则工具无法推导相位关系;第三,虚拟时钟不驱动任何内部寄存器,只用于描述外部器件的时序参考,常用于输入/输出延迟约束;第四,set_clock_groups -asynchronous 明确告诉工具这些时钟之间不需要做时序收敛,但 CDC 检查仍然需要它们之间的结构关系。

虚拟时钟的周期应设置为外部器件实际的工作周期,而不是随意填写。若虚拟时钟周期错误,输入建立/保持时间检查会得到错误结论。

三、频率与相位关系的分析

两个时钟域之间的关系可以从三个维度描述:频率比、相位关系和同源/异源属性。频率比决定了一个时钟周期内另一个时钟能采样多少次;相位关系决定了采样点落在数据有效窗口的哪个位置;同源/异源属性决定了是否可以用时序分析代替 CDC 同步结构检查。

以 100 MHz 和 25 MHz 为例,频率比为 4:1。若两者同源且相位固定,则 25 MHz 域每 4 个 100 MHz 周期采样一次,采样点位置可预测;若两者异源,则采样点相对数据变化沿的位置会持续漂移,必须使用同步器。PolarFire 的 CCC 输出之间通常保持确定相位关系,但不同 CCC 或外部恢复时钟之间则视为异源。

相位关系的描述需要统一参照系。本文约定:以源时钟的上升沿为 0° 参考,生成时钟的相位偏移以 -waveform 或 -phase 参数表示。例如,若 PLL 输出相对输入有 90° 偏移,可在生成时钟约束中写明:

create_generated_clock -name clk_100m_90 \
  -source [get_pins pll_inst/REFCLK] \
  -divide_by 1 -multiply_by 2 \
  -phase 90 \
  [get_pins pll_inst/OUT0]

相位偏移会直接影响跨域采样的建立/保持裕量。在 CDC 验证中,若两个时钟同源且相位固定,工具可以精确计算跨域路径的时序;若相位不确定,则必须按异步处理,插入两级触发器同步器或使用异步 FIFO。

PolarFire 时钟域识别与 SDC 约束关系图 外部晶振 REFCLK_0 · 50 MHz create_clock CCC / PLL pll_inst 源:REFCLK clk_100m 100 MHz · 周期 10 ns clk_25m 25 MHz · 周期 40 ns vclk_ext 虚拟时钟 · 8 ns clk_ref 域 寄存器组 A clk_100m 域 寄存器组 B clk_25m 域 寄存器组 C vclk_ext 域 外部接口 I/O CDC CDC CDC set_clock_groups -asynchronous group {clk_ref clk_100m} group {clk_25m} group {vclk_ext} 频率关系:clk_100m : clk_25m = 4 : 1(同源,相位固定) 相位关系:以源时钟上升沿为 0° 参考,-phase 90 表示滞后 90°;异源时钟相位不确定,必须按异步处理

四、约束检查与常见错误

写完 SDC 后,应在 Libero 中运行 report_clocks 和 check_timing,确认每个时钟都被正确识别、生成时钟的源关系被正确推导、没有未约束的时钟端点。常见错误包括:生成时钟缺少 -source 导致工具无法推导相位;虚拟时钟周期与实际外部器件不符;把同源但相位固定的时钟错误地放入异步组,导致 CDC 工具跳过必要的结构检查;以及分频逻辑输出未声明为生成时钟,使该域寄存器被当作无时钟路径。

不要用 set_false_path 代替 set_clock_groups -asynchronous 来处理跨域路径。前者会同时屏蔽时序分析和 CDC 结构检查,后者只屏蔽时序收敛,CDC 检查仍然有效。

最后,建议维护一份时钟域清单,至少包含:时钟名、源对象、频率、周期、相位偏移、同源/异源属性、驱动寄存器范围、是否已声明异步组。这份清单既是 SDC 编写的依据,也是后续 CDC 验证的输入。只有当时钟域被准确识别和约束后,跨域路径的同步结构检查、亚稳态分析和时序例外设置才有可靠的基础。