4. TF-A与U-Boot移植与配置
在STM32MP1平台上,从芯片上电到Linux内核启动,中间要经过两段引导程序:第一段是TF-A(Trusted Firmware-A),负责最底层的时钟、DDR、安全域和电源初始化;第二段是U-Boot,负责加载内核、设备树和根文件系统。这两段程序都来自同一个源码仓库——STM32MP的U-Boot源码树中同时包含U-Boot和TF-A,通过设备树和配置文件区分不同板级。理解它们的配置方式,是让一块自制板卡跑起来的关键。
TF-A设备树与DDR初始化
TF-A在STM32MP1上分为BL2和BL32两个阶段。BL2运行在SYSRAM中,此时DDR尚未初始化,因此BL2的设备树必须使用静态内存布局,不能依赖动态分配。DDR初始化参数由STM32CubeMX生成,输出为一段DDR寄存器配置表,通常以stm32mp1_ddr_regs结构体形式嵌入TF-A的板级文件。关键参数包括:DDR类型(DDR3/DDR3L/LPDDR2/LPDDR3)、密度、位宽、时序参数(tRCD、tRP、tRAS、tRFC等)、以及PHY的ZQ校准值。
以一块512MB DDR3L、16位位宽的板卡为例,CubeMX生成的参数中,c_regs结构体包含约40个寄存器值,其中timing字段的t_rcd、t_rp、t_ras需要与DDR颗粒手册一致。若参数错误,典型现象是BL2在DDR训练阶段卡死,串口无输出或输出乱码。调试时可在TF-A中打开DEBUG=1,观察DDR训练日志中的DDR_PHY校准结果,若ZQ0CR1的校准值偏离预期,说明PHY参数需要调整。
TF-A的设备树文件位于fdts/目录,命名格式为stm32mp157<板名>-tf-a.dts。其中必须配置的节点包括:sdmmc1或sdmmc2(用于从SD卡或eMMC加载BL2)、uart4(调试串口)、pwr(电源域)、rcc(时钟树)。特别要注意sdmmc节点的st,neg-edge和bus-width属性,若SD卡无法识别,优先检查这两项。
U-Boot板级支持与环境变量
U-Boot的设备树位于arch/arm/dts/,命名格式为stm32mp157<板名>.dts。与TF-A不同,U-Boot运行在DDR中,设备树可以完整描述所有外设。板级支持的核心是board_init()函数和board_late_init(),前者负责早期引脚复用,后者负责设置环境变量和启动参数。
环境变量是U-Boot灵活性的核心。STM32MP1的默认环境变量定义在include/configs/stm32mp1.h中,关键变量包括:
bootcmd:默认启动命令,通常为run bootcmd_mmc0或run bootcmd_mmc1,分别对应SD卡和eMMC。bootargs:传递给Linux内核的参数,如root=/dev/mmcblk0p5 rootwait rw console=ttySTM0,115200。fdtfile:设备树文件名,如stm32mp157d-dk1.dtb。kernel_addr_r、fdt_addr_r、ramdisk_addr_r:内核、设备树、根文件系统在DDR中的加载地址。
实际调试中,常通过串口在U-Boot命令行临时修改环境变量,验证无误后再用env save写入eMMC的uboot-env分区。例如,若根文件系统在eMMC的第5分区,可设置:
setenv bootargs root=/dev/mmcblk1p5 rootwait rw console=ttySTM0,115200
setenv bootcmd 'mmc dev 1; ext4load mmc 1:4 ${kernel_addr_r} /boot/zImage; ext4load mmc 1:4 ${fdt_addr_r} /boot/stm32mp157d-dk1.dtb; bootz ${kernel_addr_r} - ${fdt_addr_r}'
env save
注意mmc dev 1选择eMMC,ext4load mmc 1:4表示从eMMC第4分区(通常是bootfs)加载文件。若从SD卡启动,则改为mmc dev 0和mmc 0:4。
启动脚本与介质切换
STM32MP1的U-Boot支持boot.scr脚本,存放在bootfs分区的/boot/目录下。脚本由boot.cmd编译而来,编译命令为mkimage -C none -A arm -T script -d boot.cmd boot.scr。脚本中可包含条件判断,例如根据${board}变量选择不同的设备树,或根据mmc dev的返回值决定从SD卡还是eMMC加载内核。
从SD卡与eMMC启动的切换,本质上由BOOT引脚在上电时的电平决定。STM32MP157的BOOT0、BOOT1、BOOT2引脚组合对应不同的启动介质:BOOT0=0, BOOT1=0, BOOT2=0为SD卡;BOOT0=0, BOOT1=1, BOOT2=0为eMMC。若引脚配置正确但无法启动,应检查TF-A中sdmmc节点的cd-gpios(卡检测)和vmmc-supply(供电)是否与硬件一致。
STM32CubeProgrammer将固件烧录到eMMC。eMMC的fsbl1和fsbl2分区用于存放TF-A的BL2,ssbl分区存放U-Boot,bootfs分区存放内核和设备树,rootfs分区存放根文件系统。
MMC: no card present,检查设备树中sdmmc1的status是否为"okay",以及pinctrl是否正确引用了SDMMC1的引脚组。若eMMC无法识别,确认sdmmc2的non-removable属性已设置,且bus-width为8。
完成TF-A和U-Boot的移植后,串口应依次输出BL2的DDR训练日志、BL32的OP-TEE初始化信息、U-Boot的版本和板级信息,最后进入U-Boot命令行或自动启动内核。若在某一阶段卡住,可根据最后一行日志定位问题:DDR训练失败查CubeMX参数,BL32加载失败查OP-TEE的load_addr,U-Boot无法加载内核则检查bootcmd中的分区号和文件路径。