资讯动态

Zynq UltraScale+ SYSMON实战:从结温监控到DDR4调试的完整指南

发布时间:2026/10/27 9:48:23 来源:尧图企业网站定制
一个多月前我手头那块Zynq UltraScale板卡在DDR4训练阶段总是间歇性失败有时候开机能起来跑十分钟后读写报错。排查了一圈最后是SYSMON的温度和电压记录帮了忙——VCC_PSDDR在负载起来的时候掉得厉害。这件事之后我意识到很多人包括之前的我把SYSMON当成一个“能读温度的寄存器”而已实际上从硬件电路到软件读取再到报警保护它是一套完整系统。这篇文章就把我从零到用起来的一整套流程整理一下。你如果正在做Zynq UltraScale相关开发无论你是想监控芯片结温、检查供电电压是否正常还是想给板卡加上过温保护这篇文章都适用。我会把SYSMON的硬件架构、Vivado里的配置方式、Linux下的读取方法以及我实际踩过的几个坑一次讲清楚。内容偏工程实践不是文档翻译讲究的是拿来能用。1. 为什么说SYSMON是Zynq UltraScale里“自带体检报告”的硬核1.1 从7系列XADC到UltraScale SYSMON架构演进带来的能力变化用过7系列FPGA的工程师对XADC应该不陌生那是集成在芯片内部的模拟采样模块可以读结温和几路供电电压。到了UltraScale这一代Xilinx把它升级成了SYSMONSystem Monitor全称变了能力也变了。最直观的变化是内部ADC从7系列的12位变成了10位但采样率维持在1MSPS级别同时通道数量、报警机制、寄存器映射都做了重新设计。很多老工程师刚上手时拿7系列的习惯去套结果发现寄存器地址对不上就是这个原因。在Zynq UltraScale MPSoC上SYSMON不是一个单一模块而是分为PS侧和PL侧两部分。PS侧叫PS-SYSMON挂在APB总线上由PS系统直接控制哪怕PL侧没有加载任何逻辑它也能正常工作。PL侧是Vivado里的一个IP核通过AXI4-Lite接口或者DRP接口访问。两者都能读芯片结温但监控的电源通道各有侧重。这个区分一定要先刻在脑子里否则你会在“为什么Linux里能读温度但读不到VCCINT”这种问题上卡很久。1.2 它到底能测哪些量温度、供电、外部模拟通道SYSMON能测的东西分三类。第一类是芯片结温。芯片内部有一个基于PN结的温敏传感器直接连接到ADC。这个温度是芯片裸片上的实际温度不是外壳温度也不是环境温度。搞清楚这一点很重要下文坑的部分我还会再提。第二类是片上供电电压。PS侧可以监控VCC_PSINTFP、VCC_PSINTLP、VCC_PSAUX、VCC_PSDDR这些PS电源域PL侧可以监控VCCINT、VCCAUX、VCCBRAM等PL电源域。这些通道都是芯片内部直接连好的不需要你外接任何采样电路。第三类是外部模拟输入。SYSMON有一组差分输入VP/VN和若干VAUX辅助通道可以接外部传感器或分压网络用来监控板卡上其他电压比如12V输入、模拟前端电源、外部温度传感器等。这类通道需要你自己设计外围电路也是很多硬件工程师容易翻车的地方。1.3 数据通路DRP寄存器、APB和AXISYSMON的所有配置和采样结果最终都落到一组16位寄存器上。PL侧通常是通过AXI4-Lite接口访问这些寄存器PS侧则是通过APB接口。这里有个很容易绕晕的概念不管你是从AXI还是APB进去的寄存器内部逻辑都叫DRPDynamic Reconfiguration Port。所以你在Xilinx文档里看到的很多寄存器名前缀都是“SYSMON_”或者跟DRP相关。常用寄存器其实不多0x00是当前温度值0x04和0x08分别是温度最大值和最小值后面跟着各路电压通道。每个结果寄存器都是16位内部10位ADC的转换结果会左移6位对齐到这个16位寄存器里。换算公式也很固定温度值(°C) 寄存器值 / 65536 × 503.975 - 273.15电压值(V) 寄存器值 / 65536 × 参考电压。这些公式可以直接写进代码里不需要像某些外置ADC芯片一样查一堆表。2. 硬件电路设计供电、参考源和分压网络的处理2.1 模拟供电与参考源决定测量精度的第一步SYSMON说到底是一个模拟采样系统模拟前端供电的质量直接影响读数稳定性。很多工程师在数字电路里习惯了“电容随便摆摆”的作风到了SYSMON这里就要改一改。首先是模拟供电引脚不同封装的U系列芯片上名字略有区别PL侧一般是VCCADC_0这类名字PS侧则集成在PS域的模拟供电里。这些引脚需要一个干净的低噪声电源去耦电容不能省。我的习惯是每个模拟供电引脚放一颗0.1µF的陶瓷电容靠近引脚放置然后再在稍远处放一颗4.7µF左右的钽电容或者大容量MLCC用来吸收低频扰动。其次是参考源。SYSMON支持内部参考和外部参考两种模式大多数应用直接用内部1.2V参考就够了因为内部的精度和温漂已经做得很不错。只有当你要追求极高的绝对精度或者需要用外部参考与其他模拟链路对齐时才需要接外部参考源。接外部参考时要注意参考芯片的噪声还有VREFP/VREFN引脚的去耦这点文档里写得比较散实际踩过坑的人才知道。2.2 外部通道分压设计我推荐怎么算如果你想用SYSMON监控板卡上某一轨电压比如12V输入那就得用分压电阻把它降到SYSMON模拟输入能承受的范围。以内部参考1.2V为例外部输入建议控制在0到1.2V之间。用12V输入的话可以选11kΩ和1kΩ的电阻分压分压后正好是1V留了20%的余量。用5V输入可以用4kΩ和1kΩ分压到1V。这里有个细节很多人会忽略分压电阻一定要用精度1%以上的最好用同一批次温漂一致的电阻。因为分压比一旦漂了你算出来的实际电压就跟着漂而且这种误差是软件校准救不回来的。另外分压之后最好加一个RC低通滤波比如1kΩ串联电阻加10nF对地电容截止频率大概16kHz可以把开关电源的高频噪声滤掉不少。RC的位置要靠近FPGA引脚而不是靠近电源端。2.3 被忽略的PCB布板规则采样点要选对布板时有两个容易犯的错。第一个是把模拟走线拉得太长或者贴着开关电源的电感走。SYSMON的外部输入终究是模拟信号哪怕你前面加了滤波走线太长照样耦合噪声。我一般要求模拟走线控制在2厘米以内实在拉不开就加宽间距绝对不和PWM信号、时钟信号平行走线。第二个错误是采样点选错。监控DDR4供电有人直接从电源模块输出端飞线到分压电阻结果发现SYSMON读出来的电压比万用表量到的低了30mV。原因是PCB走线本身有阻抗大电流流过时从电源模块到负载之间会形成压降。正确做法是从FPGA的供电引脚附近取采样点也就是所谓的开尔文连接。如果板子上有专门的电压检测点优先用检测点。否则你测到的不是芯片真正吃到的电压而是电源模块输出端的电压。3. 快速跑通Vivado里集成SYSMON IP并读第一个温度值3.1 在Block Design里创建并配置SYSMON IP打开Vivado在IP Catalog里搜“sysmon”或者“System Monitor”就能找到SYSMON IP。双击添加之后配置界面里会让你选择启用哪些监控通道Temperature、VCCINT、VCCAUX、VCCBRAM还有外部的VAUX通道。这里不需要全选按你的板子实际需求来就行。选太多通道会拉低单个通道的实际采样率后面读数刷新变慢。需要特别留意的选项是访问接口。SYSMON IP支持AXI4-Lite接口和DRP接口两种模式。在Block Design里和其他IP互联最简单的是选AXI4-Lite这样系统会自动映射出一段寄存器空间你用AXI总线的读写命令就能访问温度和电压寄存器。如果只是想用一点点逻辑资源做轻量读取DRP模式更节省资源但你要自己写DRP时序工程量大不少。3.2 回忆一下连接时钟、复位和模拟电源不能漏配置完IP之后Block Design里会自动出现SYSMON的实例。接下来要连三样东西时钟、复位和模拟电源。时钟很好理解AXI总线需要时钟同时SYSMON内部转换也需要一个参考时钟。你给进来的时钟频率会影响ADC的采样时序一般按照IP配置界面给的频率范围来就行。复位信号多数情况下用Processor System Reset模块的慢速复位输出。模拟电源的坑比较隐蔽。SYSMON IP有个模拟供电输入比如VCCADC引脚如果你在Block Design里没有把它连接到正确的电压源仿真可能看不出来但上板后读出来的数据全是0或者乱跳。我见过不少同事Vivado工程仿真一切正常下载到板子上温度读出来是零排查到最后就是模拟电源引脚悬空。Block Design里这个引脚通常要接到一个constant高电平来表示“供电正常”具体处理方式按IP配置界面提示来别嫌麻烦。3.3 裸机读温度寄存器操作其实不复杂配置完IP综合实现生成比特流在Vitis里做裸机工程时最快捷的方式是用Xilinx提供的XSysMon驱动。套路是固定的先查配置再初始化然后直接调用读取函数就行。大致写法如下#include xsysmon.h #include xparameters.h XSysMon SysMonInst; int main(void) { XSysMon_Config *CfgPtr; u32 TempRaw; float TempC; CfgPtr XSysMon_LookupConfig(XPAR_XSYSMON_0_DEVICE_ID); XSysMon_CfgInitialize(SysMonInst, CfgPtr); XSysMon_Start(SysMonInst); while (1) { TempRaw XSysMon_GetTemperature(SysMonInst); TempC (TempRaw * 503.975f / 65536.0f) - 273.15f; printf(Temperature: %.2f °C\n, TempC); sleep(1); } return 0; }如果你不想用驱动直接读AXI寄存器也一样。SYSMON在地址偏移0x00处就是当前温度寄存器等待转换完成标志位之后读取即可。我自己的经验是先用驱动跑通功能再用裸寄存器写生产代码这样能少走很多弯路。裸寄存器方式读第一版代码时记得确认转换完成位否则会读到上一次的旧数据。4. Linux下读取PS侧SYSMON从设备树到sysfs的全链路4.1 PS侧自带SYSMONU-Boot阶段就能验证前面说过Zynq UltraScale的PS侧自己带了一个SYSMON不需要你动PL逻辑就能用。这意味着在你的Linux系统跑起来之前U-Boot阶段就能读温度和电压这功能用来做板级验证非常方便。U-Boot里最简单的做法是用md命令直接读寄存器。PS-SYSMON的基地址在UG1085的地址映射表里有明确标注通常是在0xFFA50000附近。你读返回的16位寄存器再套温度换算公式就算不写一行C代码也能在串口下验证芯片有没有正常启动、温度是否合理。如果U-Boot阶段读到的温度就是八九十度那大概率是散热没做好你烧写系统前就该处理了。4.2 设备树配置和IIO框架到了Linux内核里SYSMON是通过IIOIndustrial I/O子系统管理的。Xilinx官方内核里自带xilinx-sysmon驱动设备树里要做的就是把PS-SYSMON这个节点声明出来。Xilinx BSP生成的设备树里通常已经有现成节点结构大致是下面这样adcffa50000 { compatible xlnx,zynqmp-sysmon-1.0; reg 0x0 0xffa50000 0x0 0x1000; /* 具体地址和中断号以你使用的BSP设备树为准 */ };提醒一句不同芯片、不同BSP版本地址和reg大小可能有差异直接抄网上的地址不如自己打开设备树搜“sysmon”关键字。驱动probe成功后你会在/sys/bus/iio/devices/下面看到一个iio:deviceX节点。4.3 用sysfs读温度电压的小脚本IIO框架暴露出来的接口是一堆sysfs属性不同内核版本略有差别。较新的Xilinx内核一般会给出in_temp0_raw、in_temp0_offset、in_temp0_scale这样的节点以及对应的电压通道in_voltageN_raw和in_voltageN_scale。实际温度的计算方式是cd /sys/bus/iio/devices/iio:device0 raw$(cat in_temp0_raw) scale$(cat in_temp0_scale) offset$(cat in_temp0_offset 2/dev/null || echo 0) awk -v r$raw -v s$scale -v o$offset BEGIN { printf %.2f\n, (r o) * s / 1000 }如果你看到的是in_temp0_input那说明驱动已经帮你换算成毫摄氏度直接除以1000就是摄氏度。电压通道同样有scale和raw计算方式类似。我建议你把读温度、读VCC_PSINTFP、读VCC_PSDDR这几个常用通道写成一个shell脚本放到板子上调试的时候一条命令就能看全板关键状态效率高很多。4.4 什么时候才需要PL侧的SYSMON跑Linux的典型方案是直接用PS侧SYSMON因为不占PL资源也不需要额外驱动移植。但如果你要监控的是PL侧的VCCINT、VCCAUX这些PL电源域或者你的应用里PL自定义逻辑需要实时读取某些外部模拟通道并做闭环控制那就需要PL侧的SYSMON IP。这种情况下PL侧读数可以通过AXI接口给PS的Linux读也可以直接在PL逻辑里使用。两套SYSMON不冲突可以同时用只是注意别在同一个设备树里重复声明重叠的寄存器空间。5. 从“看数据”到“做保护”报警阈值、风扇联动与DDR4排查5.1 温度电压报警阈值把寄存器值算对SYSMON不仅是被动测量它还能主动报警。每个可测通道都有一组上限和下限阈值寄存器采样结果超过阈值时SYSMON会拉高报警信号。PL侧有ALARM引脚可以接到中断控制器PS侧则直接进中断控制器。设置阈值时要把物理量换算成寄存器值。以85°C温度阈值为例换算公式是// 目标温度 85°C float target_temp_c 85.0f; uint32_t threshold_reg (uint32_t)((target_temp_c 273.15f) * 65536.0f / 503.975f);电压阈值也类似比如VCC_PSDDR额定1.2V你想监测它低于1.14V时报警那阈值寄存器值就是1.14 / 参考电压 × 65536多数通道用内部参考1.2V。注意不同通道的满量程参考可能不同不要一个公式打天下。设计阈值时还要考虑芯片正常工作时电压纹波波动别把下限设得太贴近额定值否则负载一波动就误报警。5.2 风扇控制与保护逻辑的工程实现温度数据拿到手上一个很自然的应用是风扇控制。很多板卡上风扇就是全速转噪声大而且浪费用SYSMON的结温做PWM调速效果立竿见影。我不建议在驱动里做太复杂的PID一个滞回控制器足够static int pwm_duty_for_temp(float temp) { if (temp 80.0f) return 100; else if (temp 65.0f) return 70; else if (temp 50.0f) return 40; else return 25; }滞回的意思是升温到阈值和降温到阈值之间留一个差值比如65°C启动中速、55°C才降回低速避免风扇转速在两个阈值附近反复横跳。如果你用的是PL侧SYSMON报警信号可以直接硬件触发风扇全速即使PS侧Linux崩溃了保护依然生效。这种协同设计是工业设备值得投入的地方。5.3 用SYSMON辅助DDR4调试一个真实场景回到开头提到的DDR4训练问题。Zynq UltraScale的PS内建DDR控制器DDR4接口对电源质量非常敏感VCC_PSDDR和VCC_PSINTFP这些电压在负载跳变时跌落轻则训练参数偏差重则读写报错。我用SYSMON做的事情很简单在DDR4压力测试的同时每秒记录一次VCC_PSDDR和芯片温度存到日志文件里。压力测试跑半小时后回看数据发现芯片温度从45°C上升到78°C同时VCC_PSDDR的读数在某个负载阶段会掉到临界值。没有这个趋势性记录我会一直怀疑是DDR控制器配置参数的问题而不是电源。这里也提醒一句SYSMON的采样率虽然ADC本身有1MSPS但多通道轮询分摊下来每个通道实际刷新率不算高加上Linux读取和日志写入的开销你看到的曲线适合判断趋势和秒级波动不适合观察几十微秒的瞬态跌落。真要看纹波和瞬态示波器还是不可替代的。SYSMON的价值在于能长时间记录、能自动化报警这是示波器做不到的。6. 实战中容易误判的坑和我的排查思路6.1 温度读数偏高偏低先确认你测的是“结温”群里经常有人拿着SYSMON读到的温度去跟红外热像仪对比发现差个二十度就怀疑芯片坏了。实际上SYSMON读的是结温也就是芯片裸片内部的温度红外热像仪测的是封装外壳表面温度两者之间隔着封装材料和散热界面有10到20°C的差值完全正常。我做过的板卡上结温85°C时外壳可能只有65°C左右。另一种情况是温度读数漂移。如果你发现待机时温度读数来回跳好几度首先查模拟供电的电源纹波其次查内部参考电压引脚上的去耦电容。排除电源问题后再考虑芯片本身的热阻和散热条件。判断传感器是不是线性正常可以用热风枪慢慢加热芯片看读数是否跟随上升或者对比制冷状态下的读数变化。6.2 PL侧SYSMON IP读不出数据按顺序排查这个问题我在帮朋友调板时遇到过不止一次。现象很一致裸机里读温度寄存器永远返回0。排查顺序我建议这样来第一查模拟电源引脚有没有接上这在Block Design阶段最容易漏。第二查转换有没有使能SYSMON IP默认不会所有通道一起转需要你在配置界面或者代码里打开转换使能位。第三查时钟AXI时钟如果没给或者频率不对寄存器访问都不会有正确响应。第四查ILA抓取寄存器总线的实际返回值确认是寄存器全0还是读不到地址。我自己踩得最深的一次是把模拟电源引脚连到了普通IO的高电平上结果VCCO电平跟模拟供电要求不匹配读数全错。后来检查原理图才发现这个引脚需要独立的模拟电源不是逻辑高电平能代替的。6.3 设备树没生效内核配置和节点名字别搞错Linux下设备树配得对但sysfs里没设备先别急着怀疑设备树格式先看内核有没有把XILINX_SYSMON这个驱动编进去。不少人用的是裁剪过的内核驱动没编译设备树写了也白搭。驱动在的情况下用dmesg | grep -i sysmon看驱动加载时有没有报错再用ls /sys/bus/iio/devices/看是不是已经注册了设备。如果设备树里寄存器地址写错驱动会probe失败如果地址对但时钟没开读数可能一直是0。这里有一个快速验证方法直接在U-Boot阶段用md命令读同一基地址的寄存器看有没有非零值。U-Boot能读出来说明硬件通路正常问题出在Linux侧配置U-Boot也读不出那基本就是硬件电路或者启动时钟配置的问题。6.4 烧写实现后读数正常但跟万用表有偏差先分辨误差来源有一个很常见的现象SYSMON读VCC_PSDDR是1.18V万用表量板子上测试点是1.20V差值20mV。这不是SYSMON坏了很可能是采样点不同导致的真实压降。如果确实需要精确对齐可以在应用层做一次线性校准用高精度万用表量出基准电压然后把偏移量写进校准参数。Xilinx的SYSMON内部也预留了校准寄存器不过我自己更倾向于在软件里做校准因为这样不影响其他通道而且校准参数可以跟着配置文件走。另外烧写完FPGA之后片上逻辑开始翻转芯片功耗上升温度读数比空载时高十几度是正常现象。不要把这种变化当作异常反而应该用它来判断你的逻辑功耗是否在预期范围内。最后补一句比较实际的体会SYSMON是调试工具但不要把它当示波器用它能帮你发现趋势性异常、做自动保护、记录长时间数据但抓毛刺、看纹波必须请示波器。两者配合才是完整的电源和温度排查思路。每次调试DDR4或者电源问题我都习惯先把SYSMON这条链路打通它不花多少资源但关键时候真能救命。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑