3、Hi3516启动流程:BootROM、Fastboot、Uboot启动分析与优化
各位同学,咱们今天聊聊Hi3516的启动流程。说实话,这块内容我当年刚接触海思平台时,也花了不少时间才理清楚。启动流程说白了就是芯片上电后,从“啥也没有”到“跑起系统”的整个过程。Hi3516的启动分三个阶段:BootROM、Fastboot、Uboot。咱们一个一个来拆解。
3.1 BootROM:芯片的第一口“奶”
芯片一上电,CPU首先执行的是内部固化的BootROM代码。这段代码是海思出厂前就烧死在ROM里的,你改不了,也擦不掉。它的任务很简单:初始化最基本的硬件,然后根据管脚电平或efuse配置,决定从哪个介质加载下一级程序。
我个人习惯把BootROM比作“芯片的胎教”。它只做最基础的事:
- 初始化CPU、时钟、DDR控制器(但DDR还没完全跑起来)
- 检测启动介质选择管脚(比如从SPI Nor、NAND、eMMC还是SD卡启动)
- 从选定的介质读取Fastboot镜像到内部SRAM
- 校验镜像签名(如果使能了安全启动)
这里有个坑,我必须要说。我在项目中遇到过一块板子,死活起不来,查了半天发现是启动介质选择管脚的上拉电阻虚焊了。BootROM读到的电平是浮空的,导致它以为要从一个不存在的设备启动。嗯,硬件问题往往比软件问题更隐蔽。
3.2 Fastboot:承上启下的“二传手”
Fastboot是海思自己搞的一个迷你bootloader,它运行在DDR已经初始化之后的环境里。你想想看,BootROM那点SRAM根本跑不了复杂的程序,所以Fastboot的任务就是:把Uboot从存储介质搬到DDR里,然后跳过去执行。
Fastboot的典型工作流程:
- 初始化DDR控制器(这时候DDR才能用)
- 从存储介质(SPI Nor/NAND/eMMC)读取Uboot镜像
- 将Uboot加载到DDR的指定地址(比如0x40000000)
- 校验Uboot的CRC或签名
- 跳转到Uboot入口
说白了,Fastboot就是个“搬运工”。但你别小看它,搬运的速度直接影响启动时间。我记得有一次客户要求启动时间必须控制在3秒以内,我就在Fastboot阶段做了优化:
- 使用DMA方式搬运数据,而不是CPU轮询
- 调整SPI时钟频率到最大支持值
- 如果使用eMMC,开启HS200模式
3.3 Uboot:真正的“系统管家”
Uboot大家应该不陌生,它是嵌入式Linux世界最常用的bootloader。Hi3516上的Uboot是海思基于主线Uboot二次开发的,加了很多海思特有的驱动和命令。
Uboot启动流程大致如下:
| 阶段 | 主要工作 | 我踩过的坑 |
|---|---|---|
| start.S | 设置异常向量、初始化CPU模式 | 中断向量表没对齐,导致死机 |
| board_init_f | 初始化串口、时钟、DDR、GPIO | DDR时序参数配错,跑起来不稳定 |
| relocate | 将Uboot自身从Flash拷贝到DDR | 拷贝地址算错,覆盖了内核加载区 |
| board_init_r | 初始化外设、文件系统、网络 | 网卡PHY地址配错,ping不通 |
| main_loop | 进入命令行或自动启动内核 | bootargs参数写错,内核挂载不了根文件系统 |
Uboot阶段我最想强调的,是环境变量。环境变量存储在Flash里,保存了bootargs、bootcmd等关键参数。我曾经遇到过一个问题:开发板在实验室跑得好好的,一到客户现场就起不来。查了两天才发现,是环境变量里的DDR参数跟实际硬件不匹配。客户那边的DDR颗粒批次不同,时序参数有细微差异。
3.4 启动优化:把时间抠出来
做产品嘛,启动速度是个硬指标。我见过有些客户要求冷启动必须在2秒内出画面。怎么优化?咱们分阶段说。
BootROM阶段:这部分你动不了,但可以选更快的启动介质。SPI Nor比NAND快,eMMC比SPI快。如果成本允许,优先用eMMC。
Fastboot阶段:
- 关闭不必要的打印信息
- 使用DMA搬运数据
- 如果支持,开启Cache加速读取
Uboot阶段:
- 裁剪不需要的驱动和命令(比如你不需要USB启动,就把USB支持去掉)
- 使用更快的Flash读取方式(比如SPI Nor的Quad模式)
- 内核镜像压缩格式选LZ4而不是gzip,解压速度快很多
- 跳过环境变量校验(如果不需要)
这里给一段我实际用过的Uboot启动时间优化配置示例:
// 在include/configs/hi3516dv300.h中
#define CONFIG_BOOTDELAY 0 // 取消启动延时
#define CONFIG_SYS_LOAD_ADDR 0x42000000 // 内核加载地址
#define CONFIG_BOOTCOMMAND \
"sf read 0x42000000 0x100000 0x400000; " \ // 从SPI Flash读内核
"bootm 0x42000000" // 启动内核
// 裁剪不需要的功能
#undef CONFIG_CMD_USB
#undef CONFIG_CMD_NET
#undef CONFIG_CMD_PING
优化后,我那块板子的启动时间从4.5秒降到了2.1秒。说白了,优化就是做减法——去掉一切不需要的东西。
3.5 总结一下
Hi3516的启动流程,说白了就是三级跳:BootROM负责“活过来”,Fastboot负责“搬东西”,Uboot负责“搭环境”。每一级都有它的职责,也都有优化的空间。
我个人觉得,搞嵌入式开发,启动流程是基本功。你连芯片怎么跑起来的都不清楚,出了问题根本无从下手。建议各位同学拿到开发板后,第一件事就是接上串口,把启动日志完整看一遍。每一行打印都对应一段代码,看懂了,你就入门了。
好,这一章就到这里。下一章咱们聊聊Hi3516的内存布局和DDR调试,那也是个容易踩坑的地方。