ARTICLE DETAIL

资讯详情

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

FPGA动态重配置实战:MMCM/PLL时钟频率相位占空比在线调整

FPGA动态重配置实战:MMCM/PLL时钟频率相位占空比在线调整 FPGA 开发里有个场景特别常见系统跑起来了时钟频率想微调一下或者相位对不齐导致数据采样出错再或者占空比不合适让外设时序余量不够。常规做法是改 PLL 参数、重新综合、重新布局布线、重新下载一套流程下来少说十几分钟调试效率极低。而动态重配置Dynamic Reconfiguration就是来解决这个痛点的——它允许你在 FPGA 运行过程中直接通过配置接口修改 MMCM/PLL 的分频、倍频参数实时改变输出时钟的频率、相位和占空比不用重新编译整个工程。这篇内容面向已经能跑通基本 FPGA 工程、用过 MMCM/PLL 但还没接触过动态重配置的开发者。我会把 Xilinx 7 系列Artix-7/Kintex-7 等上 MMCM 的动态重配置完整流程拆开讲清楚包括 DRP 接口的时序逻辑、参数计算、状态机设计以及我在实际调试中踩过的几个坑。读完你应该能自己写出一个可用的动态时钟调整模块。1. 先搞清楚动态重配置到底改的是什么1.1 MMCM 内部结构与可重配置寄存器要理解动态重配置得先知道 MMCM 内部到底有哪些参数决定了输出时钟。一个 MMCM 的核心结构可以简化为输入分频器D、相位频率检测器PFD、电荷泵、环路滤波器、压控振荡器VCO、输出分频器O、以及每个输出通道独立的小数分频器和相位偏移单元。输出频率的公式是Fout Fin × (M / D) / O其中 M 是倍频系数D 是输入分频系数O 是输出分频系数。VCO 频率 Fin × M / D必须落在 MMCM 允许的范围内7 系列一般是 600MHz 到 1200MHz不同速度等级略有差异。动态重配置能改的就是这些分频/倍频系数以及每个输出通道的相位偏移量。这些参数在 MMCM 内部对应一组配置寄存器通过 DRPDynamic Reconfiguration Port接口读写。DRP 本质上是一个同步的读写端口类似简单的 SRAM 接口给地址、给数据、拉使能等 ready 信号返回。需要特别注意的是不是所有寄存器都能在运行时随意改。比如 VCO 频率相关的 M 和 D改动时 MMCM 会短暂失锁输出时钟会不稳定甚至停振。而输出分频比 O 和相位偏移的修改相对安全因为不改变 VCO 工作点。这个区别直接决定了你的重配置策略。1.2 频率、相位、占空比三者的调整边界频率调整是最直观的需求。比如你有个 100MHz 的输入时钟想输出 75MHz 到 150MHz 之间可调。通过改 M 和 O 的组合就能实现但要注意 VCO 频率必须始终在合法范围内。我的经验是先把 VCO 固定在一个中间值比如 1000MHz然后通过改 O 来调输出频率这样每次改频率不会导致 VCO 重新锁定稳定性好很多。相位调整分两种粗调和细调。粗调是通过改变输出分频器的边沿位置实现步进通常是 VCO 周期的整数倍细调则是通过 MMCM 内部的相位偏移单元步进可以做到 VCO 周期的 1/567 系列。实际项目中如果要做源同步接口的采样对齐细调相位是刚需。占空比调整这个需求相对少见但确实存在。比如某些外设要求时钟高电平时间特别短或特别长。MMCM 本身不直接提供占空比寄存器但可以通过组合两个相位不同的输出来实现或者用 MMCM 输出再经过逻辑做占空比整形。纯靠 MMCM 寄存器改占空比的能力有限这一点要有预期。下面这张表总结了三种调整方式的关键差异调整类型涉及寄存器是否影响 VCO输出是否中断典型步进频率调整M、D、O改 M/D 会影响改 M/D 会中断取决于 O 值相位调整相位偏移寄存器不影响不中断VCO/56占空比调整需组合实现不影响不中断受相位步进限制1.3 为什么不用外部时钟芯片有人会问既然要动态调时钟为什么不直接用 Si5341 这类可编程时钟芯片确实专用时钟芯片在抖动性能和灵活性上更好但它需要额外的硬件成本、I2C/SPI 配置逻辑而且增加了板级复杂度。如果你的系统里已经有 FPGA且对时钟抖动要求不是极端苛刻比如普通数据采集、显示驱动、通信接口用 MMCM 动态重配置是性价比最高的方案。省掉一颗芯片、省掉一路配置总线PCB 也更好布。2. DRP 接口的时序细节与状态机设计2.1 DRP 读写时序的完整拆解Xilinx 7 系列的 MMCM 动态重配置接口信号包括DADDR7 位地址、DI16 位写数据、DO16 位读数据、DEN使能、DWE写使能、DCLKDRP 时钟、DRDY就绪。时序逻辑是在 DCLK 上升沿当 DEN 为高时如果是写操作DWE1DI 上的数据写入 DADDR 指定的寄存器如果是读操作DWE0DO 上会在几个周期后输出对应地址的数据同时 DRDY 拉高一个周期表示数据有效。关键点在于 DRDY 的时序。写操作后 DRDY 通常在第 1 到第 2 个 DCLK 周期拉高读操作的 DRDY 延迟更大可能到第 3 到第 4 个周期。你不能假设固定延迟必须用状态机等 DRDY。我见过有人用固定计数器等 3 个周期就去读 DO结果在某些速度等级下数据还没准备好读回来全是错的。还有一个容易忽略的点DCLK 的频率不能太高。UG472 里建议 DCLK 不超过 100MHz实际我用 50MHz 比较稳。如果你用系统时钟直接驱动 DCLK先确认频率是否在范围内。2.2 一个可复用的 DRP 状态机实现下面是我在实际项目中反复使用的一个 DRP 控制状态机用 Verilog 写的结构清晰容易移植module drp_ctrl ( input wire clk, input wire rst_n, input wire start, input wire rw, // 1write, 0read input wire [6:0] addr, input wire [15:0] wdata, output reg [15:0] rdata, output reg done, // DRP 物理接口 output reg [6:0] DADDR, output reg [15:0] DI, input wire [15:0] DO, output reg DEN, output reg DWE, output wire DCLK, input wire DRDY ); assign DCLK clk; localparam IDLE 2d0; localparam ISSUE 2d1; localparam WAIT 2d2; localparam FINISH 2d3; reg [1:0] state; reg [1:0] wait_cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; DEN 1b0; DWE 1b0; done 1b0; wait_cnt 2d0; end else begin case (state) IDLE: begin done 1b0; if (start) begin DADDR addr; DI wdata; DWE rw; DEN 1b1; state ISSUE; end end ISSUE: begin DEN 1b0; state WAIT; end WAIT: begin if (DRDY) begin rdata DO; done 1b1; state FINISH; end end FINISH: begin done 1b0; state IDLE; end endcase end end endmodule这个状态机的逻辑很直接IDLE 等 startISSUE 发一个周期的 DEN然后进 WAIT 死等 DRDY收到后锁存数据、拉高 done最后回 IDLE。注意 DEN 只拉高一个周期这是 DRP 协议要求的不能持续拉高。2.3 多寄存器写入的顺序问题改一个输出频率往往需要写多个寄存器。比如你要改输出分频比可能需要先写 O 寄存器再写对应的分频使能位最后写一个加载寄存器让配置生效。这些寄存器之间有顺序依赖不能乱写。我的做法是把整个重配置流程做成一个查找表每个配置项对应一组地址数据对按顺序存在 ROM 里状态机依次读出并写入。这样改配置只需要改 ROM 内容不用动状态机逻辑。对于需要频繁切换几种固定频率的场景这个方案特别省事。注意写寄存器之前一定要先读一遍原始值确认当前状态。有些寄存器是保留位不能乱写直接覆盖可能导致 MMCM 行为异常。3. 参数计算从目标频率反推寄存器值3.1 频率参数的完整计算链路假设输入时钟 Fin 100MHz目标输出 Fout 66.67MHz。计算步骤如下第一步确定 VCO 频率。VCO 必须在 600-1200MHz 之间。选一个合适的值比如 800MHz。第二步算 M 和 D。VCO Fin × M / D所以 M/D 8。如果 D1M8如果 D2M16。D 的取值范围通常是 1-106M 是 2-64。选 D1M8。第三步算 O。Fout VCO / O所以 O 800/66.67 12。O 的取值范围是 1-12812 合法。所以最终配置是 M8D1O12。验证100 × 8 / 1 / 12 66.67MHz正确。但这里有个细节MMCM 的 M 和 D 寄存器不是直接存这个整数值。实际寄存器里存的是 M-1 或类似偏移值而且有些系列还有小数分频。具体映射关系必须查 UG472 的寄存器表不能想当然。我一般会先用 Xilinx 的 Clocking Wizard 生成一个目标频率的配置然后从生成的代码或报告中反查寄存器值这样最保险。3.2 相位偏移的量化计算相位偏移寄存器的值对应的是 VCO 周期的分数。7 系列 MMCM 的相位偏移精度是 VCO 周期的 1/56。假设 VCO 800MHz周期 1.25ns那么一个相位步进 1.25/56 ≈ 22.3ps。如果你需要 90 度相位偏移对应周期 1/4即 1.25ns/4 312.5ps。需要的步进数 312.5/22.3 ≈ 14。所以相位寄存器写 14。但要注意相位偏移是相对于该输出通道的基准边沿而且不同输出通道的相位偏移是独立配置的。做源同步接口时通常固定一个通道作为发送时钟另一个通道做相位偏移作为采样时钟两者配合使用。3.3 占空比调整的替代方案前面说过 MMCM 不直接支持占空比寄存器。如果你的应用确实需要非 50% 占空比有两个可行方案。方案一用两个相位不同的输出做逻辑组合。比如输出 A 相位 0 度输出 B 相位 90 度两者做异或得到的就是 50% 占空比但频率翻倍的时钟做与或组合可以得到不同占空比。这个方案需要额外的逻辑资源但精度取决于相位步进。方案二MMCM 输出后接一个计数器做占空比整形。比如 100MHz 时钟进来用计数器数 3 个周期高、7 个周期低输出 10MHz、30% 占空比的时钟。这个方案简单直接但输出频率必须是输入频率的整数分频灵活性差一些。实际选哪个取决于你的具体需求。如果占空比精度要求高且频率固定方案二更稳如果需要动态调整占空比方案一配合动态相位重配置更合适。4. 实测中遇到的几个典型问题4.1 重配置后 MMCM 失锁导致系统跑飞这是最危险的一个坑。当你修改 M 或 D 寄存器时VCO 频率会跳变MMCM 会短暂失锁输出时钟可能停振几十个周期。如果你的系统逻辑直接依赖这个时钟就会跑飞。我的解决方案是重配置期间把系统逻辑切到备用时钟比如输入时钟的缓冲等 MMCM 重新锁定后再切回来。MMCM 有个 LOCKED 信号重配置后要等 LOCKED 重新拉高才能认为时钟稳定。切换时钟用 BUFGMUX 实现切换时要确保两个时钟都稳定否则 BUFGMUX 输出会有毛刺。如果你只是改输出分频比 O 而不动 M/DVCO 不变MMCM 不会失锁输出时钟只是频率变了但一直有。这种情况下可以不用切时钟但要注意下游逻辑是否能承受频率突变。4.2 DRP 时钟域与系统时钟域的交叉DRP 接口的 DCLK 和你的系统时钟往往是同一个但 DRDY 返回是异步的相对于你的状态机。虽然实际上 DRDY 是由 DCLK 同步产生的但如果你用不同的时钟驱动状态机和 DCLK就需要做跨时钟域处理。我的建议是DRP 状态机和 DCLK 用同一个时钟这样最简单也最可靠。如果系统时钟频率太高超过 100MHz就分频出一个 50MHz 专门给 DRP 用状态机也跑在这个时钟域然后通过握手信号和系统时钟域通信。4.3 寄存器写入后不生效的排查思路有时候你写了寄存器读回来也对但输出频率就是没变。这种情况我遇到过几次原因各不相同。第一次是忘了写加载寄存器。有些配置需要最后写一个特定的寄存器位才会真正生效只写参数寄存器是不够的。第二次是写顺序错了。某个使能位必须在参数写完之后才能置位我提前置位了导致参数没被加载。第三次最隐蔽MMCM 的某个输出通道被我在约束文件里设成了固定相位模式动态重配置改不了。这种情况读寄存器值是对的但硬件行为不跟随。排查方法是查约束文件里有没有对 MMCM 输出做固定约束。排查这类问题的通用思路是先读回所有相关寄存器确认值正确再检查写顺序是否符合 UG472 的要求最后查约束和综合属性有没有限制动态重配置。4.4 动态重配置对时序收敛的影响动态重配置本身不改变逻辑的时序路径但它改变了时钟频率而你的时序约束是按某个频率写的。如果重配置后的频率高于约束频率时序可能不满足。我的做法是按最高可能频率写时序约束确保所有频率下都收敛。如果最高频率下资源紧张就分两个约束场景但这样综合实现会复杂很多。实际项目中如果频率调整范围不大比如 ±20%按最高频率约束一次就够了。5. 一个完整的动态频率调整实例5.1 系统架构与模块划分假设我们要做一个系统输入 50MHz 晶振输出一个频率可在 40MHz 到 120MHz 之间动态调整的时钟调整通过 UART 命令控制。系统包含以下模块MMCM 原语带 DRP 接口DRP 控制状态机参数查找表ROM存储几组预设频率对应的寄存器值UART 接收模块时钟切换逻辑BUFGMUX顶层控制逻辑MMCM 的配置先用 Clocking Wizard 生成一个基础配置比如输出 100MHz然后在代码里手动例化 MMCM 原语并接出 DRP 接口。Clocking Wizard 生成的代码默认不开放 DRP需要手动修改或直接用原语例化。5.2 关键代码片段与注释MMCM 原语例化时DRP 相关端口要接出来MMCME2_ADV #( .BANDWIDTH (OPTIMIZED), .CLKFBOUT_MULT_F (10.0), .CLKIN1_PERIOD (20.0), .DIVCLK_DIVIDE (1), .CLKOUT0_DIVIDE_F (5.0), .STARTUP_WAIT (FALSE) ) mmcm_inst ( .CLKIN1 (clk_in), .CLKFBIN (clkfb), .CLKFBOUT (clkfb), .CLKOUT0 (clk_out_raw), .LOCKED (locked), // DRP 接口 .DADDR (drp_addr), .DI (drp_di), .DO (drp_do), .DEN (drp_den), .DWE (drp_dwe), .DCLK (drp_clk), .DRDY (drp_drdy), .PSCLK (1b0), .PSEN (1b0), .PSINCDEC (1b0), .PSDONE () );注意 PSCLK/PSEN/PSINCDEC 是相位偏移的专用接口如果你用 DRP 改相位寄存器这几个端口可以不用。但如果你用 PS 接口做动态相位调整那就是另一套逻辑了。两种方式各有优劣PS 接口简单但只能调相位DRP 灵活但逻辑复杂。5.3 上板验证与调试方法上板后怎么确认重配置成功了最直接的方法是用示波器或频率计测输出时钟。如果没有仪器可以用 FPGA 内部的计数器测频用一个已知的参考时钟去数待测时钟的周期数通过 UART 读回来。我的调试习惯是先在仿真里验证 DRP 状态机的时序确认读写时序正确再上板测实际频率。仿真时可以用 MMCM 的行为模型Xilinx 的 unisim 库里有能模拟 DRP 行为。上板后如果频率不对先读回寄存器值确认写入成功再检查 MMCM 的 LOCKED 信号是否正常。如果 LOCKED 一直不拉高说明参数组合非法VCO 超出范围或分频比越界需要重新计算参数。5.4 从 40MHz 切到 120MHz 的实测数据我用一块 Artix-7 开发板做了实测。输入 50MHzVCO 固定在 1000MHz。40MHz 对应 O25120MHz 对应 O8.33实际取整为 8输出 125MHz误差 4%。切换时 MMCM 不失锁因为 VCO 没变。切换前后用计数器测频误差在 0.1% 以内满足大多数应用需求。切换耗时方面一次完整的 DRP 写入3 个寄存器大约需要 10 个 DCLK 周期50MHz 下就是 200ns。加上状态机开销整体切换时间在 1 微秒以内。这个速度对于大多数动态调频场景都够用了。6. 几个容易被忽略的约束与注意事项6.1 约束文件里必须放开的设置如果你的工程用了 Clocking Wizard它生成的 XDC 里可能包含对 MMCM 的固定约束。要支持动态重配置需要确认以下几点MMCM 的 CLKOUT 没有设成固定相位模式没有对 MMCM 内部寄存器做位置约束时序约束覆盖了所有可能的输出频率我一般会在 XDC 里显式声明 MMCM 输出时钟的 create_clock频率按最高值写然后对下游逻辑做时序约束。如果频率范围跨度大可以用 set_clock_groups 把不同频率场景隔开但这样会放松时序检查要谨慎使用。6.2 动态重配置与部分重配置的区别这两个概念容易混。动态重配置这里特指 MMCM 的 DRP是改时钟参数不涉及逻辑功能变化。部分重配置Partial Reconfiguration是改 FPGA 部分区域的逻辑功能是更重量级的技术。两者可以结合使用但解决的问题完全不同。本文只讨论 MMCM 的 DRP。6.3 不同 FPGA 系列的差异本文以 Xilinx 7 系列为例但不同系列差异不小。UltraScale 系列的 MMCM 寄存器映射和 7 系列不同DRP 接口时序也有变化。IntelAltera的 FPGA 用的是 ALTPLL重配置接口叫 reconfig协议和 Xilinx 完全不同。如果你用的是其他系列寄存器地址和计算方式必须查对应系列的手册不能照搬本文的数值。高云、安路等国产 FPGA 也有类似的动态配置功能但文档相对少一些调试时可能需要更多试错。建议先用厂商提供的 IP 生成工具生成基础配置再研究其寄存器映射。6.4 实际项目中的取舍建议最后说点实在的。动态重配置不是所有项目都需要。如果你的系统时钟固定不变老老实实用 Clocking Wizard 生成一个固定配置就行简单可靠。只有当确实需要运行时改变时钟参数比如多协议兼容、动态功耗管理、自适应采样时才值得引入 DRP 逻辑。引入 DRP 的代价是额外的逻辑资源、更复杂的约束、更长的调试周期。我一般会先评估是否能用固定时钟加时钟切换多个 MMCM 输出切 BUFGMUX来替代。如果预设频率种类少比如 2-3 种多 MMCM 加时钟切换更简单。只有当频率需要连续可调或种类很多时DRP 才是最优解。另外动态重配置期间系统的行为要仔细设计。如果重配置会导致时钟短暂中断必须确保系统能安全度过这个窗口。我在一个数据采集项目里就遇到过重配置时 ADC 采样时钟停了导致 FIFO 溢出。后来改成先停采集、再重配置、再恢复采集问题才解决。这种系统级的配合比 DRP 逻辑本身更考验设计功力。
返回列表