启动流程基础:嵌入式系统的典型启动链
做嵌入式底层开发,第一关就是搞懂系统是怎么“活过来”的。你按下复位键,到内核开始跑第一个进程,中间发生了什么?
我刚开始接触这块时,觉得不就是上电跑代码嘛,有什么难的。结果第一次调板子,串口死活没输出,查了三天才发现是启动介质选错了。嗯,从那以后我再也不敢小看启动流程了。
2.1 典型启动链:四段跳
嵌入式系统的启动,说白了就是一段接力跑。每一棒只干一件事:把下一棒的代码从存储介质搬到内存,然后跳过去执行。典型的链式结构是这样的:
ROM Code → SPL → U-Boot → Kernel
这是目前最主流的启动路径,尤其在 TI、NXP、Rockchip 等平台上广泛使用。
为什么需要这么多阶段?因为芯片上电时,内部 SRAM 很小,外部 DDR 还没初始化,你根本跑不了完整的 Bootloader。所以必须分阶段来,每一级逐步“解锁”更大的内存和更复杂的硬件。
第一阶段:ROM Code
这是固化在芯片内部 ROM 里的一段代码,出厂就写死了,你改不了。它的任务很简单:
- 初始化最基本的硬件(时钟、引脚、看门狗)
- 从预设的启动介质读取下一级代码
- 验证签名(如果使能了安全启动)
- 跳转到 SPL
ROM Code 通常只有几十 KB,运行在芯片内部的 SRAM 里。我遇到过最坑的一次,是某款芯片的 ROM Code 对 NAND Flash 的时序要求特别苛刻,换了个不同品牌的 NAND 就起不来了——最后只能改硬件匹配电阻。
第二阶段:SPL(Secondary Program Loader)
SPL 是 U-Boot 的一个精简版本,也叫 MLO 或 x-loader。它的大小被严格限制,通常不能超过芯片内部 SRAM 的大小(比如 64KB 或 128KB)。
SPL 的核心任务:
- 初始化外部 DDR 内存
- 初始化存储控制器(NAND、eMMC、SD 卡等)
- 从存储介质加载完整的 U-Boot 到 DDR
- 跳转到 U-Boot
我的经验:SPL 阶段最容易出问题的是 DDR 初始化。DDR 的时序参数、电压、频率,任何一个不对,板子就死在这里。我习惯在 SPL 里加一段 DDR 读写测试,打印出来确认内存没问题再往下走。
第三阶段:U-Boot
完整的 U-Boot 是功能最丰富的 Bootloader。它运行在 DDR 里,空间充裕,可以做很多事情:
- 提供命令行交互(你可以打断启动,手动输入命令)
- 加载设备树(DTB)
- 从各种介质加载内核(网络、USB、存储)
- 设置内核启动参数(bootargs)
- 最终跳转到内核
U-Boot 的灵活性很高,但也意味着配置项多。我见过不少团队在 U-Boot 里塞了太多驱动,导致启动时间超过 10 秒——其实很多外设根本用不上,裁剪掉就好。
第四阶段:Kernel
内核拿到控制权后,会做真正的系统初始化:
- 解压自身(如果是压缩内核)
- 初始化内存管理、进程调度、中断系统
- 加载设备驱动
- 挂载根文件系统
- 启动第一个用户进程(init)
到了这一步,系统就算真正跑起来了。你可以在串口看到内核的启动日志,一条条刷过去,心里踏实。
2.2 启动介质:从哪里读代码
ROM Code 怎么知道去哪里找 SPL?这取决于启动模式的选择。芯片会检测某些引脚的电平,或者读取 OTP(一次性可编程)区域,来决定从哪个介质启动。
常见的启动介质有四种,我列个表对比一下:
| 介质 | 速度 | 可靠性 | 成本 | 典型场景 |
|---|---|---|---|---|
| NAND Flash | 中等 | 低(有坏块) | 低 | 消费电子、低成本方案 |
| eMMC | 快 | 高(内置坏块管理) | 中等 | 工业控制、车机、平板 |
| SD 卡 | 中等 | 中等 | 低 | 开发调试、单板计算机 |
| 串口(UART) | 极慢 | 高 | 无额外成本 | 工厂烧录、紧急恢复 |
NAND Flash
NAND 的特点是便宜、容量大,但天生有坏块。ROM Code 读 NAND 时,必须处理坏块跳过和 ECC 校验。我踩过最大的坑是:某款 NAND 的页大小是 4KB,但 ROM Code 只支持 2KB 页,结果读出来的数据全是错的——换了个兼容型号才解决。
eMMC
eMMC 内部集成了控制器,坏块管理、磨损均衡都由它自己处理。对 Bootloader 来说,eMMC 就像一块简单的块设备,读写操作比 NAND 省心很多。我个人习惯在产品量产时优先选 eMMC,虽然贵一点,但省下的调试时间远不止这个差价。
SD 卡
SD 卡和 eMMC 协议类似,但它是可插拔的。开发阶段用 SD 卡启动非常方便,改完代码直接插卡就能测。不过量产时很少用 SD 卡,因为接触不良会导致启动失败。
串口(UART)
串口启动是一种“救急”方式。当所有存储介质都坏了,或者板子上还没焊 Flash 时,可以通过串口把代码一点点灌进去。速度很慢(通常 115200 bps),传一个 64KB 的 SPL 要好几秒。但关键时刻,它能救命。
注意:串口启动通常需要芯片支持,并且 ROM Code 里要有对应的驱动。不是所有芯片都支持串口启动,选型时一定要确认。
2.3 启动流程全景图
下面这张图把整个启动链串起来了。你可以看到每一级干了什么,以及它们之间的跳转关系。
2.4 启动模式的选择与切换
芯片怎么知道用哪种介质启动?通常有两种方式:
- 硬件引脚:芯片有专门的 BOOT 引脚,上电时采样电平,决定启动顺序。比如拉高从 eMMC 启动,拉低从 SD 卡启动。
- OTP/efuse:一次性编程区域,出厂时烧录好,之后不能改。适合量产产品,防止被篡改。
有些芯片支持“回退”机制:如果首选介质读不到数据,自动切换到下一个。比如先试 eMMC,失败后试 SD 卡,再失败就进串口下载模式。这个功能在开发调试时特别有用——你想想看,如果板子焊错了 Flash,至少还能从串口救回来。
我的建议:开发阶段把启动顺序设为 SD 卡优先,eMMC 次之。这样你改完代码,插卡就能测,不用反复烧写 eMMC。等产品定型了,再改成 eMMC 优先,并锁死 OTP。
2.5 避坑指南
做启动流程调试,有几个坑我反复踩过,分享给你:
- DDR 时序不对:SPL 阶段 DDR 初始化失败,板子直接死掉。解决办法是仔细看芯片手册的 DDR 配置部分,或者用芯片厂商提供的 DDR 工具生成参数。
- 启动介质顺序搞反:我曾经把 eMMC 和 SD 卡的启动引脚接反了,结果 ROM Code 一直从空介质读数据,板子毫无反应。查了两天才发现是原理图画错了。
- 签名验证失败:如果使能了安全启动,ROM Code 会校验 SPL 的签名。签名不对,直接停在这里。我习惯在开发阶段先关掉安全启动,等所有功能调通了再打开。
- 串口启动波特率不匹配:有些芯片的 ROM Code 串口下载固定用 115200,但你的终端软件设成了 9600——看到乱码还以为代码传错了。先确认波特率,再怀疑人生。
启动流程是整个嵌入式系统的基石。搞懂了这一块,后面调分区、调启动链联调,你心里就有底了。