4. U-Boot 优化:从裁剪到加速,让启动飞起来

U-Boot 是系统启动的第一道关卡。它跑得快不快,直接决定了你按下电源键后,用户要等多久才能看到 Logo。我见过不少项目,内核和根文件系统已经优化得很极致了,结果 U-Boot 愣是花了 3 秒多,白白浪费了宝贵的启动时间。

说白了,U-Boot 优化的核心就四个字:少干活,快干活。这一章,我们就来聊聊怎么做到这一点。

4.1 U-Boot 配置裁剪:减负就是加速

U-Boot 默认配置里塞了很多你可能永远用不到的功能。比如网络协议栈、文件系统支持、各种稀奇古怪的驱动。这些代码虽然不一定会执行,但编译进去后,U-Boot 的镜像体积会变大,加载和初始化时间都会变长。

我个人习惯,拿到一个新板子,第一件事就是做配置裁剪。目标是把 U-Boot 的二进制体积控制在 512KB 以内,甚至更小。

4.1.1 裁剪步骤

  1. 关闭不必要的命令:比如 fatload、ext2load、tftp、ping 等。如果你的产品量产时不需要通过网络更新固件,这些都可以关掉。
  2. 精简文件系统支持:只保留你实际使用的文件系统类型。比如只用 FAT32 来存放内核和设备树,那就把 ext4、btrfs 的支持都去掉。
  3. 去掉调试功能:CONFIG_DEBUG、CONFIG_LOG 这些在发布版本里一定要关掉。它们会往串口打印大量信息,拖慢速度。

关键配置项示例(在 menuconfig 中操作)

# 关闭不必要的命令
CONFIG_CMD_BOOTM=n
CONFIG_CMD_FAT=n
CONFIG_CMD_EXT4=n
CONFIG_CMD_NET=n
CONFIG_CMD_PING=n

# 关闭调试
CONFIG_DEBUG=n
CONFIG_LOG=n

# 精简驱动
CONFIG_DM_MMC=n  # 如果不用 MMC
CONFIG_DM_USB=n  # 如果不用 USB

我的经验:裁剪时别太激进。我曾经把 CONFIG_CMD_BOOTM 关掉了,结果内核启动不起来,查了半天才发现是少了这个命令。建议每次裁剪后都做一次完整的启动测试。

4.2 Fastboot 模式:量产和调试的利器

Fastboot 是 Google 提出的一种协议,用于在设备启动早期与主机通信。在 Tina Linux 中,U-Boot 支持 Fastboot 模式,这对量产烧录和现场调试非常有用。

为什么说它快?因为 Fastboot 直接通过 USB 传输数据,不需要经过网络协议栈,也没有文件系统的开销。说白了,就是裸数据传输。

4.2.1 启用 Fastboot

在 U-Boot 配置中启用 Fastboot 支持:

CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_CMD_FASTBOOT=y

然后在 U-Boot 命令行中进入 Fastboot 模式:

# fastboot usb 0

主机端就可以用 fastboot flash 命令烧录分区了。

注意:Fastboot 模式会占用 USB 设备。如果你的产品需要同时使用 USB 做其他事情(比如 ADB),需要做好切换逻辑。我记得有个项目,量产时 Fastboot 和 ADB 冲突了,导致烧录一半设备断开,最后不得不改硬件设计。

4.3 U-Boot 环境变量优化:别让配置拖后腿

U-Boot 环境变量存储在 Flash 或 EMMC 的特定分区里。每次启动时,U-Boot 都要读取这些变量。如果变量太多,或者存储位置太慢,就会增加启动时间。

4.3.1 精简环境变量

默认的环境变量里有很多调试用的变量,比如 bootargs 里可能有一长串内核参数。我的建议是:

  • 只保留必要的变量:比如 bootcmd、bootargs、fdt_addr、kernel_addr。
  • 合并变量:把多个 setenv 命令合并成一个 bootcmd 脚本。

优化前后的 bootcmd 对比

优化前(慢) 优化后(快)
setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait
setenv fdt_addr 0x48000000
setenv kernel_addr 0x40000000
fatload mmc 0:1 ${kernel_addr} uImage
fatload mmc 0:1 ${fdt_addr} sunxi.dtb
bootm ${kernel_addr} - ${fdt_addr}
setenv bootcmd 'fatload mmc 0:1 0x40000000 uImage; fatload mmc 0:1 0x48000000 sunxi.dtb; bootm 0x40000000 - 0x48000000'
setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait

你看,优化后的版本把多个 setenv 和 fatload 合并到了一个 bootcmd 里。这样 U-Boot 只需要执行一次环境变量解析,而不是多次。

4.3.2 存储位置优化

环境变量默认存储在 SPI Flash 或 NAND 上。这些存储介质的读取速度通常比 EMMC 慢。如果你的板子有 EMMC,可以考虑把环境变量移到 EMMC 上。

CONFIG_ENV_IS_IN_MMC=y
CONFIG_SYS_MMC_ENV_DEV=0
CONFIG_SYS_MMC_ENV_PART=0

避坑指南:我曾经把环境变量从 SPI Flash 移到 EMMC 后,启动时间缩短了 200ms。但要注意,EMMC 的初始化时间比 SPI Flash 长,所以这个优化不是绝对的。建议实际测量后再决定。

4.4 U-Boot 驱动延迟优化:把等待时间砍掉

U-Boot 在初始化过程中,会逐个初始化各个硬件驱动。有些驱动在初始化时会有固定的延时,比如等待 PHY 稳定、等待时钟锁定等。这些延时往往是硬编码的,但很多时候并不需要那么长。

4.4.1 常见的驱动延迟点

驱动类型 常见延迟 优化建议
Ethernet PHY 等待自协商完成(通常 2-3 秒) 如果不需要网络启动,可以跳过 PHY 初始化
MMC/SD 卡 等待卡检测(通常 100-500ms) 使用 CONFIG_MMC_DEFAULT_DEV 指定默认设备,跳过检测
USB 等待设备枚举(通常 1-2 秒) 如果不需要 USB 启动,可以关闭 USB 初始化
时钟/锁相环 等待锁定(通常 10-100ms) 检查数据手册,确认最小锁定时间,减少等待

4.4.2 实战:优化 PHY 延迟

我记得有个项目,U-Boot 启动花了 4 秒,其中 PHY 自协商就占了 2.5 秒。我们的产品根本不需要网络启动,所以直接跳过了 PHY 初始化。

在 board_init() 或 board_early_init() 中,添加条件判断:

#ifndef CONFIG_CMD_NET
    // 跳过 PHY 初始化
    return 0;
#endif

这样,U-Boot 就不会去等待 PHY 自协商了。启动时间直接从 4 秒降到了 1.5 秒。

注意:跳过驱动初始化要谨慎。如果你后续需要通过 U-Boot 的网络功能更新固件,那 PHY 初始化就不能省。建议在量产版本中关闭,在调试版本中保留。

4.5 本章知识体系

下面这张图总结了 U-Boot 优化的四个核心方向,以及它们之间的关系:

U-Boot 优化知识体系 U-Boot 优化 配置裁剪 Fastboot 模式 环境变量优化 驱动延迟优化 关闭无用命令 精简文件系统 去掉调试功能 USB 裸传输 量产烧录 现场调试 精简变量 合并 bootcmd 存储位置优化 跳过 PHY 跳过 MMC 检测 减少时钟等待 核心原则:少干活,快干活 每个优化点都要实际测量,避免过度优化导致功能缺失

嗯,到这里,U-Boot 优化的四个核心方向就讲完了。配置裁剪是基础,Fastboot 是工具,环境变量优化是细节,驱动延迟优化是硬骨头。每个方向都能帮你省下几百毫秒甚至几秒的启动时间。

记住,优化不是一蹴而就的。我建议你每次只做一个改动,然后测量启动时间,确认效果后再做下一个。这样出了问题也容易回退。