ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FPGA时钟管理详解:XC7A35T中MMCM-PLL的配置与调试

FPGA时钟管理详解:XC7A35T中MMCM-PLL的配置与调试 简介FPGA XC7A35T实现IP核之MMCM-PLL驱动Verilog HDL实现资源包围绕Xilinx Artix-7系列FPGA的时钟管理模块设计面向需要掌握MMCM与PLL配置、调用及其Verilog HDL驱动开发的硬件工程师与学习者。整套资料共306个文件压缩包约1.77MB涵盖核心源码、约束文件、Vivado工程配置、综合实现报告以及仿真与构建脚本能够直观还原从IP配置到布局布线的完整过程。已有234人学习下载。通过该工程读者可对照MMCM的分频倍频、相位偏移与PLL环路参数设置理解时钟信号生成、仿真验证流程与硬件调试方法并可直接用于XC7A35T开发板或参考迁移到其他Artix-7设计具有较高的工程参考价值与实践指导意义。1. 拿到XC7A35T之后时钟才是第一道门槛拿到一块XC7A35T开发板流水灯和按键消抖很快就能跑通但一旦开始做ADC采集、DDR读写或者LVDS接口很多人会被同一件事卡住板载晶振只有50MHz而系统需要100MHz主时钟、200MHz采样时钟、90度相移的随路时钟甚至还要一路25MHz给PHY。这时候你得靠MMCM-PLL把一路参考时钟变换成多路不同频率、不同相位的时钟。MMCM-PLL是Xilinx 7系列中负责时钟生成与对齐的核心资源XC7A35T内部有5个CMT每个CMT里各有一个MMCM和一个PLL而Vivado里的Clocking Wizard IP核正是访问这些资源的入口。这篇文章按“选型、配置、Verilog HDL驱动、仿真与ILA验证、进阶应用”的顺序把MMCM-PLL从IP核创建到上板调试的完整链路讲清楚。内容同时覆盖FPGA入门阶段最容易踩的坑和熟手也需要留意的边界条件照着做就能在Vivado里复现。2. MMCM与PLL怎么选从XC7A35T的时钟需求反推IP核配置2.1 结构差异决定了使用场景XC7A35T内部有5个CMT每个CMT由一个MMCM和一个PLL组成。MMCM的全称是Mixed-Mode Clock Manager本质上是增强型锁相环PLL则是基础的整数分频锁相环。很多工程习惯把两者混着用只有输出路数不够了才想起MMCM这个顺序是反的。合理的做法是先列出需求需要几路输出、是否需要分数分频、是否要在运行中调相位再决定用哪个。MMCM相比PLL多三个能力动态相移、分数分频、更多输出。动态相移意味着系统运行期间可以通过PSCLK、PSEN这类引脚微调输出相位分数分频让M分频和输出分频支持0.125的小数步进例如从100MHz得到125MHz这种非整数比输出方面MMCM提供7路CLKOUTPLL只有3路。PLL剩下的优势主要是抖动略低、功耗略小实现也更简洁。特性MMCMMMCME2_ADVPLLPLLE2_ADV输出时钟路数7路CLKOUT0~63路CLKOUT0~2动态相移支持不支持分数分频支持0.125步进仅整数输入频率范围更宽较窄锁定时间约几十到几百微秒略快典型用途多路/相位/分数时钟生成单路低抖动参考时钟前面提到“XC7A35T有5个CMT”这里再补充一下它的内部结构每个CMT中MMCM和PLL共享输入时钟网络但输出互不干扰。你在Clocking Wizard里选MMCM工具实际例化的是MMCME2_ADV原语选PLL则对应PLLE2_ADV原语。两者不能同时使用同一个CMT里的全部资源规划多路时钟时要避免挨着放导致局部布线拥挤。2.2 用三个问题确定选型判断选MMCM还是PLL我一般问三个问题。第一是否只要一路固定频率的时钟PLL够用把MMCM留给复杂场景。第二输出是否超过三路超过三路只能选MMCM。第三是否需要分数频率或动态相位这两点PLL完全不具备直接锁MMCM。举一个XC7A35T上的典型例子一个数据采集系统需要系统时钟100MHz、高速采样时钟200MHz、DDR随路时钟200MHz且移相90度同时还要给低速串口提供25MHz时钟。四路输出、其中一路带相移这个需求只能落在MMCM上。反过来如果只是给一颗外部PHY提供单路参考时钟频率固定且对抖动敏感用PLL更合适把MMCM资源让出来。做TDC直方图这类对时序精度敏感的项目时还要注意一点MMCM本身会引入一定抖动级联两级之后抖动会叠加直方图展宽可能就来自时钟源而非进位链。这里的选择原则不是“用更好的MMCM”而是尽量让系统在单级MMCM下完成所有时钟变换减少不必要的级联。2.3 记住VCO范围和BUFG这两个边界MMCM内部把输入时钟先做D分频再和反馈时钟比较压控振荡器跑在一个乘了M倍的频率上输出端各自除以O。核心约束是Fvco Fclkin × M / D并且Fvco必须落在器件手册规定区间内。以XC7A35T的-1速度等级为例这个范围大约是600MHz到1200MHz具体数值要查UG472中对应速度等级的表格。在Clocking Wizard里M/D/O这三个参数默认由工具自动计算但不表示可以完全不看。自动算出的结果有时会让某个输出频率存在极小误差因为MMCM的分数分频是0.125步进例如要生成一个非标准频率工具只能在最近的可取值里挑一个。手工干预时需要保证改动后的Fvco仍在VCO范围内否则综合时直接报错。另一条边界是BUFG。MMCM/PLL的输出默认插入BUFG驱动全局时钟网络但XC7A35T的BUFG总量在32个左右。如果工程里还有外部引脚时钟、以太网参考钟、PCIe时钟就需要规划哪些输出走BUFG、哪些走BUFR或BUFH。把不重要的低速时钟也塞进全局网络综合后期很容易报出布线拥塞。3. 用Clocking Wizard生成MMCM-PLL IP核配置步骤与参数表3.1 Vivado里创建一个Clocking WizardVivado对MMCM-PLL的封装就是Clocking Wizard这个IP核。打开工程后在左侧Flow Navigator里点IP Catalog搜索“Clocking Wizard”双击进入配置界面。界面第一项是Component Name建议命名为clk_wiz_0后续在源码里例化、在约束里引用都比较直观。在Clocking Options页面输入时钟频率填50与板载晶振一致Source类型选Single ended clock capable pin如果晶振是差分输出选Differential clock capable pin。页面下方有reset和locked两个信号选项默认勾上这样生成的IP核会带reset输入和locked输出。Output Clocks页面是配置重点点Add Output Clock一次添加一路输出。以50MHz输入为例通常会配四路100MHz系统时钟、200MHz采样时钟、200MHz相移90度的DDR随路时钟、25MHz低速接口时钟。每路都能独立填频率、相位、占空比。相位设置粒度受VCO周期限制实际最小可设值为VCO周期1/8对应的输出相位移工具界面里常见0度、90度、180度、270度这种组合都能正常生成。3.2 输出参数与自动分频的关系下表是一套常用配置输入为50MHz单端输出名称输出频率相位占空比用途示例clk_out1100 MHz0.0°50%系统逻辑时钟clk_out2200 MHz0.0°50%ADC采样时钟clk_out3200 MHz90.0°50%DDR随路时钟clk_out425 MHz0.0°50%PHY管理接口配置完这几路切到MMCM/PLL Settings页能看到工具自动推算出的CLKFBOUT_MULT_F、CLKOUT0_DIVIDE_F等参数。以50MHz进、200MHz出为例一套常见取值是M20、D1VCO1000MHz再对CLKOUT各位做分频。这个VCO频率落在Artix-7允许范围内工具通常不会报警。提示如果频率关系比较特殊比如100MHz输入要出125MHz工具会把输出除法设成小数。每次生成IP后顺手看一眼VCO频率是否接近上限比等到上板后排查要省事得多。输出驱动资源默认是BUFG在Output Clocks页面底部可以改成BUFR、BUFH或BUFIO。对只供局部逻辑使用的时钟改成BUFH能省下全局资源对需要跨时钟域到远端的信号保持BUFG更稳。3.3 生成IP核后的文件与端口点Generate生成后Vivado会在ip目录里形成一组文件其中clk_wiz_0.v是供顶层例化的包装模块clk_wiz_0_clk_wiz.v内部才真正例化MMCME2_ADV原语后者值得打开读一遍能看到工具替你做了哪些BUFG插入和反馈连接。顶层例化时以生成的模板为准典型写法如下clk_wiz_0 u_clk_wiz_0 ( // Clock in ports .clk_in1 (clk_50m), // 板载50MHz参考时钟 // Clock out ports .clk_out1 (clk_100m), // 100MHz 系统时钟 .clk_out2 (clk_200m), // 200MHz 采样时钟 .clk_out3 (clk_200m_90), // 200MHz 90°相移时钟 .clk_out4 (clk_25m), // 25MHz 低速时钟 // Status and control signals .reset (~rst_n), // 高有效复位 .locked (clk_locked) // 锁定指示高有效 );Clocking Wizard的reset是高有效外部复位通常写成低有效rst_n所以这里取反后接入。clk_in1直接接板级晶振引脚工具会在内部自动插入IBUFG和BUFG。locked信号在输出时钟频率达到设定值后会拉高它是下游复位释放、状态机启动的重要依赖。生成目录里还会有clk_wiz_0.xdc和VHDL封装clk_wiz_0.vho。注意这个xdc里并没有对输入时钟的约束输入时钟周期需要自己在顶层XDC里声明否则Vivado会把无约束输入当虚拟时钟对待时序报告没有意义。这个点很容易漏后面第4章的约束部分会给出写法。4. Verilog HDL驱动MMCM-PLL例化、复位与顶层集成4.1 一个完整的驱动模块把IP核例化到顶层只是第一步一个可靠的MMCM-PLL驱动模块至少还要处理三件事复位信号的电平转换、locked信号的同步释放、以及复位释放后对下游逻辑的启动控制。下面这个模块可以直接用在XC7A35T工程里module mmcm_drv_top ( input wire clk_50m, // 板级 50MHz 晶振输入 input wire rst_n, // 外部低有效复位 output wire clk_100m, // 100MHz 系统时钟 output wire clk_200m, // 200MHz 采样时钟 output wire clk_200m_90, // 200MHz 90°相移时钟 output wire clk_25m, // 25MHz 低速时钟 output wire clk_locked, // MMCM 锁定指示 output wire sys_rst_n // 同步后的系统复位低有效 ); // 例化 Clocking Wizard 生成的 IP 核 clk_wiz_0 u_clk_wiz_0 ( .clk_in1 (clk_50m), .clk_out1 (clk_100m), .clk_out2 (clk_200m), .clk_out3 (clk_200m_90), .clk_out4 (clk_25m), .reset (~rst_n), .locked (clk_locked) ); // 异步复位、同步释放locked 参与释放判断 reg [1:0] rst_sync; always (posedge clk_100m or negedge rst_n) begin if (!rst_n) rst_sync 2b00; else begin rst_sync[0] clk_locked; rst_sync[1] rst_sync[0]; end end assign sys_rst_n rst_sync[1]; endmodule先看例化部分IP核端口和顶层一一对应没有额外逻辑。关键在第14行的复位同步块rst_sync两个触发器构成经典两级同步器rst_sync[0]跟随clk_lockedrst_sync[1]再拍一拍作为系统复位信号输出。sys_rst_n只在clk_100m上升沿变化并且外部rst_n已经稳定下游逻辑拿到的是干净的复位释放时序。不能直接把clk_locked接到sys_rst_n原因是上电瞬间MMCM还没锁定locked可能经历一段不确定的低电平抖动如果系统里同时有按键复位和看门狗复位它们和locked没有同步关系直接组合会产生亚稳态。4.2 关于复位的两个经验第一个经验是和复位极性较劲。Clocking Wizard IP核的reset端口是高有效习惯低有效复位的人在例化时很容易忘记取反。一旦忘掉IP核会一直处于复位状态输出没有时钟locked永远为低。排查这类问题时先看reset脚电平往往比反复改分频参数更高效。第二个经验是sys_rst_n不要接回IP的reset。如果复位同步逻辑把sys_rst_n又送回clk_wiz_0的reset端口就会形成循环依赖MMCM等待复位释放复位释放依赖lockedlocked要等复位释放后才拉高系统直接卡死。正确做法是只把外部rst_n取反后接IP核locked只进同步逻辑不再往回绕。4.3 顶层约束输入时钟必须显式声明驱动模块写完后打开XDC文件先对板级时钟做约束create_clock -name clk_50m -period 20.000 -waveform {0 10.000} [get_ports clk_50m]period20.000对应50MHz时钟周期waveform指定0到10ns为高电平占空比50%。clk_50m是顶层端口名必须和Verilog里的命名一致。保存约束后Vivado综合实现时会自动从MMCM的输出推导出generated clock不需要对clk_100m、clk_200m再手工创建时钟。如果板载时钟引脚不是全局时钟引脚或者输入来自普通IO综合时可能报CLOCK_DEDICATED_ROUTE错误。这种场景的常见做法是加一段约束绕过专用引脚规则set_property CLOCK_DEDICATED_ROUTE ANY_CMT_COLUMN [get_nets clk_50m_IBUF_BUFG]这里的网表名以综合后实际网络为准先跑一次综合再回来填。除非排错到最后一步否则不要用FORCE它会把时钟路径上的其他问题也一起掩盖。5. 仿真与ILA验证MMCM-PLL输出的频率和相位对不对5.1 用最小testbench验证频率MMCM-PLL的仿真模型在Vivado自带仿真器里直接就能跑不需要另外准备仿真库。下面这个testbench以50MHz激励重点统计200us内clk_100m的上升沿次数timescale 1ns/1ps module tb_mmcm_drv; reg clk_50m 1b0; reg rst_n 1b0; wire clk_100m, clk_200m, clk_200m_90, clk_25m; wire clk_locked, sys_rst_n; always #10 clk_50m ~clk_50m; // 50MHz, 周期20ns reg [31:0] cnt_100m 32d0; always (posedge clk_100m) cnt_100m cnt_100m 1; mmcm_drv_top dut ( .clk_50m (clk_50m), .rst_n (rst_n), .clk_100m (clk_100m), .clk_200m (clk_200m), .clk_200m_90 (clk_200m_90), .clk_25m (clk_25m), .clk_locked (clk_locked), .sys_rst_n (sys_rst_n) ); initial begin #100; rst_n 1b1; wait (clk_locked); #200000; // 等待200us $display(cnt_100m %0d, expect 20000, cnt_100m); $display(locked %0b, clk_locked); $finish; end endmodulealways #10生成20ns周期的时钟对应50MHz输入。计数器挂在clk_100m的上升沿200us内如果频率正确应该数到20000。如果比这个值少很多先看locked是否一直为低如果locked正常但数量不对重点检查Clocking Wizard里输入频率是否填错工程里所有时钟模块的输入定义必须和板载晶振严格一致。相位验证可以在仿真里比较clk_200m和clk_200m_90的上升沿时间差。200MHz下一周期5ns90度相移对应1.25ns仿真精度1ns时能看出一个整刻度。用数值比较替代肉眼观察波形这个习惯放进回归脚本之后很有价值。5.2 上板看信号ILA探针的接法与触发仿真通过不代表上板一定通过板上调试离不开ILA。Vivado的ILA IP核在这里的用法是新建一个ILA设置Probe端口数量和数据位宽采样深度一般取1024或4096采样时钟建议用clk_50m而不是clk_100m。原因是ILA要观察MMCM从未锁定到锁定的完整过程如果用MMCM自己生成的clk_100m当采样时钟locked拉高前的波形全部看不到。探针接线建议覆盖三组信号clk_locked1位、sys_rst_n1位、以及clk_100m输出时钟。触发条件设为clk_locked上升沿这样上板后按一次复位ILA能抓下从复位释放到锁定的完整时序。把ILA例化插入顶层后重新综合实现打开Hardware Manager连接开发板、下载bitstream在ILA触发器窗口选好触发条件再按板上复位键就能看到locked拉高的相对时刻。这里提醒一个常见误区ILA不要挂太多。一个工程里放三四个ILA每个采样深度4096Block RAM会被探针存储占掉很多。XC7A35T的Block RAM一共约50块36Kb each几个ILA就能让资源报告明显变化。调试结束后记得把ILA移除再跑最终实现这是规范流程。5.3 三类典型现象的排查顺序第一类locked一直为低且reset电平正常。打开Clocking Wizard看M/D/O参数是否让VCO超出范围。对XC7A35T的-1速度等级VCO应保持在大约600MHz到1200MHz区间超范围时工具生成阶段就会给出CRITICAL WARNING综合日志里搜warning能快速定位。第二类locked拉高但没有输出时钟。重点检查输出时钟是否被误接到普通逻辑引脚又被其他信号占用或者BUFG资源耗尽。Vivado的Report Utilization里搜BUFG一项能看到占用情况。第三类仿真正常但上板后频率整体偏差。先测输入晶振实际频率再看约束里create_clock的period是否写错。实际项目中遇到过原理图标注50MHz、板上焊成48MHz晶振的情况改焊或者改约束都比反复调MMCM参数更实际。6. 进阶动态相移、时钟切换与引脚输出时钟的约束技巧6.1 动态相移的握手时序运行中需要微调相位时要在Clocking Wizard的Output Clocks页面勾选Dynamic Phase Shift生成后IP核会多出psclk、psen、psincdec、psdone四个端口。每次调整的流程是psclk提供稳定时钟psen拉高一个psclk周期同时psincdec给出方向1为相位增加、0为减小之后等待psdone拉高表示本次调整完成再发起下一次。最小相移步进是一个VCO周期对应的1/8具体数值查器件手册里的Phase Shift表格不要假设一次脉冲对应固定角度。6.2 用BUFGMUX做无毛刺时钟切换不要通过复位MMCM再重新锁定的方式切换时钟源锁定期间输出时钟会中断下游状态机全乱。需要冗余切换时常见做法是用BUFGMUX原语在两路已锁定时钟之间选择BUFGMUX u_bufgmux ( .O (clk_sel), .I0(clk_a), .I1(clk_b), .S (sel) );S为低时选择I0S为高时选择I1。BUFGMUX内部会等当前选中时钟电平回到低点再切换从而避免毛刺这是普通逻辑无法替代的。6.3 从引脚输出时钟时的最后一公里MMCM-PLL的输出如果要送到板外比如作为ADC采样时钟不要把clk_200m直接约束到普通IO引脚。Vivado会报CLOCK_DEDICATED_ROUTE违规因为普通IO没有直达时钟网络的专用路径。推荐先例化ODDR原语复制时钟ODDR #(.DDR_CLK_EDGE(SAME_EDGE)) u_oddr_out ( .Q (clk_200m_pad), .C (clk_200m), .CE(1b1), .D1(1b1), .D2(1b0), .R (1b0), .S (1b0) );D1固定1、D2固定0时ODDR的输出Q就是C的复制时钟驱动能力比直接拉线更好。然后对输出引脚做IO电平约束并在XDC里对相关网络声明set_property CLOCK_DEDICATED_ROUTE ANY_CMT_COLUMN [get_nets clk_200m_OBUF]重新跑综合实现后在Device视图里展开CMT区域能看到clk_200m从MMCM输出端经过BUFG进入ODDR再到OBUF整条路径清晰可见。如果发现ODDR被综合器优化掉说明时钟被误当成普通逻辑路径处理需要回到约束里确认I/O标准与D2端常量是否生效。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进