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

资讯详情

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

STM32N6 DEV_BOOT模式实战:TrustZone双镜像烧录与启动流程解析

STM32N6 DEV_BOOT模式实战:TrustZone双镜像烧录与启动流程解析 最近换到 STM32N6 做项目第一次上手就发现这芯片跟以前玩过的 STM32F4/H7 完全不是一个套路默认情况下你直接拿一个编译好的 bin 想烧进去就跑大概率会卡在启动阶段。原因很简单STM32N6 引入了基于硬件信任根的安全启动机制如果不先想清楚 DEV_BOOT 模式调试口被锁、镜像校验失败、Flash 里明明有程序却起不来这些坑你一个都躲不掉。这篇文章把我从“板子到手”到“TrustZone 双镜像稳定运行”的完整过程整理了一遍重点讲清楚 DEV_BOOT 模式的作用、如何正确切换以及在这种模式下怎么把应用安全区和应用非安全区的程序都烧进去并跑起来。如果你也想在 STM32N6 上快速进入业务开发而不是被启动流程困住照着这个流程走一遍基本能稳。1. DEV_BOOT 模式到底解决了什么问题1.1 STM32N6 的安全启动与传统 MCU 的区别传统 MCU 的启动流程很简单复位后从固定地址读栈顶指针然后跳转到复位向量执行用户程序。你甚至不需要烧 bootloader直接烧应用就行。STM32N6 不一样它出厂就带了一层硬件安全策略ROM 里的 system bootloader 会检查产品状态、镜像签名、回滚保护这些东西。简单说STM32N6 有几种“产品状态”比如开发状态、封闭状态、锁定状态。处于封闭状态的时候芯片只会启动带正确签名的镜像同时调试口默认关闭处于锁定状态时你可能连 Flash 都没法重新擦除。如果你没有提前搞清楚状态也没有在 DEV_BOOT 模式下做初始化很容易把自己锁在芯片外面。DEV_BOOT 模式其实就是 ST 专门为开发者留的一扇门。在这个模式下system bootloader 允许你下载未签名或者仅带开发签名的镜像同时调试口保持开放SWD/UART 都可以正常连接。这样你在前期调试阶段就可以像用普通 MCU 一样自由地擦写 Flash、反复改代码。1.2 DEV_BOOT 模式的实际应用场景我个人的经验是DEV_BOOT 模式主要在下面几种场景里特别有用第一次拿到开发板想快速确认芯片能不能连接、能不能识别。在开发阶段反复烧录应用镜像不想每次都为签名打包流程浪费时间。需要同时对安全区和非安全区镜像分别调试比如先在安全区初始化一些密钥再在非安全区跑主业务逻辑。芯片已经因为签名错误或者状态配置出错被“卡住”需要通过 DEV_BOOT 模式重新擦除恢复。需要注意的是DEV_BOOT 只适合开发阶段。真正要量产的时候你必须切换到 FINAL_BOOT 或者带完整信任链的启动方式否则产品就失去了安全启动的意义。你可以在开发后期再配置签名和证书链那时候烧进去的镜像才具备防篡改和防回滚能力。2. 准备工作硬件连接、工具链与启动配置2.1 开发板与启动引脚要进入 DEV_BOOT 模式第一步当然是硬件上的启动配置。STM32N6 的启动引脚一般有 BOOT0 和 BOOT1具体在板子上可能是拨码开关、跳线帽或者排针。以常见的 NUCLEO-N6 系列开发板来说板上通常会标注 BOOT0/BOOT1 的位置你根据板子丝印把 BOOT0 拉高、BOOT1 拉低就相当于告诉 ROM“我要从 system bootloader 启动并且进入开发模式”。不过这里有个容易忽略的点启动引脚的状态只在复位时被采样。也就是说你改完拨码开关或跳线之后必须按一下复位键或者重新上电芯片才会按新的引脚状态走启动流程。如果你改了引脚配置但没复位看起来像是“没生效”其实是芯片还在跑上一次的启动路径。另外部分 STM32N6 板卡把 DEV_BOOT 模式固化在 option bytes 里通过 BOOT_CFG 这样的选项字节字段来控制。这种情况下引脚组合不一定是唯一的判断标准你需要同时检查 option bytes 的配置。我会在下一节具体讲怎么读取和修改。2.2 安装工具链并建立连接软件方面我推荐准备这几样工具STM32CubeProgrammer用来擦除 Flash、配置 option bytes、烧录镜像既有图形界面也有命令行。STM32CubeMX用于生成 STM32N6 工程尤其是带 TrustZone 的双镜像工程用它生成初始代码最省事。STM32CubeIDE如果你习惯在 IDE 里开发可以直接用它编译、调试底层还是调 STM32CubeProgrammer 的命令行。连接方式上你可以选择 ST-LINK 的 SWD 接口也可以用串口进入 ROM bootloader。SWD 更方便调试串口更适合在没有调试器的情况下快速烧录。我平时调试时首选 SWD因为 DEV_BOOT 模式下 SWD 是开放的直接用 ST-LINK 连接就行。如果你的板子没有板载 ST-LINK需要外接一个 ST-LINK/V2 或者 ST-LINK/V3。连接时注意 SWDIO、SWCLK、GND、3.3V 这四根线别接错很多新手第一次连不上就是电源和地线接反了。2.3 判断板子当前处于什么状态连接工具链之后第一步不是急着烧代码而是先读出芯片当前状态。用 STM32CubeProgrammer 连接后查看“Option Bytes 摘要”页面重点看这几个字段TZENTrustZone 是否使能。RDP读保护级别常见的有 Level 0、Level 1、Level 2。BOOT_CFG 或类似字段启动配置决定是 DEV_BOOT 还是 FINAL_BOOT。产品状态有的 STM32N6 型号会直接显示当前状态比如 Open、Provisioning、Closed 之类。如果 TZEN 已经开启说明 TrustZone 和 SRAM/Flash 分区是生效的后面烧录时要特别注意镜像放置的地址。如果 RDP 已经是 Level 1 或 Level 2那么默认情况下调试口可能是受限的你需要在 DEV_BOOT 模式下先做一次全擦除把保护等级降回 Level 0才能继续开发。3. 实操在 DEV_BOOT 模式下烧录并运行 STM32N6 应用3.1 将开发板切换到 DEV_BOOT我建议你在第一次操作时先把所有安全相关的配置全部清零让芯片回到一个最干净的、可自由烧录的状态。具体操作步骤如下将 BOOT0 拨到 1、BOOT1 拨到 0或者按板卡说明设置到 DEV_BOOT 模式。用 USB 线连接开发板和电脑打开 STM32CubeProgrammer。在右侧接口选择里选 ST-LINK点击“Connect”。连接成功后进入“Option Bytes”页面确认当前状态。如果你用的是串口方式可以在“UART”选项卡里选择对应的 COM 口波特率一般选 115200点击“Connect”。STM32N6 的 ROM bootloader 也支持更高速率但初次连接建议用默认值避免握手失败。有一种常见情况板子已经处于封闭状态此时即使 BOOT0 拉高SWD 口也可能无法正常写入。这时候你需要通过 UART bootloader 连接因为 UART 不受 SWD 调试锁的限制通常还是能进入 ROM 引导流程。进入之后先执行全擦除把 RDP 降回 Level 0再切回正常开发流程。3.2 读取并配置 option bytes连接成功后我习惯先做一次“全家桶”清理避免之前别人的配置影响后续调试。在 STM32CubeProgrammer 的图形界面里点击“Full chip erase”这一步会擦除所有用户 Flash 和 option bytes把芯片恢复到接近出厂状态。然后检查 TZEN 选项如果你要做 TrustZone 双镜像开发需要把 TZEN 保持为 1因为应用安全区和非安全区功能是建立在 TrustZone 之上的。如果你只是想跑一个最简单的裸机工程暂时不需要 TrustZone可以把 TZEN 改为 0这样启动和调试都更简单。这里我给你一个建议既然用了 STM32N6就直接按 TrustZone 双镜像的方式来规划因为后续做 AI 应用、安全连接、密钥管理这些功能几乎都会用到安全区和非安全区分工。早点适应这种模式后面少走弯路。修改 option bytes 之后记得点击“Apply”。有些选项修改后需要复位一次芯片才会真正生效所以改完配置马上按一下板子上的复位键。3.3 烧录应用镜像安全区和非安全区分开TZEN 开启之后整个 Flash 会在地址空间上被划分为安全区和非安全区两个部分。具体划分范围由 SAU、IDAU 以及 Flash 的 SEC 属性决定你在 STM32CubeMX 生成的链接脚本里能看到实际的地址分配。假设我们使用 STM32CubeMX 生成了一个带 TrustZone 的工程它通常会输出两个镜像一个安全应用的 bin一个非安全应用的 bin。这两个镜像必须烧到不同的地址范围不能都烧到 0x08000000 起始处。我这里以一个典型的 STM32N6 工程为例用命令行方式演示烧录过程STM32_Programmer_CLI -c portSWD modeUR STM32_Programmer_CLI -c portSWD modeUR -el STM32_Programmer_CLI -c portSWD modeUR -w Secure_app.bin 0x0C000000 STM32_Programmer_CLI -c portSWD modeUR -w NonSecure_app.bin 0x0C100000 STM32_Programmer_CLI -c portSWD modeUR -rst这里的 0x0C000000 和 0x0C100000 是我项目里的示例地址实际地址以你的链接脚本为准。你在 STM32CubeMX 生成工程时它会在 scatter 文件或 linker script 里明确标出 Secure 区域的基地址和 Non-Secure 区域的基地址。如果你是通过图形界面操作就在“Download”页面里分别添加两个 bin 文件填写对应的起始地址然后点击“Download”。我特别提醒一点烧录顺序尽量先安全区、后非安全区。因为 CPU 启动时通常先进入安全世界执行安全启动代码安全代码会初始化系统然后再跳转到非安全应用入口。如果非安全镜像先烧好了安全镜像没烧或者烧错启动链就会直接断掉。3.4 复位运行烧录完成后按一下复位键或者在 STM32CubeProgrammer 里点“Reset”。如果一切正常你会看到开发板上的 LED 开始工作或者在串口终端里打印出安全区初始化和非安全区应用的日志。这里有个小技巧在 DEV_BOOT 模式下程序跑飞了或者异常了直接重新烧录就行不用每次都做全擦除。因为 DEV_BOOT 本来就是为了“快速迭代”设计的你可以把整个开发周期压缩到“编译-烧录-看串口日志”三步。3.5 命令行烧录的进阶用法如果你想提高效率可以把烧录命令写成脚本。比如说我们经常要同时烧录 bootloader、安全应用、非安全应用用一行命令完成任务是最好的。下面是我经常用的一个脚本片段#!/bin/bash # DEV_BOOT 模式下一键烧录 STM32_Programmer_CLI -c portSWD modeUR -el \ -w bootloader.bin 0x08000000 \ -w Secure_app.bin 0x0C000000 \ -w NonSecure_app.bin 0x0C100000 \ -rst注意在一条命令里带了多个-w参数后STM32CubeProgrammer 会按顺序依次写入最后统一执行复位。这样比在图形界面里反复选文件要快很多也避免了因为选错地址导致的问题。4. 应用安全区和应用非安全区TrustZone 下的双镜像开发4.1 为什么 STM32N6 要区分安全区和非安全区“应用安全区和应用非安全区功能”最近在 STM32N6 讨论里出现频率很高因为这是这颗芯片区别于传统 MCU 的一个重要特性。简单来说STM32N6 内部支持 Arm TrustZone 技术把 CPU、内存、外设划分为安全世界和非安全世界。安全世界里跑的是可信代码比如密钥管理、安全存储、固件升级校验这些敏感操作非安全世界里跑的是普通应用比如 UI、协议栈、业务逻辑。两者之间通过特定的接口通信。这样设计的好处是即使非安全应用被攻击或者崩溃攻击者也很难拿到安全世界里的密钥和敏感数据。对于嵌入式开发来说这意味着你的项目结构会发生变化。以前你习惯把所有代码都放在一个工程里现在则需要拆分成“安全区工程”和“非安全区工程”两个部分分别编译分别烧录。4.2 双镜像与 DEV_BOOT 的配合在 DEV_BOOT 模式下开发双镜像最大的好处是可以分别迭代。比如我今天只改了安全区的启动逻辑那就只重新烧录安全镜像如果只改了非安全区的界面流程那就只烧录非安全镜像。因为 DEV_BOOT 不要求安全签名所以你可以随时把其中一个镜像替换成新的调试版本。这种“独立烧录”的能力在最终安全启动模式下是不可能的。FINAL_BOOT 模式下启动镜像必须经过签名而且签名通常覆盖整个镜像包的哈希安全性更高但开发和调试成本也更高。所以 ST 才专门保留了 DEV_BOOT让开发者前期能够快速迭代。我这里列一个典型的双镜像开发流程先在 STM32CubeMX 里启用 TrustZone并分配好安全区和非安全区的 Flash、RAM 地址。生成代码后编译安全区工程得到 Secure_app.bin。编译非安全区工程得到 NonSecure_app.bin。在 DEV_BOOT 模式下把两个 bin 分别烧到对应地址。复位运行通过日志确认安全区先启动、非安全区后启动。4.3 开发过程中常见的安全区/非安全区问题我在实际调试时遇到过几个特别典型的错误这里简单说一下HardFault 发生在从非安全区调用安全区函数时。原因多半是安全函数没有放在 Non-Secure CallableNSC区域或者你忘了在安全区工程里声明安全的可调用函数。指针地址不对。安全区代码访问非安全区内存地址时会触发 BusFault非安全区代码访问安全区内存也会直接异常。所以跨区数据传递必须使用共享内存并且把共享区域配置为非安全可访问。中断向量表配置错误。安全应用和非安全应用各自有独立的中断向量表非安全中断向量表指向非安全区域的起始地址如果你把非安全工程的中断向量表设错位置中断一响应就死机。这些问题在处理“应用安全区”和“应用非安全区”功能时几乎每条都会遇到好在 DEV_BOOT 模式让定位变得容易很多。程序崩了之后直接看硬件异常栈再确认是不是跨区访问基本能一眼看出问题。5. DEV_BOOT 模式调试踩坑与排查5.1 连接不上设备和模式没生效我见过很多朋友第一次连 STM32N6 时报“No STM32 target found”第一反应是 ST-LINK 坏了但实际上多半是 BOOT 引脚状态没设置对。比如 BOOT0 拉高后板子进入 ROM bootloader此时 SWD 接口依然可用但如果 STM32CubeProgrammer 使用的连接模式是“Normal”它可能无法识别处于 ROM 状态的目标。解决办法是把连接模式改为“Under Reset”STM32_Programmer_CLI -c portSWD modeURmodeUR 也就是 Under Reset会在复位期间尝试连接这种方式对已经进入封闭状态或者 ROM bootloader 状态的芯片更有效。如果你用的是图形界面就在连接参数里勾选“Reset mode”。另外一个容易忽略的地方是接线。有些人用杜邦线连接 ST-LINK 和开发板时SWDIO 和 SWCLK 两根信号线反了或者地线没接连接自然失败。这种低级错误往往最浪费时间建议先拿万用表量一下确保 SWDIO、SWCLK、GND 三根线是通的。5.2 option bytes 配置错误导致启动异常在 DEV_BOOT 模式下配置 option bytes 时最常犯的错误是把 RDP 等级改成 Level 2然后发现调试口彻底锁死连全擦除都做不了。RDP Level 2 是不可逆的芯片会永久关闭调试接口所以在开发阶段绝对不要设置 RDP Level 2。如果不小心把系统设置为封闭状态也先别慌。只要芯片没进入 RDP Level 2通过 UART bootloader 连接后执行全擦除通常可以把状态降回来。注意全擦除会清空所有用户代码和配置需要重新烧录但至少芯片救回来了。另外TZEN 的修改时机也很讲究。TZEN 从 0 改成 1或者从 1 改成 0都需要先执行全擦除。如果你不擦除直接改 TZEN可能提示操作失败或者配置写入后镜像启动异常。这个限制是 ST 为了防止安全状态被随意切换而设的。5.3 镜像烧录成功但程序无法启动烧录过程中显示“Download verified successfully”但复位后程序没跑这种情况我排查过很多回。最可能的三个原因分别是启动地址不对。应用程序的向量表偏移没有设置正确CPU 复位后去的是默认地址而镜像烧在别的地址自然起不来。镜像格式问题。DEV_BOOT 模式下部分工具链生成的是 .elf 文件你直接把它烧录到了 Flash却没有转换地址。建议烧录前统一用 .bin 或 .hex 文件。安全区跳转非安全区的代码缺失或顺序不对。双镜像工程里安全区启动后会执行一个S_CMSE_Jump或者类似的跳转逻辑如果安全区工程编译时没有启用对应的宏或者非安全区入口地址写错启动链就断了。排查时我一般先用串口看有没有安全区日志。如果安全区日志都没有问题大概率出在启动配置或向量表如果安全区日志正常但非安全区没动静重点检查跳转代码和 NSC 区域设置。5.4 容易被忽略的复位和电源问题STM32N6 的工作电流比普通 MCU 大尤其是开始运行 AI 相关任务或者外设全速工作的时候。如果你用的是电脑 USB 口直接给开发板供电可能在启动瞬间出现电压跌落导致芯片复位或者异常重启。我自己就遇到过这种情况程序跑到一半USB 口供电不足板子自动重启看起来像是代码逻辑问题其实是电源问题。解决办法很简单使用带独立供电的 USB Hub或者用外部 5V/3.3V 电源给开发板供电让 ST-LINK 只负责调试和烧录。这个问题在用过一段时间的旧电脑上尤其明显值得排查。还有复位电路。某些开发板的复位引脚上有电容掉电后需要等一两秒才能彻底放电完成。如果你频繁拔插 USB 线可能会遇到“插上后马上点连接失败”的情况等两秒再连接就好。6. 最后再分享一点 DEV_BOOT 的日常使用技巧如果你打算长期在 DEV_BOOT 模式下开发 STM32N6我建议你做一个固定流程每次拿到一块新板子先连接 STM32CubeProgrammer把 option bytes 全部读出来截图存档。然后执行一次全擦除确认芯片回到开放状态。接着再设置 TZEN。这样做的好处是即使后面出了问题你也知道自己最初的状态是什么对比起来方便很多。另外DEV_BOOT 模式下虽然不需要签名但为了保证和最终 FINAL_BOOT 模式的兼容性我建议你从第一天起就在工程里保留签名链的生成脚本。也就是说不论目前用不用得到先把密钥、证书生成流程跑通。这样到项目后期要切换到正式安全启动时你不需要改业务代码只需要把镜像重新打包签名就能无缝过渡。最后想说的是STM32N6 的启动流程复杂度比老一代 MCU 高不少但这是安全 MCU 的必然方向。好好利用 DEV_BOOT 模式这台“开发加速器”把精力放在业务逻辑和应用安全区、非安全区的设计上你会发现在这颗芯片上做开发其实并没有那么难。
返回列表