ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Lattice FPGA时钟分频实战:Diamond PLL IP核配置与Verilog例化

Lattice FPGA时钟分频实战:Diamond PLL IP核配置与Verilog例化 1. 为什么时钟分频在FPGA项目里是个绕不开的坎做Lattice FPGA开发的朋友大概率都遇到过这样的场景板子上焊了一颗25MHz或50MHz的晶振但你的逻辑偏偏需要125MHz、75MHz、33MHz这类“非标”频率。这时候如果直接拿晶振频率去驱动逻辑要么时序跑不上去要么外设通信速率对不上。时钟分频这件事说大不大说小也绝对不小它直接决定了整个系统的稳定性和时序余量。我最早接触Lattice FPGA的时候用的是Diamond 3.13这个版本当时手头一块MachXO2的板子板载晶振是25MHz但项目里需要一路100MHz给SDRAM控制器一路12.5MHz给低速外设。一开始我图省事直接写了个计数器做分频结果综合出来一看时序报告100MHz那一路的时钟偏斜大得吓人逻辑跑起来偶尔会抽风。后来老老实实回去翻Diamond的IP核手册用内置的PLL IP核重新做了一遍问题才彻底解决。这篇文章就是把我这些年用Lattice Diamond内置PLL IP核做时钟分频的经验整理出来从PLL的基本原理、Diamond里怎么配置、Verilog代码怎么写、到实际调试中踩过的坑全部掰开揉碎讲清楚。不管你是刚入门Lattice FPGA的新手还是从Xilinx/Altera转过来的老鸟只要你的项目里有时钟分频的需求这篇内容都能直接拿来参考。提示本文所有操作基于Lattice Diamond 3.13版本器件以MachXO2系列为例其他系列如ECP5、iCE40操作逻辑类似但PLL资源略有差异具体参数请以对应器件手册为准。2. PLL IP核到底比手写计数器分频强在哪2.1 手写分频的局限性先说说为什么我不推荐用计数器做时钟分频。假设输入时钟是25MHz你想要一个12.5MHz的时钟最简单的做法就是写一个T触发器每来一个时钟沿翻转一次输出。代码大概长这样reg clk_div; always (posedge clk_in) begin clk_div ~clk_div; end这段代码综合出来确实能工作但它有几个致命问题。第一分频出来的时钟是直接由寄存器输出的它会占用普通的逻辑资源而且时钟信号走的是通用布线网络偏斜skew和抖动jitter都很大。第二这种分频方式只能做整数分频而且分频比必须是2的幂次或者简单的整数比想要125MHz这种非整数倍频率就非常麻烦。第三也是最重要的一点这种时钟信号在时序分析时会被当作普通数据信号处理工具不会把它识别为时钟域跨时钟域的问题会变得非常隐蔽。2.2 PLL的核心优势PLLPhase-Locked Loop锁相环本质上是一个闭环反馈系统它通过比较输入时钟和反馈时钟的相位差动态调整压控振荡器的频率最终让输出时钟和输入时钟保持严格的相位和频率关系。Lattice FPGA内部的PLL是硬核资源不占用通用逻辑单元输出时钟走的是专用的时钟布线网络偏斜可以控制在皮秒级别。具体来说用PLL做时钟分频有这几个实打实的好处频率精度高PLL支持分数分频可以产生任意频率的输出时钟只要在VCO的工作范围内Diamond会自动帮你算好分频参数。抖动低硬核PLL的抖动通常在几十皮秒量级远优于逻辑分频。多路输出一颗PLL通常可以输出2到4路时钟每路的频率和相位都可以独立配置非常适合多时钟域的系统。时序可控Diamond的时序分析工具能正确识别PLL输出的时钟域自动做跨时钟域检查省去很多手动约束的麻烦。2.3 什么时候该用PLL什么时候不该用PLL虽好但也不是万能的。如果你的分频比非常简单比如2分频、4分频而且对时钟质量要求不高用计数器分频反而更省资源。另外PLL从锁定到稳定输出需要一段时间通常几十微秒如果你的系统对启动时间有严格要求需要把这段锁定时间考虑进去。我个人的经验是只要分频比不是2的整数次幂或者系统里有多个时钟域需要同步优先考虑PLL。如果只是做个简单的LED闪烁分频那计数器足够了。3. Diamond里PLL IP核的配置全流程3.1 新建IP核工程打开Diamond 3.13在File菜单里选择New - IP核或者直接在左侧的IP Express面板里操作。这里要注意Diamond的IP核生成器叫IPexpress不同版本的界面略有差异但核心配置项是一样的。第一步是选择PLL模块。在IPexpress的模块列表里找到“PLL”或者“EHXPLL”之类的选项具体名称取决于你的器件系列。MachXO2系列用的是EHXPLLJECP5系列用的是EHXPLLL。选好之后给IP核起个名字比如“pll_25m_to_100m”方便后续识别。3.2 关键参数计算与配置PLL配置的核心是三个参数输入频率CLKI、输出频率CLKOP、以及VCO频率。Diamond的配置界面里你只需要填输入频率和想要的输出频率工具会自动计算分频系数。但理解背后的计算逻辑很重要不然出了问题你都不知道从哪查。PLL的基本公式是这样的VCO频率 输入频率 × CLKF / CLKI 输出频率 VCO频率 / CLKOP_DIV其中CLKF是反馈分频系数CLKI是输入分频系数CLKOP_DIV是输出分频系数。VCO频率必须落在器件手册规定的工作范围内MachXO2的VCO范围通常是400MHz到800MHz。举个例子输入25MHz想要100MHz输出。如果直接让VCO400MHz那么CLKF/CLKI16CLKOP_DIV4。Diamond会自动帮你选一组最优参数。但如果你想要125MHz输出VCO500MHzCLKF/CLKI20CLKOP_DIV4同样可以算出来。在配置界面里你还需要设置这几个关键选项CLKOP频率主输出时钟频率填你想要的数值。CLKOS频率第二路输出可以设置不同的频率或相位。相位偏移如果需要输出时钟和输入时钟有固定相位差在这里设置。锁定时间PLL从启动到稳定的时间一般保持默认即可。3.3 生成IP核与文件结构配置完成后点击GenerateDiamond会在你指定的目录下生成几个文件。以MachXO2为例通常会生成pll_25m_to_100m.vVerilog封装文件你需要在顶层例化它。pll_25m_to_100m.ipxIP核配置文件后续可以重新打开修改。pll_25m_to_100m.lpc约束文件包含PLL的时序约束。这里有个细节要注意生成的Verilog文件里其实例化了一个黑盒模块真正的实现在Diamond的库文件里。所以你不需要关心内部逻辑只需要在顶层模块里正确例化即可。4. Verilog代码解析与例化实战4.1 IP核封装文件解读打开生成的pll_25m_to_100m.v文件你会看到类似这样的代码module pll_25m_to_100m ( input wire CLKI, input wire CLKFB, input wire RST, output wire CLKOP, output wire CLKOS, output wire LOCK ); EHXPLLJ #( .CLKI_DIV(1), .CLKFB_DIV(16), .CLKOP_DIV(4), .CLKOS_DIV(8), .FEEDBK_PATH(CLKOP) ) pll_inst ( .CLKI(CLKI), .CLKFB(CLKFB), .RST(RST), .CLKOP(CLKOP), .CLKOS(CLKOS), .LOCK(LOCK) ); endmodule这段代码里EHXPLLJ是MachXO2的PLL原语参数部分就是Diamond根据你的配置自动生成的。CLKI_DIV、CLKFB_DIV、CLKOP_DIV这些参数决定了分频比。FEEDBK_PATH指定反馈路径通常选CLKOP表示用主输出做反馈。4.2 顶层模块例化要点在你的顶层模块里例化这个PLL需要注意几个关键点module top ( input wire clk_25m, input wire rst_n, output wire led ); wire clk_100m; wire clk_12m5; wire pll_lock; pll_25m_to_100m u_pll ( .CLKI(clk_25m), .CLKFB(clk_100m), .RST(~rst_n), .CLKOP(clk_100m), .CLKOS(clk_12m5), .LOCK(pll_lock) ); // 后续逻辑用clk_100m和clk_12m5 reg [7:0] cnt; always (posedge clk_100m or negedge pll_lock) begin if (!pll_lock) cnt 8d0; else cnt cnt 1b1; end assign led cnt[7]; endmodule这里有几个容易出错的地方。第一CLKFB必须接回CLKOP形成闭环否则PLL无法锁定。第二RST是高电平复位如果你手头的复位信号是低电平有效记得取反。第三LOCK信号非常重要在PLL锁定之前输出时钟是不稳定的所有依赖此时钟的逻辑都应该被复位。4.3 时钟约束的添加IP核生成时会自动创建一个.lpc约束文件里面包含了PLL的时序约束。你需要在Diamond的Spreadsheet View里确认这些约束已经正确加载。通常包括输入时钟的频率约束PLL输出时钟的频率约束跨时钟域的false path或multicycle path设置如果约束没加对时序分析会报一堆莫名其妙的错误。我遇到过好几次因为约束文件没加载导致工具把PLL输出时钟当成普通信号时序报告一片红。5. 实操中踩过的坑与排查技巧5.1 PLL不锁定怎么办这是最常见的问题。PLL的LOCK信号一直为低说明PLL没有进入锁定状态。排查思路如下现象可能原因解决方法LOCK始终为低输入时钟未连接或频率不对用示波器或逻辑分析仪确认CLKI有信号LOCK始终为低RST信号一直有效检查RST是否被意外拉高LOCK间歇性拉低输入时钟抖动过大检查晶振质量必要时加去耦电容LOCK锁定后失锁电源噪声检查PLL电源引脚的去耦电容是否足够我印象最深的一次是RST信号的问题。当时顶层模块里复位信号是低电平有效我忘了取反结果PLL一直处于复位状态LOCK死活不拉高。查了半天代码才发现这种低级错误新手特别容易犯。5.2 输出频率不对怎么查如果LOCK正常但输出频率和预期不符大概率是分频参数算错了。回到IPexpress里重新检查CLKI_DIV、CLKFB_DIV、CLKOP_DIV这三个参数。记住公式输出频率 输入频率 × CLKFB_DIV / (CLKI_DIV × CLKOP_DIV)。还有一种可能是VCO频率超出了工作范围。Diamond在配置时通常会提示但如果你手动改了参数工具可能不会再次检查。MachXO2的VCO范围是400MHz到800MHz超出这个范围PLL要么不锁定要么输出频率漂移。5.3 时序违例的常见原因PLL输出时钟的时序违例通常和约束有关。如果Diamond报告setup/hold违例先检查这几个地方输入时钟约束是否和实际晶振频率一致PLL输出时钟是否被正确约束跨时钟域路径是否设置了false path我个人的习惯是在综合之前先把约束文件过一遍确保每个时钟都有对应的create_clock约束。Diamond的Timing Analysis工具里可以直观地看到时钟树结构如果某个时钟没有出现在列表里说明约束没生效。注意修改PLL配置后一定要重新生成IP核并更新约束文件不要手动改生成的Verilog代码否则下次重新生成时会被覆盖。6. 进阶技巧多路输出与动态配置6.1 利用CLKOS做第二路时钟很多Lattice的PLL支持多路输出比如CLKOP和CLKOS。CLKOS可以设置和CLKOP不同的分频比非常适合需要多个时钟域的系统。比如你可以让CLKOP输出100MHz给高速逻辑CLKOS输出25MHz给低速外设两路时钟同源相位关系确定跨时钟域处理会简单很多。配置的时候在IPexpress里分别设置CLKOP和CLKOS的频率即可。需要注意的是两路输出共享同一个VCO所以它们的频率比必须是整数比。如果CLKOP是100MHzCLKOS可以是50MHz、25MHz、20MHz等但不能是33MHz这种非整数比。6.2 动态重配置的可能性部分Lattice FPGA的PLL支持动态重配置也就是说在运行过程中可以通过寄存器修改分频参数改变输出频率。这个功能在需要变频的场合非常有用比如软件定义无线电、自适应时钟系统等。不过动态重配置的实现比较复杂需要通过特定的接口如APB或自定义寄存器写入配置字。Diamond的IPexpress里有一个“Dynamic Phase”选项勾选后会生成额外的端口用于动态调整相位。频率的动态调整则需要更底层的操作建议参考对应器件的PLL用户手册。我实际项目中用到动态重配置的次数不多因为大部分场景下固定频率就够用了。但如果你确实需要建议先在开发板上验证基本功能再逐步加入动态调整逻辑。6.3 时钟切换与毛刺避免在多时钟系统中有时候需要在两个时钟源之间切换。如果直接用一个多路选择器切换时钟很容易产生毛刺导致后级逻辑误触发。正确的做法是使用专门的时钟切换电路或者利用PLL的CLKOS和CLKOP做无缝切换。Lattice的PLL通常支持在CLKOP和CLKOS之间切换切换过程中输出时钟会短暂停止但不会产生毛刺。具体实现方式可以参考Diamond的IP核文档这里不展开。7. 一些个人经验与建议说了这么多最后分享几个我在实际项目中总结的小经验。第一PLL的输入时钟尽量走专用的时钟输入引脚不要用普通IO。专用时钟引脚的抖动和偏斜都更小PLL锁定更稳定。MachXO2的时钟输入引脚在引脚规划器里有明确标注分配引脚时注意一下就行。第二PLL的电源引脚一定要加足够的去耦电容。我见过因为去耦电容没焊好导致PLL间歇性失锁的案例查了好久才定位到硬件问题。一般建议每个电源引脚配一个0.1uF和一个10uF的电容尽量靠近引脚放置。第三如果项目里用了多个PLL注意它们之间的时钟域关系。同源的PLL输出时钟之间是同步的不同源的PLL输出时钟之间是异步的跨时钟域处理方式完全不同。我一般会在代码里用注释明确标出每个时钟的来源和频率方便后续维护。第四Diamond的IPexpress生成的IP核是可以重新打开的。如果你后期需要修改频率直接双击.ipx文件就能重新配置不用从头新建。这个功能省了我不少时间。第五仿真的时候记得把PLL的行为模型加进去。Diamond生成的IP核通常会附带一个仿真模型在ModelSim里编译时要把对应的库文件加进去否则仿真会报找不到模块。具体路径在IP核生成目录的sim文件夹下。时钟分频这件事说到底是FPGA设计的基本功。用好了PLL整个系统的时序余量和稳定性都会上一个台阶。希望这篇内容能帮到正在折腾Lattice FPGA的朋友少走一些我当年走过的弯路。
返回列表