上篇文章写了7系列FPGA的时钟资源,评论区不少朋友催更,说要做UltraScale却搞不清楚新的时钟架构。确实,从7系列切到UltraScale/UltraScale+,最让人头疼的就是时钟这一块——名字看着差不多,实际的路由方式和资源细节全变了,连做约束的习惯都得改。这篇就把UltraScale系列的时钟资源从头到尾捋一遍,讲清楚它和7系列的本质区别,再说说不同场景下怎么选、怎么用。
这篇文章适合正在做UltraScale/UltraScale+项目选型的人、刚接触这个系列想快速上手的人,以及遇到了时钟约束不过、时序收敛不了、LVDS/MIPI采集误码这类问题的人。关于MMCM/PLL的动态相移、进位链TDC的时间测量、图像传感器像素时钟处理这些高频场景也会带一嘴,但主线还是时钟资源本身。
1. 先搞懂UltraScale的时钟拓扑到底变在哪
很多从7系列迁移过来的工程师第一个反应是:BUFG还在,MMCM还在,名字都差不多,是不是直接替换就行?千万别这么想。UltraScale的时钟架构在三个层面做了大改动:全局时钟变成了真正的两级结构、时钟区域细分出独立的I/O时钟域、MMCM的输出路径上加了额外的时钟选择逻辑。这些改动对时序收敛和资源利用的影响极大。
1.1 两级时钟路由:全局时钟树和区域时钟树
7系列里的BUFG驱动的全局时钟网络是贯穿整个芯片的单一树状结构,所有区域共享同一棵时钟树。UltraScale改成了两级:全局时钟树加上区域时钟树。全局时钟树仍然覆盖整个器件,叫Global Clock Tree,区域时钟树则按Clock Region划分,每个区域有自己的时钟资源。
具体来说,UltraScale里水平方向分成若干个Clock Region,垂直方向上全局时钟树和区域时钟树是分离的。一个BUFG输出首先进入全局时钟树,然后每个区域可以选择从这个全局树“接水”进来,驱动本区域的时序单元。区域时钟树由BUFG_REG和BUFCE_REG控制,它们本质上是一个可选的区域级时钟门控,能够把全局时钟树分叉到特定的物理区域。
这意味着什么?如果你全部逻辑都用全局时钟,完全可行,但时钟功耗和资源占用都不是最优的。更合理的做法是:早期就用区域时钟把不同功能模块隔离,比如把一组LVDS采集通道的全部逻辑约束在一个区域,把图像处理管线约束在另一个区域。这样不仅控制功耗,还能减少不同模块之间的时钟串扰。
1.2 时钟输入IO与内部时钟分配的关系
UltraScale的时钟输入IO也有讲究。MRCC(Multi-Region Clock Capable)和SRCC(Single-Region Clock Capable)这两类引脚仍然是时钟输入的主力,但行为发生变化——MRCC驱动的时钟可以进入相邻多个区域,SRCC只能驱动本区域。另一个关键区别:UltraScale里MRCC/SRCC不像7系列一样通过BUFG绕一圈,而是直接进到区域的BUFG_GT或BUFCE,再进入逻辑,路径上少一级延迟。
实际做FPGA设计的时候,外部晶振或设备给的参考时钟接到哪个脚、用什么类型引脚,不光是引脚约束的问题,它直接决定了这个时钟能覆盖多少区域。比如MIPI D-PHY的时钟恢复出来以后,如果希望它只驱动本区域的接收逻辑,用SRCC就够了;如果后续还要把数据送到另一个区域做处理,就得规划好走MRCC或者再跨一次全局树。
有一点容易踩坑:UltraScale+上部分MRCC引脚和普通IO是复用的,需要在XDC里通过“IO Standard”和“Pin Name”明确指定。项目里如果用了厂商的例化模板没留意这个细节,经常会出现“时钟约束合法,但时序一直不过”的错案。
2. 把时钟缓冲器家族一次说透
型号一多,选择困难症就来了。BUFG、BUFGCE、BUFGCE_CT、BUFG_GT、BUFH、BUFIO、BUFR……每个都有自己适合的场景。我对UltraScale+实际项目中各种缓冲器的定位做了一个总结,列在下表。
| 缓冲器 | 主要作用 | 典型场景 | 需要注意的问题 |
|---|---|---|---|
| BUFG | 驱动全局时钟树 | 系统级的低速/高速时钟 | 功耗最高,覆盖全器件 |
| BUFGCE | 带时钟使能的全局缓冲 | 动态开关时钟、低功耗管理 | 门控时钟沿要注意毛刺 |
| BUFGCE_CT | 带时钟使能和跨域控制的全局缓冲 | 多时钟域切换、复用I/O时 | 切时钟顺序务必按手册来 |
| BUFG_GT | 从GT收发器的时钟输出引入全局树 | 高速串行收发器关联逻辑 | 绑定GT的专用位置,不能随机选 |
| BUFH | 水平方向区域时钟缓冲器 | 单区域或少数区域的逻辑 | 跨区域需要额外BUFG资源 |
| BUFIO | 驱动I/O逻辑时钟 | LVDS/IO接口数据捕获 | 仅用于IO逻辑,别拿去驱动普通逻辑 |
| BUFR | 区域时钟分频器 | 低速IO时钟域、源同步接口 | 分频输出注意相位对齐 |
2.1 BUFG与BUFGCE的选择逻辑
有的人喜欢把能省的省掉,别人都在用BUFG他偏要用BUFGCE,到了后期才发现时钟使能信号的时序根本hold不住,一路追到极点。这个问题的本质在于:BUFGCE的“CE”是异步使能,只有使能信号在时钟高电平期间稳定时,输出时钟才没有毛刺。如果使能信号由逻辑产生,并且和时钟不同步,毛刺风险非常大。
选择逻辑其实很简单:如果这个时钟始终运行,或者你仅仅需要在上电复位阶段保持关闭,那直接用BUFG就够了,别加多余的使能;如果这个时钟需要在运行中关闭再打开(比如传感器空闲时切掉像素时钟降低功耗),那只能上BUFGCE,但使能信号必须由同步逻辑打两拍产生,最好还在同一时域内。
2.2 BUFH与BUFIO在LVDS采集中的配合
做LVDS源同步接收的时候,除了用IO逻辑自带的东西之外,通常还需要把随路时钟引入BUFIO,驱动内部的ISERDES/IDDR采样逻辑。这时BUFIO就是唯一正确选择,因为它直接驱动I/O逻辑,延迟最小,不经过全局树。但别忘了,BUFIO覆盖范围只限于本区域内的IO逻辑,如果接收逻辑在另一区域,就必须把时钟再用BUFH或者BUFG引过去。
我做过一个8通道LVDS图像采集的设计,每个通道一组随路时钟和数据,最初方案是所有通道各用各的BUFIO,数据解析逻辑放在同一个区域。后来因为布局太紧凑,两组通道的解析逻辑跨了区域,就发现其中一组的采样时序总是不稳定——查下来是BUFIO的时钟只覆盖了物理上的IO bank区域,而解析逻辑在外部区域,时序偏了。加了一个BUFH把时钟转发过去才解决。这个细节在时序报告里其实能看到:跨区域的路径,时钟延迟明显比同区域多几个百皮秒。
3. MMCME3与PLL,别再闭眼Instantiation
UltraScale/UltraScale+里的时钟管理模块叫MMCME3(UltraScale上叫MMCME3,UltraScale+上也是MMCME3,只是工艺和电压不同),和7系列的MMCME2相比,最明显的改进就是输出时钟可以做到更高的频率精度和更细的相移步进。但很多人拿到IP核直接默认配置,频率对了就交付,忽略了VCO范围和环路带宽的关系,最后分配出来的时钟抖动大得惊人。
3.1 MMCME3架构要点:从VCO到输出通道的理解
MMCME3的本质是一个锁相环加后置分频器。输入参考时钟经过鉴相、电荷泵、压控振荡器(VCO)之后产生一个高频时钟,然后这个VCO时钟通过分频器得到多个输出。
这里有一个最常见的错误:VCO频率范围在UltraScale+的MMCME3里大概是600MHz~1200Hz,如果你要产生一个250MHz的时钟,VCO如果设在500MHz,分频器用整数分频2,那VCO不在范围内;如果VCO设在750MHz,分频3,就在范围内。但如果你设了“Integer”模式的MMCM,VCO频率由输入参考时钟和M系数直接决定,不一定能命中范围,这时工具就会自动把M系数调大,而调大M系数会放大参考时钟抖动,最终输出的时钟抖动反而变差。
正确做法:先确认VCO落在额定范围内,且尽量让M系数小于等于某个阈值(一般建议在40以下),让反馈回路的带宽落在合理区域。实际调试中,如果参考时钟本身有100ps级别的抖动,差参数设置下输出的clock jitter可能从150ps涨到300ps——LVDS接口马上开始误码。
3.2 动态相移和延迟调节
很多图像传感器和MIPI接口需要调整采样时钟与数据的相位关系,这时候MMCM的Dynamic Phase Shift(动态相移)功能就派上用场了。UltraScale+的MMCME3相移步进是VCO周期除以56,不再是7系列那么粗糙的“最大1/8输出周期”。这意味着你可以更精细地调整采样点,对高速接口非常有利。
但相移操作有个前提:必须在时钟稳定后进行操作,并且一次只能移一个步进,两个步进之间需要留出clock cycle的间隔,避免瞬间的相位跳变导致逻辑采样错误。之前看到有工程把相移当成普通的寄存器写操作,在一个pulse里连移好几个步进,结果接收端的数据瞬间全错,排查了半天才发现是相移时序违规。
如果你的项目里要做基于进位链的TDC时间测量,时钟的相位稳定性尤其重要。TDC测量的是数据信号和参考时钟沿之间的时间间隔,任何参考时钟的抖动或者相位跳变都会直接变成测量误差。用MMCM的动态相移做粗略标定是可以的,但最终精度还得靠稳定时钟源和物理约束。
3.3 锁相环参数选择与环路带宽
MMCM/PLL的环路滤波器参数在FPGA里一般是对外部可见的有限选项,像“Bandwidth”有Low/Normal/High三档。默认的Normal适合多数场景,但如果你参考时钟的噪声大(比如来自板上PLL的时钟),可以选Low,让环路滤掉更多高频噪声;如果你参考时钟很干净但需要快速锁定,选High。
这里有一个值得注意的点:“Low带宽”同时意味着锁定时间更长。在需要频繁切换频率的应用(比如频率测量、动态重构)里,锁定时间可能从几百微秒变成几毫秒。做上电流程设计时,如果先把MMCM锁定信号作为复位释放条件,低带宽模式下上电到正常工作的时间就会明显变长,得在外部留够等待时间。
4. 实操环节:几个典型场景的时钟方案
理论说再多,不如直接看几个具体场景怎么搭时钟。下面我按实际项目里最常遇到的几个场景展开。
4.1 LVDS高速采集的时钟设计与约束
LVDS接收是现代图像采集、ADC数据采集中最常见的接口,而LVDS能否稳定工作,几乎完全取决于随路时钟的处理质量。
步骤拆开来看:
- 输入引脚约束为LVDS标准,随路时钟引脚用SRCC/MRCC类型;
- 时钟进入BUFIO或者直接利用IOBank的专用延迟结构,驱动ISERDESE3的CLK端口;
- 数据位用IDELAYE3做逐位延迟对齐,时钟路径用MMCM做相位微调;
- 时钟约束方面,用set_input_delay约束随路时钟与数据的关系,同时把VCO频率设到接近最佳工作点。
常见的失误是:IDELAYE3的延迟值没有做training,而是拍脑袋设了一个固定值。不同通道之间的走线长度差异和SDR采样窗口宽度完全不一样,引脚延迟不一致会导致某些lane完全收不到数据。正确做法是利用逻辑产生训练序列,用状态机扫描IDELAY值,直到找到最大眼图中心。
4.2 基于进位链TDC的时钟分配与优化
TDC(Time-to-Digital Converter)在高精度测距、激光雷达、医疗成像里都是核心模块,现在主流FPGA方案都是利用进位链的传播延迟做细粒度时间测量。有的项目想做到皮秒级别的精度,这时候时钟资源和布局的讲究就非常大。
TDC通常需要两个时钟:一个高频基准时钟驱动进位链的起点,另一个低速时钟驱动计数器和数据采集逻辑。高频基准时钟往往通过MMCM产生,且到达进位链起点区域的时刻必须非常稳定。原因是:基准时钟沿到达的微小抖动会被TDC解释成几十皮秒甚至上百皮秒的时间误差。
实操建议是:基准时钟用BUFG驱动,并且用set_clock_groups隔离低速时钟域和测量时钟域,避免低速时钟的频繁翻转串扰到基准时钟;进位链所在的CLB列最好紧贴产生基准时钟的MMCM所在的clock region,缩短时钟走线长度。此外,做完布局后用反馈显示查看时钟走线,确认没有绕远路。
4.3 MIPI/图像传感器接入的像素时钟处理
MIPI D-PHY接入FPGA的常规做法是:外部MIPI信号先经过电平转换,高速时钟在IO逻辑里恢复,低速像素时钟通过时钟恢复模块产生。UltraScale+里推荐的做法是使用Xilinx的MIPI RX IP或自己实现一个D-PHY接收差分对,恢复出来的Byte Clock再进入BUFG/BUFH,驱动后级图像处理管线。
图像处理的难点在于跨时钟域。传感器输出的像素时钟域和处理管线的核心时钟域往往是异步的,处理模块必须处理好FIFO缓存和同步握手。这时候不仅要考虑时钟频率匹配,还要考虑缓存深度和数据突发长度。如果传感器输出分辨率是1080p@60Hz,像素时钟约148.5MHz,而处理管线核心跑在200MHz,FIFO深度至少要覆盖一帧内最长的突发间隙,不然就会丢帧。
4.4 频率测量设计的参考时钟处理与精度提升
频率计、周期仪这类设备用FPGA实现,核心就是测频。常见的做法有直接计数法、等精度测量法、游标法。这里重点讲时钟资源在等精度测量里的影响。
等精度测频法本质上是用一个高频参考时钟作为时间基准,同时测量输入信号的边沿数和参考时钟的计数值。参考时钟的精度和稳定性直接决定测量精度。如果参考时钟来自板上普通晶振,温度漂移可能会让你测出来的频率差出几十ppm;使用TCXO或者OCXO,配合MMCM倍频到500MHz以上,可以把分辨率提升一个量级。
MMCM在这里的作用不只是倍频,还能生成同步的闸门信号脉冲。但要注意:把输入的待测信号和参考时钟跨时钟域比较边沿时,亚稳态是躲不开的,必须用两级或三级同步器消除亚稳态。这时候同步器用的时钟最好和参考时钟同源,否则同步器的MTBF依然不够看。
5. 常见问题与排查技巧实录
以下是过去几年实战中积累的高频问题,按出现频率排列,每个都给出排查思路和解决办法。
| 问题 | 现象 | 排查思路 | 解决方案 |
|---|---|---|---|
| LVDS高速采样误码 | 图像有雪花点、ADC采到的值跳变 | 先看时序报告中的input delay path | 检查IDELAYTAP是否训练到位;MMCM相移调整到眼图中心 |
| 时钟抖动偏大 | 高速接口间歇性错误 | Vivado里查看Clock Jitter报告 | 调整MMCM的VCO频率和M系数,避免M过大 |
| 跨区域时钟路径差 | 部分区域时序长时间无法收敛 | 看Clock Region布局 | 使用BUFG或BUFH跨区转发时钟;物理约束区域 |
| 时钟域亚稳态 | 偶发数据错误 | 用ILA看跨时钟域信号的采样点 | 加两级同步器,数据加FIFO或握手 |
| 上电后MMCM不锁定 | 外部状态机卡在等待锁定信号 | 检查参考时钟是否稳定到 | 等待时间加长;检查参考时钟引脚类型 |
| TDC测量结果噪声大 | 测时间间隔数值跳动明显 | 检查基准时钟抖动、电源噪声 | 用低抖动时钟源;进位链区域与时钟区域对齐 |
5.1 时钟抖动导致LVDS误码的现场排查
一个刚上电跑的板子,图像偶尔花一下,频率不高但很恼人。
第一步我会先看时序报告。导入约束之后的“clock uncertainty”如果已经超过100ps,而采样窗口只有几百ps,基本就可以断定问题出在时钟抖动上。打开“Report Clock Networks”,查看MMCM输出的jitter值,确认是输入参考时钟抖动大还是MMCM配置不佳。
多数时候原因是MMCM的M系数太大。比如外部参考100MHz,想要700MHz VCO,M=7,N=2,分频后D=2,这组参数很合理;但有些人图省事用自动模式,工具为了满足输出频率,把VCO拉到1200MHz附近,M系数变成12,抖动自然涨上去了。手动把VCO设到800MHz左右,重新选一个能整除的分频比,问题基本就能解决。
5.2 异步时钟域亚稳态与同步器设计
所有做过FPGA的人都会遇到跨时钟域问题,但很多人以为加了同步器就万事大吉。实际上同步器解决的是“什么时候采到稳定数据”,而不是“数据会不会丢”。如果目标时钟域的周期比来源时钟域的数据持续周期还短,同步器依然会丢掉数据。
处理方案通常是异步FIFO。用FPGA里的FIFO硬核,或者自己实现格雷码指针同步。用UltraScale+时我习惯优先用硬核FIFO(FIFO36E2/SDP),延迟、功耗都更可控。跨时钟域的复位也是一个坑:异步复位在多时钟域里释放时必须要做复位同步器,否则系统一跑就出现内部状态初始化失败。
5.3 时钟资源不足或布局冲突
一个大型图像处理工程,模块拆得细,每个模块都例化自己的MMCM+PLL,后面用CLOCKING IP检查的时候发现时钟资源已经用了80%以上,后面想加新功能都没得分配。
提前规划永远是上策。从一开始就把系统级时钟分成三个域:IO接口时钟域、核心处理时钟域、低速配置时钟域。每个域尽量只用一个MMCM,所有频率都在这个MMCM上扩展而来。实在需要独立抖动特性的,再申请独立的MMCM。有了这个规划,99%的项目不需要担心时钟资源不足。
5.4 时钟功耗优化
功耗敏感的嵌入式项目里,时钟网络的功耗占比不容忽视。一个跑着大批量数据的高端FPGA,时钟翻转占动态功耗的比例可能到30%。
优化的方向有三个:
- 优先用BUFGCE/区域时钟而不是无脑BUFG,让不活动区域的时钟停掉;
- 低频时钟用区域分频器BUFR产生,避免从高频时钟源下来一路分频;
- 合理利用UltraScale+的时钟门控,把待机时不需要的视角管线时钟全部门控掉。
这几个方法配合起来,功耗能省下明显的一截,尤其是传感器接入但没有图像处理任务时的待机场景。
5.5 时钟约束检查的两个实用小技巧
做完时钟配置,别急着跑实现,先跑一次“Report Clock Utilization”和“Check Timing”两个报告。很多Vivado工程里clock utilization 100%出现时,未必是真的物理资源不够,常常是你的时钟约束里写多了create_clock,把本来可以分频的时钟都当成独立时钟建了。用“derive_pll_clocks”替代手写的所有MMCM输出时钟约束,能省很多麻烦。
另外,跨时钟域路径的约束不可省略。两个异步时钟之间的路径若不设置set_clock_groups或set_false_path,时序分析工具会报一堆无可收敛的violation,全部变成噪音。规范地标记每一个异步域的边界,后期发现问题时你才能快速定位是哪一段逻辑出了问题。
6. 关于“时钟资源”系列接下来的思路
这个主题写成系列的话,下一步肯定要把UltraScale+和Versal做对比。Versal的时钟架构再次升级,加入了AI引擎、NoC、可配置时钟网络,和传统FPGA时钟资源完全是两个物种,值得单独开一篇。另外,关于“时钟约束的艺术”,我之前挖坑说要写set_clock_groups与set_false_path的完整实战案例,这个坑也该填了。
我在实际项目中做过一套比较别扭的设计,整个系统里有5个异步时钟域交叉,开始用set_false_path大刀阔斧一刀切,后来发现功能验证的时候偶发问题无法复现。一步步把每个异步域边界找出来,明确哪些路径确实不需要时序检查、哪些路径其实可以同步化,验证问题就消失了。想表达的观点很简单:时钟资源用得再漂亮,约束没有本质地跟上,设计依然是跑不起来的。另外再分享一个小技巧——做板级调试时,在Vivado里打开“Clock Region View”看看自己用了多少区域的时钟,很多莫名其妙的问题只要布局时钟区域错开一位就能解决。