本帖最后由 小恩GG 于 2025-6-30 14:47 编辑
概述
NXP的MCXN947是一款高性能微控制器,支持从内部Flash或外部Flash启动(Boot)。对于一般应用来说,内部Flash容量充足,代码和资源可以全部驻留其中。然而在AI应用、图像处理、语音识别等大模型场景中,内部Flash的容量就显得捉襟见肘。例如,在使用eIQ工具链部署深度神经网络模型时,模型体积通常远大于内部Flash的存储能力。
MCXN947虽然支持外部Flash启动和执行(XIP),但通常来说,系统需要从一个特定的启动介质(内部或外部)启动,代码执行的入口地址是固定的。那么,是否可以实现一种折中方案:系统从内部Flash启动,但部分大资源或代码段放入外部Flash,并从那里运行?
本文将介绍我设计实现的一个Demo,它正是采用了这种“内部启动 + 外部执行”的混合方案,在不牺牲启动速度和灵活性的前提下,有效扩展了可用的存储空间。
硬件环境:
开发板:FRDM-MCXN947
软件环境:
IDE:MCUXpresso IDE v11.9.0
SDK:SDK Builder | MCUXpresso SDK Builder (nxp.com)
基础工程: frdmmcxn947_tflm_cifar10
1. 外部Flash硬件配置与管脚配置
以下是官方FRDM-MCXN947中的八线Flash的原理图。

Flash芯片引脚 | | | CS | | | SCK | | | DQS | | | DQ0 | | | DQ1 | | | DQ2 | | | DQ3 | | | DQ4 | | | DQ5 | | | DQ6 | | | DQ7 | | | 配置代码为 例如
- /* Enables the clock for PORT3: Enables clock */
- CLOCK_EnableClock(kCLOCK_Port3);
- const port_pin_config_t port3_0_pinB17_config = {/* Internal pull-up/down resistor is disabled */
- kPORT_PullDisable,
- /* Low internal pull resistor value is selected. */
- kPORT_LowPullResistor,
- /* Fast slew rate is configured */
- kPORT_FastSlewRate,
- /* Passive input filter is disabled */
- kPORT_PassiveFilterDisable,
- /* Open drain output is disabled */
- kPORT_OpenDrainDisable,
- /* Low drive strength is configured */
- kPORT_LowDriveStrength,
- /* Pin is configured as FLEXSPI0_A_SS0_b */
- kPORT_MuxAlt8,
- /* Digital input enabled */
- kPORT_InputBufferEnable,
- /* Digital input is not inverted */
- kPORT_InputNormal,
- /* Pin Control Register fields [15:0] are not locked */
- kPORT_UnlockRegister};
- /* PORT3_0 (pin B17) is configured as FLEXSPI0_A_SS0_b */
- PORT_SetPinConfig(PORT3, 0U, &port3_0_pinB17_config);
- // ...依次配置其他FLEXSPI管脚
复制代码 2. FlexSPI 模块初始化
为了使用FlexSPI进行XIP,需正确配置时钟并初始化FlexSPI模块。以下是关键步骤:
2.1 配置FlexSPI时钟
/* Flexspi frequency 150MHz / 2 = 75MHz */
CLOCK_SetClkDiv(kCLOCK_DivFlexspiClk, 2U);
CLOCK_AttachClk(kPLL0_to_FLEXSPI); /*!< Switch FLEXSPI to PLL0 */
2.2 添加FlexSPI驱动并初始化
在工程中引入 FlexSPI 驱动(SDK 中已有),然后在主程序中添加初始化代码:
/*Get FLEXSPI default settings and configure the flexspi. */
FLEXSPI_GetDefaultConfig(&config);
/*Set AHB buffer size for reading data through AHB bus. */
config.ahbConfig.enableAHBPrefetch = true;
config.rxSampleClock = EXAMPLE_FLEXSPI_RX_SAMPLE_CLOCK;
config.ahbConfig.enableAHBBufferable = true;
config.ahbConfig.enableAHBCachable = true;
FLEXSPI_Init(base, &config);
/* Configure flash settings according to serial flash feature. */
FLEXSPI_SetFlashConfig(base, &deviceconfig, FLASH_PORT);
#if defined(EXAMPLE_FLASH_RESET_CONFIG)
uint32_t TempFastReadSDRLUTCommandSeq[4];
memcpy(TempFastReadSDRLUTCommandSeq, FastReadSDRLUTCommandSeq, sizeof(FastReadSDRLUTCommandSeq));
#endif
等
3. 配置MCUXpresso工程支持Octal Flash
在 MCUXpresso IDE 中,进入 MCU Settings > Memory,添加一个新的 memory 区域:
名称:OSPI_FLASH(或 OCTAL_FLASH)
起始地址:根据你芯片连接的外部flash地址设置,
根据用户手册,FlexSPI起始地址为0x80000000。大小:例如 128MB
然后,选择 NXP 提供的对应外部flash驱动,
FRDM_MCXN947连接的flash为mt35xu512aba,该flash支持SFDP,故我们可以选择MCXN9xx_SFDP_FlexSPI.cfx
添加后,可以看到Memory details如下。
4. 添加链接脚本和模型数据迁移
4.1 创建链接脚本片段
在工程的 linkscripts/ 文件夹中添加两个文件:
text.ldt(用于代码段)
rodata.ldt(用于模型只读数据)
内容如下:
<#if memory.name=="OSPI_FLASH">
KEEP (*(.model_data*))
KEEP (*(model.o*))
KEEP(*(.text.OSPI_FLASH*))
*(.text.QSPI_FLASH*)
*(.text.${memory.alias}*)
*(.text.${memory.name}*)
</#if>
确保模型数据(如 .model_data 段)被正确保留并放入 XIP 区域。
4.2 模型数据放入XIP区域
将模型通过如下方式放置到 .model_data 段中(C代码):
__attribute__((section(".model_data")))
const unsigned char model_data[] = {
#include "model_data.inc" // 从模型文件导出的原始数据
};
5. 编译与验证 在构建项目后,MCUXpresso IDE 的映像映射报告将显示部分 .text 和 .rodata 段已成功放置至 Octal Flash(OSPI_FLASH)区域,例如: OSPI_FLASH: 100144 B 262140 KB 0.04% 下载运行后,系统从内部Flash启动,并成功从外部Flash中直接读取模型数据,完成AI推理任务。
结语
通过本文介绍的配置步骤,MCXN947成功实现了内部启动 + 外部XIP执行的混合存储方案。该方案既保留了启动速度优势,又显著拓展了程序/模型的可用存储容量,为部署复杂AI模型提供了良好支撑。
对于资源有限的MCU平台而言,这种结构不仅是实用可行的选择,也是未来嵌入式AI应用普遍采用的一种优化思路。
|