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 之间无需做时序分析,跨域路径由设计者通过同步器保证。若不做此约束,工具会尝试分析跨域路径,产生大量无法满足的时序违例,掩盖真正的问题。
约束建模的常见陷阱
第一,把 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”,则说明已正确设置为异步。时序收敛的第一步,就是让约束真实反映硬件的时钟结构。