多数 SoC 的启动链是:

上电复位 → BootROM(选启动介质+搬运下一阶段到 SRAM)→ SPL/BL2/FSBL(初始化/训练 DDR)→ U-Boot(驱动+加载 kernel/dtb/initrd)→ Linux kernel(初始化+启动用户态)

BootROM 只做最小初始化并把 SPL 从启动介质搬到片内 SRAM 执行;SPL 在 SRAM 中完成 DDR 初始化/训练(并可完善启动介质接口),然后把更大的 U-Boot proper 搬到 DDR 运行。

芯片启动流程解析(按阶段理解)

0. 结论

很多人会把启动简化成“BootROM → U-Boot → Linux”,但在绝大多数现代 SoC 上,真实链路更接近:

上电复位 → BootROM(片内 ROM) → 第一阶段装载器(SPL/FSBL/BL2/Preloader,在 SRAM) → 第二阶段引导器(U-Boot proper/UEFI,在 DDR) → Linux kernel(在 DDR) → 挂载 rootfs(在存储上)

关键点:BootROM 只做“最小启动”并把下一段小程序搬进 SRAM;DDR 初始化/训练通常由中间那段“第一阶段装载器”完成;U-Boot proper 通常是 DDR 可用后才运行的完整引导器。

完整的一般描述

芯片上电并完成电源稳定后,复位释放,CPU 从复位向量(reset vector)开始取指执行。多数 SoC 会首先进入片内固化的 BootROM(Boot ROM code)。BootROM 的职责通常是完成最小化的启动准备:读取启动配置(如 boot mode/strap/efuse),初始化足以读取启动介质所需的最小硬件(如时钟/引脚/启动介质接口的基础配置),并从指定启动介质(SPI NOR/eMMC/SD/NAND/USB/UART 等)读取下一阶段启动镜像,将其加载到片内 SRAM(片上静态 RAM,不是 DDR)并跳转执行。

下一阶段启动程序通常称为 SPL/BL2/FSBL(不同平台命名不同)。它运行在 SRAM 中,负责完成更完整的底层初始化,尤其是外部 DDR 的初始化,使 DDR 具备可靠读写能力。随后,该阶段会把更大、更完整的引导加载器(例如 U-Boot proper,或在安全启动链中把 TF-A/OP-TEE/U-Boot 等后续阶段)加载到 DDR 中并移交控制权。

进入 U-Boot proper 后,系统会建立完整的运行环境(如重定位、清 BSS、设置栈、初始化必要外设与驱动、解析环境变量/启动脚本),并从存储介质加载 Linux 内核镜像、设备树(DTB)(以及可选的 initrd)到 DDR 的合适内存区域,设置内核启动参数,然后跳转到内核入口地址。自此 Linux 内核接管 CPU,完成内核初始化并启动用户空间(init/systemd 等),系统进入正常运行。


1. 启动涉及的存储区域和加载动作

四个关键存储区域

  1. 片内 ROM:BootROM 固化在这里,CPU 复位后最先取指
  2. 片内 SRAM(或 Cache-as-RAM):启动早期可用的 RAM(栈/临时数据/小程序运行地),不是 DDR
  3. 外部非易失存储:SPI NOR / eMMC / SD / NAND / UFS / USB 等,保存各阶段固件/镜像
  4. 外部 DDR:容量大,但必须先初始化/训练才可靠可用

三类关键加载动作

  • 执行(取指):CPU 从 PC 指向的地址空间取指执行
  • 加载/搬运(copy/load):把二进制从存储读出,复制到 SRAM 或 DDR
  • 移交(jump/handoff):跳转到已加载镜像入口,让它开始执行

所谓“加载某段代码”,本质就是:读出 → 放到可执行的内存地址 → 跳过去。


2. 分阶段详解:谁加载谁、从哪到哪、在哪执行

Stage 0:上电复位 → BootROM 执行(片内 ROM)

  • 代码来源:芯片内部固化的 BootROM,原厂固化的
  • 执行位置:片内 ROM(取指)
  • BootROM做什么(通常情况)
    1. 读取启动配置(boot mode/strap/efuse)
    2. 初始化“足够读取启动介质”的最小硬件(例如基础时钟/引脚/存储控制器最小读能力)
    3. 从指定启动介质中读取“下一阶段启动镜像”(第一阶段装载器)
    4. 把它加载到片内 SRAM(或 Cache-as-RAM)并跳转执行

注意:BootROM 通常很小、不可更新,因此通常不承担复杂的 DDR 训练(当然少数平台例外,但不是通用规律)。


Stage 1:第一阶段装载器(SPL/FSBL/BL2/Preloader)执行(片内 SRAM)

这是容易忽略、但极关键的“中间层”。

  • 常见名字(不同生态叫法不同,本质相同):
    • U-Boot 体系:TPL / SPL
    • 厂商体系:FSBL / Preloader
    • TF-A 体系里常见:BL2(作为可信装载阶段之一)
  • 代码来源:外部存储(flash/eMMC/SD…),由 BootROM 读出
  • 执行位置:通常在 片内 SRAM(因为 DDR 还不可用)
  • 核心任务(最典型、最通用)
    1. 初始化/训练 DDR(DDR bring-up)
    2. 可能会把启动介质接口配置得更完善/更高速(例如切换更高频率/更宽总线)以便读取更大的后续镜像
    3. 从外部存储读取“更大、更完整的下一阶段”(第二阶段引导器/安全固件等)
    4. 把后续镜像加载到 DDR,并跳转移交

可以把这一层理解成:“让 DDR 第一次真正可用”的那段小程序。没有它,后续大的 U-Boot/UEFI 往往没法运行。


Stage 2:第二阶段引导器(U-Boot proper / UEFI / BL33)执行(DDR)

  • 代码来源:通常由 Stage 1 从外部存储读出并放入 DDR
  • 执行位置DDR
  • 主要任务
    1. 建立更完整的运行环境(重定位、清 BSS、栈/全局数据等)
    2. 初始化更多外设与驱动(存储、串口、网络、USB…)
    3. 选择启动配置(环境变量/脚本/extlinux 等)
    4. 从外部存储加载 Linux 启动三件套到 DDR:
      • KernelImage(ARM64) / zImage(ARM32) / uImage(旧格式)
      • DTB*.dtb
      • initrd/initramfs(可选)
    5. 设置内核启动参数并跳转到内核入口(booti/bootz/bootm

Stage 3:Linux 内核执行(DDR)→ 挂载 rootfs(存储/网络)

  • 内核执行位置:DDR(可能先解压/重定位到 DDR 的另一段区域)
  • 内核做什么
    1. 建立内存管理(MMU)、初始化调度/中断/驱动子系统等
    2. 使用 DTB 识别硬件并加载驱动
    3. 挂载根文件系统(rootfs)并启动用户态(init/systemd)

重要区分:

  • U-Boot 一般只把 kernel/dtb/initrd 搬进 DDR
  • rootfs 通常不是被 U-Boot 整体搬进 DDR,而是由内核去挂载存储上的文件系统(除非走纯 initramfs 的“全内存根”模式)。

3. 安全启动(Secure Boot)

如果启用安全启动,启动链条中会多一个动作:“验证后再加载/跳转”。通常表现为:

  • BootROM 或早期装载器利用 efuse/OTP 中的根信任信息
  • 对下一阶段镜像做签名/哈希校验
  • 校验通过才允许继续执行下一阶段
    这不会改变“ROM→SRAM→DDR→Kernel”的基本结构,只是每一跳更严格。

4. 常见“镜像文件”逐个对号入座

不同平台文件名不同,但功能角色可以按下表映射:

4.1 第一阶段装载器(SRAM 里跑)

  • 可能文件名:spl.bin / u-boot-spl.bin / tpl.bin / fsbl.bin / preloader.bin / bl2.bin / idbloader.img(厂商差异)
  • 谁加载:BootROM
  • 加载到:SRAM
  • 做什么:DDR 初始化/训练,加载第二阶段到 DDR

4.2 第二阶段引导器(DDR 里跑)

  • 可能文件名:u-boot.bin / u-boot.img / u-boot.itb(FIT 打包)
  • 谁加载:第一阶段装载器(或配合 TF-A 链路)
  • 加载到:DDR
  • 做什么:加载 kernel/dtb/initrd,并跳内核

4.3 Linux 三件套(被 U-Boot 放进 DDR)

  • Image / zImage / uImage(kernel)
  • *.dtb(设备树)
  • initrd / initramfs.img(可选的)

4.4 RootFS(通常由内核挂载,而非 U-Boot 搬运)

  • rootfs.ext4 / rootfs.squashfs / rootfs.ubi / rootfs.ubifs

5. 总结

CPU 复位后执行片内 BootROM;BootROM 依据启动模式从外部存储读取第一阶段装载器并放入片内 SRAM 运行;第一阶段装载器在 SRAM 中完成 DDR 初始化/训练后,把更大的第二阶段引导器(如 U-Boot proper/或配套安全固件)加载到 DDR 并移交;第二阶段引导器从存储加载 kernel+dtb(+initrd) 到 DDR 并跳转到内核;内核在 DDR 中初始化系统并挂载存储上的 rootfs,启动用户态进入正常运行。


芯片启动流程解析(按照上电启动时间线理解)

CPU 在哪里取指(PC/执行位置)**、栈在哪里数据缓冲在哪里、**是谁/通过哪个控制器把代码从哪搬到哪

约定:

  • ROM = 片内 BootROM(不可写)
  • SRAM = 片内静态 RAM(可写,容量小)
  • DDR = 外部内存(需初始化后才可靠)
  • Boot Device = 启动介质(SPI NOR / eMMC / SD / NAND / USB…)

T0:电源稳定、复位释放

  • CPU 取指地址:还没开始“正常执行”(仍在 reset / 等待复位释放)
  • :无
  • 缓冲:无
  • 加载控制器:无
  • 发生了什么:电源/时钟域逐步稳定,复位信号被解除,CPU 准备从 reset vector 取第一条指令

T1:CPU 执行第一条指令(进入 BootROM)

  • CPU 取指(PC):指向片内 ROM 的 reset vector(固定地址,厂商设计决定)
  • :通常马上设置到 SRAM 的某个顶端(BootROM 会挑一段 SRAM 当栈)
  • 缓冲:在 SRAM(BootROM 需要可写空间做读写/校验缓冲)
  • 加载控制器:暂无(还在初始化阶段)
  • 发生了什么:BootROM 开始运行,做最基础的硬件 bring-up(最小化)

画面:CPU 在 ROM 里跑,但“写东西”都写到 SRAM(因为 ROM 不能写)。


T2:BootROM 读取启动模式,决定启动介质

  • CPU 取指:仍在 ROM
  • 栈/缓冲:仍在 SRAM
  • 加载控制器:开始触碰“启动选择相关逻辑”
  • 发生了什么:BootROM 读取 strap 引脚/efuse/寄存器,确定从哪启动:SPI/eMMC/SD/NAND/USB/UART…

T3:BootROM 初始化“启动介质控制器”的最小读能力

这一步很关键:BootROM 不会把所有外设都初始化,只初始化“能读出下一段代码”的那一点点。

  • CPU 取指:ROM
  • :SRAM
  • 缓冲:SRAM(读出来的数据先落这里)
  • 加载控制器:取决于启动介质
    • SPI 启动:初始化 SPI/QSPI 控制器(能发读命令、基本时序)
    • eMMC/SD 启动:初始化 SDHCI/eMMC 控制器(能读固定扇区)
    • NAND 启动:初始化 NAND 控制器 + 基础 ECC/坏块处理(最小)
    • USB/UART:初始化对应 USB device/UART 下载控制逻辑
  • 发生了什么:控制器刚“能读”就行,不追求性能/完整协议栈

T4:BootROM 从启动介质读取“第一阶段装载器”到 SRAM

  • CPU 取指:ROM(BootROM 代码)
  • :SRAM
  • 缓冲:SRAM(读入缓冲 + 校验缓冲)
  • 加载发生在哪个控制器:就是选中的启动介质控制器
    • 例如:SPI 控制器发读 → 数据进片内缓冲/通过总线写入 SRAM
    • 例如:eMMC 控制器 DMA/PIO 读扇区 → 写入 SRAM
  • 搬运路径(概念化)
    Boot Device(外部存储) →(启动介质控制器)→ SRAM
  • 发生了什么:BootROM 把 SPL/FSBL/BL2 这段“小程序”装进 SRAM

这一步就是之前理解的“搬运到 SRAM”。


T5:BootROM(可选)校验/解密第一阶段装载器,然后跳转

  • CPU 取指:ROM
  • 栈/缓冲:SRAM
  • 加载控制器:同上
  • 发生了什么
    • 若有 secure boot:BootROM 在 SRAM 缓冲区里做签名/哈希校验,或解密
    • 然后设置入口地址 → 跳转到 SRAM 中的第一阶段装载器入口
  • 控制权移交:PC 改为 SRAM 中的入口地址

T6:第一阶段装载器开始执行(SPL/FSBL/BL2 在 SRAM)

  • CPU 取指SRAM(现在 CPU 真正在 SRAM 里跑)
  • :仍在 SRAM(通常换一个更合适的栈位置)
  • 缓冲:SRAM(继续做临时缓冲)
  • 加载控制器:可能重新初始化时钟/PLL、复用引脚等,为后续 DDR/高速读做准备
  • 发生了什么:进入“可更新代码”阶段,开始干 DDR bring-up 这种板级强相关的事

T7:第一阶段装载器初始化/训练 DDR(关键转折点)

  • CPU 取指:SRAM(DDR 未可靠前必须如此)
  • :SRAM
  • 缓冲:SRAM(训练数据、临时表格)
  • 控制器DDR 控制器 + DDR PHY
  • 发生了什么
    • 配置 DDR 控制器寄存器
    • DDR PHY 初始化/训练(读写延迟、校准等)
    • 做内存测试/训练收敛(平台差异大)

这一段为什么不放 BootROM:复杂、板级差异大、需要更新迭代。


T8:DDR 可用后,第一阶段装载器把“工作区/堆/缓冲”迁到 DDR(常见做法)

  • CPU 取指:仍在 SRAM(此刻还没跳走)
  • :可能开始切到 DDR(不少实现会把栈迁到 DDR,方便做大搬运)
  • 缓冲:逐渐迁到 DDR(因为 DDR 大)
  • 控制器:仍会用启动介质控制器(SPI/eMMC/NAND…)
  • 发生了什么:为加载“大块的第二阶段引导器”做准备(搬运更快、缓冲更大)

T9:第一阶段装载器从启动介质加载第二阶段引导器到 DDR

  • CPU 取指:SRAM(执行搬运代码)
  • :常见是 DDR(也可能仍 SRAM)
  • 缓冲:DDR(大缓冲)
  • 加载发生在哪个控制器:启动介质控制器(SPI/eMMC/NAND/USB…)
  • 搬运路径
    Boot Device →(存储控制器)→ DDR
  • 载荷通常是什么
    • U-Boot proper
    • (可选)TF-A BL31 / OP-TEE 等安全固件
    • 或者一个 FIT 包(*.itb)里面打包了以上多个组件

T10:跳转到第二阶段引导器(U-Boot proper 在 DDR)

  • CPU 取指DDR(PC 指向 DDR 的 U-Boot 入口)
  • :DDR
  • 缓冲:DDR
  • 控制器:U-Boot 开始初始化更多驱动(串口、存储、网络、USB…)
  • 发生了什么:现在进入“功能完整的引导器环境”(可交互、可脚本化)

T11:U-Boot 加载 Linux 启动三件套到 DDR

  • CPU 取指:DDR(U-Boot 代码)
  • :DDR
  • 缓冲:DDR(文件读缓冲、解压缓冲)
  • 加载发生在哪个控制器:取决于启动方式
    • 从 eMMC/SD 分区读:eMMC/SD 控制器
    • 从 SPI NOR 读:SPI/QSPI 控制器(可能 XIP/也可能 copy)
    • 从 NAND/UBI 读:NAND 控制器 + UBI 层
    • 从网络 TFTP/NFS:网卡 MAC/PHY
  • 搬运路径
    Boot Device/Network →(对应控制器/驱动)→ DDR
  • U-Boot 放到 DDR 的东西
    • Image/zImage/uImage(kernel)
    • *.dtb(设备树)
    • initrd/initramfs(可选)

T12:U-Boot 设置启动参数并跳转内核

  • CPU 取指:DDR(U-Boot)
  • 栈/缓冲:DDR
  • 发生了什么
    • 准备寄存器状态/CPU 模式
    • 传递 DTB 地址、initrd 地址、bootargs
    • 执行 booti/bootz/bootm → 跳到内核入口

T13:Linux 内核执行并完成自身初始化

  • CPU 取指:DDR(内核入口)
  • :内核会切换到自己的栈(仍在 DDR)
  • 缓冲:内核管理的内存(DDR)
  • 控制器:内核逐步初始化各类驱动与子系统
  • 发生了什么
    • 建立 MMU、页表
    • 解压(如果是压缩内核)
    • 解析 DTB、探测设备

T14:内核挂载 rootfs 并启动用户态

  • CPU 取指:DDR
  • rootfs 在哪
    • 常见:仍在 eMMC/SD/NAND 上,由内核挂载
    • 或者:initramfs(rootfs 已在 DDR 内存中)
  • 发生了什么:启动 init/systemd,系统进入正常运行

T15:总结

  1. BootROM 在 ROM 取指,但栈/缓冲必须在 SRAM(ROM 不可写)
  2. SPL 在 SRAM 取指,因为它要把 DDR 拉起来(DDR 未就绪前不能依赖 DDR)
  3. U-Boot proper 在 DDR 取指,因为它大、功能多
  4. 任何“加载”都可视为:
    启动介质 →(对应控制器)→ SRAM/DDR,然后跳转

芯片启动流程解析(按不同阶段使用的固件名称理解)

启动过程按照上述说明的流程

BootROM(片内 ROM)
→ 加载 第一阶段装载器(SPL/FSBL/BL2/Preloader 等,小)SRAM 执行
→ 第一阶段初始化 DDR
→ 加载 第二阶段引导器(U-Boot proper/UEFI 等,大)DDR 执行
→ 第二阶段加载 Kernel + DTB (+ initrd)DDR
→ 跳转 Linux 内核
→ 内核挂载 rootfs(存储上的文件系统/分区)并启动用户态

关键:最终看到的“镜像文件”,本质就是这条链路里每一跳要用到的“载荷”。


1) 常见文件名 → 属于哪一阶段 → 跑在哪 → 谁加载谁

说明:下面是“名字族谱”,项目里文件名可能不同,但功能位置一致。

A. BootROM 相关(一般拿不到文件)

常见名字 属于哪一阶段 代码在哪里跑 谁加载它
BootROM / iROM / BROM / PBL 阶段0(固化 ROM) 片内 ROM(取指)+ 少量 SRAM 作栈 没人加载,复位后直接执行

B. 第一阶段装载器

常见名字(生态差异很大) 属于哪一阶段 典型运行内存 谁把它从哪加载到哪
spl.bin / u-boot-spl.bin / u-boot-tpl.bin 阶段1(SPL/TPL) SRAM(或 Cache-as-RAM) BootROM:从 SPI/eMMC/SD/NAND 读出 → 放到 SRAM → 跳转
fsbl.bin / bl2.bin / preloader.bin / idbloader.img(厂商叫法) 阶段1(FSBL/BL2/Preloader) SRAM(或少量片内 RAM) BootROM:从启动介质读出 → 放到 SRAM → 跳转

这层通常做什么:

  • DDR 初始化/训练(最关键)
  • 把启动介质接口配置得更完整/更快(可选)
  • 把后续更大的引导器(U-Boot proper/TF-A/UEFI)搬到 DDR

C. 安全固件(可选,但越来越常见,尤其 ARMv8)

常见名字 属于哪一阶段 典型运行内存 谁加载它
bl31.bin(TF-A)、atf.bin 常驻固件(EL3) Secure SRAM/secure DRAM(平台相关) 一般由 SPL 或 U-Boot/FIT 一起被加载到 DDR/安全内存后启动
tee.bin / optee.bin 常驻固件(TEE) Secure DRAM 同上

可以把它理解成:SPL 拉起 DDR 后,会把“安全世界固件 + U-Boot”一起安排好,然后再进入 U-Boot(非安全世界)。


D. 第二阶段引导器(“U-Boot”通常指它)

常见名字 属于哪一阶段 典型运行内存 谁加载它
u-boot.bin / u-boot.img / u-boot-nodtb.bin / u-boot-dtb.bin 阶段2(U-Boot proper) DDR SPL/FSBL:从启动介质读出 → 放到 DDR → 跳转
u-boot.itb(FIT 打包) 阶段2(把多个东西打成一包) DDR(解包后各自放置) SPL/FSBL:读到 DDR → U-Boot/或 SPL 解包/加载其中的组件

U-Boot proper 负责:

  • 初始化更多外设(存储、网络、USB…)
  • 选择启动配置(环境变量、脚本、extlinux)
  • 加载 kernel/dtb/initrd
  • 跳转内核

E. Linux 启动三件套

常见名字 属于哪一阶段 放在哪 谁加载它
Image(ARM64)、zImage(ARM32)、uImage(老式 U-Boot 格式) 阶段3:内核 DDR U-Boot 从存储读出 → 放 DDR
*.dtb(设备树) 阶段3:内核参数 DDR U-Boot 从存储读出 → 放 DDR
initrd / initramfs.img / ramdisk.img(可选) 阶段3:早期根文件系统 DDR U-Boot 从存储读出 → 放 DDR(或编进内核)

注意:DTB 不是“驱动”,它是内核描述硬件的配置数据;没有 DTB 的 ARM Linux 通常起不来


F. RootFS(真正的系统文件)

常见名字 用途 在哪 谁“加载/使用”它
rootfs.ext4 / rootfs.squashfs / rootfs.ubifs / rootfs.ubi 根文件系统 eMMC/SD 分区、NAND UBI 卷、NOR 分区等 Linux 内核挂载(不是 U-Boot 把它整体搬进 DDR)
modules.tar.gz / /lib/modules/* 内核模块 rootfs 里 内核/用户态按需加载

通用规律:U-Boot 负责把“内核三件套”放进 DDR;rootfs 通常不整体搬进 DDR,而是内核去挂载存储上的文件系统。


2) “一个工程里通常有哪些文件”——按两种最常见打包方式分别对号

方式 I:裸文件 + 分区/偏移

可能会看到类似这些文件:

  • spl.bin / u-boot-spl.bin
  • u-boot.bin
  • ImagezImage
  • board.dtb
  • rootfs.ext4rootfs.ubi

加载关系就是:

  1. BootROM:从启动介质读取 spl.bin → 放 SRAM → 跳 spl
  2. SPL:初始化 DDR → 读 u-boot.bin → 放 DDR → 跳 U-Boot
  3. U-Boot:读 Image + *.dtb (+ initrd) → 放 DDR → booti/bootz/bootm
  4. Kernel:挂载 rootfs 分区/卷 → 起用户态

方式 II:FIT

把很多东西打包成一个 *.itb,可能会看到:

  • spl.bin
  • u-boot.itb(里面可能包含:U-Boot、ATF BL31、OP-TEE、甚至 kernel/dtb)

加载关系还是同一套,只是“文件数减少”:

  1. BootROM:加载 spl.bin 到 SRAM
  2. SPL:初始化 DDR → 加载 u-boot.itb 到 DDR → 解包/放置各组件 → 跳转到 U-Boot/或先到 BL31
  3. U-Boot:从 FIT 或外部再加载 kernel/dtb/initrd → 跳内核

3) 两个定位问题的判断口诀

口诀 A:看“它必须在哪跑”

  • DDR 没起来之前:只能在 ROM/SRAM(或 Cache-as-RAM)
    ⇒ 所以“负责 DDR 初始化”的那段,几乎必然是 SPL/FSBL/BL2 这一层
  • U-Boot proper 大概率在 DDR 跑(体积大、功能多)

口诀 B:看“谁需要谁先活着”

  • U-Boot proper 要正常工作通常依赖 DDR ⇒ 它不太可能负责“让 DDR 第一次可用”
  • BootROM 体积小且固化 ⇒ 它倾向于只把“可更新的小程序”拉起来

4) 总结

  • 启动介质:SPI NOR / eMMC / SD / NAND / …
  • ROM 阶段:BootROM(固化)
  • 第一阶段装载器(SRAM)______(SPL/FSBL/BL2/Preloader)
    • 负责:DDR init/训练;加载下一阶段到 DDR
  • 第二阶段引导器(DDR)______(U-Boot proper/UEFI)
    • 负责:加载 Kernel(______) + DTB(______) + initrd(______) 到 DDR 并跳转
  • 内核与系统
    • Kernel:______(Image/zImage/uImage)
    • DTB:______
    • RootFS:______(ext4/squashfs/ubi/ubifs),由内核挂载

问题:为什么不一上电就直接在 SRAM/DDR 跑?为什么还要有“片内 ROM 执行 BootROM”

核心原因是:上电瞬间“可用的东西非常少”,必须先有一段“永远可用、不可被破坏、无需外部条件”的代码把系统带到“可运行”的状态。

1) 片内 ROM(BootROM)为什么存在:为了“永远能启动”

上电复位后,系统面临的问题是:

  • 外部 DDR 完全不可用(没初始化/没训练)
  • 外部存储(eMMC/SD/SPI/NAND…)也未必可读(控制器时钟、引脚复用、时序、校验、坏块、ECC 等都可能没配)
  • 甚至时钟源/电源域/复位域都刚刚稳定

这时你需要一段代码满足三个条件:

  1. 无需任何外部存储就存在(否则鸡生蛋:要读代码得先有代码)
  2. 可靠且不可被刷坏(否则刷错固件就“变砖”)
  3. 能在极简硬件条件下跑起来(不依赖 DDR、不依赖复杂驱动)

所以厂商把这段“最小起步代码”固化在片内 ROM

  • ROM 天生“上电即在”、不可擦写、稳定
  • 负责把 SoC 从“刚醒”带到“能加载下一段程序”

这就是 BootROM 的意义:保证最差情况下也能有路可走(包括救砖模式:UART/USB 下载等)。


2) 片内 SRAM为什么要用:因为“ROM不能写 + 需要栈/数据区”

你可能会问:那 BootROM 既然在 ROM 里执行,为什么不能一直在 ROM 里把所有事做完?

原因是 ROM 的天然限制:

  • ROM 不能写,而程序运行需要写入内存:
    • 栈(函数调用、局部变量)
    • BSS/全局数据
    • 缓冲区(读外部存储时得有 buffer)
    • 解密/解压/校验也需要工作区

因此 BootROM 执行时,必须找一个可写的地方当“工作台”。
最早能用、最可靠的可写内存就是:片内 SRAM(或 Cache-as-RAM)

所以 BootROM 的典型行为是:

  • 自己在 ROM 里取指执行
  • 把栈/缓冲区放在 SRAM
  • 从外部存储读出“第一阶段装载器”到 SRAM
  • 然后跳转到第一阶段装载器继续执行

3) 为什么“第一阶段装载器”也要在 SRAM跑:因为 DDR还没起来

你已经掌握了最关键点:DDR 必须初始化/训练之后才能可靠使用
那“初始化 DDR”的代码必须运行在哪?

  • 不能运行在 DDR(因为 DDR 还不能用)
  • ROM 很小、不够放复杂 DDR 初始化/训练逻辑,而且 ROM 不可更新(DDR 参数/板级差异很大,ROM 做不通用)
  • 最自然的选择就是:让第一阶段装载器在 SRAM 里跑,专门把 DDR 拉起来

这就是 SPL/FSBL/BL2 的存在意义:

用一段小程序(适配板级参数、可更新)在 SRAM 里把 DDR bring-up,然后把大程序搬到 DDR。


4) 为什么不直接把“第一阶段装载器”也做进 ROM,省事?

主要是四个工程原因:

(1) ROM空间非常宝贵

ROM 做大了:

  • 芯片面积/成本上升
  • 验证周期更长,Bug 代价巨大(ROM 修不了)

所以 ROM 通常只放“最小功能集”。

(2) 板级差异太大,ROM无法通吃

DDR 参数(走线、颗粒型号、频率、时序、训练策略)强依赖具体板子。
ROM 如果把 DDR 初始化写死,换板/换颗粒就麻烦了。

把 DDR init 放在可更新的 SPL 里更合理。

(3) 安全与救砖

ROM 固定+最小化,容易做:

  • 安全启动链根信任
  • 失败回退到 USB/UART 下载(maskrom 模式等)
  • 不会因为刷坏外部存储就彻底没法启动

(4) 可维护性

第一阶段装载器可以升级(修 DDR 兼容、提速、修 bug、加新存储支持),ROM 不行。


5) 可以用一个非常形象的比喻记住它

把 SoC 启动当成“搭舞台”:

  • BootROM(ROM):剧院自带的“应急照明和消防通道”,永远在、永远可靠,保证你能进门
  • SRAM:舞台边上的“小工作台”,空间小但马上能用,用来搭起大舞台(DDR)
  • DDR:真正的大舞台,空间大,灯光音响全在这儿,但得先把它搭好、调好才能演出
  • U-Boot proper / Linux:正式演出的大团队,必须在大舞台(DDR)上才能展开

6) 总结为什么要分 ROM 和 SRAM 执行”

  • ROM:为了“上电必有代码可执行、不可被刷坏、无需外部条件”
  • SRAM:为了“提供可写工作区(栈/缓冲/数据),并在 DDR 未就绪前承载可更新的早期装载器去初始化 DDR”

参考:

很多描述不对:

  1. https://zhuanlan.zhihu.com/p/611059923?share_code=SsKaAPHNMpxo&utm_psn=1991613055549849848

  2. https://blog.csdn.net/qq_43467135/article/details/137133759

  3. https://schandupatla.medium.com/u-boot-spl-vs-u-boot-proper-002187445ee0

  4. 参考GPT输出答案