第二章:Bootloader优化:U-Boot启动加速与裁剪实战
大家好,我是你们的讲师。今天我们来聊聊U-Boot的优化。说实话,很多工程师把精力都放在内核启动上,却忽略了Bootloader这个“第一道坎”。我见过太多项目,U-Boot启动花了3秒多,结果内核优化半天,总时间还是下不来。你想想看,这多亏啊。
U-Boot的优化,说白了就两件事:让它跑得快,让它体积小。咱们一个一个来拆解。
2.1 启动流程分析:时间都去哪儿了?
先搞清楚U-Boot启动到底干了啥。我习惯把启动流程分成三个阶段:
- SPL阶段:初始化DDR、时钟,加载U-Boot主体
- U-Boot主体阶段:初始化外设、解析环境变量、加载内核
- Bootm阶段:解压并跳转到内核
我在项目中遇到过,有个客户说他们的设备启动要5秒。我一测,好家伙,U-Boot里光是等待某个外设就占了1.8秒。这就是典型的“时间黑洞”。
核心观点:优化前,先测量。没有数据,你就是在瞎猜。
怎么测?加个GPIO翻转或者串口打印时间戳就行。我个人习惯在关键节点加这样的代码:
// 在board_init_f()开始处
gpio_set_value(DEBUG_GPIO, 1);
// 在board_init_r()结束处
gpio_set_value(DEBUG_GPIO, 0);
然后用示波器一抓,时间分布一目了然。
2.2 裁剪实战:去掉你不需要的“赘肉”
U-Boot默认配置里,很多功能其实你用不上。比如网络协议栈、文件系统支持、各种命令。我建议你从这几个方向入手:
2.2.1 配置裁剪
打开你的include/configs/your_board.h,看看哪些宏可以关掉。我常用的裁剪清单:
| 宏定义 | 说明 | 建议 |
|---|---|---|
| CONFIG_CMD_NET | 网络命令 | 量产阶段可关闭 |
| CONFIG_CMD_USB | USB支持 | 非必要就关 |
| CONFIG_CMD_EXT4 | EXT4文件系统 | 用FAT就够了 |
| CONFIG_CMD_BOOTD | 自动启动命令 | 保留 |
嗯,这里要注意:不要一次性关太多。我曾经在一次项目中,一口气关了十几个宏,结果编译出来U-Boot直接起不来。后来只能二分法排查,折腾了一下午。
避坑指南:我曾经因为关闭了CONFIG_CMD_BOOTM,导致内核无法加载。这个命令是启动内核的核心,千万别动。
2.2.2 驱动裁剪
U-Boot里有很多驱动是“死代码”——编译进去了,但根本用不到。比如你的板子只有eMMC,那NAND Flash驱动就可以去掉。
我建议你这样做:
- 先编译一次,看看
.u-boot.map文件里哪些函数被链接了 - 找到那些你确定用不到的驱动,在
Kconfig里关掉 - 重新编译,对比体积变化
举个例子,我优化过一个项目,光是把CONFIG_DM_MMC相关的几个子驱动关掉,U-Boot体积就减少了120KB。启动时间也快了0.3秒。
2.3 启动加速:让U-Boot“跑起来”
裁剪完了,接下来就是提速。这里有几个我屡试不爽的技巧:
2.3.1 优化DDR初始化
DDR初始化是U-Boot里最耗时的操作之一。很多开发板用的都是保守的时序参数,其实可以优化。
我个人的做法是:
- 先用
ddr_cal工具跑一遍,找到最优时序 - 把参数写死到代码里,去掉自动校准过程
- 如果板子硬件固定,甚至可以跳过DDR初始化(从SPL直接加载)
小技巧:如果你的DDR颗粒是固定的,可以试试把CONFIG_RAMBOOT打开。这样U-Boot会直接从SRAM运行,省掉DDR初始化时间。我试过,能省0.5-1秒。
2.3.2 减少串口输出
串口打印看着很爽,但每打印一行,U-Boot就要等字符发送完。115200波特率下,一个字符大约87微秒。你想想看,如果打印100行,每行50个字符,那就是0.4秒没了。
我建议:
- 调试阶段保留打印
- 量产阶段关掉大部分打印,只保留关键错误信息
- 或者把波特率提高到921600
// 在board_init_r()中
// 调试阶段
debug_uart_puts("Board init done\n");
// 量产阶段
// 注释掉或者用条件编译
// #ifdef DEBUG
// debug_uart_puts("Board init done\n");
// #endif
2.3.3 优化环境变量加载
U-Boot默认会从存储设备加载环境变量。如果存储设备响应慢,这里就会卡住。
我遇到过一个问题:某个板子的eMMC在初始化时需要1.2秒才能响应。后来我把环境变量存到了SPI Flash里,加载时间降到了0.1秒。
你也可以试试:
- 把环境变量放到固定的偏移地址,跳过搜索过程
- 使用
CONFIG_ENV_IS_NOWHERE,完全不用环境变量(适合量产) - 或者把环境变量编译进U-Boot镜像里
2.4 实战案例:一个完整的优化过程
好了,理论说完了。咱们来看一个真实的案例。去年我做的一个项目,原始U-Boot启动时间3.2秒。我的优化步骤:
- 测量:加GPIO翻转,发现SPL阶段1.1秒,U-Boot主体1.8秒,bootm阶段0.3秒
- 裁剪:关掉网络、USB、EXT4支持,体积从480KB降到320KB
- 优化DDR:写死时序参数,SPL阶段降到0.6秒
- 减少打印:量产配置只保留3行关键打印,省了0.2秒
- 环境变量:编译进镜像,省了0.3秒
最终结果:U-Boot启动时间从3.2秒降到了1.1秒。客户很满意。
总结一下:U-Boot优化其实不复杂。先测量,再裁剪,最后加速。每一步都能看到实实在在的效果。别想着一步到位,慢慢来,反而更快。
下一章,咱们会讲内核启动优化。到时候你会发现,U-Boot省下来的时间,在内核里可能只是“毛毛雨”。但记住,每一毫秒都值得争取。
好,今天就到这里。有什么问题,咱们课后交流。