尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32MP257 TSN Switch引发重启卡死与RTC时间重置的深度排查

STM32MP257 TSN Switch引发重启卡死与RTC时间重置的深度排查 最近在调一块基于STM32MP257的主控板被一个现象折磨了整整三天系统里打开了内部TSN switch业务流量跑起来一切正常但只要执行reboot大约有七八成概率会直接卡死在重启流程里屏幕定格在最后一行日志按复位键才能救回来更诡异的是等系统再起来date命令直接回到Thu Jan 1 00:00:00 UTC 1970dmesg和systemd日志的时间线全部错乱。一开始我当成两个独立问题分开查走了不少弯路后来才发现date reset和reboot hang在MP257这个平台上往往是一根藤上的两颗瓜背后牵扯到RTC电源域、TSN switch的时钟域、以及内核关闭设备时的停时顺序。这篇文章把我完整的排查过程、根因定位和最终修复方案整理出来给同样踩在STM32MP25x TSN switch坑里的朋友做个参考。1. 先把问题说清楚现象、环境和复现路径1.1 开发平台与TSN switch背景STM32MP257是ST新一代MP2系列的中间型号双核Cortex-A35加单核Cortex-M33主打工业以太网和边缘计算。它对工业场景最大的吸引力就是内部直接集成了一颗支持TSN的以太网交换核心配合两路千兆MAC可以在单颗芯片上实现多网口、低延时的确定性网络交换典型应用是PLC控制器、运动控制主站、边缘网关这类需要EtherCAT主站之外再做TSN背板通信的设备。我这块板子实际是做的是一台小型工业交换机加边缘计算盒子用到了MP257内部的TSN switch能力两路外接PHY走RGMII接口一路CPU内部口接MAC通过802.1AS做gPTP时间同步802.1Qbv做时间感知整形给运动控制报文让出固定时隙。整套系统跑起来之后实时通信的抖动实测控制在微秒级说实话这个集成度在单芯片方案里确实能打。但问题就出在重启这个看起来最基础的操作上。1.2 两个异常现象的精确复现描述先说表现方便大家对号入座。现象Adate reset。每次reboot之后系统时间回到1970年1月1日hwclock -r读出来的RTC时间也是这个值。这不是偶发是每次都这样。如果只是系统刚开机那会儿显示1970随后被NTP拉回来那说明RTC可能是临时没读到但我这里的情况是NTP根本拉不回来时间就一直停在1970直到手动date -s或者hwclock --systohc再写一次。现象Breboot hang。执行reboot或systemctl reboot后串口日志打印到最后一条然后整个系统卡死。具体卡在哪条日志不固定有时候是reboot: Restarting system之前有时候是打印完这句之后CPU就再也没有动静了。按一下板子上的复位键系统能正常起来但起来之后date又是1970。而且这个概率不是100%大概重启七八次能成功两三次成功的那几次反而是更吓人的——因为说明复位路径本身是有竞态的。值得一提的细节这两个现象单独看都不算罕见但如果板子上不使能TSN switch只把两路GMAC配成普通网口reboot hang的复现概率几乎降为零date reset变成偶发。这让我强烈怀疑TSN switch不是无辜的旁观者。1.3 初步判断与排查方向拿到这个问题我当时的判断是两条线date reset大概率是RTC相关的硬件或驱动问题reboot hang大概率是网络驱动停时或复位顺序问题。所以最初的排查也是分头进行的。但查了两天之后我发现一个关键线索在TSN switch使能的情况下如果我把网络服务里的NTP关掉reboot hang照样存在date reset也照样存在反过来如果只修RTC让时间不再重置reboot hang依然存在。这说明两者确实不是同一根因果链上的但它们共享同一个坏的条件——就是TSN switch使能之后系统的整个电源、时钟、复位行为都变了。所以后来我把定位策略从各查各的改成先把TSN switch带来的系统级变化摸清楚问题才快速收敛。2. 为什么date会重置RTC时间线全部捋一遍2.1 date重置到1970意味着什么嵌入式Linux里date显示1970年1月1日本质上是内核的墙上时钟wall clock从零开始走。内核的墙上时钟依赖两个来源一是RTC硬件二是系统启动后由NTP或用户手动校时。如果你开机后date是1970那说明内核在启动过程中没有从RTC初始化系统时间或者RTC读出来本身就是1970。我这边第一次出现这个问题时第一反应是内核里的RTC驱动没生效于是先查了设备节点ls /dev/rtc* cat /sys/class/rtc/rtc0/time dmesg | grep -i rtc结果/dev/rtc0存在读出来的时间也确实1970。这个信息很关键RTC驱动是probe成功了的硬件地址也能访问但寄存器里的值就是1970。说明RTC芯片本身被复位过或者掉过电备份域的计数值被清了。2.2 RTC驱动、VBAT与fake-hwclockSTM32MP25的RTC和MP1一样放在备份电源域里由VBAT引脚供电。主电源掉电或者备份域被别人复位RTC寄存器和后备寄存器就会全部清零。我这块板子是评估板改的VBAT一开始是接到了3.3V系统电源上的没接独立电池。这个设计本身在绝大多数场景下没问题因为系统一上电VBAT就有电RTC不会无缘无故丢。但问题恰恰出在reboot hang上如果系统卡死在复位流程里硬件复位后主电源会有一个短暂的跌落如果VBAT的滤波电容不够大备份域电压跟着掉RTC寄存器就保不住。用示波器抓VBAT引脚确实看到复位瞬间有一个大约300毫秒的跌落跌到了1V以下。这基本上就是date reset的物理原因了。然后是软件层面的放大因素。就算RTC寄存器没丢Linux下还有一个坑叫fake-hwclock。很多BSP为了节省成本默认不开RTC驱动而是用一个fake-hwclock服务在关机和开机时把时间存到文件里。如果RTC驱动是后面才使能的device tree里RTC节点状态是okay但系统里可能同时残留了fake-hwclock两边打架时间就会被错误覆盖。我在排查时特意确认了这一点systemctl status fake-hwclock systemctl list-units | grep -i time如果确认在用真实RTC建议直接disable fake-hwclock避免开机时它把上次存的时间写回RTC覆盖掉真正的时间。2.3 TSN对时间的要求放大了问题date reset本身看起来只是不方便但在TSN场景里这事很要命。TSN的时间敏感整形802.1Qbv和帧抢占802.1Qbu都严重依赖所有节点共享一个同步时钟802.1AS gPTP会通过PTP报文把主时钟分发到全网。gPTP同步的是系统的PTP时钟域但它需要从系统墙上时钟获取UTC参考这样才能把PTP时间映射到真实世界时间。如果系统date在1970PTP域时间虽然能通过硬件时间戳继续同步但一旦需要换算成UTC或者需要和上层应用的时间戳关联就会产生巨大的时间偏移。而且我们这边有个要求是断电重启后最好能在几秒内恢复gPTP主时钟的绝对时间如果RTC每次重启都丢主时钟就得从头等待NTP或者手动校时这在现场是完全不可接受的。一句话总结这条线date reset的直接原因是备份域掉电根本诱因是reboot hang导致复位路径异常软件上又缺少RTC保护机制三件事叠加才让1970这个问题天天见。3. reboot hang的定位从驱动停时序到TSN switch3.1 reboot在MP257上到底怎么走排查reboot hang之前先要搞清楚MP257上重启的完整路径。这条路径和普通MCU完全不同很多第一次接触MP2系列的工程师容易在这里懵。MP257跑的是标准ARM生态TF-ATrusted Firmware-A在最底层然后是OP-TEE、U-Boot、Linux内核。用户执行reboot后Linux内核会挨个执行所有注册了.shutdown回调的驱动然后通过PSCIPower State Coordination Interface调用SYSTEM_RESET这个调用最终由TF-A处理TF-A会做一些最后的清理再把复位信号交给RCC。所以reboot hang可能发生在三个位置一是某个驱动的shutdown回调里二是PSCI调用进入TF-A之后三是硬件复位信号发出之前。我的做法是把串口日志开到最详细然后反复reboot看最后一条日志到底停在哪。几次复测后发现大概率停在内核打印的reboot: Restarting system之后、TF-A打印任何信息之前而且有时TF-A会打印一条NOTICE: BL31: PSCI_SYSTEM_RESET就再也不动了。这说明问题很可能出在硬件层面复位信号虽然发了但某些外设没有跟着复位干净或者总线还在被占用。3.2 隔离实验关闭TSN switch验证为了验证我的猜测我做了几个隔离实验。第一个实验是把TSN switch功能关掉两个网口配成普通独立MAC跑一晚上10次reboot全部通过date reset也只出现了一次。第二个实验是恢复TSN switch但把两根PHY的网线拔掉结果reboot hang概率反而更高这说明问题不在链路上的业务流量而在TSN switch本身的复位行为上。第三个实验最有价值把TSN switch使能但修改内核cmdline加上nohlt之类的参数没什么用我换成了在驱动里临时加打印观察shutdown阶段TSN switch相关驱动是否执行完。结果发现停在了某个等待DMA描述符完成的循环里——驱动在等一个永远不会来的中断。这个现象就很典型了。TSN switch和GMAC之间是内部总线连着switch内部还有自己的转发引擎和buffer。内核shutdown的时候如果上层MAC已经停了但switch还在正常转发状态那么某些内部计数器、描述符环就会处于半停状态。此时驱动如果做了一个不严谨的等待所有DMA传输完成操作就永远等不到因为已经没有新的数据进来了但硬件也不会主动产生空中断。用大白话说一扇门说要等最后一个客人出门再锁门但客人其实早走完了门卫不知道就一直等着。3.3 根因TSN switch的停时与时钟域深入看代码之后问题更清晰了。MP257的TSN switch有自己独立的时钟控制逻辑和两路GMAC的时钟是分开管理的。正常运行时switch的时钟由RCC开启reboot时Linux侧的驱动shutdown只做了MAC层面的停止没有对switch核心做一次完整的停止转发、清空队列、软复位操作。随后PSCI复位信号到达RCC硬件理论上应该把所有外设时钟关掉但这个switch如果正处于一个内部状态机的中间态复位信号就可能无法完成整个握手导致A35核停在等待复位的死循环里。我用ST-LINK接到调试口在hang住的时候用JTAG halt住CPU看了PC指针发现它停在一个WFI之后的等待循环里栈回溯指向PSCI的reset实现——也就是说CPU在等TF-A把复位做完但TF-A那边其实也卡住了因为它要等待RCC的复位状态寄存器返回确认。这就形成了死锁内核等PSCITF-A等RCCRCC等switch内部状态机switch等驱动来清理它。这个结论和date reset那条线完美串起来了因为复位流程没走完硬件被卡在半复位状态主电源被拉出异常波纹VBAT备份域跟着掉电RTC时间丢失。所以先修reboot hangdate reset的触发源也就自然消失了一半剩下的一半还要把RTC硬件和软件保护做好。3.4 设备树与内核配置层面的排查除了驱动代码层面的问题设备树和内核配置也值得反复核对。MP257的TSN switch在设备树里的表现和你用的BSP版本强相关。我用的BSP是基于内核6.1的版本设备树里TSN switch相关的节点通常长这样ethernet1 { status okay; phy-mode rgmii-id; pinctrl-names default, sleep; pinctrl-0 eth1_rgmii_pins; pinctrl-1 eth1_sleep_pins; phy-handle phy0; phy-reset-gpios gpioh 2 GPIO_ACTIVE_LOW; phy-reset-duration 20; };我在排查时发现如果phy-reset-gpios没配置reboot hang的概率会明显上升。原因也好理解PHY在复位过程中需要一颗GPIO拉低再拉高如果没有对应的复位信号PHY可能保持在异常状态MDIO总线上的访问就会卡住而MAC的shutdown流程可能会去读PHY的寄存器一卡就卡死。所以哪怕最终根因是switch状态机phy reset gpio这种配置项也一定要补齐少一个就会多一个坑。内核配置方面确认这几个选项是开启状态CONFIG_STMMAC_ETHy CONFIG_STMMAC_PLATFORMy CONFIG_PTP_1588_CLOCKy CONFIG_NET_DSAy CONFIG_RTC_DRV_STM32y CONFIG_ARM_SMCCCy其中CONFIG_RTC_DRV_STM32尤其重要。如果你的BSP默认没开这个而你在设备树里把RTC节点设成了okay那么系统会找不到RTC驱动date reset就是必然的。我见过不止一块板子死在这个配置漏项上。4. 完整解决方案与实操步骤4.1 修date resetRTC硬件与软件保护一起做先说硬件。VBAT必须接一个可靠的电源域如果板子空间允许直接上一颗CR2032纽扣电池座或者至少用一个大电容加低漏电二极管隔离。我这边最后是加了电池座加1mF的电容保证系统在掉电复位瞬间备份域电压不会跌破1.2V。做完之后用示波器复测复位瞬间VBAT电压最低只跌到2.8V完全在安全范围内。然后软件上确认RTC设备树节点是开启状态rtc { status okay; };启动后确认RTC被正确识别dmesg | grep -i rtc cat /sys/class/rtc/rtc0/time hwclock -r如果一切正常把系统时间校准一次然后reboot再读一次。按这套走下来date reset的问题就从根本上解决。另外提醒一句如果有NTP环境建议用chrony而不是systemd-timesyncd因为chrony在RTC补偿方面做得更细开机后能快速判断RTC偏移并自动校准对工业设备这种长时间不重启的场景友好很多。4.2 修reboot hang驱动停时序列与复位顺序这是整篇文章最核心的修复点。思路是在TSN switch相关驱动里补一个完整的shutdown回调把switch的转发功能和DMA先停干净再让上层MAC停止最后再进入PSCI复位。具体来说我参考了Linux网络子系统的标准做法在驱动里增加了如下逻辑调用switch的端口管理接口把对外转发端口全部disable让报文不再进出调用帧生成/帧捕获相关的停止接口确认没有任何在途的帧清空switch内部的队列和buffer把描述符环复位到初始状态通过RCC把switch的时钟先关掉之后再通知上层MAC停止。由于各家BSP的驱动接口命名不完全一样这里不贴完整代码只讲关键点shutdown回调里一定要做先停数据面再停控制面的顺序绝对不能反过来。数据面还在跑的时候直接停控制面就会出现我等DMA描述符等到天荒地老的情况。另外在U-Boot侧也做了一个兜底在board_preboot或者board_reset阶段加一个对TSN switch的软复位寄存器写操作确保不管内核怎么死的U-Boot起来之前switch都是干净状态。这个兜底对于现场救急非常有用即使后面驱动没来得及修也能把reboot hang的概率从七八成降到一两成。4.3 设备树与内核配置清单结合前面的排查我整理了一份配置清单照着查一遍基本不会漏检查项期望状态说明VBAT供电独立电池或大电容复位不掉电date reset的硬件根因rtc节点statusokay关闭会导致内核无RTCCONFIG_RTC_DRV_STM32yBSP必须开启phy-reset-gpios正确GPIO带active low缺少会增大reboot hang概率phy-modergmii-id或实际硬件对应错误会导致链路异常TSN switch shutdown回调先停数据面再停控制面核心修复点U-Boot reset兜底软复位switch现场救急fake-hwclock使用真实RTC时disable避免时间被覆盖这套配置在我们的板子上验证通过后连续reboot 50次没有一次hangdate也始终保持正确。后面我又在另一块只开了普通GMAC、没开TSN switch的板子上跑了同样的测试结果也稳定。4.4 验证与回归测试修完之后不能只看能重启成功就收工要做一个稍微正规的回归。我的做法是这样连续reboot 50次记录每次是否能正常起来、date是否保持在业务跑着的状态下TSN流量全速转发执行reboot验证是否还会hang执行echo b /proc/sysrq-trigger直接触发内核热复位验证底层复位路径用脚本记录reboot后系统启动时间到日志文件确认启动流程没有额外阻塞。在测试过程中我还发现一个小问题如果第一次reboot成功但系统时间因为网络没起来而没有被同步第二次reboot时RTC存储的仍然是上次的时间可能出现时间回跳。这个问题后来通过在systemd里加了一个简单的timer开机5分钟后执行一次hwclock --systohc把内核时间写回RTC就彻底解决了。这个小细节虽然不致命但在实际项目交付时很容易被客户挑刺建议提前处理。5. 常见问题与排查技巧实录5.1 问题速查表调试这几天踩了不少坑有些问题是新手很容易撞上的我整理成一张速查表现象可能原因快速定位方法解决方案开机date一直1970RTC驱动未加载 / VBAT掉电dmesg | grep rtc、示波器测VBAT开RTC驱动补VBAThwclock读出来1970且RTC设备存在RTC备份域被复位示波器抓复位瞬间VBAT波形加电池/大电容reboot卡在Restarting system之后switch状态机未清理干净JTAG halt看PC栈回溯驱动加shutdown停数据面reboot偶发卡死概率低PHY复位时序问题确认phy-reset-gpios补齐GPIO复位配置NTP拉不回来时间一直错网口未起来 / NTP配置缺失ip link、chronyc sources检查链路与NTP配置reboot成功后时间回跳RTC没有定期写回查看hwclock日志加开机定时hwclock --systohc5.2 几个独家排查技巧第一一定要学会用JTAG在hang住的时候看现场。很多嵌入式工程师遇到挂死就只会看串口日志但串口日志在CPU彻底卡死的时候是没用的。用ST-LINK连接后在卡死状态下做一次halt然后看所有核的PC和栈回溯能极大缩小排查范围。我这次能确认问题在PSCI/RCC层面就是靠这个手段纯靠猜代码的话可能还要多花两天。第二善用内核的sysrq。在reboot之前先echo 1 /proc/sys/kernel/sysrq然后通过echo s /proc/sysrq-trigger做一次同步确保文件系统干净再echo b /proc/sysrq-trigger做热复位。sysrq的热复位路径和正常reboot路径有一定差异如果它在热复位下不卡、正常reboot下卡那问题基本就锁定在驱动的shutdown回调里如果它在两种路径下都卡则更可能是硬件复位本身的问题。这个区分法非常管用。第三记录dmesg最后几条日志的时候可以临时把内核printk级别调低echo 8 /proc/sys/kernel/printk这样所有调试打印都会输出有些驱动在shutdown时会打印关键状态信息平时被日志级别挡掉了调低之后能多看到很多细节。我这次就看到了一条stmmac: stopping eth0 tx ...之后再也没有后续直接指向了TX停止流程的问题。第四如果怀疑是时钟域的问题可以在U-Boot阶段做一次最小验证进U-Boot命令行直接reset命令重启看会不会卡。U-Boot的reset路径不经过Linux内核如果U-Boot里reset也卡那就是TF-A或RCC层面的事如果U-Boot reset不卡那问题确实在内核驱动的shutdown阶段。这个二分法能帮你快速切分责任层。6. 写在最后的一点体会这次调试给我最大的感受是STM32MP25这种带TSN switch的工业级MPU和以前玩MCU完全是两个世界。MCU出了问题顶多是重启一下或者看个寄存器MPU则要在Linux驱动、TF-A、RCC时钟树、备份电源域之间来回穿梭任何一个环节的想当然都会让你多熬两个通宵。我个人建议如果你也要在MP257上做TSN相关的项目最好在硬件设计阶段就把VBAT和PHY复位引脚规划好软件层面把RTC驱动、NTP策略、驱动shutdown回调这三件事一次性做对不要等到现场出了问题再补。另外把U-Boot和TF-A的日志默认打开哪怕只是DEBUG级别的部分日志也能在后期排查时省掉大量重新编译固件的时间。最后再分享一个小技巧调试期间把每次reboot前后的dmesg时间戳、hwclock输出、以及串口最后几行日志都存成一个文件命名带上时间。这个习惯帮我快速发现了时间回跳这个隐蔽问题也让最终交付时的测试报告有据可查。希望这篇文章能让你少踩几个坑有遇到类似问题但还没解决的朋友可以按文中的排查顺序走一遍大概率能定位到你的根因。
返回列表