
开门见山说吧只要是做过带监控功能的FPGA板卡几乎都绕不开Xilinx内部那颗XADC硬核。上电先读一下芯片温度、看看内核电压有没有起来这套动作在板级调试里就像开机自检一样自然。而XADC的DRP接口就是搞这件事的主干道。很多人卡在同一个地方IP核图形界面配置完顶层确实多了好几个端口但DRP时序怎么打、寄存器地址怎么填、读回来的16位数据又怎么换算成温度网上资料零散官方文档又厚得吓人。这篇文章就把整条链路完整串一遍从Xilinx Vivado里的XADC IP核配置开始讲到DRP接口的信号时序、寄存器映射、Verilog读取状态机最后附上实际调试中踩过的坑和排查思路适合刚接触FPGA、或者项目里需要加电压温度监控但不想从头啃UG480的朋友。1. 项目背景与方案选型DRP为什么是主流玩法1.1 XADC硬核到底能测什么不管是Artix-7、Kintex-7、Virtex-7还是Zynq-7000系列芯片内部都集成了一个或多达两个XADC硬核。这个硬核本质上是一个12位、采样率最高1MSPS的模数转换器但它不是普通ADC它连了一堆内部传感器和模拟输入引脚芯片结温Temperature对应寄存器0x00。内部供电电压VCCINT、VCCAUX7系列还有VCCBRAM对应0x01到0x03。专用差分输入VP/VN这是最常用的外部模拟输入通道满量程1V。16路辅助差分输入VAUXP/VAUXN[0:15]用来采集外部传感器或分压后的电压对应0x04到0x13。片内还带报警输出ALM[0:7]过温、电压越限都能直接给硬件中断信号。也就是说大多数板卡要监测的“健康状态”指标XADC全包了。外部再接个PT1000热电阻、NTC或者霍尔传感器只要有对应的信号调理电路一样能通过VAUX通道进来。Zynq-7045这一类带PS的芯片更特殊除了PL侧能用DRP访问XADCPS侧也有专用的接口驱动但工程上最常见的还是PL侧通过DRP自行控制因为这样不依赖PS端软件环境上电就能跑。1.2 三种访问方式JTAG/DRP/PS怎么选XADC硬核物理上同时暴露了多种访问途径但同一时间只能有一条路真正接管它JTAG接口通过JTAG TAP控制器访问不需要任何用户逻辑Vivado Hardware Manager的“View XADC”功能就是走这条通道。优点是调试方便缺点是无法在用户逻辑里实时读取数据。DRP接口就是本文的主角Dynamic Reconfiguration Port一组并行寄存器读写端口用户逻辑通过它读取转换结果、写配置寄存器。这是FPGA内部逻辑访问XADC的标准方式。PS接口仅ZynqPS端通过专用寄存器访问但如果你在PL里也要读本质上还是要走DRP两者之间需要做共享和互斥。所以方案选型很简单如果只是测试时看一眼温度用JTAG就行如果要在FPGA逻辑里实时监控、做阈值判断、把数据送到串口或DDR那就必须用DRP。还有一点要特别注意IP核配置界面里DRP和JTAG是二选一的如果你勾了“Enable DRP”但没关JTAG访问实际调试时JTAG工具可能把XADC占住导致DRP读回来的值不动或者全零这个坑后面专门说。1.3 技术链路总览与开发环境准备整个项目链路很清晰在Vivado中例化XADC IP核按需勾选模拟通道和接口模式。拿到IP核生成的顶层端口把DRP信号接进自研读取状态机。状态机按DRP时序发起读请求等待DRDY有效后锁存DO数据。把DO[15:4]这12位数据按公式换算成温度、电压值。用ILA在线抓取DRP信号和Vivado自带的XADC Viewer交叉验证。开发环境方面我用的Vivado 2019.2XADC IP核从2015.4到现在的界面都差不多高版本只是多了某些选项的默认值调整不影响本文的流程。这里顺手提一句工具链的事如果电脑上装了老版本的Xilinx SDK 2015.4再往上升级最好先把旧环境卸载干净残留驱动在访问XADC这类内部外设时容易出现莫名其妙的版本冲突工程还没开始先把环境坑踩一遍就太亏了。2. XADC IP核配置把GUI里的选项和硬件行为对上2.1 添加IP并选择DRP工作模式在Vivado IP Catalog里搜“XADC”双击添加XADC Wizard界面第一页就是接口模式。这里必须选择“DRP”作为接口类型如果选了JTAG顶层生成出来就是jtag接口的信号DRP相关端口根本不会出现。选完接口类型后关键选项有这么几个Operating Mode默认是Continuous Sequence Mode也就是连续扫描模式。建议保持默认这个模式下XADC会自动循环采集所有被勾选的通道结果寄存器里的数据永远是最新的用户逻辑只需要在需要的时候去读对应地址。ADC Clock这里决定采样时钟。XADC内部需要一个参考时钟来驱动转换逻辑如果你给DRP的DCLK频率合适可以直接用它如果DCLK频率过高就配置内部降频。实际工程里我习惯给100MHz的DCLK采样率完全够用时序也容易收敛。如果你想跑满1MSPS的采样率就需要把内部ADC时钟配到200MHz但要注意这个频率下模拟输入信号阻抗匹配和板级噪声要求更高。Enable Averaging平均值滤波开关可以选16次平均。不启动平均的原始数据跳动比较大尤其是温度通道每次转换结果可能有几十个LSB的波动。开了16次平均之后读数会平滑很多但代价是有效采样率变成原来的1/16。对板级监控来说这个代价完全可以接受。很多人配完IP就不管了直接接线读数据结果读出来的温度在30°C和40°C之间来回跳。其实问题就出在均值滤波没开。如果后面做阈值报警这种原始数据的抖动会导致报警触点反复触发非常烦人。2.2 通道序列与DCLK怎么配XADC IP核界面的Channel Sequencer页面就是决定自动扫描哪些通道的地方。默认会把Temperature、VCCINT、VCCAUX和VCCBRAM都勾上这没问题。如果你需要采集外部模拟量比如板卡上一个分压电阻输出的电压信号就在External Inputs里勾上对应的VAUX或者VP/VN。这里有几个容易被忽略的细节VAUX通道使能之后该通道对应的FPGA引脚会被固定为模拟输入功能不能再当普通IO用所以不要随便勾。每个VAUX通道的模拟输入范围是0到1V。如果外部信号超过了1V必须加电阻分压或者运放调理否则XADC内部保护二极管会钳位轻则读数满量程重则损坏引脚。IP核会按照你在GUI里勾选的顺序自动生成通道序列最终读取时只需要按寄存器地址读就行。如果不小心漏勾了一个通道却按照固定地址去读那读回来的可能是上一个通道的旧值。DCLK的配置也在这一页附近需要你告诉IP实际供给的频率。这个时钟和DRP的状态机是同步的寄存器读写和状态转换全部跟着DCLK走。DCLK不需要特别精准几十MHz都能工作但不能乱给如果给到200MHz以上又没有配置内部分频XADC内部的时序裕量会很紧张长期运行可能偶发读到错误数据。2.3 报警、均值与“看起来无关但会影响结果”的选项XADC IP核还有几个报警页面Temperature Alarm、VCCINT Alarm等等可以设置上限和下限。默认是关闭的用户逻辑可以用ALM输出做硬件中断比如芯片温度超过85°CALM信号直接拉高后端电路据此关断电源或者上报上位机。不过真正影响读取结果的不是报警本身而是均值滤波和自动校准这两个功能。XADC内部有一个校准逻辑IP核默认是开启的它会在后台周期性测量内部基准修正增益误差。这个功能不要关开着能显著提高精度。实际测试中同样的板子校准开和关温度读数能差2到3°C。还有一个容易忽略的点如果你在IP配置里同时启用了多个VAUX通道记得给每个通道都配置好对应的引脚网表约束。XADC IP核生成的约束文件里已经包含模拟引脚的SYS_*属性但前提是你必须把FPGA引脚位置约束做好。很多项目综合报错追根溯源都是XADC模拟引脚没有合理放置导致的。3. DRP接口信号、寄存器与时序动手前的必修课3.1 DRP端口信号逐个拆解XADC IP核的DRP信号一共就那么几个每个都解释清楚信号方向作用dclkinputDRP接口同步时钟所有操作都在dclk上升沿采样deninputDRP使能信号拉高一个周期表示发起一次读或写操作dweinput读写选择0读1写配合den使用daddr[6:0]input要访问的寄存器地址7位宽度di[15:0]input写操作时的数据读操作时可置0do[15:0]output读操作返回的数据DRDY为高时有效drdyoutputDRP操作完成标志拉高一个周期表示本次读或写已经完成busyoutputXADC正在忙转换期间不能发起新的DRP操作eocoutput单通道转换结束脉冲eosoutput整个序列扫描结束脉冲很多人一上来就把eoc当数据有效信号用这是不严谨的。eoc只是告诉你某个通道的转换结束了结果寄存器已经更新但你不知道它更新的是哪个通道。DRDY才是DRP读写事务完成的标志只有在DRDY为高时DO上的数据才是你请求的那个地址的内容。3.2 寄存器地址速查与数据格式说明XADC的结果寄存器地址和数据格式是整篇文章里最值得收藏的部分地址对应通道数据说明0x00Temperature12位DO[15:4]有效0x01VCCINT12位DO[15:4]有效0x02VCCAUX12位DO[15:4]有效0x03VCCBRAM12位DO[15:4]有效7系列才有0x04 ~ 0x13VAUX[0:15]12位DO[15:4]有效0x20 ~ 0x25状态、配置、测试寄存器一般不直接操作特别注意数据格式DO是16位的但真正有效的ADC转换结果只在高12位也就是DO[15:4]低4位永远为0。换算的时候要么整体右移4位要么只取高12位。新手最容易犯的错就是拿整个DO去套公式结果算出来的温度和电压大得离谱。关于0x20到0x25这些控制寄存器IP核在配置阶段已经写好了普通项目直接读结果寄存器就行。只有需要动态切换通道、更改报警阈值或者修改均值配置时才需要去写这些寄存器。如果你对每个bit的定义还没有吃透建议不要轻易去动一旦写错很可能导致XADC进入非预期的工作模式。3.3 读写时序模型DRDY才是关键DRP读时序可以简化为三步在dclk上升沿置den为高dwe为低同时把要读的地址放到daddr上。保持一拍后把den拉低。等待几个周期后drdy拉高此时锁存do上的数据。写时序类似只是dwe要高di上放要写的数据。需要注意DRP接口不是流水线式的必须等上一次操作drdy返回之后才能发起下一次操作。如果频繁发起读请求间隔小于XADC内部处理时间后面的请求会被忽略或者导致数据错乱。busy信号和DRP的关系也要搞明白。当XADC正在做模数转换时busy会拉高如果此时发起DRP操作操作不会立即执行或者读到的数据并不是最新转换结果。稳妥的做法是读地址之前先看busy等它拉低再发起读。不过对于温度、电压监控这种低速场景只要你不是一直在疯狂读寄存器busy的影响基本可以忽略。4. 完整读取实现从状态机到换算公式4.1 可复用读取控制器Verilog原代码我直接给一段可以放进工程的Verilog代码实现连续读取4个通道的温度、VCCINT、VCCAUX和VCCBRAM读完后把结果锁存到4个寄存器并产生一个done脉冲。这个状态机的思路是自己发起读请求、等drdy、锁存数据、切下一通道非常适合做板级监控的基础逻辑。timescale 1ns / 1ps module xadc_drp_reader ( input wire clk, // 系统时钟接到IP核的dclk端口 input wire rst_n, // 低有效复位 input wire start, // 启动一次扫描 output reg [6:0] daddr, // DRP地址 output reg den, // DRP使能 output reg dwe, // DRP读写选择固定为0表示读 input wire [15:0] do, // DRP读数据 input wire drdy, // DRP操作完成 output reg [15:0] result_temp, // 温度通道原始数据 output reg [15:0] result_vccint,// VCCINT原始数据 output reg [15:0] result_vccaux,// VCCAUX原始数据 output reg [15:0] result_vccbram,// VCCBRAM原始数据 output reg scan_done // 四个通道都读完 ); localparam IDLE 3d0; localparam ISSUE 3d1; localparam WAIT_REQ 3d2; localparam DONE 3d3; reg [2:0] state; reg [1:0] ch_index; reg wait_one; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; den 1b0; dwe 1b0; daddr 7d0; ch_index 2d0; wait_one 1b0; result_temp 16d0; result_vccint 16d0; result_vccaux 16d0; result_vccbram 16d0; scan_done 1b0; end else begin scan_done 1b0; case (state) IDLE: begin if (start) begin ch_index 2d0; state ISSUE; end end ISSUE: begin // 每个通道在发下一次请求前先等一个时钟 if (wait_one) begin wait_one 1b0; case (ch_index) 2d0: daddr 7h00; // 温度 2d1: daddr 7h01; // VCCINT 2d2: daddr 7h02; // VCCAUX 2d3: daddr 7h03; // VCCBRAM endcase den 1b1; dwe 1b0; state WAIT_REQ; end else begin wait_one 1b1; end end WAIT_REQ: begin den 1b0; if (drdy) begin case (ch_index) 2d0: result_temp do; 2d1: result_vccint do; 2d2: result_vccaux do; 2d3: result_vccbram do; endcase if (ch_index 2d3) begin ch_index 2d0; scan_done 1b1; state IDLE; end else begin ch_index ch_index 2d1; state ISSUE; end end end endcase end end endmodule这段代码在实际工程里可以直接用。start信号可以用一个周期脉冲触发也可以接一个计数器让它自动周期扫描。状态机的核心逻辑就是ISSUE发请求、WAIT_REQ等drdydrdy一到立刻锁存并切下一通道。dwe被固定为低所以整个模块只做读操作不会误写XADC内部寄存器。4.2 换算公式与显示方法原始寄存器值拿到手后先右移4位得到真正的12位ADC码。换算公式并不复杂但每一个通道都有自己的量程和偏置最容易搞混。温度换算是所有通道里公式最明确的摄氏温度 (ADC码 × 503.975) / 4096 - 273.15ADC码是12位满量程4096对应的是XADC内部温度传感器的上限503.975这个系数在UG480里有明确出处。如果你用浮点运算不方便也可以用定点方式近似温度乘以100保留两位小数再用整数算。外部VP/VAUX通道的电压换算更直接电压值(V) ADC码 × 1.0 / 4096因为VAUX和VP的输入满量程就是1VFD码4096对应1V所以电压值等于码值除以4096。如果外部加了电阻分压比如1:3那真实电压还要再乘以4。至于VCCINT、VCCAUX这些内部电源通道片内已经接好了分压网络你直接处理寄存器值时需要查一下对应芯片的满量程系数不同7系列芯片略有差异。个人建议不要把时间和精力耗在背这些系数上实践中最可靠的做法是用Vivado Hardware Manager的XADC Viewer看同一个通道的参考数值然后反向校准你的换算公式。比如Viewer显示温度45.2°C你的ADC码换算出来只有44.7°C那就把偏置修正一下。显示方面如果只是做监控把换算后的数据通过UART打印到串口助手就够了。如果需要界面上实时显示波形可以把这几个寄存器接到ILA或者挂一个AXI-Lite从机让PS端软件来读。这个后面会提。4.3 仿真、上板和交叉验证步骤第一轮建议先仿真再看真实波形可以有效缩短调试时间。仿真时写一个简单的testbench给IP核的drdy信号人为延迟几个周期返回模拟真实的DRP时序。重点观察状态机是不是在ISSUE、WAIT_REQ之间正确跳转drdy到来时有没有正确锁存DO。XADC IP核本身不带行为仿真模型的话可以直接对IP输出端口打激励来验证自己的状态机逻辑不用等XADC全功能模型。上板之后先用ILA抓四个信号的波形daddr、den、drdy、do。只要看到drdy有规律的脉冲就说明DRP读通路已经打通。此时把ILA捕获到的do值和寄存器地址对应起来虚拟验证读取结果。最后用Vivado Hardware Manager的View XADC功能做交叉验证。这个工具通过JTAG读XADC内部的温度电压虽然和DRP是两条路径但读的是同一颗硬核数值应该基本一致。如果两边读数差很多优先检查是不是地址读错了或者数据没右移4位就做了换算。5. 常见问题与调试心得5.1 读不到数据、DO恒为零的检查顺序这个现象在论坛里出现频率非常高通常不是XADC坏了而是时序或者配置问题。我排查的顺序是固定的基本能在五分钟内定位先看drdy有没有反应。ILA里如果den拉高之后drdy完全没动静首先怀疑dclk和复位。dclk没接上、复位一直有效状态机根本跑不起来。drdy有脉冲但do一直为零。重点查daddr和IP核配置是否一致。比如你在IP里勾选了VAUX通道但读的地址却是0x20这种配置寄存器或者地址操作关系没搞清楚。检查是不是JTAG占用了XADC。如果IP核配置里同时开启了JTAG Debug AccessJTAG工具一旦连上芯片XADC所有权就归JTAG了DRP读请求会被忽略但drdy依然会有响应读回来的数据却不变。这个坑我当时排了一整天才发现。看busy电平。如果XADC正在处理大量内部操作比如刚上电自动校准还没结束DRP访问会被延后。不要担心等一段时间再读。5.2 读到的数值“跳变”“异常”怎么排查数据跳变最常见的原因是均值滤波没开。XADC单次转换结果在温度通道上的噪声可以达到几十个LSB换算成温度至少波动1到2°C。开了16次平均或者软件端做滑动滤波之后波动能降到0.1°C级别。如果你做的是高精度温度监控这个步骤省不了。数据异常的大多数情况是量程和分压匹配问题。VAUX输入超过1V读数会顶到4095输入是负压读数会显示0附近。这时就要检查前端调理电路。还有一类异常是通道错位读0x01却指望它是外部VP的电压这纯粹是地址表没弄熟对着文章里那张表改回去就行。5.3 JTAG冲突、复位、工具链老化等坑JTAG和DRP的关系要反复强调DRP和JTAG在同一时刻不可能同时访问XADC内部寄存器。Vivado的Hardware Manager一旦连接目标板即使你只是在看ILAJTAG侧的XADC访问权就已经被占用了。如果此时PL逻辑在用DRP读XADC轻则读数不动重则整个DRP接口挂起无响应。解决办法是在IP核配置里把JTAG相关选项关掉或者在调试时统一用一条访问通道。复位方面XADC内部有一个全局复位硬复位之后需要等待一段时间才能开始正常转换。有的IP核版本自带一个延迟模块有的需要外部等待足够时间。建议在状态机IDLE状态下加一个上电延时计数器至少等待几百个DCLK周期再发第一笔读请求。工具链侧老版本Xilinx工具和驱动残留的问题也碰到过。有一次我在升级Vivado版本后一直无法通过Hardware Manager正确读取XADC温度最后发现是旧版SDK的USB驱动没卸载干净导致JTAG链的不稳定。解决方案就是先把旧环境彻底清理再做干净安装。5.4 把采集逻辑封装为AXI-Lite IP再往后扩展当你跑通DRP读取之后我强烈建议走一步长远棋把上面这段读取状态机包成一个AXI-Lite从机IP让Zynq的PS端或者外部CPU可以通过总线直接读取温度电压寄存器。这样做的价值在后期非常明显上位机软件里写一句readl就可以拿到芯片温度不再需要修改PL逻辑、重新综合下载。封装的时候注意几点AXI-Lite的时钟和XADC的dclk可能是异步的结果寄存器需要做跨时钟域处理简单的方式是两地寄存器打两拍。读取结果到AXI回读通路之间的握手逻辑也要做好不要让CPU在数据没准备好的时候读到无效值加一个valid标志位就可以。这一步做完整个XADC采集子系统就具备产品雏形了后面无论是接串口、接OLED、还是上报到上位机监控系统都只是软件层面的活了。我在Zynq-7045和Artix-7两个平台上反复验证过这套流程除了IP配置界面勾选项略有差异核心的DRP读取代码一字未改都能跑通。个人最大的体会是XADC本身并不可怕真正花时间的往往是那些“看似无关”的细节——JTAG冲突、复位时序、数据右移、均值滤波。把DRP的drdy当作唯一的握手信号盯住把寄存器地址表贴在电脑屏幕上再配合Vivado内置的XADC Viewer交叉验证温度电压采集这个功能基本一次就能做成。