4. 开发环境搭建

U-Boot移植,说白了就是让Bootloader认识你的板子。很多新手一上来就想着改代码,其实最关键的步骤反而不是改,而是搞清楚你的板子跟哪个参考板最像。我这些年移植过的板子少说也有几十块,踩过的坑能写满一本笔记本。今天咱们就从零开始,把U-Boot移植的完整流程走一遍。

4.1 获取U-Boot源码

U-Boot的源码管理用的是git,这个没什么好说的。我个人习惯从官方仓库拉取LTS版本,稳定且社区支持好。

git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot
git checkout v2024.01

为什么要切到具体版本?因为master分支可能正在开发中,今天能编过明天可能就挂了。我在项目中遇到过好几次,追着master跑结果编译环境跟着变,最后浪费一整天排查。

我的建议: 选一个你目标芯片厂商官方推荐的U-Boot版本。比如全志的芯片,他们通常会在某个LTS版本上做深度测试。

4.2 理解U-Boot的板级结构

拿到源码后,先别急着编译。你得搞清楚U-Boot是怎么组织板级代码的。说白了,每个板子对应三个核心目录:

目录/文件 作用 我常踩的坑
board/<vendor>/<board_name>/ 板级初始化代码、低层驱动 忘记改board.c中的board_init函数
include/configs/<board_name>.h 板级配置宏定义 DDR参数配错,板子直接变砖
arch/arm/dts/<board_name>.dts 设备树描述硬件拓扑 引脚复用没对上,外设死活不工作

嗯,这里要注意:设备树现在是U-Boot的主流配置方式。老版本的board.c里硬编码的初始化逻辑,现在都移到dts里了。你想想看,改一个dts文件比改C代码安全多了,至少不会因为一个指针越界就死机。

4.3 选择参考板

移植U-Boot最聪明的做法不是从头写,而是找一个跟你目标板最接近的参考板,然后复制粘贴再修改。我一般按这个优先级找:

  1. 同芯片厂商、同系列 —— 比如你的板子是i.MX8M Mini,那就找i.MX8M Nano的参考板
  2. 同SoC、不同外设 —— 比如都是全志H3,但你的板子网卡芯片不同
  3. 同架构、同内存类型 —— 实在找不到,至少DDR控制器要一样

核心原则: 参考板的DDR初始化代码可以直接复用,这是移植成功的关键。DDR参数配错了,U-Boot连串口都打印不出来。

4.4 添加新板级支持

假设我选好了参考板,现在要添加自己的板子。以添加一块名为"myboard"的板子为例:

4.4.1 创建板级目录

mkdir board/myvendor/myboard
cp -r board/refvendor/refboard/* board/myvendor/myboard/

复制完后,记得把目录里的文件名和内容中的"refboard"全部替换成"myboard"。我曾经有一次漏改了Kconfig里的板名,结果编译出来的U-Boot还是参考板的配置,串口打印的还是参考板的名字,排查了半天才发现。

4.4.2 配置单板

U-Boot用Kconfig管理配置,所以你得在几个地方注册你的板子:

  1. arch/arm/mach-imx/Kconfig —— 添加你的板子选项
  2. board/myvendor/myboard/Kconfig —— 板级配置菜单
  3. board/myvendor/myboard/MAINTAINERS —— 维护者信息

举个例子,在Kconfig里添加:

config TARGET_MYBOARD
    bool "Support myboard"
    select CPU_V8
    select IMX8M
    help
      Support for MyBoard based on i.MX8M Mini

4.4.3 创建设备树

设备树是U-Boot认识硬件的眼睛。复制参考板的dts,然后修改:

cp arch/arm/dts/refboard.dts arch/arm/dts/myboard.dts

你需要改的主要是:

  • 内存节点 —— 容量和地址
  • 串口节点 —— 引脚复用和时钟
  • 网卡节点 —— PHY地址和复位引脚
  • GPIO和LED —— 板级特色功能
血的教训: 设备树里的reg属性一定要跟原理图对得上。我见过有人把UART2的基地址写成了UART1的,结果串口能打印但数据全是乱码。这种问题用示波器量一下TX脚的电平就能发现,但很多人想不到去查设备树。

4.5 编译U-Boot

配置和编译命令其实很固定:

make myboard_defconfig
make -j$(nproc)

但这里有个坑:交叉编译工具链必须提前配好。我习惯用环境变量指定:

export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm

如果你用的是ARM 32位,CROSS_COMPILE就是arm-linux-gnueabihf-。这个搞错了,编译出来的二进制在目标板上跑不起来。

4.6 烧录与验证

编译成功后,你会得到u-boot.bin或u-boot.imx(取决于芯片)。烧录方式因平台而异:

芯片平台 烧录方式 常见工具
i.MX系列 SD卡烧录 dd if=u-boot.imx of=/dev/sdb bs=1k seek=1
全志系列 FEL模式 + USB烧录 sunxi-fel
Rockchip系列 Maskrom模式 + 工具烧录 rkdeveloptool

烧录完成后,上电看串口输出。如果能看到U-Boot的启动日志,恭喜你,移植成功了第一步。如果没有任何输出,别慌,先检查:

  • 串口电平是否匹配(3.3V vs 1.8V)
  • DDR初始化是否成功(很多芯片会通过LED闪烁指示)
  • 启动介质选择是否正确(SD卡还是eMMC)

我的调试习惯: 第一次移植时,我会在board_init_f函数里加一个GPIO翻转的代码。如果GPIO能翻转,说明CPU已经跑起来了,问题出在后面的初始化。这个技巧帮我快速定位过至少5次DDR配置错误。

4.7 本章知识体系

下面这张图总结了U-Boot移植的核心流程,我建议你把它打印出来贴在工位上:

U-Boot移植核心流程 获取U-Boot源码 选择参考板(同SoC优先) 添加新板级支持(3个目录) 配置defconfig & 编译 烧录 & 串口验证 关键避坑点 • DDR参数必须与原理图一致 • 设备树引脚复用不能错 • 交叉工具链版本要匹配 我的调试习惯 • 先加GPIO翻转确认CPU跑起 • 串口无输出先查电平匹配 • 用示波器量TX脚排除硬件

这张图把整个流程串起来了。你注意看,从获取源码到烧录验证,每一步都依赖上一步的正确性。尤其是选择参考板这一步,很多人图省事随便选一个,结果后面DDR初始化调了一周都没搞定。我建议你花半天时间仔细对比参考板和目标板的原理图,把差异列出来再动手。