尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32N6软复位黑屏问题:FSBL热启动路径的根因分析与修复

STM32N6软复位黑屏问题:FSBL热启动路径的根因分析与修复 如果你最近在 STM32N6 上跑 FSBL 应用这种两阶段启动工程大概率也会撞上这个鬼问题代码里调用HAL_NVIC_SystemReset()本以为开发板会干净利落地重启结果屏幕一黑再也不亮串口一个字都没有可你抬手按一下板子上的 NRST 按键或者干脆拔电重插一切又好得跟没发生过一样。这个问题我前前后后查了将近两天中间一度怀疑 FSBL 被写坏、怀疑外部 Flash 时序不稳、甚至怀疑是不是这颗芯片本身有硬件 bug。最后定位到的根因其实藏在 STM32N6 的复位体系、FSBL 的启动分支以及应用退出前没有对外设做“优雅退避”这三者的交叉点上。这篇文章我就把整个排查过程、底层原理和最终修复方案完整写出来给同样在这颗芯片上踩坑的人一个直接可用的参考。我会按这个顺序讲先确定现象边界再拆解 STM32N6 的复位链路然后跟着我当时的排错步骤走一遍最后给出修复后的验证结果和一套可复用的检查套路。1. 现象边界不是应用挂死而是启动链根部的异常1.1 故障现场还原先说清楚复现环境。我手里的板子是 STM32N6 系列外挂一颗 Octal-SPI NOR Flash 存放 FSBL主应用跑在外部 PSRAM 里带一块 RGB 显示屏。工程里用到了 NPU 做推理加速同时开启了 TrustZoneFSBL 跑在安全侧应用跑在非安全侧。复现步骤非常简单应用正常启动屏幕点亮推理任务跑起来。代码里调用HAL_NVIC_SystemReset()期望整个系统软复位重新启动。结果屏幕黑掉系统没有任何响应串口没有任何打印调试器也连不上。按一下 NRST 按键系统正常启动。断电重新上电系统也正常启动。最让人困惑的是第 4 步既然软件复位和 NRST 引脚复位都叫“系统复位”为什么一个失败一个成功如果说是复位信号没生效可问题又确实复位了——屏幕灭了说明系统已经崩掉。这更像是“复位之后起不来”而不是“没有复位”。1.2 从现象能反推出哪些信息这类问题有一个共同特征复位的触发方式不同后续行为差异巨大。这正是排查的切入点。如果问题出在应用本身的初始化逻辑那 NRST 重启后同样会走到应用应用一样会挂但实际不会。如果问题出在硬件电源或晶体振荡器不稳定那断电上电应该更容易失败但实际它成功了。唯一合理解释是软复位产生的复位标志位把 FSBL 带到了另一条启动路径上而这条路径在某些前置条件不满足时会卡死。所以第一阶段的结论是问题极大概率出在“从复位信号产生到 FSBL 完成基础初始化”这一段也就是启动链的最前端。下一步必须把 STM32N6 的复位机制搞透。2. 从寄存器层面拆解 STM32N6 的复位链路SYSRESETREQ、NRST、POR 三条路径差在哪2.1 HAL_NVIC_SystemReset() 到底做了什么先看代码。HAL 库里的HAL_NVIC_SystemReset()本质是调用了 CMSIS 的NVIC_SystemReset()它做的事情可以用这几行概括void NVIC_SystemReset(void) { __DSB(); // 确保前面的访存完成 SCB-AIRCR (0x5FAUL 16) // VECTKEY 写保护钥匙 | SCB_AIRCR_SYSRESETREQ_Msk; // 请求内核复位 __DSB(); while (1) { /* 等待复位到来 */ } }这里面的关键就是往AIRCR寄存器写一个SYSRESETREQ位。这个位会让 Cortex-M55 内核产生一个复位请求交给芯片内部的复位控制器RCC 域去执行真正的复位动作。注意它只是“请求”复位具体复位哪些逻辑由 RCC 决定不是内核自己说了算。还有一个容易忽略的细节HAL 库里HAL_NVIC_SystemReset()在调用内核复位前会先把 FAULTMASK 置 1防止中断服务程序干扰复位流程。这个对结果影响不大但说明 ST 官方默认的软复位路径就是不关外设、不管外设状态的——这为后面的问题埋下了伏笔。2.2 复位状态寄存器SFTRSTF / PINRSTF / PORRSTF 是怎么被置位的STM32N6 的 RCC 里有一个复位状态寄存器不同型号名称略有差异常见叫法是RCC-RSR。它会把最近一次复位的原因记下来而 FSBL 可以通过读它来判断当前是从哪种复位中恢复的。下面是三种复位对应的标志位复位来源标志位说明上电复位 / 掉电复位PORRSTF由 VDD 上升到阈值或下降到阈值触发NRST 引脚复位PINRSTF按复位键或外部电路拉低 NRST软件复位SYSRESETREQSFTRSTF写 AIRCR 的 SYSRESETREQ 位触发独立看门狗复位IWDGRSTFIWDG 计数器超时触发窗口看门狗复位WWDGRSTFWWDG 计数器超时触发低功耗复位LPWRRSTF特定低功耗模式退出时触发我在板子上实测过正常上电后读RCC-RSRPORRSTF 为 1按 NRST 后读PINRSTF 为 1执行HAL_NVIC_SystemReset()后再读SFTRSTF 为 1。也就是说FSBL 是能够区分这三种复位方式的这给它“看人下菜碟”提供了基础。2.3 系统复位和掉电复位不是同一个层次很多人有个误区觉得复位就是把芯片恢复到“刚上电的样子”。实际上在 STM32N6 这种大规模 MCU 上复位是分层级的上电复位POR整个芯片所有寄存器和 SRAM 全部回到默认状态外部 PHY、PMIC 时序全部重来是最彻底的复位。系统复位SYSRESET/NRST/SYSRESETREQ复位 CPU 和大部分外设寄存器但备份域、部分调试逻辑不会复位而且复位后各模块是“并行”回到默认状态没有上电时序。内核复位Cortex-M local reset只复位内核外设纹丝不动。HAL_NVIC_SystemReset()和 NRST 都落在“系统复位”这个层级但它们留下的“痕迹”复位标志不一样。NRST 会让 RCC 记录成 PINRSTF软复位会记录成 SFTRSTF。如果引导程序对这两个标志采取不同的处理策略就会看到完全不同的启动结果。这个差异恰好就是 STM32N6 FSBL “热启动路径”与“冷启动路径”的入口判断。3. FSBL 的启动逻辑它会根据复位源“看人下菜碟”3.1 STM32N6 的启动链BootROM → FSBL → 应用STM32N6 不是那种上电直接跳应用的传统 MCU。它带 NPU、ISP、双核还引入了 FSBL 机制整个启动链条是这样的芯片内部 BootROM 首先执行它根据 option bytes/OTP 决定从哪个外部存储加载 FSBL。FSBL 被从外部 Octal-SPI Flash 加载到内部 SRAM 并执行。FSBL 负责初始化时钟树、电源、外部 PSRAM/Flash、串口等基础外设并完成安全校验。FSBL 最终把应用镜像从外部存储加载到外部 PSRAM然后跳转到应用入口。这个架构和 Cortex-A 的 ATF/BL2 思路非常像。FSBL 承担了“让主 CPU 在复杂环境下能被正确引导”的职责。3.2 FSBL 如何区分冷启动和热启动我当时把 STM32N6 的 FSBL 源码认真翻了一遍发现它启动后会做一件很重要的事读取 RCC-RSR根据复位标志决定走冷启动还是热启动分支。伪代码逻辑大致是这样uint32_t rst_flags RCC-RSR; if (rst_flags RCC_RSR_PORRSTF) { // 冷启动完整初始化时钟、电源、PSRAM、外部 Flash fsbl_cold_boot_init(); } else if (rst_flags RCC_RSR_SFTRSTF) { // 热启动假定外设状态没有被破坏走快速路径 fsbl_warm_boot_init(); } else { // NRST、看门狗等复位 fsbl_default_boot_init(); }这个设计的初衷很好软件复位前上一个应用还在运行时钟、PSRAM、PLL 的配置都还在FSBL 没必要再把几十微秒到几百微秒的关键初始化全部做一遍直接进入应用加载流程可以大幅缩短复位时间。但问题恰恰出在这个“假定”上。FSBL 热启动路径假定的是“应用已经为热复位做好了准备”比如 PSRAM 进入了自刷新、NPU 停了、DMA 停了、外部存储控制器状态干净。而现实是绝大多数应用开发者在调HAL_NVIC_SystemReset()之前根本不会去做这些收尾工作。3.3 安全区和非安全区模式下复位源还会影响安全校验STM32N6 带 TrustZone 安全扩展FSBL 本身就运行在安全世界应用在非安全世界。这颗芯片还支持“应用安全区和应用非安全区”的隔离功能Secure Boot 流程会校验 FSBL 镜像、应用签名、回滚计数器。在安全启动开启的情况下复位源标志会被用于判断“这次启动是否可信”。举例来说上电复位后安全启动流程会认为这是一次完整的冷启动按规范执行全部校验。软件复位后如果 FSBL 走了热启动路径某些安全状态不会被重新建立比如安全侧外设的隔离配置、Secure Manager 的内存保护设置。如果应用在非安全世界直接操作AIRCR触发复位FSBL 热启动时发现安全侧状态不完整就可能直接卡在某个安全校验或内存保护异常里。我在复现时也验证过这个方向把 TrustZone 和 Secure Boot 全部关掉只跑裸机 FSBL 更简单现象会减轻一些但依然存在。这说明根因不是“纯安全配置问题”安全配置只是放大了问题。4. 完整排查链路从日志、电源、看门狗到总线状态4.1 第一步确认 FSBL 到底有没有跑起来第一件事不是改代码而是确认 FSBL 是否真的没跑。很多工程师一上来就怀疑应用代码但这个问题里应用根本没有执行机会。我把 FSBL 的第一行代码处加了一个 GPIO 翻转点用来指示“FSBL 已进入”。实测结果是软复位后这个 GPIO 完全没有翻转也就是FSBL 的第一行代码都没有执行到。这就把问题范围进一步缩小到两块要么数据从外部 Flash 加载到 SRAM 失败了要么 FSBL 在 BootROM 到 FSBL 入口之间的某个环节被卡住了。同时串口没有任何打印也印证了这一点不是 FSBL 打印了但我们没看到而是 FSBL 的串口初始化还没跑就挂了。4.2 第二步抓取 FSBL 卡死的位置我尝试用调试器去停在复位后的现场。连接 SWD 后让系统执行软复位然后立刻 haltPC 停在了 BootROM 区域而不是 FSBL 加载后的地址。这说明 BootROM 阶段就出了问题——FSBL 压根没有被正确加载到 SRAM。BootROM 逻辑是芯片固化的没法断点调试。但我注意到一个关键细节BootROM 在加载 FSBL 之前会读取外部 Flash 的首几个 sector。如果外部 Flash 控制器的状态在复位后没有被正确处理BootROM 第一次读 Flash 就会挂在总线上。继续排查的切入点有两个检查软复位后外部 Flash 控制器的寄存器状态。检查是不是有总线主机还在占用外部存储总线。4.3 第三步排除 IWDG 的复位循环中间我一度怀疑是 IWDG 在搞鬼。理由是IWDG 由 LSI 时钟驱动系统复位后它不一定会被清零如果软复位前使能了 IWDG复位后它继续跑而 FSBL 启动初期没人喂狗于是 FSBL 还没跑完就被 IWDG 再次复位形成循环震荡从外部看就是永远黑屏。验证方法很简单在调用HAL_NVIC_SystemReset()之前先把 IWDG 关闭IWDG_HandleTypeDef hiwdg; // 用 Key 寄存器解锁并禁止 IWDG hiwdg.Instance IWDG; if (HAL_IWDG_Stop(hiwdg) ! HAL_OK) { // 处理错误 }我实测下来关闭 IWDG 后软复位依然失败说明 IWDG 不是根因但它确实是一个高频坑。如果你也遇到类似现象这一步一定要做因为很多 STM32 上 IWDG 在软复位后是继续跑的这会导致周期性复位循环表现非常像“FSBL 没起来”。4.4 第四步量电源轨排除供电时序问题既然 BootROM 都卡住了我也认真考虑过供电问题。STM32N6 的板子一般带外部 PMIC如果应用在复位前把某个 GPIO 拉到了错误电平而这个 GPIO 控制着 PMIC 的 enable那么软复位后 PMIC 可能直接关断输出外部 PSRAM 或 Core 电压掉电BootROM 自然起不来。我用示波器同时量了 3.3V、1.2V内核以及 NPU 电源轨在触发软复位的同时抓波形。结论是电源电压在软复位前后保持稳定没有出现掉电或跌落。这基本排除了 PMIC 供电被意外切断的问题。如果你们板子在软复位后电源异常优先查这几点PMIC 的 enable 引脚是否被 MCU GPIO 控制复位后该 GPIO 电平是否符合 PMIC 使能要求。应用是否通过 I2C 配置过 PMIC 的待机电压复位后 PMIC 寄存器还维持着低压设置。外部看门狗芯片是否在软复位期间把 NRST 拉低了一段时间。4.5 第五步最终锁定在“热启动路径未清理外设”把电源、看门狗、BootROM 固件问题都排除掉后方向就很明确了FSBL 在软复位后走了热启动路径而热启动路径依赖的外设清理工作没有做。具体验证方法是在 FSBL 里强制“不分青红皂白”全走冷启动初始化uint32_t rst_flags RCC-RSR; // 调试用无论什么复位源都强制执行完整初始化 if (1) { // 强制冷启动 fsbl_cold_boot_init(); } else { fsbl_warm_boot_init(); }这样改完软复位后 FSBL 居然正常起来了。虽然这个改法粗暴但确实验证了“热启动路径是罪魁祸首”这个判断。进一步分析原因我们的应用在调用HAL_NVIC_SystemReset()之前NPU 可能还在跑、DMA 还有未完成的描述符、外部 PSRAM 也没有进入自刷新状态。软复位信号一过来CPU 被复位了但 NPU/DMA 这些总线主控可能还没停它们在外部存储总线上挂着任务。FSBL 热启动路径只做了最简初始化没有去把这些总线主控重新复位一遍于是第一次访问外部 PSRAM 或 Flash 就 stall 在总线上整个系统锁死。NRST 和上电复位为什么能成功因为这两种复位对整个芯片的复位面更大NPU、DMA、甚至总线矩阵都被强制复位到默认状态FSBL 无论走哪条路径外设都是干净的。只有软复位的复位面相对集中问题才会暴露。5. 修复方案让软复位前系统进入“可重启状态”5.1 复位前清理 NPU、DMA 和外部存储根因清楚后最直接的修复就是在应用调HAL_NVIC_SystemReset()之前把关键外设停下来。下面是我们最终采用的清理顺序你可以根据自己工程的实际外设做增删void System_PreResetCleanup(void) { // 1. 停掉所有高优先级中断防止复位过程中 ISR 抢占 __disable_irq(); // 2. 停 NPU等待空闲;具体 API 以你的神经网络库为准 NPU_Stop(); while (NPU_GetState() ! NPU_STATE_IDLE) {} // 3. 停 DMA等待正在传输的通道结束或强制中止 for (uint32_t i 0; i DMA_CHANNEL_COUNT; i) { HAL_DMA_Abort(hdma[i]); } // 4. 将外部 PSRAM 切到自刷新/低功耗模式具体寄存器看 FMC/OCTOSPI 配置 PSRAM_EnterSelfRefresh(); // 5. 关闭不必要的时钟和电源域 __HAL_RCC_NPU_CLK_DISABLE(); __HAL_RCC_DMA_CLK_DISABLE(); // 6. 最后再执行系统复位 HAL_NVIC_SystemReset(); }核心思想是在复位前把外部总线上的所有主机任务清干净让 FSBL 热启动路径认为自己面对的是一个干净、可恢复的系统。这比单纯换一个复位方式要可靠得多。有一点注意__disable_irq()只能关掉当前 CPU 的中断不能阻止 NPU/DMA 正在进行的总线访问所以第 2、3 步不能省。如果你的应用里有以太网或 USB 在跑也需要一并停掉避免外设 DMA 还在访问 SRAM。5.2 FSBL 侧兜底把软件复位也当冷启动处理应用侧做了清理之后现象已经消失了。但从固件健壮性角度我建议同时在 FSBL 里做一个兜底只要发现外部存储控制器状态异常就强制走完整初始化。最省事的做法是把软复位也映射到冷启动路径代价是启动时间变慢但对稳定性收益巨大。具体修改方式是在 FSBL 里把分支条件改成if ((rst_flags RCC_RSR_PORRSTF) || (rst_flags RCC_RSR_SFTRSTF)) { // POR 和软复位都走完整初始化 fsbl_cold_boot_init(); } else { // 其他复位源可以尝试快速路径 fsbl_warm_boot_init(); }这样即使未来某个版本的应用忘了做复位前清理FSBL 也能通过完整初始化把系统拉起来不会直接黑屏。嵌入式产品固件升级时这种兜底尤其重要——你没法保证每个升级包里的应用都遵守同样的清理协议。5.3 利用安全侧服务进行受控复位如果你的 STM32N6 工程开启了 TrustZone应用跑在非安全区我更推荐另一种方案不要直接在非安全应用里调HAL_NVIC_SystemReset()而是通过安全侧提供的系统服务请求复位。理由很简单非安全世界直接写AIRCR等于绕过所有安全侧的状态管理。NPU 和部分外设可能由安全侧独占非安全应用连它们的状态都不清楚怎么可能做好清理正确做法是定义一条 SCMI/IPC 消息让安全侧完成全系统清理后再触发复位。// 非安全侧伪代码 void RequestSystemReset(void) { // 通过 IPC 调用安全侧的 shutdown/reset 服务 IPC_SendMessage(IPC_CHANNEL_SECURE, IPC_CMD_RESET_SYSTEM); }安全侧收到请求后先做外设去初始化、检查 NPU 状态、把外部存储切到安全模式最后调用真正的复位。这套方案在量产的 STM32N6 产品上非常推荐因为它把“如何复位”这个策略问题收拢到了安全侧统一实现避免每个应用工程师各写一套清理逻辑。6. 复盘与经验遇到“只有软复位不正常”该怎么查6.1 排查顺序建议这次踩坑花了两天其实大部分时间都浪费在“从应用代码里找问题”而真正的根因在引导链。复盘下来如果你也遇到类似问题我建议按下面这个顺序排查能省好几个小时先确认引导代码到底跑没跑到在 FSBL 最前面打点看 GPIO 或串口输出。如果连第一行都没到说明问题在 BootROM 或 FSBL 加载之前。查复位状态寄存器软复位和 NRST 分别读一次RCC-RSR确认 FSBL 能看到不同的复位标志。这一步同时告诉你“FSBL 有没有可能走不同分支”。暂时关闭 IWDG排除看门狗复位循环这是最便宜的排除法。量电源轨排除 PMIC 供电问题尤其是 GPIO 控制 PMIC enable 的设计。强制 FSBL 走冷启动人为验证“复位标志分支导致不同行为”这个猜想用最小的改动换取关键信息。确认外部总线主机状态NPU、DMA、以太网、USB 这些主机是否在复位时还占着总线。这套顺序的核心逻辑是从“离 CPU 最近的执行流”往“离 CPU 最远的硬件状态”逐步推进而不是一上来就怀疑应用逻辑。6.2 几个容易忽视的小坑不要把软复位当成“上电复位”来用。在带 FSBL、带外部存储、带 NPU 的芯片上软复位的语义更接近“热重启”它与 CPU 和外设的“上电后第一次启动”有本质区别。需要干净状态的场景直接设计成断电重启或调用完整复位服务比在HAL_NVIC_SystemReset()外面包一层清理要稳。注意复位标志位的生命周期。RCC-RSR里的标志位一般需要软件主动清零否则下次启动时读到的可能是旧的复位原因导致 FSBL 判断错误。我见过有工程在应用启动时把整个RCC-RSR清掉了结果 FSBL 在下次启动时完全无法判断复位来源最后不得不通过 option bytes 兜底。如果你的产品用了外部看门狗芯片软复位可能不会停止它。有些外部看门狗在 MCU 软复位期间会继续计时导致 FSBL 还没起来就被狗咬。这类问题的表现和这次很像但根因完全不同排查时要一并考虑。安全启动开启时非安全应用的复位必须走安全通道。如果只关掉 Secure Boot 来排查可能会误认为安全功能无关紧要实际上在量产配置下非安全应用直接写 AIRCR 还可能导致安全侧状态被破坏下次启动直接进入安全失败处理流程。建议哪怕排查阶段也要保留一个最小的 TrustZone 环境来复现。最后说点个人的体会。嵌入式系统里最磨人的问题往往不是某个复杂的算法而是“同一个操作在不同触发条件下表现不一致”。这次的问题本质上
返回列表