4. U-Boot移植与启动流程

在 TI Sitara(如 AM335x、AM437x、AM57x)这类工业 Linux 平台上,U-Boot 并不是一个“上电就直接运行”的单一程序,而是由芯片内部固化的 ROM Code 逐级引导出来的。理解这条启动链路,是后续裁剪 SPL、配置 DDR 参数、切换网络启动与存储启动的前提。本节以 AM335x 为例展开,其他 Sitara 器件的阶段划分类似,只是镜像格式与偏移地址不同。

一、Sitara 启动链路:从 ROM Code 到内核

AM335x 上电后,CPU 首先执行芯片内部 ROM 中的固化代码(ROM Code)。ROM Code 会按 启动顺序 依次探测各启动介质:先检查 SYSBOOT 引脚决定的启动模式列表,例如 MMC0、MMC1、NAND、SPI、UART、USB 等。找到可引导介质后,ROM Code 从该介质的固定位置读取一个很小的头部(TI 称为 boot header,包含镜像大小、加载地址等信息),然后把紧随其后的 SPL(Secondary Program Loader,二级程序加载器) 搬入芯片内部的 SRAM(AM335x 约 128 KB)并跳转执行。

SPL 运行在 SRAM 中,空间极其有限,因此它只做最关键的几件事:初始化时钟与 DDR 控制器、配置引脚复用(PinMux)、把完整的 U-Boot 从启动介质搬到 DDR 中,最后跳转到 U-Boot 的入口。U-Boot 运行在 DDR 中,具备完整驱动与命令环境,负责加载内核、设备树与根文件系统,最终把控制权交给 Linux。整条链路可概括为:ROM Code → SPL → U-Boot → Linux Kernel。

Sitara(AM335x)启动链路与介质映射 ROM Code 芯片内部固化 按 SYSBOOT 探测介质 SPL 运行于内部 SRAM 初始化 DDR / PinMux U-Boot 运行于 DDR 完整驱动与命令环境 Linux Kernel + DTB + RootFS 启动介质(由 SYSBOOT 引脚选择) MMC0 / SD raw 偏移 0x20000 NAND 页对齐 + ECC SPI NOR boot header 引导 UART / USB 串口/DFU 下载 网络启动(TFTP) U-Boot 阶段加载 关键约束:SPL 必须能放进内部 SRAM(AM335x 约 128 KB),因此 SPL 只保留 DDR 初始化、介质读取与跳转逻辑; U-Boot 运行在 DDR 中,可加载内核、设备树与根文件系统,并支持网络启动与存储启动两种方式。 启动介质顺序由 SYSBOOT[4:0] 引脚决定,硬件设计阶段即固定,软件无法在运行时更改。

二、SPL 与 U-Boot 的配置要点

在 U-Boot 源码中,Sitara 平台的配置通常通过 include/configs/am335x_evm.h 与设备树 arch/arm/dts/am335x-*.dts 共同完成。SPL 阶段最关键的配置是 DDR 参数与启动介质。以 AM335x 为例,DDR 初始化由 board/ti/am335x/board.c 中的 sdram_init() 完成,参数来自 EMIF 寄存器配置,必须与硬件上实际使用的 DDR 颗粒(容量、位宽、时序)匹配,否则 SPL 会在 DDR 初始化后挂死,串口只打印到 SPL: 便无输出。

编译时通常使用 am335x_evm_defconfig,再通过 make menuconfig 调整。需要重点确认的选项包括:CONFIG_SPL=y、CONFIG_SPL_MMC_SUPPORT、CONFIG_SPL_NAND_SUPPORT、CONFIG_SPL_SERIAL_SUPPORT,以及 CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR(U-Boot 在 MMC 中的扇区偏移)。若使用 NAND,还需配置 CONFIG_SPL_NAND_BASE 与 ECC 模式。编译产物中,MLO 即 SPL 镜像,u-boot.img 为完整 U-Boot,二者需按介质要求分别烧写。

# 典型编译流程(示例)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am335x_evm_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
# 产物:MLO(SPL)、u-boot.img(U-Boot)、u-boot.dtb

三、环境变量:启动行为的“软配置”

U-Boot 的环境变量保存在 env 分区或 uEnv.txt 中,是切换启动方式最灵活的手段。常用变量包括 bootcmd(自动启动命令)、bootargs(传给内核的启动参数)、ipaddr、serverip、gatewayip、netmask、ethaddr 等。例如,网络启动时典型配置为:

setenv ipaddr 192.168.1.100
setenv serverip 192.168.1.10
setenv bootargs 'console=ttyO0,115200n8 root=/dev/nfs rw nfsroot=192.168.1.10:/nfsroot ip=192.168.1.100'
setenv bootcmd 'tftp 0x82000000 zImage; tftp 0x88000000 am335x-evm.dtb; bootz 0x82000000 - 0x88000000'
saveenv

存储启动则把 bootcmd 改为从 MMC 或 NAND 读取,例如 mmc rescan; fatload mmc 0:1 0x82000000 zImage; fatload mmc 0:1 0x88000000 am335x-evm.dtb; bootz ...。环境变量修改后必须 saveenv 才会写入持久存储,否则仅当前会话有效。若环境变量损坏,可在串口下按住启动键进入 U-Boot 命令行,用 env default -a 恢复默认值。

四、网络启动与存储启动的对比与选择

网络启动(TFTP + NFS)适合开发调试阶段:内核、设备树、根文件系统都放在主机上,修改后无需重新烧写,重启即可生效。但它依赖网络环境与 TFTP/NFS 服务,现场部署时可靠性不足。存储启动(MMC/NAND/SPI)适合量产与工业现场:镜像固化在板载介质中,上电即启动,不依赖外部服务器。实际项目中常见做法是:开发阶段用网络启动快速迭代,量产阶段切换到 MMC 或 NAND 启动,并通过 bootargs 指定 root=/dev/mmcblk0p2 或 root=ubi0:rootfs。

对比项网络启动存储启动
镜像来源主机 TFTP 服务器板载 MMC / NAND / SPI
根文件系统NFS 挂载本地分区或 UBI 卷
迭代速度快,改完即重启需重新烧写
现场可靠性依赖网络高,独立运行
典型场景开发调试量产部署
调试建议:若串口只输出 CCCC,说明 ROM Code 正在等待 UART 下载,通常是启动介质未识别或 MLO 未正确烧写;若输出 SPL: 后停止,优先检查 DDR 参数与 SPL 大小是否超出 SRAM 限制。

掌握这条链路后,后续裁剪内核、制作根文件系统、配置设备树时,就能清楚每个镜像该放在哪里、由谁加载、失败时该从哪一级排查。启动链路是 Sitara 平台系统集成的“地基”,值得在项目初期就反复验证。