3. Zynq时钟架构与时钟约束

Zynq 器件的时钟体系是时序收敛的起点。PS(Processing System)内部有独立的 PLL 组,PL(Programmable Logic)侧则依赖 BUFG/BUFH、MMCM、PLL 等时钟资源。约束文件若不能准确描述“时钟从哪里来、经过什么、最终到达哪个寄存器”,时序引擎给出的报告就只是数字游戏。本节围绕 PS 时钟生成、PL 时钟资源、MMCM/PLL 以及时钟域交叉,说明如何用 create_clock 与 create_generated_clock 正确建模。

PS 时钟生成:从晶振到各时钟域

以常见的 Zynq-7000 为例,PS 侧通常外接一颗 33.333 MHz 晶振,接入 PS_CLK 引脚。该时钟进入 PS 后先经过 PLL,再通过分频器产生 CPU、DDR、UART、SD、QSPI 等时钟。对时序约束而言,PS 内部时钟大多由工具自动推导,但 PL 侧使用的 FCLK_CLK0~3 必须显式约束,因为它们是从 PS 输出到 PL 的时钟源。

假设在 Vivado 的 PS 配置中,FCLK_CLK0 设为 100 MHz,FCLK_CLK1 设为 50 MHz。约束写法如下:

create_clock -name clk_ps_ref -period 30.000 [get_ports PS_CLK]
create_clock -name clk_fclk0 -period 10.000 [get_pins processing_system7_0/FCLK_CLK0]
create_clock -name clk_fclk1 -period 20.000 [get_pins processing_system7_0/FCLK_CLK1]

这里 PS_CLK 是物理输入端口,周期 30 ns 对应 33.333 MHz;FCLK 是 PS 内部输出引脚,周期 10 ns 对应 100 MHz。注意:FCLK 虽然由 PS 内部 PLL 生成,但对 PL 而言它是“外部输入”,因此用 create_clock 而非 create_generated_clock。若误用 generated clock,工具会尝试追溯 PS 内部路径,导致约束失效或报错。

PL 时钟资源:BUFG、BUFH 与时钟能力

PL 侧的时钟网络由全局时钟缓冲(BUFG)、区域时钟缓冲(BUFH/BUFR)和 I/O 时钟缓冲(BUFIO/BUFR)组成。BUFG 可驱动整个芯片的时钟网络,适合高扇出、跨时钟区域的信号;BUFH 只驱动单个时钟区域,功耗更低但覆盖范围有限。MMCM 和 PLL 的输出必须经过 BUFG 或 BUFH 才能进入时钟树。

在约束中,时钟缓冲本身不改变频率,但会影响时钟延迟和偏斜。若设计中使用了 BUFG,工具会自动将其识别为时钟路径的一部分,无需额外约束。但若使用了 BUFGCTRL 做时钟切换,则需要用 create_generated_clock 描述切换后的时钟,否则时序分析会丢失切换路径。

MMCM/PLL:倍频、分频与相移

MMCM 和 PLL 是 PL 内产生衍生时钟的核心。以 MMCM 为例,输入 100 MHz,通过 CLKFBOUT_MULT_F 和 CLKOUT0_DIVIDE_F 产生 200 MHz 输出。约束应写成:

create_generated_clock -name clk_mmcm_out \
  -source [get_pins mmcm_inst/CLKIN1] \
  -divide_by 1 -multiply_by 2 \
  [get_pins mmcm_inst/CLKOUT0]

其中 -source 指向 MMCM 的输入时钟引脚,-multiply_by 2 表示倍频 2,-divide_by 1 表示分频 1,最终得到 200 MHz。若 MMCM 还配置了相移,例如 CLKOUT0_PHASE = 90,则需在约束中补充 -phase 90,否则工具无法正确计算建立/保持关系。

PLL 的约束方式与 MMCM 类似,但 PLL 不支持小数分频和精细相移,约束参数更简单。实际工程中,若 MMCM 输出直接驱动 BUFG,get_pins 应指向 MMCM 的输出引脚,而非 BUFG 的输出,因为 generated clock 的源是 MMCM 输出。

时钟域交叉:同步与约束

当两个时钟域之间存在数据传递时,必须区分“同步交叉”和“异步交叉”。同步交叉指两个时钟同源且频率比固定,例如 100 MHz 和 200 MHz 来自同一 MMCM。此时工具可以自动计算跨域路径的时序,但需要用 set_clock_groups 或 set_false_path 明确关系。异步交叉指两个时钟完全无关,例如 PS 的 FCLK 与外部晶振。此时必须使用同步器(如两级触发器)并设置 set_false_path 或 set_max_delay -datapath_only。

以下约束示例描述两个异步时钟域:

set_clock_groups -asynchronous \
  -group [get_clocks clk_fclk0] \
  -group [get_clocks clk_ext]

该约束告诉工具:clk_fclk0 与 clk_ext 之间无需做时序分析,跨域路径由设计者通过同步器保证。若不做此约束,工具会尝试分析跨域路径,产生大量无法满足的时序违例,掩盖真正的问题。

Zynq 时钟架构与约束建模 PS 时钟生成 PS_CLK 33.333 MHz PS PLL 倍频/分频 FCLK_CLK0 100 MHz FCLK_CLK1 50 MHz PL 时钟资源与 MMCM/PLL MMCM 倍频/分频/相移 PLL 倍频/分频 BUFG 全局时钟 BUFH 区域时钟 create_generated_clock -source CLKIN1 -multiply_by 2 -divide_by 1 输出 200 MHz,经 BUFG 进入时钟树 时钟域交叉(CDC)与约束策略 时钟域 A clk_fclk0 (100 MHz) 源寄存器 时钟域 B clk_ext (50 MHz) 目的寄存器 同步器 两级触发器 / 异步FIFO set_false_path 或 set_max_delay set_clock_groups -asynchronous -group [get_clocks clk_fclk0] -group [get_clocks clk_ext] 异步时钟域之间不做时序分析,由同步器保证数据安全 PS 时钟 PL 时钟

约束建模的常见陷阱

第一,把 FCLK 写成 generated clock。FCLK 对 PL 是输入时钟,应使用 create_clock。第二,MMCM 的 -source 指向错误。应指向 MMCM 的输入引脚(如 CLKIN1),而非输入端口或 BUFG 输出。第三,忘记相移参数。若 MMCM 配置了 CLKOUT0_PHASE,约束中必须用 -phase 描述,否则建立/保持分析会偏差。第四,异步时钟域未加 set_clock_groups,导致工具在无关路径上浪费分析资源并产生虚假违例。

实用检查清单:
  • PS_CLK 是否用 create_clock 约束?周期是否与晶振一致?
  • FCLK_CLK0~3 是否用 create_clock 约束?周期是否与 PS 配置一致?
  • MMCM/PLL 输出是否用 create_generated_clock?-source 是否指向输入引脚?
  • 倍频/分频/相移参数是否与 IP 配置一致?
  • 异步时钟域之间是否设置了 set_clock_groups -asynchronous?
  • 跨域路径是否使用了同步器?是否设置了 set_false_path 或 set_max_delay -datapath_only?

完成上述约束后,可通过 report_clocks 查看时钟树是否完整,通过 report_clock_interaction 检查时钟域之间的交互关系。若某两个时钟被标记为“Timed”,说明工具会分析它们之间的路径;若标记为“Asynchronous”,则说明已正确设置为异步。时序收敛的第一步,就是让约束真实反映硬件的时钟结构。