1、XIP概述

各位,我们直接进入正题。XIP,全称 Execute In Place,中文叫“原地执行”或“就地执行”。

这名字听着有点玄乎?别急。说白了,它解决的是一个很实际的问题:CPU 能不能直接在 Flash 或者 ROM 上把程序跑起来,而不是非得把代码先搬到 RAM 里再执行。

我个人第一次接触 XIP,是在一个工业 SoC 项目里。当时硬件工程师跟我说:“这块 Flash 可以直接取指。”我第一反应是——这能行吗?Flash 那么慢,CPU 不得饿死?

结果还真行。这就是 XIP 的魅力。

1.1 什么是 XIP

传统模式下,CPU 执行代码的路径是这样的:

  1. 上电后,Boot ROM 中的一段小程序把 Flash 里的代码复制到 RAM。
  2. 复制完成后,CPU 跳转到 RAM 里开始执行。
  3. 后面的所有指令和数据都从 RAM 里取。

这个流程有个很明显的问题:你得等。等那几百 KB、甚至几 MB 的代码从慢速 Flash 搬到快速 RAM。启动时间就这么被拖长了。

XIP 的思路是:把 Flash 或 ROM 直接映射到 CPU 的地址空间里。CPU 发出取指请求,地址直接命中 Flash 里的某个位置,数据就回来了。不用搬,当场执行。

你想想看,这不就省了一步吗?

核心概念:XIP 让 CPU 能从非易失性存储器(NOR Flash、ROM)直接取指令并执行,无需预先将代码加载到 RAM。这是通过将存储器的物理地址直接映射到系统地址空间实现的。

不过这里有个陷阱要注意。我说的“直接取指”,指的是指令。数据读写呢?那得另说。XIP 主要解决的是代码执行的问题,数据段通常还是放在 RAM 里。这个后面几章会详细讲,先记住这个区分就好。

1.2 应用场景

XIP 不是万金油,它有几个很明确的主战场。

场景 为什么用 XIP 我的经验
嵌入式系统启动 减少启动时间。有些设备要求上电后几百毫秒内就要干活。 我在一个工业控制器项目里,用 XIP 把启动时间从 1.2 秒压到了 300 毫秒。
SoC 内部 Boot ROM Boot ROM 是只读的,固件写死在芯片里。CPU 直接在里面跑初始化代码。 几乎所有的 ARM Cortex-M 系列芯片都这么干,算是最典型的例子。
低功耗设备 省掉一块大容量 RAM?那功耗和成本都能降。IoT 设备很喜欢这个。 有个客户做智能传感器,电池要用三年。我把代码放到 NOR Flash 里 XIP 执行,省掉了那颗 8MB 的 PSRAM。
实时系统 有些系统要求确定性延迟。从 Flash 直接取指的时间是固定的,比依赖缓存命中率的 RAM 更可预测。 嗯,前提是 Flash 读时序要稳定。这个跟 Flash 控制器设计有关。

1.3 XIP 的核心优势

好,我们总结一下,为什么你要考虑 XIP。我建议你记住这几点:

  • 秒级启动成为可能:不用搬代码,上电就能开始执行。特别是冷启动,效果立竿见影。
  • 节省 RAM 空间:代码段不用占 RAM 了。省下来的 RAM 可以给数据、堆栈,或者干脆去掉一部分 RAM 颗粒,降成本。
  • 系统更简单:少了一个“复制代码”的步骤。Bootloader 的复杂度也跟着降了。
  • 运行时切换固件:在一些设计中,更新 Flash 里的代码后,可以直接跳转到新地址执行,不需要重启系统。这个特性在 OTA 升级里很好用。

当然,优势背后都有代价。最直接的问题是:Flash 速度跟不上 CPU 怎么办?

这个问题我会在后面的课程里用一整章来讲。现在你只需要知道,业界有几种解决思路:缓存加速、预取机制、多 Bank 交替。这些都是 XIP 体系结构里绕不开的话题。

一个习惯:我个人在评估一个系统是否适合 XIP 时,会先看它的 代码密度 和 Flash 读延迟。代码密度高的(比如 Thumb 指令集),Flash 带宽利用率就好。Flash 读延迟超过 50ns 的,建议配上 I-Cache,不然 CPU 真的会饿肚子。

1.4 XIP 的基本工作流程

为了让概念更直观,我画了一张图,展示 XIP 下 CPU 是如何拿到指令的。

别小看这张图。地址映射、总线仲裁、Flash 控制器的时序,这几个点会在后面的章节里反复出现。现在你有个整体印象就好。

XIP 地址映射与取指流程示意图 XIP 地址映射与取指流程 CPU 核心 发出取指地址 地址 0x6000_0000 SoC 内存控制器 地址解码与映射 映射到 Flash 片选 NOR Flash 存储指令代码 指令数据返回(通过数据总线) 地址空间映射示意 0x0000_0000 - 0x1FFF_FFFF : DDR RAM (数据段、堆栈) 0x6000_0000 - 0x6FFF_FFFF : NOR Flash (代码段,直接取指) 0xE000_0000 - 0xE00F_FFFF : 外设寄存器

看懂这张图了吗?流程是这样的:

  1. CPU 发出一个取指地址,比如 0x6000_0000。
  2. SoC 内存控制器拿到地址,做地址解码。发现这个地址落在 Flash 的映射区间。
  3. 控制器选中 NOR Flash,发出读命令。
  4. Flash 把对应位置的指令数据吐出来,沿着数据总线返回给 CPU。
  5. CPU 拿到指令,直接执行。

整个过程,没有经过 DDR 或者 SRAM。这就是 XIP 的核心路径。

一个容易踩的坑:我曾经在一个项目里,把 Flash 的访问地址写错了,映射到了 DDR 的保留区域。结果 CPU 一取指就访问了空洞,直接 Hard Fault。排查了好几个小时才意识到是地址映射的问题。嗯,所以做 XIP 地址映射时,一定要对照 datasheet 确认地址窗口,别想当然。

1.5 你需要在芯片层面关注什么

如果你的工作是做 SoC 集成或者嵌入式软件,那对于 XIP,你需要关注这几个点:

  • Flash 接口的时序参数:读延迟是几 cycles?能不能调?
  • 地址映射寄存器:把 Flash 的基地址配到哪个区间?这个通常由 memory map 决定。
  • 缓存和预取:有没有 I-Cache?有没有预取 Buffer?这两个对 XIP 性能影响非常大。
  • 总线仲裁:如果 Flash 同时被 DMA 和 CPU 访问,会不会冲突?

这些问题,后面每一章都会展开。这一章我们先打个地基。