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读到的电平是浮空的,导致它以为要从一个不存在的设备启动。嗯,硬件问题往往比软件问题更隐蔽。

注意:BootROM阶段的SRAM非常小,Hi3516系列通常只有几十KB。所以Fastboot镜像必须严格控制大小,否则装不下。

3.2 Fastboot:承上启下的“二传手”

Fastboot是海思自己搞的一个迷你bootloader,它运行在DDR已经初始化之后的环境里。你想想看,BootROM那点SRAM根本跑不了复杂的程序,所以Fastboot的任务就是:把Uboot从存储介质搬到DDR里,然后跳过去执行。

Fastboot的典型工作流程:

  1. 初始化DDR控制器(这时候DDR才能用)
  2. 从存储介质(SPI Nor/NAND/eMMC)读取Uboot镜像
  3. 将Uboot加载到DDR的指定地址(比如0x40000000)
  4. 校验Uboot的CRC或签名
  5. 跳转到Uboot入口

说白了,Fastboot就是个“搬运工”。但你别小看它,搬运的速度直接影响启动时间。我记得有一次客户要求启动时间必须控制在3秒以内,我就在Fastboot阶段做了优化:

  • 使用DMA方式搬运数据,而不是CPU轮询
  • 调整SPI时钟频率到最大支持值
  • 如果使用eMMC,开启HS200模式
小技巧:Fastboot阶段可以打印一些调试信息,但生产环境建议关掉。串口打印很慢,每多打一行,启动时间就多几十毫秒。

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颗粒批次不同,时序参数有细微差异。

核心建议:量产时,把环境变量固化成默认值,不要依赖Flash里存储的变量。或者至少做一次校验,发现不匹配就恢复默认。

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秒。说白了,优化就是做减法——去掉一切不需要的东西。

警告:裁剪功能时要小心。比如你砍掉了网络命令,万一以后需要通过网络更新固件,那就麻烦了。建议保留一个“救砖”接口,比如通过GPIO进入烧录模式。

3.5 总结一下

Hi3516的启动流程,说白了就是三级跳:BootROM负责“活过来”,Fastboot负责“搬东西”,Uboot负责“搭环境”。每一级都有它的职责,也都有优化的空间。

我个人觉得,搞嵌入式开发,启动流程是基本功。你连芯片怎么跑起来的都不清楚,出了问题根本无从下手。建议各位同学拿到开发板后,第一件事就是接上串口,把启动日志完整看一遍。每一行打印都对应一段代码,看懂了,你就入门了。

好,这一章就到这里。下一章咱们聊聊Hi3516的内存布局和DDR调试,那也是个容易踩坑的地方。