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

Layerscape 系列 SoC(如 LS1028A、LS1046A、LS1012A)的启动过程与消费级 ARM 平台有本质区别:它采用“片上 ROM 固件(BootROM)→ 预引导加载器(PBL/RCW)→ U-Boot SPL → U-Boot 主程序 → Linux 内核”的多级链式启动。理解这条链路,是移植 U-Boot 并适配自定义工业板卡的前提。本节围绕启动流程、U-Boot 配置编译、环境变量与启动参数定制、设备树加载方式四个层面展开。

一、Layerscape 启动链路解析

上电后,BootROM 首先从选定的启动介质(QSPI NOR、SD/eMMC、NAND 或通过 TFTP 的以太网)读取 RCW(Reset Configuration Word)和 PBL。RCW 是一组 512 字节以内的配置字,决定 SerDes 通道协议、DDR 时钟、CPU 频率、启动介质等关键硬件参数。RCW 与 PBL 通常合并为一个 4KB 的 PBL 镜像,由 BootROM 加载到片上 SRAM 执行。PBL 随后初始化 DDR,并把 U-Boot SPL 搬入 DDR;SPL 再加载完整的 U-Boot(u-boot.bin)到 DDR 并跳转执行。U-Boot 主程序完成外设初始化后,加载内核镜像(Image/Image.gz)、设备树(.dtb)和根文件系统,最后通过 booti 或 bootm 启动内核。

这条链路中,RCW 属于硬件级配置,一旦 SerDes 协议或 DDR 参数错误,板卡会在 PBL 阶段直接“静默死机”,串口无任何输出。因此工业板卡移植的第一步,往往是基于 NXP 官方参考板的 RCW 源文件(.rcw)修改 SerDes 与 DDR 参数,再用 rcw.py 工具编译成二进制。

Layerscape 多级启动链路与关键产物 BootROM 片上固化代码 读取启动介质 RCW + PBL SerDes/DDR/时钟 4KB 镜像 U-Boot SPL 初始化 DDR 加载 u-boot.bin U-Boot 主程序 外设初始化 环境变量解析 Linux Kernel+DTB RootFS 启动介质 QSPI / SD / eMMC / NAND 关键编译产物 rcw.bin · u-boot-spl.bin · u-boot.bin · u-boot-with-spl.bin 环境变量与启动参数 bootargs · fdt_addr · loadaddr · bootcmd 移植关注点: ① RCW 中 SerDes 协议与 DDR 参数必须匹配自定义板卡硬件;② U-Boot 设备树(u-boot.dtsi)需描述板级外设; ③ 环境变量中 fdt_addr 与内核加载地址不能与 DDR 保留区冲突;④ 工业板卡常需固定 MAC、禁用未使用外设以缩短启动时间。

二、U-Boot 配置与编译

NXP 官方 U-Boot 源码(如 u-boot-lsdk 或主线 u-boot)中,Layerscape 平台使用 defconfig 机制。以 LS1028A 为例,参考板配置为 ls1028ardb_tfa_defconfig。适配自定义板卡时,建议复制一份 defconfig 并重命名,例如 myboard_ls1028a_defconfig,在其中修改 CONFIG_TARGET_LS1028ARDB 为自定义目标,并调整 CONFIG_DEFAULT_DEVICE_TREE 指向自己的设备树文件。

# 编译示例(以 LS1028A 自定义板卡为例)
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make myboard_ls1028a_defconfig
make -j8

# 生成的关键文件
# u-boot.bin              —— U-Boot 主程序
# spl/u-boot-spl.bin      —— SPL
# u-boot-with-spl.bin     —— 合并镜像,可直接烧录 QSPI
# arch/arm/dts/fsl-ls1028a-myboard.dtb  —— U-Boot 自身设备树

编译前需确认交叉工具链版本与 U-Boot 源码兼容。LSDK 通常要求 GCC 9.x 或 10.x;主线 U-Boot 对工具链要求更宽松。若编译报错 unknown target,多半是 defconfig 名称拼写错误或未放入 configs/ 目录。

三、环境变量与启动参数定制

U-Boot 环境变量是启动流程的“软配置层”。工业板卡常见的定制需求包括:固定 MAC 地址、设置静态 IP、指定内核与设备树加载地址、传递自定义 bootargs。以下是一组面向工业场景的示例环境变量(属于教学示例,实际地址需根据板卡 DDR 布局调整):

setenv ipaddr 192.168.1.50
setenv serverip 192.168.1.10
setenv ethaddr 00:1b:21:aa:bb:cc
setenv fdt_addr 0x90000000
setenv kernel_addr 0x80080000
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait
  ip=192.168.1.50:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off'
setenv bootcmd 'mmc read ${kernel_addr} 0x8000 0x8000; mmc read ${fdt_addr} 0x10000 0x400;
  booti ${kernel_addr} - ${fdt_addr}'
saveenv

其中 bootargs 的 console=ttyS0,115200 必须与硬件串口一致;root= 指向实际根文件系统分区;ip= 参数用于内核启动阶段配置网络,格式为 client:server:gateway:netmask:hostname:device:autoconf。若板卡使用 QSPI 启动,bootcmd 应改为 sf probe; sf read ... 系列命令。

修改环境变量后务必执行 saveenv,否则重启后丢失。若环境变量存储在 QSPI 或 eMMC 的特定偏移,需确认 CONFIG_ENV_OFFSET 与 CONFIG_ENV_SIZE 不与 U-Boot 镜像区域重叠。

四、设备树加载方式与适配

Layerscape 启动涉及两套设备树:U-Boot 自身使用的设备树(编译进 u-boot.bin 或独立存放)和传递给 Linux 内核的设备树(.dtb)。前者描述 U-Boot 需要初始化的外设(串口、网口、MMC、QSPI),后者描述内核可见的完整硬件。移植自定义板卡时,通常以参考板 DTS 为模板,修改以下节点:

  • 内存节点:memory@80000000 的 reg 属性需与实际 DDR 容量一致,例如 2GB 应写为 <0x0 0x80000000 0x0 0x80000000>。
  • 串口节点:确认 &duart0 或 &lpuart0 的 status = "okay",并核对引脚复用。
  • 以太网节点:LS1028A 含 ENETC 和交换机,需根据实际 PHY 地址、接口模式(RGMII/SGMII)修改 phy-handle 与 phy-mode。
  • QSPI/MMC 节点:确保启动介质控制器状态为 okay,并配置正确的总线频率。

设备树加载方式有两种:一是将 .dtb 与内核镜像一起打包进 FIT 镜像(推荐,便于签名校验);二是将 .dtb 单独存放在启动分区,由 U-Boot 的 load 命令读取到 fdt_addr 后通过 booti 传递。工业场景若需安全启动,应优先采用 FIT 镜像方式,并在 U-Boot 中启用 CONFIG_FIT_SIGNATURE。

常见排障:若内核启动后串口无输出,先检查 bootargs 中 console 设备名是否与内核设备树一致;若网口不通,用 mdio list 在 U-Boot 下确认 PHY 是否被识别。RCW 错误导致的“无串口输出”无法通过 U-Boot 阶段排查,需用 CodeWarrior 或 JTAG 回读 RCW 确认。

完成上述配置后,将 u-boot-with-spl.bin 烧录至 QSPI 偏移 0x0,或通过 dd 写入 SD 卡。上电后观察串口输出:若依次出现 “U-Boot SPL”、“U-Boot 2021.04”、“Loading Device Tree” 等字样,说明启动链路已贯通。最终以 booti 成功进入 Linux 登录提示符作为移植完成的判断依据。