STM32N6 DEV_BOOT启动模式详解:从原理到安全区/非安全区调试实战

发布时间:2026/8/30 12:13:45
STM32N6 DEV_BOOT启动模式详解:从原理到安全区/非安全区调试实战
1. 先搞清楚 DEV_BOOT 到底是什么以及为什么非要折腾它STM32N6 这芯片出来以后不少人拿到开发板第一件事就是想把工程跑起来结果发现事情没想象中那么简单。最典型的一个问题就是为什么我点了一下 RUN芯片完全没有反应或者调试器根本连不上这时候十有八九就和启动模式、签名校验、安全区这些概念搅在一起了。STM32N6 作为意法半导体新一代带 NPU 的高性能 MCU内核跑的是 Cortex-M55还集成了 Ethos-U55 神经网络加速单元启动链路和传统的 STM32F4、H7 完全不是一个量级。DEV_BOOT 模式就是意法半导体专门为开发阶段准备的一种启动方式它允许你在不经过完整签名校验、不烧写最终产品级安全配置的情况下直接把应用代码下载到芯片里跑起来。这个模式的价值在于开发期间你不需要每次都走完整的产线烧录流程也不需要维护一把私钥去签名每一个测试固件拔掉跳线、配置好引脚就能像以前写 STM32F103 那样自由地擦除、烧写、调试。对于刚接触 STM32N6 的工程师来说理解 DEV_BOOT 是跨入这颗芯片的门槛之一因为它直接影响你能不能顺利点亮板子。同时STM32N6 在架构上引入了更明确的 TrustZone 安全扩展BootROM 上电后会先检查系统配置再决定是进入安全世界还是把控制权交给非安全区的应用。很多初学者在这里栽跟头明明代码编译成功了烧进去就是跑不起来翻了一整天日志才意识到是安全区和非安全区地址映射没配对。所以这篇文章我会把 DEV_BOOT 模式的原理、实际进入步骤、以及安全区/非安全区工程的分区策略放在一起讲适合正在用 STM32N6 做评估、或者已经把板子买回来但卡在启动这一步的工程师作为参考。2. STM32N6 的启动链路先弄明白这几个“关键角色”再动手2.1 BootROM 和 BSEC上电之后芯片内部到底发生了什么STM32N6 和以往 STM32 的一个本质区别在于芯片内部固化了一段 BootROM这段代码在每次复位后都会先被执行。BootROM 的作用是读取启动引脚的电平状态、检查 OTP一次性可编程存储器里的配置位然后决定接下来的启动流程。这里有一个很容易忽略的细节BootROM 本身就运行在安全状态下也就是说芯片上电的第一条指令是处于 Secure 世界的后续跳转到用户应用时才会通过 TrustZone 的机制切换到 Non-Secure 状态。BSECBoot and Security Controller是这一代芯片里负责管理安全启动、OTP 读取、调试使能等功能的控制模块。你可以在 OTP 里面配置 DEV_BOOT 相关的 fuse也可以配置是否锁定 JTAG/SWD 调试口。很多人在拿到新板子之后直接拿 STM32CubeProgrammer 去连发现连接失败就是因为 BootROM 在 OTP 里看到了类似于“调试口关闭”的配置直接把调试接口锁死了。遇到这种情况光靠换线换调试器是解决不了问题的必须先把 OTP 的相关位检查清楚。DEV_BOOT 模式和产品模式的典型区别在于产品模式下 BootROM 会强制校验应用固件的签名而 DEV_BOOT 本质上是“跳过或放宽签名校验、开放调试接口”的开发模式。这也就是为什么你在数据手册里会看到 DEV_BOOT 只适用于开发阶段不适合量产设备。量产时应该关闭 DEV_BOOT、启用安全熔丝、锁定调试口否则芯片里面的固件就比较容易被人读出来。2.2 Boot 引脚和启动源按一下复位之后芯片从哪里拿代码STM32N6 的启动引脚并不是简单的一个 BOOT0通常会有一组引脚组合来决定启动源。以常见的 N6 系列评估板为例板上一般会预留 BOOT 相关的拨码开关或者跳线需要根据板卡原理图去确认具体引脚编号。常见的启动源包括从内部 Flash 启动、从 BootROM 进入下载模式、从外部存储比如 QSPI Flash启动等。进入 DEV_BOOT 的方式通常有两种一种是把启动引脚配置为从 BootROM 的 USB 下载模式启动然后通过 USB 连接做代码下发另一种是在 OTP 中使能 DEV_BOOT 功能然后通过调试器直接连接调试口。前者适合在没有高级调试器的情况下做烧录后者适合日常开发调试。我个人的习惯是优先用 ST-Link 配合 DEV_BOOT 模式因为调试体验完整能看变量、打断点strace 起来效率高。这里提醒一点不同板卡的 BOOT 引脚定义差异很大不要拿着 F4 的经验想当然。有的板子是在丝印上标了 BOOT0/BOOT1有的板子是拨码开关还有的板子把 BOOT 引脚直接引到了排针上。动手之前先睁开眼看清板卡丝印和原理图避免瞎猜导致硬件上出现意外情况。2.3 TrustZone 和“安全区/非安全区”为什么 STM32N6 绕不开分区这件事STM32N6 的 Cortex-M55 内核支持 ARM TrustZone 技术它可以把整个芯片的内存、外设、中断划分为安全世界和非安全世界。安全区代码可以访问全部资源而非安全区代码只能访问被标记为非安全的资源。BootROM 和应用启动初期的初始化代码通常都放在安全区等系统全局初始化完成、外设分配妥当后才跳转到非安全区运行主应用。对应到工程上就是同一个 STM32N6 项目可能会拆成两个镜像一个 Secure 工程一个 Non-Secure 工程。Secure 工程负责安全启动、密钥管理、安全服务调用Non-Secure 工程跑业务逻辑、算法、UI、通信协议栈。两个工程通过 SGSecure Gateway指令和 NSCNon-Secure Callable区域相互协作。在 DEV_BOOT 模式下这两个镜像同样要分区加载、区别对待不能简单地把所有代码塞到同一个地址段里。这就引出一个很多人问的“应用安全区和应用非安全区功能”问题安全区和非安全区不是你想放什么就放什么而是要根据安全边界来决定。安全区放的是可信根、安全存储、密钥派生、代码签名校验这类关键功能非安全区放的是用户交互、网络协议、业务逻辑等不需要高等级保护的内容。开发阶段为了图省事你可以在 DEV_BOOT 模式下先跑一个裸机工程不做安全分区但只要你用到了 STM32CubeMX 的新版本生成带有 TrustZone 支持的工程默认就会给你拆成 S 和 NS 两个子工程这个结构从第一个 Blinky 工程开始就会遇到。3. 实操一步一步把 STM32N6 应用在 DEV_BOOT 模式下跑起来3.1 准备工作硬件、工具链和软件版本缺一个都白搭在动手之前先把开发环境备齐。我这里用的是一块 STM32N6570-DK 评估板搭配 ST-Link 调试器。如果你用的是其他 N6 系列的板子原理相同引脚和默认配置可能会有差异需要以官方板卡的文档为准。工具方面建议按下面的清单准备STM32CubeProgrammer建议用最新版本旧版本对 N6 的支持不够完整操作 OTP 和 DEV_BOOT 配置时容易出问题STM32CubeMX用于生成带 TrustZone 支持的工程骨架ARM Compiler 或 GCC arm-none-eabi 工具链取决于你习惯用哪种ST-Link 驱动和最新固件注意 ST-Link 固件太旧会导致连接 N6 的调试口失败一块 STM32N6 开发板外加 USB 线、杜邦线、跳线帽。注意STM32N6 的 SVD 文件和 Flash Loader 在不同版本的 CubeProgrammer 中差别比较大建议直接安装最新版本不要用大学时期的老安装包凑合。除了软件之外建议把 STM32N6 的参考手册RM、数据手册和对应开发板原理图都下载到本地。调试 DEV_BOOT 时经常会翻到 OTP 位定义和启动序列的章节没有手册在手边纯靠记忆猜大概率会浪费时间。3.2 进入 DEV_BOOT 模式引脚配置和 OTP 设置第一步是配置启动引脚让芯片复位后进入 BootROM 的 DEV_BOOT 流程。以我手上的板子为例板上有一个标着 BOOT 的拨码开关按文档说明将其拨到 BootROM 或 Dev 一侧。有的板子没有专门开关而是通过排针跳线帽把 BOOT 引脚拉高或拉低具体以板卡原理图为准。第二步是连接调试器。将 ST-Link 的 SWDIO、SWCLK、GND、VCC如果需要供电接到板子对应的调试接口。部分 N6 开发板支持板上集成 ST-Link这种情况下只需要用 USB 线连接开发板的 ST-Link USB 口即可。第三步是打开 STM32CubeProgrammer选择 ST-Link 连接方式在右侧接口设置里选择 SWD 模式连接速率建议先设置为低一些如 1.8 MHz 或 4 MHz因为 N6 的调试口在特定 OTP 配置下对时序比较敏感速率太高容易握手失败。点击 Connect正常情况下应该能识别到芯片型号和内核信息。第四步是检查 OTP 中与 DEV_BOOT、调试相关的配置。在 CubeProgrammer 的 OTP 视图中逐个检查相关 fuse 位。如果你是第一次接触这块板子大概率 OTP 是空白状态DEV_BOOT 是默认允许的。如果之前有人折腾过板子把调试口锁了或者关闭了 DEV_BOOT这里就能看出来。如果发现 DEV_BOOT 处于关闭状态需要按需写入相应的 OTP 位。提示OTP 是一次性可编程区域写进去之后不能随便改回来。开发板上折腾 OTP 要格外小心确认你确实需要修改某个位再点 Write。一旦写错轻则芯片变砖重则只能换芯片。如果是从 BootROM USB 下载模式进入 DEV_BOOT步骤会稍有不同先把 BOOT 引脚设置为 USB 下载模式然后复位板子此时电脑上会出现一个新的 USB 设备通常是 DFU 类设备在 CubeProgrammer 的 USB 连接方式下就能识别到。这种方式适合手头没有 ST-Link 的场景但调试功能会受限不能在线打断点。3.3 生成和编译带安全区/非安全区的工程在 DEV_BOOT 模式下跑应用很多人第一步做的其实是编译工程而不是烧录。STM32N6 的官方软件包中提供了大量例程打开 STM32CubeMX选择对应的芯片型号在 TrustZone 设置中勾选“Enabled”然后配置 Secure 和 Non-Secure 两个工程的 Flash/RAM 地址区域。典型的内存划分方式是把内部 Flash 的高地址段分给 Non-Secure 工程比如 0x8020000 开始把低地址段留作 Secure 工程0x8000000 开始中间留一块作为 NSC 区域。RAM 同理由 SAU 和 IDAU 配合决定哪些地址是 Secure、哪些是 Non-Secure。CubeMX 生成的链接脚本里已经按照这个思路做好了默认划分你需要做的只是在 scatter 文件或 lds 文件中根据实际需求调整大小。这里特别说明一下“应用安全区和应用非安全区功能”的分配逻辑并不是所有代码都必须拆成两个工程。如果你只是简单跑一个 GPIO 翻转的 Blinky完全可以把整个工程都放在 Secure 区不启用 Non-Secure 部分。但如果你需要用到 NPU 跑神经网络推理、搭配摄像头接口和显示接口通常建议把业务代码放到 Non-Secure 区安全相关服务比如密钥存储、固件升级校验放到 Secure 区。这样做的好处是一旦业务侧出了问题Secure 世界的关键数据和服务不会直接暴露给攻击者。在 MDK-ARM 或者 IAR 工程里编译时记得先编译 Secure 工程再编译 Non-Secure 工程。因为 Non-Secure 工程需要引用 Secure 工程生成的导入库通常包含 NSC 区域的函数地址编译顺序反了会导致链接失败。3.4 烧录应用用 CubeProgrammer 写入 Secure 和 Non-Secure 镜像编译完成后会得到两个镜像文件Secure 工程的 .elf/.hex/.bin 和 Non-Secure 工程的 .elf/.hex/.bin。烧录顺序也有讲究先烧 Secure 镜像再烧 Non-Secure 镜像。这不只是习惯问题因为 Secure 镜像里的启动代码负责初始化 SAU、MPU、堆栈和系统时钟Non-Secure 镜像是在 Secure 代码的引导下才能正常运行的。在 CubeProgrammer 的烧录界面中可以把两个镜像的地址都填好一次性烧录。比如 Secure 镜像烧到 0x8000000Non-Secure 镜像烧到 0x8020000。如果你生成的是 hex 文件文件里已经包含了地址信息直接选择文件、点击烧录即可。如果是 bin 文件就必须手动指定起始地址填错一个字节的地址芯片大概率就跑飞。烧录完成后把 BOOT 引脚拨回内部 Flash 启动模式然后复位板子。此时观察 UART 串口日志如果有打印、LED 状态、或者用调试器连接看 PC 指针走向确认程序是否真的跑起来了。一个比较快的验证方法是在 Secure 工程的 SystemInit 末尾和主循环入口各打断点在 Non-Secure 工程的主函数入口也打断点按一下复位看断点是否按预期顺序命中。如果 Secure 工程命中后跳到 Non-Secure 时断点没响八成是 NSC 区域配置或者跳转地址出了问题。3.5 运行验证和一个低调但常用的调试技巧程序跑起来之后不代表万事大吉。ST 官方 SDK 里通常会提供类似stm32n6xx_secure_fw和stm32n6xx_non_secure_app的例程组合首次接触时可以先用原版例程验证工具链和烧录流程是否通顺。直接在原版例程上修改业务逻辑远比从零搭建两个工程的雏形要快。调试时我比较喜欢用串口打日志因为 DEV_BOOT 模式下调试口并非一直可用某些情况下即使程序正常运行SWD 口也可能会因为时钟配置或引脚复用被“抢占”而连不上。这时候串口就成了唯一的观察窗口。建议在 Secure 工程启动阶段就初始化一个调试串口用来打印 Boot 阶段的关键状态比如 BootROM 跳转是否成功、SAU 配置是否完成、Non-Secure 世界是否已经进入。加了这几行日志能帮你节省大量排查时间。还有一个小技巧值得你记住在开发早期把非安全区的起始地址始终固定在链接脚本里一个统一的位置不要频繁改动。因为你一旦在 Secure 工程里把跳转地址写死比如跳转到 0x8020000后面每次调整 Non-Secure 工程的 Flash 布局都必须同步修改 Secure 工程的跳转目标漏改一处整个系统都跑不起来。这种“启动了但没完全启动”的故障是最难排查的。4. STM32N6 在 DEV_BOOT 模式下的常见问题与排查笔记4.1 ST-Link 连接不上芯片提示“No STM32 target found”这是最常被问到的问题也是大多数情况下的第一个拦路虎。处理思路按优先级排列先查接线SWDIO、SWCLK、GND 三根线是不是接到对应的引脚上再查上电顺序部分板卡要求先给目标板上电再连接调试器或者反过来需要看一下板卡说明接着查 BOOT 引脚状态如果板子当前处于内部 Flash 启动模式且 Flash 里没有可运行代码调试口可能被 BootROM 关闭或者挂起最后查 ST-Link 固件版本旧固件不支持 N6 的 IDCODE会直接报找不到目标。如果以上都查过还不行尝试用 CubeProgrammer 的“Port”选项卡切换成 USB 模式并进入 BootROM 的 DEV_BOOT先用 USB 连接确认芯片本身是好的。再不行就把板卡断电、拆掉不必要的排针跳线尽量把最小系统单独拉出来调试。4.2 烧录成功但程序不运行复位后 PC 指针乱跑这类问题大多数和两个因素有关安全区/非安全区地址配置不一致或者中断向量表位置不对。Cortex-M55 的向量表基地址可以通过 VTOR 寄存器设置Secure 工程和 Non-Secure 工程各自维护一份向量表。如果 Non-Secure 工程被链接到了 0x8020000但它的 VTOR 没有在启动代码里被正确设置中断一触发就找不到处理函数程序自然表现为“跑飞”。另外要注意的是 Secure 工程跳转到 Non-Secure 区时必须通过 NSC 区域中的 SG 指令来完成安全状态切换不能直接用普通函数指针跳转。SDK 生成的启动代码里通常已经处理好了这一点但如果你手动改过链接脚本把 NSC 区域删了或者改小了跳转就会触发 UsageFault 或者 HardFault。遇到这类问题优先查.sct文件MDK或.ld文件GCC中 NSC 段的内存映射。4.3 程序下载进芯片后第二次复位就失败这个现象通常说明 OTP 或者选项字节里某些配置发生了变化。比如之前用过 DEV_BOOT USB 下载模式把启动源配置改成了优先从 BootROM 启动那么就算你把 BOOT 引脚拨回 Flash 启动芯片也可能还是每次都进 BootROM。排查方法是重新把 BOOT 引脚拨到正确的档位并确认是 Flash 启动模式然后在 CubeProgrammer 里读取选项字节看启动源设置是否和你期望的一致。如果 OTP 里已经把调试接口锁定那就麻烦了。OTP 一经烧录就无法回退芯片基本就只能以产品模式运行再也进不了开发调试流程。所以再次强调不要拿量产固件或者烧录脚本在开发板上随便执行 OTP 写操作。4.4 安全区和非安全区之间的外设访问权限问题TrustZone 不仅仅是内存分两个区外设也可以配置为 Secure 或 Non-Secure。如果你在 Non-Secure 工程里调用了一个 Secure 外设的寄存器比如直接操作一个被标记为 Secure 的 UART读写的时候会触发总线错误或 HardFault。CubeMX 生成的工程里外设的 Security 属性通常会通过设备树或配置代码设定好但如果你自己新增了一个外设别忘记在 Secure 工程里把它初始化和配置好并且在 Non-Secure 工程里优先使用 HAL/驱动层提供的安全调用接口而不是绕过安全机制直接踩寄存器。这块在实践中很常见Non-Secure 代码里调用 printf 重定向到某个 UART但这个 UART 被配置成 Secure 外设结果一打印就崩。判断方法很简单在 HardFault 处理函数里保存 PC 和调用栈回溯出来往往就是外设访问冲突。解决办法是重新划分外设的安全属性或者把打印逻辑放到 Secure 工程里实现。4.5 常见问题速查表现象可能原因处理建议ST-Link 无法连接BOOT 引脚配置错误 / ST-Link 固件过旧 / OTP 锁定调试口检查引脚、更新 ST-Link、用 USB DEV_BOOT 模式交叉验证烧录报错 Flash Timeout目标板没有供电 / 接线松动 / 时钟配置异常重插线、确认供电、降低 SWD 速率程序烧录后无反应启动源没切回 Flash / 向量表偏移错误 / 地址映射不对复位后检查 PC 指针、核对链接脚本打印乱码串口波特率不匹配 / 时钟频率配置不一致统一系统时钟配置确认串口波特率非安全区代码访问外设崩溃外设安全属性不匹配在 Secure 工程中正确配置外设安全属性复位后 Debug 断点失效DEV_BOOT 模式未正确使能确认 OTP 中的 DEV_BOOT 配置4.6 给出一个比较省心的验证路径如果你急着先点亮一块 STM32N6 开发板建议不要上来就自己搭工程而是按下面顺序操作先用官方例程里的 Secure/Non-Secure 组合工程原样编译、烧录、运行确认整套链路是通的再用 CubeMX 新建一个最简单的工程只保留 UART 打印和 LED 闪烁验证自己生成代码没有问题最后再逐步加入业务模块。每加一块功能就验证一次避免把所有变量混在一起排查。我自己在第一次调试 N6 时就是因为不信邪直接拿一个带 NPU 的视觉识别工程上手结果三天时间都在和设备树、链接脚本、TrustZone 配置作斗争后来退回最原始的 Blinky 流程才确认工具链没问题。STM32N6 的启动链路比传统 MCU 复杂得多先让最小系统转起来剩下的问题才谈得上排查。5. 我踩过 DEV_BOOT 坑之后的一点体会回到最开始那个问题如何在 DEV_BOOT 模式下运行 STM32N6 应用。其实剥开看核心就三件事把启动模式切对把 OTP 配置检查好把安全区/非安全区地址划分一致。任何一个环节出了偏差都可能导致整块板子看起来“死掉”。我个人的建议是第一次接触 STM32N6 时一定要注意区分开发板和量产板的操作差异DEV_BOOT 模式下烧录的灵活性很强但对应的安全保护几乎为零。你可以在开发板上尽情折腾但一旦某一天要把代码往产品上迁移提前了解正式启动流程和安全启动要求能避免后面推倒重来。最后再分享一个小技巧调试这种带安全和非安全系统的芯片不要只用调试器也别只靠串口尽量让 Secure 工程的关键状态通过独立 GPIO 输出电平调试的时候用示波器或者逻辑分析仪看一眼 GPIO 翻转判断程序走到哪一步了。有时候这比打断点、看寄存器还要直观尤其当调试器本身连接不稳定时。按这个思路去排查你会发现在 DEV_BOOT 模式下把 STM32N6 调通只是时间问题。