做嵌入式开发这些年,换过的平台不少,但真正让我在开箱阶段就停下来重新审视“存储”这件事的,STM32N6 算头一个。这颗基于 Cortex-M55、主频最高 800MHz 并集成 NPU 的旗舰级 MCU,内部居然没有 Flash。对,你没看错:代码和数据的“家”不再在芯片内部,而是在外部,通过 XSPI 接口连接。很多从 H7、F4 迁移过来的朋友,第一反应是:外挂 Flash 怎么选?有限制吗?选错会有什么后果?这篇文章我就基于实际选型和调试经验,把 STM32N6 外部 Flash 选型里那些“真限制”和“伪限制”一条条理清楚。适合正在画 N6 板卡的硬件工程师,也适合准备做 AI 应用评估、被烧录报错折腾得头疼的软件工程师。
1. 先搞清楚:STM32N6 的 Flash 到底在哪
1.1 一颗没有内置 Flash 的旗舰 MCU
做过 MCU 开发的人都知道,传统 STM32 的 Flash 都在芯片内部,程序用调试器一烧,上电就跑,简单粗暴。但 STM32N6 把这条路径彻底改了:它没有内部非易失存储器,所有固件和业务数据都必须放在外部 Flash 里。第一次看数据手册的人很容易漏掉这个细节,等到画完板、焊完样片才发现“怎么下载失败”,那就晚了。
为什么 ST 要这么干?说白了就是性能与工艺的取舍。Cortex-M55 在 800MHz 下跑,指令带宽需求很高,而嵌入式 Nor Flash 的读取速度、随机访问延迟和容量扩展都受限,硬塞到内部会让功耗、面积、工艺成本全面失控。N6 选择把存储放到外部,通过高性能 XSPI 接口做 memory-mapped 映射,再用 Cortex-M55 自带的 I-Cache/D-Cache 以及片内大容量 SRAM 来“兜底”。这个架构思路跟当年 PC 上把 BIOS 放到 SPI Flash、系统盘放到 SSD 是类似的:CPU 并不直接面对慢速存储,而是靠缓存和分层把延迟藏起来。
这也意味着,外部 Flash 不再是“随便挂一个能存数据的芯片”,而是决定了系统能不能启动、代码能不能稳定运行、AI 模型能不能装得下的核心组件。选型这件事,从“采购顺手”变成了“系统设计的一部分”。
1.2 外部 Flash 在系统中的三个角色
在 STM32N6 的典型应用里,外部 Flash 至少要扮演三个角色中的一两个。
第一个角色是代码存储与执行介质。N6 上电后,内置 ROM 引导程序会从 XSPI 接口读取启动镜像,并把外部 Flash 映射到 CPU 地址空间中,CPU 直接取指执行。这个过程叫 XiP(Execute in Place)。如果你的代码量不大、对实时性要求不极端,完全可以从外部 Nor Flash 直接跑,省去拷贝到 SRAM 的步骤。
第二个角色是数据与 AI 模型的仓库。STM32N6 的核心卖点是 NPU,而 NPU 跑模型需要海量权重数据。一颗几十 MB 甚至上百 MB 的外部 Flash,能把模型、图像、日志、固件备份都塞进去。这个时候,NAND Flash 或者大容量 Nor Flash 就派上用场了。
第三个角色是安全启动和安全分区的基础。N6 支持 TrustZone 以及安全启动,代码分为安全区和非安全区,分区信息、签名密钥、启动镜像都存放在外部 Flash 里。Flash 选不好,不只是“跑不起来”的问题,而是整个安全体系地基就不稳。
所以,选型前先想清楚:你的系统里这颗 Flash 是“代码盘”“数据盘”还是“安全盘”?不同定位,选型约束完全不同。
2. Flash 选型的硬性限制清单
2.1 接口限制:必须是 XSPI 兼容器件
STM32N6 访问外部 Flash 的通道是 XSPI 接口,芯片上通常提供两个 XSPI 实例。这个接口向下兼容传统 SPI、双线 SPI、四线 QSPI,也支持八线 OctalSPI。听起来很宽松,但这里有个关键点:如果只是接一颗普通 QSPI Flash,比如经典 W25Q128,能不能用?能。但 800MHz 的 CPU 跑到一颗四线 SPI Flash 上,取指带宽会严重不足,代码执行基本靠缓存续命,稍微有点 cache miss 就卡顿。换句话说,接口物理上兼容,但性能上限完全不同。
我建议把“至少支持 4 线,优先 8 线”作为选型红线。做完整性能验证的板子,直接选 Octal NOR Flash,比如 Macronix MX25 系列、Winbond W25Q 高端系列、ISSI IS25WX 系列等。普通 QSPI Flash 只适合做低功耗原型验证、数据存储,不适合承载高频代码执行。
另外,XSPI 对 Flash 的指令集有要求。N6 的 ROM 引导程序在启动时会尽量读取 JEDEC SFDP 参数表来自动配置时序,这意味着所选 Flash 必须实现标准 SFDP 表,否则引导程序无法正确识别容量、页大小、读命令等关键参数。某些小厂 Flash 或者老型号 Flash 的 SFDP 表不完整,很可能连启动都过不去。
2.2 性能限制:读速度决定代码能不能“跑得动”
很多人觉得“只要接口对上就能用”,其实真正卡脖子的是时序和带宽。XSPI 在 N6 上可以跑到最高约 200MHz 的时钟频率,配合 DTR 双沿传输和 8 根数据线,理论峰值带宽能做到 400MB/s 级别。但这是总线侧的理论值,实际能不能达到,取决于 Flash 内部的阵列读速度和 AC 时序参数。
举个例子,DTR 模式下,数据在时钟上升沿和下降沿都有效,这对 Flash 的数据输出建立时间要求很高。如果 Flash 的 tDV(数据输出有效时间)裕量不足,高速运行时就会出现偶发读错,表现是“程序大部分时间正常,偶尔跑飞、死机、校验失败”。这种问题最难受,因为不是必现,排查起来极耗精力。
所以选型时,不要只看 Flash 标称的“最高频率 200MHz”,要重点看它在 DTR 模式、8 线模式下的实际最高工作频率,以及数据手册里的 AC Timing 是否留有余量。还有一个小细节:为了配合 CPU 的 cache line 填充,最好选择支持 wrap burst(回卷突发读)的 Flash。没有这个功能,cache miss 时总线会多读无效数据,带宽浪费很严重。
另外要明确:无论 XSPI 多快,它跟片内 SRAM 还是有量级差距。把全部代码放外部 Flash 跑,性能一定不如放内部 SRAM。我的做法是:启动代码、RTOS 内核、高频中断、延时敏感的 DSP 处理全部放 SRAM,普通业务逻辑放外部 Flash,NPU 模型也放外部 Flash,运行时按需加载。这样既省了内部 SRAM,又保住实时性。
2.3 电压与信号完整性限制
Flash 选型还有一个容易被忽略的硬限制:电压域。N6 的 XSPI I/O 工作电压需要跟 Flash 供电电压匹配。很多高性能 OctalFlash 是 1.8V 器件,如果你的板子 DDR 域或者主电源是 3.3V,要么加电平转换,要么直接选 3.3V 版本。电平转换在高频 DTR 模式下会引入额外延迟和信号畸变,我建议直接用同电压域的 Flash 或者通过成熟的电平转换方案,不要拍脑袋加电阻分压。
高速信号不是插上就能用的。XSPI 在 200MHz DTR 模式下,信号上升沿和下降沿的时间裕量非常紧张。PCB 走线要尽量短、等长,注意阻抗连续,避免过孔和长 stub。这跟布 DDR 的思路很像,只是把地址线换成命令线,数据线也只有 8 根,但该做的仿真和走线控制不能省。实际调试中,如果程序在低温或高温下偶发异常,第一怀疑对象就是采样延迟和信号完整性,而不是代码逻辑。
2.4 容量限制:Memory Map 和启动窗口
外部 Flash 在 memory-mapped 模式下会被映射到 CPU 地址空间的特定区域,比如 XSPI1 通常映射在 0x7000_0000 附近,XSPI2 映射在 0x9000_0000 附近,具体以参考手册为准。这意味着 CPU 能直接寻址访问的外部 Flash 空间是有限的,不能无限接大容量 Flash。
这个限制对 Nor Flash 问题不大,常见的 8MB、16MB、32MB、64MB 都在映射窗口内。但对 NAND Flash 就要注意:一颗 1Gbit 甚至 2Gbit 的 SPI NAND,映射窗口可能装不下完整空间,或者只能映射部分区域。超出映射范围的部分,需要用 XSPI 的非映射模式(间接模式)通过寄存器读写,不能直接执行代码。所以你想把一个大 NAND 全部映射成代码空间,是行不通的。
ROM 引导程序对启动设备也有自己的“小九九”。它只会按预设流程去外部 Flash 的起始位置找启动镜像,如果 Flash 容量或地址映射方式和引导程序的预期不符,启动就会失败。这个限制在数据手册的“Boot configuration”章节写得比较隐晦,我踩过一次之后才明白:不是所有 Flash 都能当启动盘,必须选引导程序验证过的类型和配置。
3. NOR 还是 NAND:选型边界的进一步细分
3.1 NOR Flash:主流且省心的选择
对绝大多数 STM32N6 项目,首选是 Octal NOR Flash。原因很简单:支持 memory-mapped 模式,代码可以直接在外部执行;随机读取延迟低,指令流和常量访问都能接受;寿命相对长,不需要复杂的坏块管理和 ECC;软件栈也成熟,所有烧录工具和引导程序都优先支持 NOR。
我见过很多工程师拿传统 W25Q128(QSPI)先跑起来,验证逻辑没问题,但等到性能调试阶段就发现瓶颈。W25Q128 在 QSPI SDR 模式下带宽有限,跑大程序、频繁访问常量表时,明显感觉响应变慢。换成 Octal NOR 之后,瓶颈基本消失。所以我现在的建议是:如果做的是量产产品,直接按 Octal NOR 设计;如果只是学习评估,手头有 QSPI Flash 可以先顶着,但别把性能测试结果当真。
官方评估板通常使用的都是经过验证的 Octal NOR Flash。最省事的做法,就是照着官方评估板(比如 STM32N6570-DK)的 BOM 选同一款或同系列 Flash。这样启动流程、外部加载器、CubeMX 配置都有现成参考,能省掉一大半从零摸索的时间。
3.2 NAND Flash:能存大模型,但执行受限
如果 AI 模型很大,Nor Flash 的容量和成本会让你肉疼,这时候 SPI NAND 就来了。N6 的 XSPI 也支持 SPI NAND,但有几个先天限制必须认清:第一,NAND 不支持按字节随机读,页式读取结构决定了它做 XiP 不现实,代码和数据要先拷到 SRAM 才能执行;第二,NAND 有坏块问题,出厂坏块和运行中新增坏块都需要软件管理,还要做 ECC 校验,这部分代码和逻辑不能省;第三,NAND 的读延迟比 NOR 高,即使走 XSPI,短时间内大量连续读的吞吐可能不错,但随机读和中断现场恢复场景就比较痛苦。
所以 NAND 在 N6 系统里的典型定位是“数据仓库”:存 AI 模型、存文件系统、存升级包。启动把精简引导从 NOR 加载到 SRAM,然后从 NAND 把模型和数据搬到 SRAM 或 PSRAM。这种 NOR + NAND 的组合方案,在边缘 AI 设备上非常常见。
顺带说一句,很多人看到“3D NAND”这个词就以为跟 SPI NAND 是两回事。其实 3D NAND 是闪存内部的堆叠工艺结构,它决定了页大小、块大小和寿命特性。选型时要关注的是封装和接口,而不是纠结堆叠层数。
3.3 对比表与决策建议
| 维度 | NOR Flash | SPI NAND |
|---|---|---|
| 代码直接执行(XiP) | 支持,主流用法 | 不支持,需拷贝到 SRAM |
| 随机读延迟 | 低 | 高 |
| 连续读带宽 | 较高,适合指令流 | 较高,适合大数据块 |
| 容量 | 常见 8MB~64MB,更大偏贵 | 常见 128MB~1GB,容量成本优势明显 |
| 坏块管理 | 不需要 | 需要软件管理 + ECC |
| 写擦寿命 | 通常 10 万次左右 | 通常 1 万次~10 万次,参数差异大 |
| 典型用途 | 代码、常量、启动镜像 | AI 模型、文件系统、日志 |
决策逻辑很简单:跑代码、做启动,选 NOR;装模型、存大文件,选 NAND;既要快速启动又要大容量,就 NOR + NAND 双 Flash,两个分别挂到两个 XSPI 实例上。预算极其紧张且对性能无要求,可以只用小容量 NOR 加映射窗口外的数据区,但我不推荐,因为后期扩展很被动。
4. 实操:从选型到烧录的完整路径
4.1 参考设计与器件清单
拿到一颗新平台,我最先做的是找官方评估板的原理图和 BOM,不是自己从头发明。STM32N6 的评估板原理图里,外部 Flash 的接线、上拉电阻、去耦电容、启动配置都有标准答案,直接抄过来是最稳妥的。如果你用的 Flash 不在官方验证列表里,那我建议至少先用手头目标 Flash 做一块最小系统板,把启动和烧录跑通,再决定是否全板铺开。
选型阶段可以按这个清单过一遍:接口是否支持 8 线 DTR;SFDP 表是否完整;是否支持 4 字节地址模式;是否支持 wrap burst;工作电压和 I/O 电压是否匹配;是否有已验证的外部加载器;耐温和寿命是否满足产品要求。前四项直接决定能不能用、好不好用,后三项决定开发效率和产品可靠性。
4.2 使用 STM32CubeProgrammer 烧录外部 Flash
STM32N6 没有内部 Flash,所以烧录方式跟传统 STM32 完全不同。传统 STM32 用 ST-Link 直接下载到内部 Flash;N6 需要借助外部加载器(External Loader,文件后缀 .stldr)。这个加载器本质上是一段小固件,由 STM32CubeProgrammer 下载到 SRAM 中运行,然后通过 XSPI 接口操作外部 Flash。
具体操作流程:连接 ST-Link,打开 STM32CubeProgrammer,在 External Loader 一栏选中所用 Flash 对应的 .stldr 文件,然后正常执行擦除、编程、校验。加载器文件一般在 CubeProgrammer 安装目录的 ExternalLoader 文件夹下,或者由 STM32CubeMX 根据你的 Flash 型号自动生成。这里最常翻车的就是:忘选 External Loader,或者选错了型号,程序会直接报错。
如果你用的外部 Flash 没有现成加载器,那就需要自己写。把 XSPI 初始化、读 ID、擦除、写入、校验这几个函数实现出来,按加载器规范编成 .stldr 文件。这个过程第一次做比较繁琐,但做一次之后就能复用,后面换型号只改参数表和指令序列。
4.3 启动与性能验证
烧录成功不代表启动成功。N6 要从外部 Flash 启动,必须保证 Boot 引脚配置正确,同时 Flash 起始地址处的数据结构符合引导程序的预期。镜像通常需要包含合法的向量表、栈指针初始值和入口地址,如果这些信息不对,引导程序会认为镜像无效,表现为“烧了程序但上电没反应”。
启动通了之后,性能验证别只看跑分,要看真实场景。我一般会做一个简单的 GPIO 翻转测试:把同一个翻转循环分别放在外部 Flash 和内部 SRAM 里跑,用示波器或者逻辑分析仪测翻转频率。这个数据能直观反映 XiP 模式下的实际执行效率。如果外部 Flash 执行速度远低于预期,优先检查三件事:XSPI 是否配成了 DTR 模式;采样延迟参数(RxSampling)是否合适;I-Cache 是否开启。
提示:采样延迟不是越大越好,也不是越小越好。它取决于 Flash 的 AC 时序和 PCB 走线长度,最稳妥的方法是用 CubeMX 里的调试工具扫一遍采样点,选一个在所有温度和电压条件下都稳定的值。
5. 常见问题与排查实录
5.1 “Flash Download Failed: Cortex-M3”的几种成因
这个报错对 N6 新手来说几乎是必经之路。第一次看到“flash download failed - cortex-m3”时,很多人会懵:N6 不是 Cortex-M55 吗,怎么跟 Cortex-M3 扯上关系?其实这跟内核无关,而是调试工具在识别目标失败时退回的通用错误信息。常见成因有三个:第一,SWD 连接不稳定,调试器没有正确读到内核 ID;第二,没有选择或者选错外部加载器,下载器不知道该往哪写;第三,目标板供电异常或复位时序不对,导致调试器无法建立稳定的调试会话。
排查时先把 SWD 时钟降到低速,比如 4MHz,排除高速通信不稳的问题。然后确认 ST-Link 固件、驱动、IDE 的器件支持包都是最新版本,N6 是新器件,太老的工具可能根本不认识。最后检查板子的供电和复位按键,很多“下载失败”其实是硬件没上电或者复位脚有干扰。
另一个高频报错是“erase failed! cannot access memory internal command error flash download failed”。这个通常是下载器进入了外部 Flash 但擦除过程中访问失败,原因可能是 Flash 供电不稳、加载器里擦除命令时序不对,或者 Flash 处于写保护状态。遇到这种问题,我习惯先手动读一下 Flash ID,如果 ID 都读不到,问题大概率在硬件连接或加载器配置;如果 ID 能读到但擦除失败,再看状态寄存器的保护位。
5.2 启动失败的排查思路
启动失败的表现很多:上电后电流异常、调试器连不上、复位后 PC 指针乱跳、程序跑一半死掉。我的排查顺序是:先确认 Boot 引脚和启动源配置,看引导程序到底从哪个外设找镜像;再用示波器量 XSPI 的时钟和片选信号,确认引导程序有没有真的去访问外部 Flash;最后抓取 Flash 数据线的波形,看读出来的数据和镜像文件是否一致。
这里最容易踩的坑是时序余量不足。如果 Flash 在常温下能启动,但一冷一热就起不来,或者同一批板子有的能启动有的不能,基本可以断定是 XSPI 采样时序处在临界状态。解决方法是调采样延迟参数,同时检查 PCB 走线长度和阻抗。还有一次我遇到“程序能跑但一开中断就死”,最后发现是中断向量表配置在外部 Flash,但中断触发时 Cache 刚好没命中,导致取指延迟过大。解决办法是把中断向量表和关键中断服务函数挪到 SRAM。
5.3 安全区与非安全区配置的坑
N6 的 TrustZone 安全机制把外部 Flash 空间也纳入了管理范围。应用安全区和应用非安全区的划分,直接影响调试器和运行时代码能否访问对应存储区域。在这个上面我吃过一次大亏:使能安全启动后,忘记调整调试器对非安全区域的访问权限,结果调试会话直接失联,程序跑在安全区里,调试器却停在非安全区,两边互相看不到。
如果你在工程里开了安全分区,烧录和调试时要注意:先用支持 TrustZone 的调试方式连接,确认当前 CPU 处于哪个安全状态;CubeProgrammer 烧录时,外部加载器的访问也要匹配目标分区属性。还有一个很容易忽略的点:安全启动使能后,引导程序会校验镜像签名和加载地址。如果你只是临时改了链接脚本,把镜像加载地址挪了,但没更新签名头里的地址信息,同样会被拒绝启动。遇到“bootloader 明明能跑,就是加载不了我们的镜像”的情况,先查签名和地址,而不是怀疑 Flash 坏了。
5.4 NAND 坏块与 ECC 的软件功课
如果系统里用了 NAND,还有一个绕不开的功课:坏块管理和 ECC。SPI NAND 出厂就可能有坏块,而且使用过程中还会新增。N6 的 XSPI 控制器不会替你处理这些,必须在文件系统层或者驱动层自己做映射、跳过坏块、附加 ECC 校验信息。选型时要注意 NAND 的页大小(常见 2KB 或 4KB)和 spare area 大小,这直接决定 ECC 数据怎么摆放。很多人在开发早期图省事不写坏块管理,结果产品用几个月后出现数据读错,返修排查极其痛苦。
我的建议是:如果项目一定要上 NAND,在立项阶段就把坏块管理、磨损均衡、掉电保护这三件事列入计划,不要把这个当成“以后再加”的功能。文件系统可以选择成熟方案,它们对 NAND 的坏块处理相对完善,但也要根据具体 NAND 型号做适配和压力测试。
最后再分享一点个人体会
写到这里,回头看这几次 STM32N6 项目的经历,最大的感受是:外部 Flash 选型这件事,本质上是在跟“性能、成本、可靠性”做三角博弈。官方验证列表里的 Flash 永远是省心之选,但如果你有特殊容量或成本要求,也不要怕踩坑,重要的是一开始就把时序验证、加载器适配、安全分区规划排进开发计划里。
我自己现在的一套固定动作是:先抄官方评估板原理图,用同款 Octal NOR 跑通启动和烧录;再把 AI 模型搬到外部 NAND,用 SRAM 做运行时缓存;最后才根据量产成本优化 Flash 具体型号。这个流程看起来保守,但踩坑最少、交付最快。如果你也正在 N6 上挣扎 Flash 选型,希望这篇能帮你少走几段弯路。后续如果大家感兴趣,我可以再把外部加载器自研流程和 XSPI 时序扫点的方法单独拆开聊。