4. U-Boot 编译与配置:从源码到烧录的完整链路
U-Boot 是嵌入式系统的“第一道门”。说白了,它负责把硬件初始化好,然后把内核加载进内存。在 Rockchip 平台上,U-Boot 的配置和编译有一套固定的套路。我刚开始接触 RK 平台时,也被那堆 defconfig 文件和 dts 搞得晕头转向。后来做多了,发现其实就那么几步。
4.1 获取 U-Boot 源码
Rockchip 官方维护了一个 U-Boot 分支,里面集成了对自家芯片的支持。我个人习惯直接从 GitHub 拉取:
git clone https://github.com/rockchip-linux/u-boot.git
cd u-boot
git checkout -b my_rk3588_branch origin/next-dev
这里要注意,next-dev 分支通常是最新的开发分支,包含了最新的 SoC 支持。如果你做量产项目,我建议切到某个稳定 tag,比如 rk3588-2023.07 之类的。我在项目中吃过亏,用了一个未稳定分支,结果某个驱动在量产时出了诡异问题,查了两天才发现是 U-Boot 的 bug。
configs/ 目录下有没有你板子的 defconfig 文件。比如 rockchip_rk3588_evb_defconfig。如果没有,就得自己配了。
4.2 板级配置:找到你的 defconfig
每个 Rockchip 芯片都有对应的参考板(EVB)。厂商会提供一套默认配置。你拿到自己的板子后,通常是在参考板基础上改。
查看支持的板级配置:
ls configs/ | grep rk3588
输出大概长这样:
rockchip_rk3588_evb_defconfig
rockchip_rk3588s_evb_defconfig
rockchip_rk3588_evb7_defconfig
选一个最接近你硬件的配置。比如我手头是 RK3588 EVB,那就用 rockchip_rk3588_evb_defconfig。
配置命令很简单:
make rockchip_rk3588_evb_defconfig
这条命令会把默认配置写入 .config 文件。你可以用 cat .config | grep -i "CONFIG_BAUDRATE" 看看串口波特率是不是 1500000,Rockchip 默认喜欢用这个速率。
4.3 menuconfig:按需裁剪
默认配置通常包含很多你用不到的功能。比如你不需要网络启动,就可以关掉网络协议栈。我个人习惯用 menuconfig 做可视化裁剪:
make menuconfig
界面里常用的配置项:
| 配置项 | 说明 | 建议 |
|---|---|---|
CONFIG_BAUDRATE |
串口波特率 | 保持 1500000 或改为 115200 |
CONFIG_BOOTDELAY |
启动倒计时(秒) | 量产设为 0,开发设为 3 |
CONFIG_BOOTCOMMAND |
默认启动命令 | 根据你的分区表填写 |
CONFIG_CMD_NET |
网络命令支持 | 不需要就关掉,省空间 |
CONFIG_CMD_USB |
USB 命令支持 | 量产可关 |
举个例子,我想让板子从 eMMC 启动内核,可以这样设置 CONFIG_BOOTCOMMAND:
setenv bootargs 'console=ttyS2,1500000 root=/dev/mmcblk0p5 rootwait'
setenv bootcmd 'ext4load mmc 0:4 0x4000000 /boot/Image; ext4load mmc 0:4 0x6000000 /boot/rk3588-evb.dtb; booti 0x4000000 - 0x6000000'
saveenv
嗯,这里要注意,booti 是启动 ARM64 内核的命令,32 位用 bootz。别搞混了。
CONFIG_DM(驱动模型),结果所有外设都挂了。建议先备份 .config,改一步编译一次,确保没坏。
4.4 编译:一步到位
配置搞定后,编译就简单了。Rockchip 平台通常用 ARM64 交叉编译器:
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64
make -j$(nproc)
编译产物在 u-boot.bin 和 u-boot.dtb。但 Rockchip 的启动流程比较特殊,它需要把 U-Boot 打包成 idbloader.img 和 u-boot.itb。别急,后面会讲。
如果你改了 dts 文件,记得先编译设备树:
make dtbs
然后重新编译 U-Boot:
make -j$(nproc)
我习惯写个简单的脚本,一键完成:
#!/bin/bash
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64
make distclean
make rockchip_rk3588_evb_defconfig
make menuconfig
make -j$(nproc) 2>&1 | tee build.log
echo "Build done. Check build.log for errors."
undefined reference to `__aeabi_*',说明你的交叉编译器是 32 位的。Rockchip 平台必须用 aarch64 编译器。我刚开始就犯过这个错,折腾了半天才发现工具链不对。
4.5 烧录:让板子跑起来
编译出来的 u-boot.bin 不能直接烧。Rockchip 的启动 ROM 会先加载 idbloader.img(包含 DDR 初始化代码和 TPL/SPL),然后加载 u-boot.itb(包含 U-Boot 本身和 ATF)。
生成 idbloader.img:
cd u-boot
./tools/mkimage -n rk3588 -T rksd -d tpl/u-boot-tpl.bin idbloader.img
cat spl/u-boot-spl.bin >> idbloader.img
生成 u-boot.itb:
make u-boot.itb
烧录到 SD 卡(开发阶段常用):
sudo dd if=idbloader.img of=/dev/sdX seek=64
sudo dd if=u-boot.itb of=/dev/sdX seek=16384
sync
烧录到 eMMC(量产):
# 在 U-Boot 命令行中
ums 0 mmc 0
# 在 PC 端
sudo dd if=idbloader.img of=/dev/sdX seek=64
sudo dd if=u-boot.itb of=/dev/sdX seek=16384
这里的 seek 值很关键。偏移 64 扇区(32KB)是给 idbloader 的,偏移 16384 扇区(8MB)是给 u-boot.itb 的。不同芯片可能不同,RK3588 就是这样。
DDR 初始化阶段,多半是 idbloader 有问题。如果停在 Loading U-Boot,那就是 u-boot.itb 的问题。我一般会在 u-boot.itb 里打开 CONFIG_DEBUG_UART,这样能看到更详细的输出。
4.6 知识体系总览
下面这张图总结了 U-Boot 从源码到烧录的完整流程。你可以把它当作一个检查清单:
4.7 常见问题与避坑
- 编译报错: 检查交叉编译器版本。我推荐用
gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu,太新或太旧都可能出问题。 - 烧录后板子没反应: 先确认串口接线是否正确。Rockchip 的调试串口通常是 UART2,引脚是 TX/RX/GND,不要接 VCC。
- DDR 初始化失败: 检查
idbloader.img是否包含了正确的 TPL。不同 DDR 型号(比如 LPDDR4 vs LPDDR5)需要不同的初始化代码。 - U-Boot 启动后找不到内核: 检查
bootcmd里的分区号和文件路径。我习惯在 U-Boot 命令行里先用mmc part查看分区表,确认无误再设 bootcmd。