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

在TI Sitara(如AM335x、AM437x、AM57x)平台上,U-Boot并不是一个“单块”程序,而是由多个阶段协作完成从芯片上电到操作系统内核接管的完整链路。理解这条链路,是自定义板卡移植能否成功的关键。很多“板子起不来”的问题,根源并不在内核,而在SPL阶段DDR没有正确初始化,或者环境变量与启动脚本不匹配。

一、U-Boot的两级架构:SPL与主U-Boot

Sitara芯片内部有一小段固化ROM(Boot ROM),上电后它只能从有限的启动介质(MMC/SD、NAND、SPI、UART、USB等)读取很小的镜像到片内SRAM(AM335x约128KB)。这个镜像就是SPL(Secondary Program Loader),也叫MLO。SPL的任务非常聚焦:配置时钟、初始化DDR、把主U-Boot从存储介质搬到DDR中并跳转执行。

主U-Boot(u-boot.img)运行在DDR里,空间充裕,负责更完整的功能:串口控制台、网络、环境变量、文件系统、镜像加载、启动脚本等。因此移植工作要分两层看待——SPL决定“能不能活”,主U-Boot决定“能不能用”。

Sitara 上电启动链路:Boot ROM → SPL → U-Boot → Kernel Boot ROM 片内固化 读 MLO 到 SRAM SPL / MLO 时钟 + DDR 初始化 加载 u-boot.img 主 U-Boot 网络 / 环境变量 bootcmd 启动脚本 Linux Kernel zImage + DTB 挂载根文件系统 SPL 内 DDR 初始化关键参数(示例) • DDR 类型:DDR3 / DDR3L,容量 512MB • 时序:tRFC、tRAS、tRP、CL 由 EMIF 寄存器配置 • 校验:SPL 打印 "DDR: 512 MiB" 表示初始化成功 环境变量与启动脚本(bootcmd 示例) ipaddr=192.168.1.100 serverip=192.168.1.10 gatewayip=192.168.1.1 bootcmd=tftp 0x82000000 zImage; tftp 0x88000000 am335x-custom.dtb; bootz 0x82000000 - 0x88000000 bootargs=console=ttyO0,115200n8 root=/dev/nfs nfsroot=192.168.1.10:/nfsroot ip=dhcp

二、DDR初始化:SPL阶段最不能出错的部分

DDR初始化由SPL中的board_init_f调用sdram_init()完成,核心是配置EMIF(External Memory Interface)控制器。以AM335x为例,关键参数包括DDR类型、容量、行/列地址位宽、以及一组时序寄存器(如EMIF_SDRAM_TIM_1/2/3、EMIF_SDRAM_CONFIG)。这些值通常来自TI提供的EMIF_Config工具或参考板board/ti/am335x/board.c中的ddr3_data结构体。

移植自定义板卡时,若DDR颗粒型号与参考板不同,必须重新计算时序。一个实用的判断方法是:SPL串口输出中若出现DDR: 512 MiB,说明容量识别正确;若卡在SPL: DDR init failed或直接无输出,则优先检查DDR供电、VTT端接、以及EMIF_SDRAM_CONFIG中的reg_nsdram和reg_ibe位。注意,DDR初始化失败时SPL无法把主U-Boot搬入内存,串口通常只打印一行就停止,这是与“主U-Boot阶段失败”最明显的区分点。

三、环境变量与启动脚本

主U-Boot启动后,会从存储介质(如MMC的uEnv.txt或SPI的env分区)加载环境变量。对于网络启动调试,最常用的是tftp加载内核与设备树,再通过bootz启动。一个可参考的配置如下:

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

这里0x82000000是内核加载地址,0x88000000是设备树地址,二者不能重叠,且需避开U-Boot自身占用的内存区域。执行saveenv后,环境变量写入存储介质,下次上电自动生效。若使用NFS根文件系统,还需确保主机端已配置好/etc/exports并启动nfs-kernel-server。

调试网络启动时,可先在U-Boot命令行手动执行ping 192.168.1.10验证网络连通性,再执行tftp。若ping不通,优先检查PHY地址、RMII/RGMII模式以及CONFIG_PHY_ADDR是否与原理图一致。

四、自定义板卡移植的实操要点

移植通常从TI参考板配置出发,复制一份include/configs/am335x_evm.h为am335x_custom.h,并在arch/arm/dts/下新增am335x-custom.dts。关键修改包括:DDR参数、PHY地址、MMC/SD引脚复用、以及CONFIG_BOOTCOMMAND。编译时使用make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am335x_custom_defconfig,生成MLO和u-boot.img。

烧录到SD卡时,MLO必须放在FAT分区起始位置,且文件名严格为MLO(部分ROM版本对大小写敏感)。上电后串口应依次看到SPL和U-Boot的版本信息。若SPL正常但U-Boot无输出,检查u-boot.img是否被正确加载到DDR,以及CONFIG_SYS_TEXT_BASE是否与链接地址一致。

修改DDR时序后务必先用较低频率(如303MHz)验证,再逐步提升到目标频率(如400MHz)。直接使用高频参数可能导致偶发启动失败,且难以定位。

完成上述步骤后,一个可复现的验证闭环是:上电→SPL打印DDR容量→U-Boot打印版本与网络信息→ping通主机→tftp加载内核与DTB→bootz启动→内核打印Uncompressing Linux...并最终进入控制台。若卡在某一环,按“SPL是否过DDR、U-Boot是否过网络、内核是否过解压”三段定位,即可快速缩小排查范围。