ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32N6 FSBL启动解析:BootROM签名、512K限制与Flash烧录

STM32N6 FSBL启动解析:BootROM签名、512K限制与Flash烧录

简介:《LAT1491 STM32N6的FSBL基本流程介绍-v1.0》是一份面向ARM架构MCU开发者、嵌入式技术爱好者及希望深入理解STM32N6启动机制的专业人士的技术文档,重点解决片内BootROM之后如何验证并加载FSBL、如何在Dev boot与Flash/Serial boot之间选择,以及如何组织外部Flash应用程序并制作可启动镜像等实际问题。包内为1个PDF文件,约2.07MB,系统梳理BootROM对1024字节Header的校验与FSBL搬运、FSBL单独运行、FSBL加Load & Run、FSBL加XiP三种典型流程,同时说明ST Signing Tool的签名参数、Cut1.1与Cut2.0对应的header版本差异,以及BootROM最大512KB拷贝限制等工程注意事项。文档还以IAR与STM32N6 DK板为例,给出从工程创建、编译配置到下载部署的示例步骤,便于读者对照完成镜像签名、Flash编程和启动异常排查,并附有开发配置提示。已有162人学习,可作为STM32N6启动配置、二进制签名与外部存储部署实践的参考材料,适合中高级嵌入式开发者查漏补缺。

1. FSBL:为什么一块只带 NPU 的 MCU 要把启动拆成两段

STM32N6 这类芯片有个反直觉的地方:芯片内部的 ROM 只有一小段 BootROM,容量很小,而且写死之后无法修改;用户代码必须放到外部 Flash,或者通过串口临时灌进来。这和过去“Flash 就在片内、上电直接取指”的体验完全不同,也是很多从传统 MCU 转过来的开发者第一次遇到问题的根因。

多出来的这一段,就是 FSBL(First Stage Boot Loader,第一阶段引导加载程序)。它不是可选的附加功能,而是对硬件约束的直接回应:BootROM 只负责“信任第一段代码”,剩下的搬运、配置、跳转交给 FSBL;把“芯片出厂时固化、不可修改的启动能力”和“用户每次都能修改的应用逻辑”分开,让支持 NPU 的这类 MCU 也能可靠地实现启动。读懂这段分层,你就会明白后面那些签名、地址、XiP 到底在解决什么问题。

2. BootROM 与 FSBL 的分工:512K 拷贝上限决定了架构

2.1 BootROM 只做三件事

把 BootROM 的行为拆开看,它做的事情很少:读 BOOT 引脚确定启动模式,验证目标镜像的头,把镜像搬到指定 SRAM 地址并跳转。以外部 Flash 启动为例,BootROM 会先校验 FSBL 头部那 1024 字节的 Header 信息;校验通过后,把 FSBL 搬到内部 SRAM2 的0x34180000,再把 PC 指针指过去。

这里有个容易被忽略的设计:Header 是独立于代码正文的一段元数据。代码本身怎么搬、搬到哪、能不能执行,全由 Header 里声明的信息决定。这意味着 BootROM 不需要理解你的代码,它只认 Header。这是一种典型的“用元数据解耦”的做法:ROM 固化一次,后续所有启动策略的变化,都落在可重新生成的 Header 上。

2.2 为什么 FSBL 必须签名

FSBL 必须完成签名,否则无法正常启动。签名工具会在原始 bin 前面拼上 Header,产物形如FSBL-trusted.bin。签名动作本质上就是把“镜像描述信息 + 完整性校验”变成 BootROM 能读懂的格式,这也是为什么调试阶段不需要签名、而烧到外部 Flash 前必须签名——两条路径的入口不是同一个。

注意:签名前需要安装对应版本的 CubeProgrammer,并把签名工具的路径加入系统 PATH,否则命令行直接报找不到可执行文件。

2.3 512K 这条硬线

单 FSBL 工程也能直接包含应用逻辑,编译烧录一个工程看起来最省事。但 BootROM 最大只搬 512K 字节。工程一旦超限,就必须换成 FSBL + APP 的两级结构:FSBL 保持精简,负责把 APP 完整搬到内部 SRAM(含 data 和 code)再跳转。这条限制不是建议,而是硬性的物理约束,也是三种启动架构分化的起点。

2.4 三种启动架构的取舍

架构搬运内容启动后执行位置适用场景
仅 FSBLBootROM 搬 FSBL内部 SRAM2逻辑简单、体积在 512K 内
FSBL + Load & RunFSBL 搬 APP 全文内部 SRAMAPP 超出 512K,且可接受启动拷贝耗时
FSBL + XiPFSBL 只配置 Flash 为 XiP外部 Flash 原地执行APP 体积大,想省 RAM 与拷贝时间

Load & Run 把 Appli 的完整内容搬进内部 SRAM,代价是占用 RAM 且启动阶段有拷贝开销,好处是执行速度稳定。XiP(Execute in Place)则让 FSBL 把外部 Flash 配置成可直接取指的模式,完成后 PC 直接跳到外部 Flash 中 App 的第一条指令,省掉整段拷贝,但对 Flash 的读时序和访问延迟更敏感。选哪种,本质是在 RAM 占用、启动时间、执行性能三者之间做权衡。

3. 签名与镜像布局:Header 里到底装了什么

3.1 一条命令背后的参数语义

签名命令有两种版本分支,区别只在-hv的取值,对应芯片的不同 Cut 版本:

# Cut 1.1 版本 STM32MP_SigningTool_CLI.exe -bin FSBL.bin -nk -of 0x80000000 -t fsbl \ -o FSBL-trusted.bin -hv 2.1 -dump FSBL-trusted.bin # Cut 2.0 版本 STM32MP_SigningTool_CLI.exe -bin FSBL.bin -nk -of 0x80000000 -t fsbl \ -o FSBL-trusted.bin -hv 2.3 -dump FSBL-trusted.bin

各参数的含义需要逐个核对:-bin指向原始未签名的 bin;-nk表示不使用密钥相关选项,即 no keys;-of是 option flags,0x80000000这个值决定了镜像的属性位;-t fsbl声明镜像类型,BootROM 靠它判断这是不是自己能接的第一阶段镜像;-o是输出文件;-hv是 Header 版本号,必须与芯片 Cut 版本匹配;-dump把生成后的 Header 打印出来便于核对。

-hv写错是最隐蔽的一类错误:命令能跑完,文件也生成了,但 BootROM 校验不过,板子毫无反应。这里没有后悔药,只能靠-dump的输出和芯片版本对一遍。

3.2 Header 与前 1024 字节的关系

BootROM 验证的是镜像最前面那 1024 字节。签名工具的作用就是生成这段内容并拼到 bin 前面,所以FSBL.bin和FSBL-trusted.bin的大小差正好是 Header 的长度。理解这一点后,很多现象就顺了:比如你直接拿未签名的 bin 去烧,Flash 里第一个字节就不是 BootROM 期待的格式,校验自然失败。

3.3 开发调试与量产启动是两条路

调试阶段走的是 Dev boot:BootROM 直接从内部 RAM 启动,不经过签名校验。这也是为什么在 IDE 里单步调试一切正常,一旦切到 Flash 启动就卡死——两种模式的入口、校验、搬运逻辑全都不同。开发时跑通不等于量产时能启动,这个认知差是新手最常见的翻车点。

4. 从 RAM 调试到 Flash 启动:一次完整链路的机制对照

4.1 Dev boot 与 Flash boot 的引脚差异

启动模式由 BOOT1 引脚决定。Flash boot 对应 BOOT1 = 0、BOOT0 = 0,上电后 BootROM 尝试从外部 Flash 启动;Dev boot 则让 BootROM 从内部 RAM 启动。开发板上通常有跳线或按键切换,切换后必须复位才能生效,热切换是无效的。

4.2 工程配置里几个决定性选项

工程能编译通过,不代表能正常下载调试。需要逐项确认的包括:Device 列表中存在 STM32N6 对应型号(没有就先装补丁包)、ST-LINK 配置正确、Output 选项符合预期、Debugger 的 Reset 方式设为 Software。Reset 方式这一项如果留在默认的硬件复位,调试会话往往起不来,改完还要退出调试、重新下载、再进调试,程序才能从串口正常打印。

补丁包的处理也值得单独说一句:Keil 和 IAR 需要装 N6 相关补丁,CubeIDE 则需要更新到支持该芯片的版本。补丁包在 SDK 目录里有明确位置,漏装的表现通常是器件识别异常或链接脚本报错,而不是一句清晰的“缺补丁”。

4.3 烧录地址:hex 与 bin 的分水岭

烧到外部 Flash 时,建议指定地址烧 bin,而不是直接烧 hex。原因很具体:工程编译出的 hex 默认地址一般在 RAM,直接烧进去等于把代码写到了错误的位置,板子当然起不来。命令行方式如下:

# Linux:DKEL 为 External loader 的环境变量名 STM32_Programmer_CLI -c port=SWD mode=HOTPLUG ap=1 -el $DKEL \ -hardRst -w FSBL-trusted.bin 0x70000000 # Windows STM32_Programmer_CLI -c port=SWD mode=HOTPLUG ap=1 -el %DKEL% \ -hardRst -w FSBL-trusted.bin 0x70000000

参数逐个说明:-c port=SWD mode=HOTPLUG ap=1指定 SWD 连接、热插拔模式、访问端口 1;-el指定 External loader,DK 板和 Nucleo 板自带对应的 loader,自制板需要自己开发;-hardRst在下载后执行硬复位;-w写入数据,后面跟文件与目标地址。0x70000000是外部 Flash 的映射地址,写错这个地址的后果和烧 hex 一样——代码在 Flash 里,但 BootROM 找不到。

如果走图形界面,流程等价:切到 dev 模式连接、选对应的 External loader、下载时明确指定地址。烧录前的第一件事是确认手上这个工程是不是 FSBL 工程,是的话必须先签名,否则从外部 Flash 启动时一定失败。

4.4 重启与 Attach 调试

烧录完成后把 BOOT 引脚切回外部 Flash 启动模式,复位,BootROM 就会从外部 Flash 走完整链路。在 Cut 1.1 版本上,Flash 启动模式并没有关闭调试口,可以用“attach to running target”的方式挂上去调试。这个能力在排查“为什么 Flash 启动失败”时非常有用——能挂上去,说明芯片至少在跑;挂不上去,问题多半在 Header 校验或搬运阶段。

5. 避坑清单:五条血泪经验

5.1 现象:调试一切正常,切 Flash 启动毫无反应

原因:FSBL 未签名,或-hv与芯片 Cut 版本不匹配,BootROM 校验头部失败后直接放弃启动,表现为完全静默。 解决:先用-dump把 trusted bin 的 Header 打出来核对版本号,再确认烧进去的确实是FSBL-trusted.bin而非原始 bin。

5.2 现象:烧录成功,但程序跑飞或进入异常

原因:烧的是 hex,默认地址指向 RAM,或者 bin 的目标地址写错了。 解决:统一改成“bin + 显式指定0x70000000”的方式烧录;如果必须用 hex,先确认链接地址已改到外部 Flash 映射区。

5.3 现象:工程编译过了,下载后无法进入调试

原因:Debugger 的 Reset 方式不是 Software,或者 ST-LINK 配置与实际硬件不匹配。 解决:把 Reset 改为 Software,退出调试会话重新下载,再重新进入调试;必要时检查 Device 型号是否精确对应。

5.4 现象:工程越做越大,某天突然启动不了

原因:单 FSBL 工程超过了 BootROM 的 512K 搬运上限。 解决:拆分架构,改成 FSBL + Load & Run 或 FSBL + XiP,让 FSBL 只负责搬运或配置,应用逻辑独立成 APP。这条坑的隐蔽之处在于它不会在编译期报错,而是在你加了某个大数组或字库之后才发作。

5.5 现象:IDE 提示器件缺失、链接脚本异常

原因:没装 N6 补丁包,或 IDE 版本过低。 解决:从 SDK 指定目录安装补丁,CubeIDE 则升级到支持该芯片的版本,装完后重新确认 Device 列表。

6. 设计取舍:为什么把复杂度前移到启动阶段

回头看这套机制,真正的设计取舍在于“把复杂度放在哪里”。传统 MCU 把启动做成一跳直达,代价是 Flash 必须和内核在同一片硅上、容量受工艺限制。N6 选择把代码赶到外部 Flash,于是必须在启动早期引入一段可信代码,用它去配置外部存储、搬运镜像、决定执行位置。BootROM 越小越安全,因为它一旦出厂就不可修改;FSBL 越灵活越好,因为它可以随工程重新签名生成。这个分界点选在 1024 字节 Header 上,恰好是“足够描述一个镜像”和“小到能硬编码校验逻辑”的平衡。

三种架构的分化也是同一个思路的延伸。仅 FSBL 适合小工程,简单但被 512K 卡死;Load & Run 用 RAM 换通用性,启动阶段有确定的拷贝开销,换来执行性能的稳定;XiP 反过来,用更严格的 Flash 读时序要求换掉整段拷贝,适合大镜像。没有哪个绝对更好,只有你的 RAM 预算、启动时间要求和 Flash 器件特性凑不凑得上。判断方法很直接:先量 FSBL 和 APP 的实际体积,再看 RAM 余量,最后看 Flash 的访问延迟是否支撑原地取指。

验证思路我一般建议按这个顺序走:先在 Dev boot 下把功能全部调通,再单独验证签名产物能否被-dump正确解析,然后烧到外部 Flash 并 attach 观察 PC 是否落在预期地址,最后才切引脚冷启动。这个顺序的价值在于每一步失败都能定位到具体环节,而不是在“板子不亮”这个笼统现象里瞎猜。

这套流程第一次走的时候我在-hv上栽过跟头:命令跑完、文件生成、烧录成功,板子一动不动,排查了大半天才发现 Header 版本和芯片 Cut 对不上。从那以后我每次签名后都强制先跑一遍-dump,把版本号和目标地址对一遍再往下走,这个习惯省下的时间远比那一条命令多。文档里对三种启动架构和烧录地址的说明比较具体,配合 SDK 里的示例工程对照着看,理解会快很多,希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进