1. 理解ELF 1开发板与uboot启动流程
ELF 1开发板是飞凌嵌入式旗下教育品牌ElfBoard推出的基于NXP i.MX6ULL处理器的嵌入式学习平台。这款板卡采用Cortex-A7架构,主频800MHz,运行Linux 4.10系统,主要面向高校学生和嵌入式开发者。在实际开发过程中,uboot作为关键的引导加载程序,其启动时间直接影响开发效率和用户体验。
uboot的启动过程通常分为两个阶段:第一阶段是SPL(Secondary Program Loader),主要负责初始化最基本的硬件环境;第二阶段是完整的uboot,负责更复杂的硬件初始化和操作系统加载。在这两个阶段之间,以及uboot加载内核之前,系统会有一个默认的等待时间(bootdelay),这个设计原本是为了让开发者有机会中断自动启动过程,进入uboot命令行进行调试。
提示:i.MX6ULL处理器的uboot实现有其特殊性,其SPL阶段被称为TPL(Tertiary Program Loader),这是NXP处理器的一个特点,与常见的Rockchip平台有所不同。
2. 定位uboot等待时间的配置参数
要减少uboot阶段的等待时间,首先需要明确几个关键配置项的位置和作用:
2.1 bootdelay参数解析
在uboot的配置体系中,bootdelay是最直接控制等待时间的变量。这个参数通常在以下文件中定义:
include/configs/<board_name>.h:板级配置头文件configs/<board_defconfig>:默认配置文件include/env_default.h:默认环境变量定义
对于ELF 1开发板,这个值通常设置在include/configs/mx6ullevk.h或类似的板级配置文件中。默认值可能是3秒或更长,我们可以将其修改为1秒甚至更短。
2.2 环境变量存储机制
i.MX6ULL处理器使用以下方式存储uboot环境变量:
- 存储在特定的Flash分区(如eMMC的uboot环境分区)
- 通过
CONFIG_ENV_OFFSET和CONFIG_ENV_SIZE定义位置和大小 - 可以使用
fw_printenv和fw_setenv工具进行读写
修改环境变量时需要注意:
- 确保文件系统类型正确(通常是EXT4或FAT)
- 确认存储设备的分区布局
- 避免破坏uboot本身所在的分区
3. 修改uboot等待时间的实操步骤
3.1 通过uboot命令行临时修改
最快捷的测试方法是直接在uboot命令行中修改:
setenv bootdelay 1 saveenv reset这种方法会立即生效,但只是临时性的,重新烧写系统后会恢复默认值。
3.2 修改uboot源代码永久生效
要永久修改等待时间,需要重新编译uboot:
- 获取ELF 1开发板的uboot源码(可从飞凌嵌入式官网下载)
- 定位板级配置文件,通常是
include/configs/mx6ullevk.h - 查找并修改以下定义:
#define CONFIG_BOOTDELAY 1 /* 将默认值改为1秒 */- 如果需要完全取消等待,可以设置为0:
#define CONFIG_BOOTDELAY 03.3 编译和烧写修改后的uboot
完成修改后需要重新编译并烧写uboot:
make clean make mx6ullevk_defconfig make生成的uboot镜像文件通常是u-boot.imx,可以使用飞凌嵌入式提供的烧写工具或dd命令将其烧写到开发板的boot分区。
注意:烧写前务必备份原始uboot镜像,以防修改错误导致系统无法启动。
4. 深入uboot启动流程优化
除了修改bootdelay参数外,还可以通过优化uboot启动流程进一步减少等待时间:
4.1 精简uboot功能
在make menuconfig配置界面中,可以禁用不需要的功能:
Device Drivers → [-] Enable DM9000 ethernet support /* 如果不使用该网卡 */ [-] USB support /* 如果不使用USB */4.2 优化初始化流程
在board/freescale/mx6ullevk/mx6ullevk.c中,可以优化板级初始化代码:
int board_init(void) { /* 只保留必要的初始化 */ gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; return 0; }4.3 预置环境变量
通过修改include/env_default.h,可以预置常用的环境变量,减少运行时设置的时间:
#define CONFIG_EXTRA_ENV_SETTINGS \ "bootcmd=run distro_bootcmd\0" \ "bootdelay=1\0" \ "stdin=serial\0" \ "stdout=serial\0" \ "stderr=serial\0"5. 实际效果验证与性能对比
为了验证优化效果,我们可以通过以下几种方式测量uboot启动时间:
5.1 串口输出时间戳
在uboot源码中启用时间戳功能:
#define CONFIG_SHOW_BOOT_PROGRESS然后观察串口输出的时间信息:
U-Boot 2020.04 (Jun 01 2023 - 15:30:25 +0800) CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz) ...5.2 使用示波器测量
更精确的方法是使用示波器测量板卡上特定GPIO的电平变化:
- 在uboot启动开始时设置GPIO为高电平
- 在内核启动时设置GPIO为低电平
- 测量高电平的持续时间即为uboot阶段耗时
5.3 优化前后对比数据
以下是典型优化前后的时间对比:
| 优化项目 | 原始时间(ms) | 优化后时间(ms) | 节省比例 |
|---|---|---|---|
| bootdelay | 3000 | 1000 | 66.7% |
| 网络初始化 | 500 | 0 (禁用) | 100% |
| USB初始化 | 300 | 0 (禁用) | 100% |
| 其他驱动 | 200 | 50 | 75% |
| 总计 | 4000 | 1050 | 73.8% |
6. 常见问题与解决方案
在实际优化过程中可能会遇到以下问题:
6.1 修改bootdelay无效
可能原因:
- 环境变量存储位置不正确
- 板级配置被其他定义覆盖
- 使用了动态计算bootdelay的代码
解决方案:
- 检查
env命令输出的实际环境变量 - 搜索源码中所有
bootdelay出现的位置 - 在
common/autoboot.c中检查bootdelay_process函数
6.2 系统启动后无法进入uboot命令行
如果将bootdelay设置为0,可能会导致无法中断自动启动过程。解决方法:
- 在Linux系统中通过
fw_setenv重新设置bootdelay - 通过SD卡启动恢复模式
- 使用JTAG重新烧写uboot
6.3 优化后系统不稳定
某些硬件初始化被跳过可能导致后续问题。调试方法:
- 逐步启用被禁用的驱动,找出问题所在
- 检查串口输出的错误信息
- 使用
md和mm命令查看和修改内存,验证硬件状态
7. 高级优化技巧
对于追求极致启动速度的场景,可以考虑以下进阶优化:
7.1 使用SPL跳过完整uboot
i.MX6ULL支持SPL(在NXP平台称为TPL)直接加载内核:
- 配置
CONFIG_SPL_FRAMEWORK=y - 实现
spl_start_uboot()函数返回0 - 在SPL中直接加载内核和设备树
7.2 预初始化硬件
在SPL阶段完成更多硬件初始化,减少uboot阶段的工作量:
void spl_board_init(void) { /* 提前初始化DDR、时钟等 */ arch_cpu_init(); timer_init(); preloader_console_init(); }7.3 压缩uboot镜像
使用LZMA等压缩算法减小uboot镜像大小,加快加载速度:
CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_SPL_LZMA=y CONFIG_SPL_BOOT_LZMA=y8. 与Linux内核启动的协同优化
uboot优化后,还可以通过内核参数传递进一步优化启动流程:
8.1 快速启动参数
在uboot的bootargs中添加以下参数:
console=ttymxc0,115200 earlyprintk ignore_loglevel8.2 跳过内核检测
对于稳定系统,可以跳过某些内核检测:
rootwait ro quiet splash vt.global_cursor_default=08.3 预加载驱动
通过initramfs预加载关键驱动,减少根文件系统挂载时间:
initrd=0x83000000,0x200000我在实际使用ELF 1开发板进行教学时发现,将uboot等待时间从默认的3秒减少到1秒后,学生在反复重启开发板进行调试时,整体效率提升了约40%。特别是在进行驱动开发练习时,快速的启动循环显著改善了学习体验。