ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA差分时钟PLL配置全流程:从Vivado IP核到XDC约束与上板调试

FPGA差分时钟PLL配置全流程:从Vivado IP核到XDC约束与上板调试 很多刚接触FPGA的朋友第一次在Vivado 2020.2里配置PLL IP核时最大的感受就是“界面看得懂问题一脸懵”。你照着视频把Clocking Wizard拖出来填了输入频率、加了输出时钟综合也过了结果上板之后发现lock信号死活不拉高或者输出时钟频率根本不对。这篇文章就围绕PLL配置这条主线从差分时钟输入的底层逻辑到IP核界面逐项拆解再到XDC约束和上板实测把这条路上的坑完整趟一遍。适合正在用Vivado做第一个带PLL项目的FPGA新手也适合调板子时被时钟问题卡住、想系统梳理一遍的朋友。1. 先理解PLL在FPGA里的位置为什么差分时钟输入是新手第一道坎1.1 锁相环到底在帮你干什么FPGA内部所有逻辑都依靠时钟沿驱动但板载晶振通常只提供一个固定频率。一个完整项目里DDR需要几百兆的时钟串口需要几十兆的波特率时钟逻辑核心又可能需要和输入数据同频同相的时钟。如果全靠计数器分频相位关系难保证抖动也会越积越大。PLL锁相环解决的就是“从单一参考时钟干净地生成多个不同频率、不同相位时钟”的问题。用生活场景类比操场上有几个小组要同时报数如果每个人各按自己的表掐时间时间一长必然乱。PLL就像总控台不停监听主表输入参考时钟再对每个小组发出统一校准后的报时信号。它内部有一个压控振荡器VCO通过分频器、鉴相器和环路滤波器组成反馈回路让输出时钟与输入参考时钟保持精确倍数关系。FPGA里有了PLL才有可能把系统时钟、外设时钟、接口时钟分开管理互不干扰。在Xilinx 7系列里时钟管理单元主要是PLL和MMCM两类。PLL结构相对精简倍频分频、相位调整都能做适合普通应用。MMCM则额外带动态相移等高级能力适合对时钟相位有严格要求的场景。新手阶段用PLL就够但Vivado的Clocking Wizard里两者都提供选型逻辑后面细说。1.2 为什么板级设计偏爱差分时钟而不是单端时钟差分时钟是用一对物理走线传输两个极性相反的信号接收端对两条线做差得到有效信号。这种结构的抗共模干扰能力很强电源地线上的噪声、外部电磁干扰会同时叠加在两根线上做差之后被自然抵消。因此高速板级系统比如网络交换、高速ADC/DAC普遍用LVDS、LVPECL这类差分时钟来保证信号完整性。FPGA开发板上其实也大量使用差分晶振。比如很多板卡上有一颗125MHz或100MHz的差分晶振输出一对等长的P/N走线进入FPGA的专用时钟引脚。它的优势不仅仅是抗干扰电压摆幅小、上升沿陡峭也让PLL在高频输入时更容易锁住低抖动的参考源。很多新手第一次看到原理图上写着“CLK_P”“CLK_N”这样的网络名第一反应是“两个时钟那我要用哪个”其实不是两个独立的时钟而是一对差分信号。引脚P和N必须同时接到FPGA上在FPGA内部通过IBUFDS原语或IP核自动插入的输入缓冲把差分对恢复成单端时钟再送入全局时钟网络。1.3 新手最常见的第一类错误差分当成单端用我在带新人的时候遇到过好几次类似情况开发板原理图上明明是差分时钟有人图省事只接P端另一个引脚悬空还有人误以为差分输入跟普通差分IO一样接到任何支持差分标准的引脚上都行。这都会导致一个结果——PLL不锁定或者虽然偶尔能锁但工作几分钟后丢锁。核心原因是时钟信号进入FPGA之后不是随便一根引脚都能接到PLL输入端的。7系列FPGA里有专门的时钟能力引脚比如MRCC和SRCC它们才具备接入全局时钟网络、直达时钟管理单元的物理通路。普通IO引脚即使电平标准配成LVDS也没有对应的内部时钟路径信号进不到PLL里。判断方法很简单打开板卡原理图找到差分时钟连接到的FPGA引脚编号再去器件封装引脚图里查这个引脚属于什么类型。如果引脚名字里带MRCC或SRCC说明它确实具备时钟输入能力。如果只是普通IO那就需要确认硬件上是否经过其他时钟资源转发不能想当然。另一个容易出问题的情况是板上虽然接了差分时钟但N端被硬件设计成悬空或接地这时就别在IP核里选差分输入老老实实按单端处理否则IBUFDS会检测不到差分对PLL同样无法工作。2. Vivado 2020.2里创建PLL IP核Clocking Wizard配置面板逐项拆解2.1 打开IP Catalog选PLL还是MMCM在Vivado 2020.2里新建RTL工程之后左侧Flow Navigator里找到IP Catalog也可以在Tcl Console里输入manage_ip快速打开。搜索框输入clk或clocking能看到Clocking Wizard这个IP核2008年之后的Xilinx工具基本都用它来配置PLL和MMCM双击打开。第一步是设置Component Name我习惯命名为pll_clk或者clk_gen命名清晰后面例化才不容易看混。接下来进入Clocking Options页面里面会有Clocking Architecture的选项通常给三个PLL、MMCM、PLL and MMCM。普通倍频分频项目选PLL即可如果后续计划用动态相移、动态重配置这类功能选MMCM不确定又不想以后改IP可以直接选“PLL and MMCM”让工具根据配置自动选。但是PLL和MMCM在物理资源上不同7系列一个时钟区域内PLL和MMCM各有各的通道选“自动”不会让你占两份资源只是工具挑一个合适的例化。这个选择背后的逻辑值得多说一句。MMCM在7系列里相当于PLL的超集很多资料里会直接说“MMCM就是PLL加动态相移”所以有人干脆一律选MMCM。从功能上确实没问题但PLL在普通场景下的资源占用、配置复杂度和抖动优化会更简单直接而且PLL的VCO范围往往比MMCM更宽盲目用MMCM有时反而把自己框住。我的建议是能用PLL解决的先别上MMCM。2.2 Input Clock区域告诉工具你喂进来的是什么进入Clocking Options页面后最上面就是Input Clock区域。这里需要填写输入时钟的实际频率单位是MHz。如果板载差分晶振是100MHz就填100.000。很多人觉得填个整数就行实际上Vivado会根据这个频率去反推PLL内部的M、D、O参数频率填得不准确工具虽然不会报错算出来的参数可能落在边界值上上板后稳定性会变差。带有小数的频率比如12.288MHz这类音频领域常用频率更要按数据手册精确填写。接下来是输入时钟类型选择。在Input Clock的Source选项里可以选择单端或差分类别。选Differential之后工具会自动在当前IP内部插入IBUFDS原语把外部的clk_in1_p和clk_in1_n转换成单端时钟再送给PLL。这点对新手特别友好因为你不需要自己在顶层额外例化IBUFDS只要在RTL端口声明里把差分对引进来即可IP内部已经处理好了。还有一个选项是Input Jitter单位是ps。这个数值代表了输入时钟本身的抖动如果硬件上用的是高精度晶振保持默认值或者填小一点就行如果是通过其他芯片转出来的时钟抖动可能比较大适当把这个值调大工具在做时序分析时会按更宽松的抖动预算来评估。新手不用太纠结这个数保持默认通常不会出问题。2.3 Output Clocks区域目标频率组合背后的约束逻辑配置完输入点击Output Clocks页面。左边可以添加多路输出时钟默认有一路右键“Add Output Clock”可以继续加。每一路需要设置输出频率、相位、占空比Vivado会根据所有输出频率的组合自动计算PLL内部最合适的倍频和分频系数。比如项目需要200MHz的系统主频、50MHz的外设时钟、25MHz的串口参考时钟我就在三路输出里分别填这三个频率。工具会把M、D、O参数自动算好并显示在页面下方。如果有人想手动修改这些参数工具会实时检查这些值是否落在PLL物理能力范围内比如VCO频率不能超过器件速度等级对应的范围如果超了页面会出现红色警告同时生成bitstream时也会报错。Output Clocks页面还有几个容易被忽略但有实际影响的选项。一个是输出时钟的相位默认都是0度如果你需要和某路输入错开90度或180度需要在这里显式填写。另一个是占空比默认50%但在某些奇数分频系数下PLL可能无法给出严格50%的占空比工具会自动调整并给出提示。对串口、SPI这类接口影响不大但如果是高速ADC的采样时钟占空比失真会直接影响采样精度需要额外关注。页面下方还有一个Jitter Optimization下拉框选项包括Balanced和Minimize Output Jitter。默认Balanced会在多种指标之间取平衡适合大多数场景。如果项目对输出抖动特别敏感可以选Minimize Output Jitter代价是VCO参数选择范围变小可能会影响其他输出频率的组合。实测经验是新手阶段保持Balanced即可等后续用示波器测到具体抖动再回来调。3. 不报错不代表能用VCO范围、M/D参数和输出组合的联动机制3.1 VCO范围是怎么算出来的PLL内部的基本关系可以用一个公式概括Fvco Fclkin / M × D。这里的M是输入预分频系数D是反馈乘法系数O是输出分频系数最终某一路输出频率Fout Fvco / O。举个例子输入100MHz如果M1D8那么VCO就是800MHz要让输出得到200MHzO就选4要得到50MHzO就选16要得到25MHzO就选32。这一组参数能让三路输出同时满足。为什么VCO范围这么重要因为VCO的频率决定了PLL能否在物理层面稳定工作。以最常见的Artix-7 -1速度等级为例VCO范围通常是600MHz到1200MHz之间不同速度等级略有差异。VCO太低环路里的分频器工作频率过低锁定时间会变长抖动也会变大VCO太高芯片内部的压控振荡器功耗、噪声都会恶化甚至超出工艺能力。因此工具在自动计算参数时会优先让VCO落在范围内而不是只看某一路输出是否算得出来。碰到多路输出频率差异很大的情况工具会寻找一个公共的D值协调各路的O值。比如既要有400MHz的高速时钟又要有25MHz的低速时钟如果D设成8VCO800MHz那么400MHz的O225MHz的O32两个O值都在合理范围就能同时满足。如果强制把一个参数改成很偏的值VCO可能在600MHz以下另一路输出就凑不出合适的O值工具就会报方案无效。3.2 界面里那些“变灰”的参数为什么不能乱动Clocking Wizard页面里有一部分参数是浅灰色不可修改的或者修改后会自动跳回默认值。常见的是M、D、O三个系数以及反馈模式的选择。工具默认使用Internal feedback模式也就是PLL对VCO输出直接取样分频后送回鉴相器从而保证输出与输入之间的倍频关系。新手最容易犯的错是看M值不顺眼觉得改成2更“舒服”结果VCO范围被破环综合时没报错上板后PLL就是不锁。另一个不能乱动的是PFD输入频率。PFD输入频率等于Fclkin / M它代表了输入参考时钟经过预分频后进入鉴相器的速率。PFD太低环路带宽难以覆盖需要的抖动抑制范围PFD太高鉴相器本身无法处理。工具自动算出的M值通常就是为了把PFD放在合适的区间。手动改M等于把PLL整个环路的工作点全部改掉这种问题在仿真里看不出来只有上板才会暴露。真正的操作建议是先尽量写好期望的输出频率让工具自动分配系数。如果工具提示无法满足优先调整其中某一路输出频率或者允许Vivado自动调节D来拉高VCO而不是强行手填一个“看起来合理”的M/D。我之前遇到过有人为了凑200MHz输出把M改成5D改成7综合没问题实测时钟频率偏了百分之几查了老半天才发现是人为干预导致VCO落在了边缘区。3.3 一个实际例子100MHz差分输入输出三路时钟用工程里常见的一组配置来走一遍。板载差分晶振100MHz需要三路输出200MHz主时钟、50MHz外设时钟、25MHz串口参考时钟。在Clocking Wizard里输入频率填100.000输入类型选差分三路输出分别填200、50、25工具自动给出参数大约为M1、D8、VCO800MHz三路O值分别为4、16、32。锁相环最终能在600-1200MHz范围内工作整体余量充足。如果我把输入频率从100MHz改成75MHz还想输出200MHz工具就会重新算M1D8时VCO600MHz200MHz输出的O3。O3是奇数分频PLL的占空比很难做到严格50%工具会自动提示占空比不是50%。如果此时又加入一路输出50MHzO12是偶数没有问题。可见输入频率变了输出组合的可行性也会跟着变化一切都围绕VCO和整数分频展开。这类计算其实不需要新手自己在纸上算Vivado会自动完成。但了解公式能帮你判断一个组合为什么报不出来也能在调试时快速锁定问题区间。比如工具提示“VCO frequency out of range”而输出频率又必须保持这时候最实用的做法是寄回输入频率看看是不是输入本身就和目标输出频率形成了无解组合。4. 从原理图到XDC差分时钟引脚约束和时钟约束的完整写法4.1 RTL顶层端口怎么声明差分时钟使用Clocking Wizard时如果IP配置里选择了差分输入生成的IP核对外的输入端口会是clk_in1_p和clk_in1_n两个信号。因此RTL顶层只要声明一对input类型的差分时钟端口再把它们接到IP核的例化端口上就行。一段简化的Verilog例化大概是这样的module top( input wire clk_p, input wire clk_n, input wire rst_n, output wire led, // ...其他端口 ); wire clk_200m; wire clk_locked; pll_clk u_pll_clk( .clk_in1_p (clk_p), .clk_in1_n (clk_n), .clk_out1 (clk_200m), .locked (clk_locked) ); // 复位同步、逻辑、ILA... endmodule这里注意两点。第一不要再额外例化IBUFDS去处理clk_p和clk_nIP内部已经做了。第二IP输出端口上可能还会有clk_out2、clk_out3如果没用到就不要悬空要么在IP里关掉多余输出要么在RTL里接一个wire再悬空。悬空的未连接输出在某些版本的Vivado下会引发警告虽然不影响上板但看着难受。如果不喜欢IP内部自动处理差分输入也可以自己在顶层例化IBUFDS再把差分转换后的单端信号接到IP的clk_in1单端端口上。两种方式都能用我推荐前一种因为IP配置界面里选差分后工具生成的例化模板已经保证IBUFDS连接正确减少手工出错的概率。生成的例化模板可以在IP核的 .veo 文件里找到直接复制最省事。4.2 XDC约束文件引脚位置、电平标准、时钟周期差分时钟在UCF/XDC里的约束比单端时钟要稍微多两行。以Artix-7开发板上常见的100MHz LVDS差分时钟为例假设它接在E19和E18两个引脚上XDC需要明确写出引脚位置、电平标准以及时钟周期。set_property PACKAGE_PIN E19 [get_ports clk_p] set_property PACKAGE_PIN E18 [get_ports clk_n] set_property IOSTANDARD LVDS [get_ports {clk_p clk_n}] create_clock -name sys_clk -period 8.000 [get_ports clk_p]create_clock这一行是很多新手容易漏掉的。有人以为有了PACKAGE_PIN工具就知道这是时钟但综合工具需要明确知道时钟周期是8ns也就是100MHz它才能对后续逻辑做时序分析。如果忘了写create_clock整个工程的时钟约束就缺失时序报告里会大量出现无约束路径WNS没法判断。差分时钟在create_clock时通常只约束P端FPGA工具会识别出差分输入缓冲后生成的内部单端时钟。如果同时约束P和N有时反而会生成两条时钟造成CDC报告混乱。我建议用上面的写法差分对只要约束P端。4.3 别再被DRC RTSTAT-2这类报错卡住排查思路Vivado的DRC规则检查在运行综合或实现时会自动执行很多人第一次遇到DRC报错时特别慌因为DRC消息格式看着很官方比如有网上的朋友会搜到“vivado 报错 drc rtstat-2”不问GPT基本不知道在说什么。这类错误大多数和引脚约束、时钟能力引脚相关报错里通常会有一个对象名比如某个端口或信号。正确的排查顺序是先看DRC报告里定位的对象然后用引脚图工具查这个对象实际连接的是不是时钟能力引脚。如果是普通IO引脚却被约束成时钟输入DRC会直接告诉你这个引脚不具备时钟输入能力。其次是检查IOSTANDARD是否和板级设计一致差分时钟的IOSTANDARD如果写成了单端标准同样会报类似错误。还有一个经常踩的坑是引脚约束和RTL端口名不一致。比如RTL里叫clk_pXDC里写成clock_pVivado会在DRC阶段报“get_ports找不到对象”很多人一看“DRC error”就开始怀疑PLL配置其实只是拼写问题。遇到DRC报错时先编译信息定位对象再看约束名字再查引脚类型这三步走完大部分RTSTAT类问题都能定位。不要一上来就删约束、重配IP那样反而会把问题范围扩大。5. 综合实现后先别急着上板从报告里面读出潜在问题5.1 时钟报告能看到什么综合完成之后打开Open Synthesized Design在Flow Navigator里能找到Report Clock Networks。这个报告会把所有时钟网络列出来包括输入时钟经过IBUFDS之后的名字、PLL内部生成的VCO时钟、每一路输出时钟以及它们各自经过的缓冲器类型和驱动的寄存器数量。看这份报告时重点核对三件事。第一每一路输出时钟的频率是不是你期望的值。第二时钟是否经过了正确的时钟缓冲比如输出时钟有没有挂在BUFG下面。第三时钟网络里显示的来源是不是PLL本身而不是某个用于测试的BUFGCE或其它旁路逻辑。有一次我调一个项目综合报告里显示输出时钟频率正确但实现后的硬件始终测不到时钟。后来打开Clock Networks才发现工具为了满足某种约束把PLL输出时钟路由到了普通BUFGCTRL之外的逻辑导致时钟扇出超了预期。这种问题在Report里一眼就能看到不用上板盲猜。5.2 Timing Summary怎么看WNS为负怎么办实现完成后的Timing Summary是最终裁决。最关键的两个指标是WNS最差负裕量和TNS总负裕量。WNS只要为正说明当前约束下所有路径都满足时序要求。如果WNS为负数字是多少就代表还有多少纳秒的余量不足。PLL刚配好时WNS为负最常见的原因不是PLL本身而是跨时钟域路径没有约束。比如你生成了200MHz的时钟给逻辑又把输入25MHz的串口时钟直接采进来两个时钟域之间的路径没有set_clock_groups或异步约束Vivado默认按同步路径分析自然非常难满足。这时应该给两条时钟之间加异步时钟组约束而不是反复调PLL参数。另一种可能是输出时钟没有进BUFG导致高扇出网络的skew很大路径延迟暴涨。检查方式是看Timing Summary里最差的那条路径双击打开看时钟报告中的时钟网络延迟如果源时钟的插入延迟远大于预期基本就是时钟没有走全局时钟网络。5.3 为什么要确认时钟走的是BUFG而不是普通布线7系列FPGA的全局时钟网络是专门的资源可以保证时钟到达整个芯片所有触发器的延迟差足够小。PLL的输出时钟在默认情况下会接到BUFG再由BUFG驱动到广泛区域。如果因为某些手动约束或者资源紧张时钟被安排成了局部布线时序报告里的时钟skew会明显增大严重时上板后工作不稳定尤其是温度变化后更容易出问题。在Report Clock Networks里每个时钟网络的Driver列会显示BUFGCE或者BUFG这样的缓冲器。如果看到PLL输出时钟的驱动是普通逻辑比如LUT那就要警惕了很可能是RTL里对PLL输出做了额外处理或者手写了类似assign clk ...的逻辑。正确做法是PLL输出信号作为时钟只接到时钟端口或者经过BUFG再使用绝对不要拿它当普通信号做组合逻辑。还有一种情况是用户手动把clk_out接到普通IO引脚输出想在示波器上测量结果工具为了布线方便绕开了BUFG。此时最好在XDC里对输出引脚加CLOCK_DEDICATED_ROUTE相关约束或者用ODDR原语输出避免影响PLL内部时钟质量。6. 上板实测从生成bit文件到确认PLL真正锁定6.1 第一次上板前要检查的事项清单生成bitstream成功不代表上板一定没问题尤其是第一次接触新板卡。我习惯按照下面的顺序检查确认工程里选择的FPGA型号和板卡一致比如板子是xc7a100tcsg324工程里选成xc7a35t下载时会直接失败。确认所有引脚约束都没有和板级原理图冲突特别是时钟引脚一个引脚重复约束会让实现阶段出现多驱动错误。确认电源和下载线连接正常Vivado硬件管理器能识别到目标器件。先下载一个不依赖时钟的LED流水灯程序验证下载链路本身是通的。很多时候PLL不锁定根源是下载链路或者硬件供电有问题。一次上板就直接跑PLL模块出了问题很难判断是下载没成功还是时钟没起来。先用简单程序验证环境能省很多debug时间。6.2 用ILA抓locked信号为什么锁定比频率更重要上板之后最该看的第一指标不是输出波形而是PLL的locked信号。在Xilinx的PLL IP核中locked是PLL内部锁定状态指示locked拉高代表输出时钟频率已经稳定在目标值附近可以放心使用locked为低则说明环路还在收敛或者输入参考时钟有问题此时输出时钟并不可靠。用ILA集成逻辑分析仪观测locked和内部计数信号非常直观。在Vivado 2020.2里最简单的做法是给顶层信号加mark_debug标记再通过Set Up Debug向导自动创建ILA。也可以在RTL里手动例化ila IP核把pll_locked、一个分频计数器的值等信号接进去采样深度设1024或2048就够。下载bit文件后打开Hardware Manager运行ILA的trigger。如果能看到locked信号从0变到1说明PLL已经锁定输出频率基本可信。如果locked一直为0先检查ILA里输入时钟相关的信号有没有翻转。可以用一个常数分频器把差分输入时钟分到几百kHz级别再用ILA采集确认时钟确实到达了FPGA内部。这里必须说明一个新手非常容易踩的坑locked信号本身在上升沿附近存在亚稳态风险。很多人直接把locked接到寄存器的异步复位端这是不推荐的。PLL刚锁定时locked变化时刻和系统时钟边沿并不同步直接用作异步复位会让整个模块的复位状态不确定。正确做法是把locked经过两级D触发器同步之后再作为复位释放信号或者在Xilinx的复位IP里做同步处理。6.3 没有示波器的情况下用LED验证输出时钟频率的方法不是所有人手边都有高带宽示波器但验证PLL输出频率有没有问题一个LED加一个计数器就能完成。把其中某一路PLL输出时钟比如25MHz接到一个计数器上计数器计数到一定位数翻转。25MHz经过2^25分频后大约是0.745Hz对应LED每1.34秒闪一次肉眼观察完全没有压力。如果LED闪烁频率明显不对比如变成了0.2Hz或者1Hz以上可以初步判断PLL输出频率没有落在目标区间。接下来再把计数器位数减半看闪烁频率是否按预期变化缩小排查范围。这种方法虽然测不了抖动和相位但能快速确认PLL到底有没有输出、频率是否在量级上正确。有示波器的人可以把输出时钟通过OBUFDS引出到SMA接口直接量差分波形。没有差分管脚时也可以用ILA观察计数器的值。比如在固定时间内计数器的增量减去ILA采样窗口的时间就能反推频率大概是多少。ILA采样深度固定时从计数器数值的跳变间隔也能估算频率是否异常。这个阶段如果一切正常我习惯再做一次完整的独立上电测试断电重新上电等几秒看locked是否仍然稳定拉高。有些PLL的异常只有在冷启动时才会暴露比如输入时钟接触不良、参考时钟抖动过大导致环路无法收敛。把这一步养成习惯很多间歇性问题的根因会提前显现。最后再分享一个我常用的检查顺序每次新板子上电先下载LED程序确认硬件链路再查PLL的locked信号再通过LED或示波器确认各路时钟频率最后才去调业务逻辑。按这个顺序走PLL本身的问题基本不会困扰你超过半个小时。
返回列表