MADL!AR
Code is cheap, show me the PPT!
首页
分类
Fragment
关于
N657i0从外部存储器执行
分类:
硬件
发布于: 2026-04-25
2025年中旬,为了验证STM32N6的NPU性能以及体验官方的Model Zoo生态,自制了一块STM32N6570核心板,结果因为工作忙加上没配IO板,在抽屉里吃灰了一年多。其实这块板子挺让人纠结,芯片贵、电路复杂、软件配置也麻烦,对于只是业余随便折腾的电子爱好者来说,在这些琐碎细节上耗精力实在不划算,所以一直不太想碰。但考虑到当初打板就花了一千五百多,直接扔了又太可惜,思来想去还是给它补了一块IO板,打算验证完外部存储器功能就彻底搁置,给它一个完整的生命周期。 相关文件已开源到github: [https://github.com/cl-ei/stm32n657i0-dev-board-hardware](https://github.com/cl-ei/stm32n657i0-dev-board-hardware) ## 一、硬件配置 主控STM32N657I0H3Q,板上晶振48Mhz,有一个低电平驱动的红色LED灯,连接PG10引脚。核心电压默认给到了0.89v,配置R30和R31可以调整到0.81v。Flash型号为MX66UW1G45GXDI00,1G bit(128MB),需要先焊接SB1将VDDA1V8供电输入到Flash芯片的VDD引脚。IO配置如下: ```cpp /* MX66UW1G45GXDI00 Pin Map */ #define FLASH_NCS PN1 #define FLASH_DQS PN0 #define FLASH_CLK PN6 #define FLASH_IO0 PN2 #define FLASH_IO1 PN3 #define FLASH_IO2 PN4 #define FLASH_IO3 PN5 #define FLASH_IO4 PN8 #define FLASH_IO5 PN9 #define FLASH_IO6 PN10 #define FLASH_IO7 PN11 ``` ## 二、启动过程 ### stm32N6 启动模式 系统复位后会执行内部的bootROM,启动模式依赖BOOT0、BOOT1引脚和一个TAMP backup register以及一个OTP(一次性编程)的word决定。BOOT1引脚默认为PA6。启动模式如下: | BOOT0 | BOOT1 | Boot source | |-------------------|---------------------|---------------------------| | - | 1 | Development boot | | 0 | 0 | Flash boot | | 1 | 0 | Serial boot | Serial boot即从串口或USB接口启动,不在讨论范围。Dev boot是开发调试时,调试器直接将代码搬到对应的内存地址进行执行;而Flash boot即从外部存储器启动,可以通过OTP配置启动方式,比如选择从SPI NOR Flash启动还是SDMMC等。 这里的OTP很坑,只能一次性编程,如果配置错了标志位,就可能导致启动时找不到对应外部设备而变砖,整块板子都会报废。好在芯片中有很多OTP是用户用途的,比如用来写入用户定义的序列号、秘钥等,可以先找一个无关系统功能的OTP word进行配置,以此熟悉配置过程。 ### FSBL 第一段引导加载程序(FSBL)是 STM32N6 微控制器启动流程中的关键组件。它负责初始化系统、配置硬件,并将应用程序代码从外部存储器加载至内部或外部存储器中执行。 当boot引脚拨到 0 0 时,这里有几种启动方式,分为两种类型,一是在内部SRAM上执行代码,二是在外部存储器上执行。 __启动流程__ 复位之后,bootROM会检查外部存储器中存储的的FSBL前1K byte的内容(签名,其实只有前512 byte有效,后512 byte为填充值),如果校验OK,则bootROM会将FSBL从外部存储器搬到内部SRAM中,然后跳转到SRAM中FSBL的起始位置(跳过1K byte的签名)开始执行,此时执行的就是内部SRAM的FSBL。 #### 在内部SRAM中执行代码 如果图简单,FSBL可以包括启动的配置(bootloader)和用户代码,即用户应用被包含在FSBL之内,采用上述的启动流程就可以运行。这里的好处就是,一旦进入到SRAM中的FSBL,则外部存储器可以完全移除或者断电,而且代码运行在内部sram中,速度也比较快。 但缺点是,FSBL被限制在512KB,除去1K的签名部分,只有511KB。如果用户代码大小超过了这个限制,就得另寻它法。除此之外,FSBL还抢占了宝贵的内部SRAM空间,虽然这个芯片宣称有4.2MB SRAM,但其中的2.2MB是直接挂载到NPU的,即CPU无法直接访问,可用的只有2MB,而FSBL的地址恰好在1~1.5 MB的位置,它将连续的2MB内存,切分成了两段。 #### 在内部SRAM中执行的FSBL中,搭载加载与运行(Load & Run)功能 此种模式是对前一种方式的优化,bootROM 从外部存储器获取FSBL,并加载到内部SRAM中运行,然后FSBL对外部存储器进行配置,并获取第二个存储在外部存储器的二进制文件(application code),并将它拷贝到内部SRAM。这第二个二进制文件加载完成之后,FSBL就跳转到SRAM的新位置执行(即application code所在SRAM的位置)。 这里的好处是,可以将applicaion搬到SRAM的任意合适位置,比如搬到SRAM末尾,这样就可以获得连续的1.5MB内存,灵活性很高。 这里有详细介绍: [How to create an STM32N6 FSBL load and run](https://community.st.com/t5/stm32-mcus/how-to-create-an-stm32n6-fsbl-load-and-run/ta-p/768206) #### 在外部存储器上执行代码 这里的不同点在于,在FSBL中直接将外部存储器配置为存储映射模式(Memory Mapped Mode),跳转到外部存储器application的代码地址进行执行。 这将彻底释放内部SRAM。此时,我们需要生成两个文件,一个是FSBL,可以把它视作bootloader,它必须包含有效的文件头或签名,烧录到外部存储器映射后的首地址;另一个是application,是用户代码,烧录到外部存储器的其他位置,只要不与FSBL重叠就行(推论一)。 参考 [How to execute code from the external serial NOR using the STM32N6](https://community.st.com/t5/stm32-mcus/how-to-execute-code-from-the-external-serial-nor-using-the/ta-p/771048) ## 三、在外部存储器上执行代码的验证 分为以下几步执行: 1. 创建工程。__这里有一个踩坑的点,后续分析__,创建工程有两个选项:“Secure and Non Secure Domains” 和 “Secure domain only”,如果选择前者会创建两个application,分别是NS和S。这个选项确定是否启用硬件级的 TrustZone 安全隔离技术,这里是选定了“Secure and Non Secure Domains”。 2. 配置LED灯,context选择 Application。 3. 配置XSPI Flash,由于n657i0只有1个xspi端口,这里选择XSPI2 to Port2, 1 & 3 not used。XSPI2的配置如下: ``` Mode: Octo SPI Port: Port2 Octo Chip Select Override: NCS1 -- Port2 # 这里其实是要把CS端口设置为PN1,参考上述的芯片连线 Generic 配置项: - Fifo Threshold: 4 - Memory Mode: Disable - Memory Type: Micron - Memory Size: 1 GBits - Chip Select High Time Cycle: 1 - Free Running Clock: Disable - Clock Mode: Low - Wrap Size: Not Supported - Clock Prescaler: 0 - Sample Shifting: None - Delay Hold Quarter Cycle: Enable - Chip Select Boundary: Disabled - Maximum Transfer: 0 - Refresh Rate: 0 - Memory Select: NCS1 - Switching Duration Clock Number: 1 ``` 并在Middleware and Software Packs中配置ExtmemManager,在Runtime usage中勾选FSBL和子选项Active External Memory Manager。详细配置: ``` Boot: - Select boot code generation: Enable (勾选) - Selection of the boot system: Execute In Place - Header size: 0x400 XIP: - select the memory: Memory 1 - Application offset: 0x100000 - Application offset NS: 0x180000 ``` 在子选项卡Memory 1中: ``` Select driver Select the memory driver EXTMEM_NOR_SFDP Configuration Memory Instance XSPI2 Number of memory data lines EXTMEM_LINK_CONFIG_8LINES ``` 配置XSPI的时钟,参考文档中写的是50Mhz。 然后生成代码,编译成两个文件,分别在项目下寻找FSBL和Application文件夹,并在输出路径下寻找.bin文件。这里将它们拷贝到一个文件夹(C:\sign_n6),执行命令对这两个文件都进行签名: ``` C:\sign_n6> dir >>> 2026/04/25 22:52 4,864 N657i0_ext_xip_AppliSecure.bin 2026/04/25 22:49 60,384 N657i0_ext_xip_FSBL.bin .. # 执行签名: C:\sign_n6>"C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\STM32_SigningTool_CLI.exe" -bin C:\sign_n6\N657i0_ext_xip_FSBL.bin -nk -of 0x80000000 -t fsbl -o FSBL-trusted.bin -hv 2.3 -dump FSBL-trusted.bin -align C:\sign_n6>"C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\STM32_SigningTool_CLI.exe" -bin C:\sign_n6\N657i0_ext_xip_AppliSecure.bin -nk -of 0x80000000 -t fsbl -o Appli-trusted.bin -hv 2.3 -dump Appli-trusted.bin -align ``` 然后进行烧录,在cubeProgremer中选定下载算法,这里可以参考官方的N657x0-DK,然后在左侧栏选定下载按钮,勾选“校验编程”和“下载文件”,将: 1. FSBL-trusted.bin 烧录到0x70000000 2. Appli-trusted.bin 烧录到0x70100000 将两个boot引脚拨到 0 0 的位置,让它从外部存储器加载,即可观察到启动过程,实测已成功。  ## 四、踩坑记录 ### 1. 关于cubeProgrammer的的偏移量配置与(推论一) XSPI2_BASE 为 0x70000000UL,这也就是从0x70000000烧录FSBL的原因。至于为什么Appli的偏移量是0x100000(0x70100000 - 0x70000000),该参考文档没有说明,这里足足有1MB的空间,而按照前面的分析,完全不需要偏移这么多。 查看了一下前面生成的FSBL的大小,大约是60KB,我们给它留128KB的空间,把application放在偏移128KB的位置即0x70020000,看看能否正常启动。当然这里要同步修改前面的ExtmemManager参数,将Application offset的值改成0x20000。__事实证明,确实成功了,验证了我所想的,这个0x70100000是原作者随意设置的一个值,可能是随便找的一个整数而已。__ ### 2. 创建工程时勾选“Secure and Non Secure Domains” 和 “Secure domain only”的影响 在我按照教程逐步尝试的过程中,发现FSBL的代码可以正常执行,而Application却无法执行,在跳转之后,芯片像是死机一样不执行任何命令。我检查了所有参数,也排查了硬件问题,但都无果。 这里的区别在于,按照启动流程,FSBL是在SRAM中执行的,而Application是XIP在外部存储器执行,我怀疑是硬件问题导致XSPI总线的时钟无法支持到50Mhz的频率(实际上50M已经很低了),便采取降频策略再试,也无果。 在FSBL中的ErrorHandler中添加一些日志输出,发现压根没有进入到FSBL的ErrorHandler,而是在跳转之后发生的问题。这让我很头疼,查看Application的代码,才注意到以下关键几行: ``` // 在main.c中: /* Secure SysTick should rather be suspended before calling non-secure */ /* in order to avoid wake-up from sleep mode entered by non-secure */ /* The Secure SysTick shall be resumed on non-secure callable functions */ HAL_SuspendTick(); /*************** Setup and jump to non-secure *******************************/ NonSecure_Init(); ... while (1) { /* USER CODE BEGIN 3 */ ... } ``` 在注释中,已经说明了HAL_SuspendTick等函数的用处,我心想可能是这两个函数,要么停了时钟,要么跳转到一个预期外的地址,导致程序死循环或者跑飞。注释掉这两个函数,故障解决,代码按照预期的方式正常运行了。 查阅了资料,关于Secure domain only(单域模式)的说明: > 仅使用安全域,不启用 TrustZone 隔离。所有代码和资源都在安全侧运行,整个芯片作为一个整体项目开发。这类似于传统的单片机开发模式,开发流程简单直接,适合不需要复杂安全隔离的应用场景 所以,作为个人开发者或者业余爱好者,完全可以不使用TrustZone。 ### 3. 长尾问题记录 此次打了两块板子,其中一块板子(即用于验证的这块)可以正常跑通代码,没有什么异常之处。但另一块板子可以烧录程序,却无论如何也点不亮LED灯。仔细观察,发现用于产生VDD3V3的TPS62088YFP芯片貌似碎了一角(很轻微),用手摸了一下芯片,十分烫手。测量其电压,只有0.8V,而原本它会继续降压产生1V8给Flash芯片和部分io供电,测量这一路电压,也只有0.8V。 推测可能是TPS62088YFP已损坏,也可能是主芯片或者某个电容大短路,或者其它原因。 精力有限,此问题暂时搁置。 ## 总结 这里已经验证完了这块板的flash部分,也宣告了硬件设计上基本成功了。今天以后它将彻底沦为收藏品,我是不愿意再用它了,除非特殊情况。 槽点有很多,首先它的硬件复杂,需要5路电源并且有启动时序,与普通MCU的复杂程度完全不在一个级别,它更像是一个MPU阉割的产物。其次启动复杂,没有内部Flash,无论是创建项目、下载还是调试,都极为不便,尤其是在外部flash上断点调试,堪称地狱。最后,st主推的Model Zoo和CUBEAI的生态,也有很多问题,环境配置极其复杂,很多都需要版本强绑定,而且硬件支持有限,甚至很多模型下载下来还要做大量修改才能跑通,这对于一个业余爱好者来讲,每一条都足够劝退了。MCU还是不能让它脱离逻辑控制的主战场,真要高性能计算的场景,不如上MPU。总的来说,N6还是太鸡肋了。 这对以后选择折腾的对象提供了参考意义,应当把有限的精力放在关键地方,毕竟人生苦短。