详细讲解可执行程序在 Nor Flash 上运行的原理与技术实现
摘要:本文深度解析嵌入式系统中可执行程序直接在 Nor Flash 上运行(XIP, eXecute In Place)的核心原理。从物理特性、硬件映射、编译链接配置到运行时波形分析,全方位图文拆解 XIP 技术实现细节。
1. 技术背景说明
Nor Flash 的物理特性
与 NAND Flash 不同,NOR Flash 的设计初衷就是为了替代 ROM(Read Only Memory),它具备芯片内执行(XIP)的关键特性。
- 并行/串行总线接口:传统的 Parallel NOR Flash 具有独立的地址线和数据线,CPU 可以像访问 SRAM 一样随机读取任意地址的数据。现代的 QSPI/OSPI NOR Flash 通过高频串行总线和 Cache 机制也实现了 XIP。
- NOR 门阵列结构:存储单元采用 NOR 门连接方式,允许对每个存储位进行独立读取,从而支持随机访问(Random Access)。
- 读取时序:其读取操作无需像 NAND 那样先发送复杂的命令序列再等待数据搬移到缓冲区,而是直接通过地址总线寻址。

图 1:Parallel NOR Flash 典型读取时序示意图(Simplified)
2. 核心原理分析
2.1 XIP (eXecute In Place) 技术
XIP 指的是 CPU 直接从存储器(如 Flash)中提取指令并执行,而不需要先将代码拷贝到 RAM 中。
内存映射机制
为了实现 XIP,CPU 的内存控制器(Memory Controller)必须将 Nor Flash 的物理地址映射到 CPU 的统一寻址空间中。

图 2:CPU 统一寻址空间中的 XIP 映射关系
- 硬件层面:SoC 内部的总线矩阵(Bus Matrix)将特定地址段(如
0x08000000)的访问请求直接路由到 Flash 控制器。 - 软件层面:编译器和链接器必须知道代码将被存放在这个特定的物理地址范围内,生成的机器码中的跳转指令(Branch/Jump)必须基于该绝对地址或相对偏移。
2.2 与 NAND Flash 的对比
NAND Flash 设计用于高密度数据存储(如 SSD、U 盘),其物理结构决定了它不支持原生的 XIP。
| 特性 | Nor Flash | NAND Flash |
|---|---|---|
| 寻址方式 | 字节/字寻址(支持随机访问) | 页 (Page) 寻址(需先读到 Buffer) |
| XIP 支持 | 直接支持 | 不支持 (必须 Shadow 到 RAM) |
| 读取速度 | 快 (随机读取 ns 级) | 慢 (首字节延迟 μ\muμs 级) |
| 写入/擦除 | 较慢 | 快 |
| 容量/成本 | 低容量,高成本 | 高容量,低成本 |
| 主要用途 | 代码存储 (Bootloader, OS Kernel) | 数据存储 (文件系统) |
3. 实现细节
要在 Nor Flash 上运行程序,不仅需要硬件支持,还需要工具链的配合。
3.1 编译工具链配置
以 GCC (ARM Cortex-M) 为例,我们需要定义特定的宏并指定链接脚本。
# 编译参数
# -mthumb: 生成 Thumb 指令集(节省空间)
# -D__XIP__: 定义宏,用于代码中区分 XIP 环境(可选)
CFLAGS += -mthumb -mcpu=cortex-m3 -D__XIP__
# 链接参数
# -T: 指定链接脚本
LDFLAGS += -T nor_flash.ld
3.2 链接脚本关键配置
链接脚本 (.ld) 是实现 XIP 的核心。它必须区分加载地址 (LMA) 和 运行地址 (VMA)。对于 XIP 程序:
- 代码段 (.text):LMA = VMA = Flash 地址。
- 数据段 (.data):LMA = Flash 地址(用于持久存储初始值),VMA = RAM 地址(运行时读写)。
/* nor_flash.ld 示例 */
MEMORY {
/* 声明 Flash 区域,属性为只读/可执行 (rx) */
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 512K
/* 声明 SRAM 区域,属性为读写/可执行 (rwx) */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS {
/* 代码段和只读数据直接放在 ROM 中 */
.text : {
. = ALIGN(4);
KEEP(*(.isr_vector)) /* 中断向量表必须在起始位置 */
*(.text*)
*(.rodata*)
} > ROM
/* 已初始化数据段 */
/* AT() 关键字指定加载地址在 ROM 中 */
_sidata = LOADADDR(.data);
.data : {
. = ALIGN(4);
_sdata = .; /* RAM 中的起始地址 */
*(.data*)
_edata = .; /* RAM 中的结束地址 */
} > RAM AT > ROM
/* 未初始化数据段 (BSS) 仅在 RAM 中 */
.bss : {
_sbss = .;
*(.bss*)
*(COMMON)
_ebss = .;
} > RAM
}
4. 运行机制图解
4.1 启动流程
上电复位后,CPU 如何从 Flash 启动并构建 C 语言运行环境?

图 3:XIP 程序的启动与环境初始化流程
- 复位:硬件从 Flash 基地址(如
0x08000000)读取栈指针(SP)和复位中断向量(PC)。 - 执行:CPU 跳转到
Reset_Handler(位于 Flash)。 - 搬移:启动代码(汇编或 C)读取 Flash 中的
.data初始值,写入 SRAM 对应地址。 - 清零:将 SRAM 中的
.bss区域清零。 - 跳转:环境准备就绪,跳转到
main()函数。
4.2 运行时内存访问
在 main() 函数运行过程中,指令取指和数据访问走不同的物理路径。

图 4:运行时指令与数据的总线流向
- 指令流 (I-Code):CPU 通过 I-Bus 直接从 Nor Flash 读取指令。
- 数据流 (D-Code):
- 读写变量:CPU 通过 D-Bus 访问 SRAM。
- 读取常量:CPU 通过 D-Bus 访问 Nor Flash(如字符串常量)。
5. 性能优化策略
由于 Flash 的访问速度(通常 50MHz-100MHz)远低于 CPU 核心频率(可能几百 MHz),XIP 可能会成为性能瓶颈。
- 指令预取 (Prefetch):Flash 控制器通常具备预取 Buffer,在 CPU 处理当前指令时,提前读取后续指令。
- 指令缓存 (I-Cache):开启 CPU 内部的 L1 Cache,大幅减少对 Flash 的实际访问次数。
- 关键代码重定位 (ITCM):将对实时性要求极高的中断服务程序 (ISR) 或核心算法,在启动时从 Flash 拷贝到 SRAM(甚至 TCM 紧耦合内存)中运行,实现“部分 XIP,部分 RAM 运行”。
6. 实际案例演示
STM32 上的 XIP 执行
以 STM32F103 为例,其内部 Flash 即为 Nor Flash。
逻辑分析仪波形模拟:
下图展示了在执行一段 XIP 代码时,Flash 接口(片选 CS#、时钟 CLK、数据 Data)的活动情况。可以看到在 CPU 顺序执行指令时,Flash 总线上呈现出连续的突发读取波形。

图 5:XIP 模式下 Flash 接口的读操作波形(模拟)
7. 注意事项
- 擦写期间无法执行 (RWW 限制):
- 大多数单 Bank 的 Nor Flash 在擦除或写入操作期间,无法进行读取。
- 后果:如果在 XIP 代码中调用了擦除 Flash 的函数,CPU 取下一条指令时会读取失败,导致 HardFault。
- 解决:必须将 Flash 驱动代码(Erase/Program API)放到 RAM 中运行。
- 异常处理:确保异常向量表指向有效的地址。
- 代码加密:XIP 意味着代码明文暴露在 Flash 总线上(对于外置 Flash),容易被逻辑分析仪抓取。需考虑支持 OTFDEC(实时解密)的控制器。
8. 扩展阅读建议
- ARM Cortex-M3/M4 Technical Reference Manual: 关于 Bus Matrix 和 I-Code/D-Code 的详细描述。
- ST Application Note (AN4852): Execute-in-place (XIP) from Quad-SPI Flash memory.
- RT-Thread/Zephyr: 搜索其源码中关于
xip的 ld 文件配置。

1万+

被折叠的 条评论
为什么被折叠?



