简介围绕芯片从上电复位到操作系统启动的完整链路这份 docx 文档以 ROM Code、SPL、U-Boot、Kernel 四个阶段为主线梳理各阶段的职责边界与衔接关系面向从事嵌入式驱动开发、Bootloader 移植和系统启动调试的初中级工程师也适合准备面试或补强底层知识的学生。文档结合 SD 卡、UART、USB、eMMC 等常见启动方式说明镜像如何从存储介质搬运到片内 SRAM 与 DDR 中运行并对比 ptset 测试模式与下载模式的处理差异其中对 ROM Code 初始化、SPL 分批搬运、U-Boot 加载内核到 DDR、Kernel 进入用户空间的顺序描述较完整可用于排查启动卡死、镜像加载失败、内存搬运异常等问题。压缩包内为 1 个 docx 文件约 1.41MB内容集中、便于通读与整理笔记。已有 923 人学习下载可作为理解芯片启动流程的入门到进阶参考。1. 上电之后最先跑的是什么从复位向量到 ROM Code 的第一条指令芯片启动 boot 这件事很多人调板子时只盯着串口有没有打印真正翻车的地方往往在最前面那几百毫秒。上电、复位、CPU 从固化地址取第一条指令到屏幕上出现第一个字符中间要穿过 ROM Code、SPL、U-Boot、Kernel 四道门而且每道门失败的样子完全不同有的连串口都不通有的打印到一半就断有的明明显示成功却在 DDR 里跑飞。这份《芯片的 boot 过程》文档把四段链路连同 USB/UART 下载、SD/eMMC 启动、ptset 测试模式几条分支一起拆开了适合正在做 bring-up 的底层软件工程师、需要定位启动失败的系统工程师以及刚接触 SoC 启动流程想建立整体图的人。后面我不复述文档目录而是沿着它给的骨架把每一段能动手的部分补成可执行的步骤和判断依据。2. ROM Code 阶段CPU 初始化、系统初始化与启动介质判定ROM Code 是整条链路上唯一改不了的一段它被烧在芯片内部的掩膜里出厂即定稿。正因为它改不了它的设计取向非常明确只做“不做后面就完全动不了”的事凡是以后可能变的东西全部甩给 SPL。理解这一点后面所有奇怪现象都好解释了——ROM 阶段能打印的字符极少、能读的介质有限、能用的内存只有片内那一小块出了问题的表现自然就是“安静得像没上电”。2.1 复位向量落在哪handler 模式与 CPU 初始化的实际动作上电复位后CPU 从芯片内部固化的复位向量取指进入特权模式不少架构里叫 handler 模式也有叫 supervisor 或 machine 模式的这条 reset 异常是所有事情的起点。此刻 MMU 没开、cache 状态未知、DDR 控制器没初始化、PLL 还在默认频点整个系统里唯一能确定可用的内存就是片内 SRAM容量通常几十到几百 KB。ROM Code 的 cpu init 就在这个环境里展开先把栈指针指到片内 SRAM 的一段保留区再清掉可能残留的中断挂起状态把异常向量表准备好然后把主 PLL 拉到能跑的目标频率同时打开后面要用的那几个外设时钟门控。这套动作之所以要做得这么“抠”是因为 ROM 的容量是按 KB 计的多一个功能就意味着多一份掩膜成本而且一旦流片就再没有反悔的余地。所以 ROM 里通常不会做 DDR 训练、不会解析文件系统、不会做复杂的容错重试它只保证“能把下一段代码搬进 SRAM 并跳过去”。这也解释了一个常见疑惑为什么有些板子换一颗 DDR 颗粒就必须改 SPL而 ROM 完全不用动。判断 ROM 有没有跑起来最直接的办法是抓串口。ROM 阶段的打印量很少但通常会有介质类型和搬运长度这两条关键信息# 冷启动时串口抓到的 ROM 阶段日志115200 8N1无硬件流控 [ROM] boot media: SD [ROM] load spl 0x00000000 - 0x0000A000, len 0x11800 [ROM] jump 0x0000A000这段日志的价值在于三个参数boot media告诉你 strap 判定成了哪种介质如果这里显示的不是你预期的那一项后面所有分析都白做len是 ROM 决定的搬运长度它一般由镜像头部字段给出也不是随便定的最后一个jump地址就是 SPL 的入口和你链接脚本里的ORIGIN必须一致。抓不到任何字符时先别怀疑芯片坏了把波特率、电平标准、TX 引脚复用这三件事各核一遍血泪经验里至少一半的“芯片不上电”最后都是这三个原因。2.2 System Init键盘、UART 这些“小模块”为什么必须先配文档里那句“主要完成键盘/UART 等需要的对应模块的初始化配置”容易被一眼扫过去实际上它是 ROM 阶段最实的一块工作。这里的“键盘”一般指按键扫描控制器用途是检测强制进入下载模式的按键组合——板子上电时按住某个键ROM 读到这个电平就直接跳过正常的介质启动转去等上位机下发镜像这是救砖通道的硬件入口。UART 则要配引脚复用、时钟源、波特率分频很多 SoC 在 ROM 里用的是内部 RC 时钟精度有限所以早期串口出现少量乱码属于正常现象等 SPL 把 PLL 切过去之后就干净了。下表是我整理这类阶段时惯用的记录格式把“配了什么”和“为什么必须现在配”写在一起后面排查时对着看比翻手册快得多模块典型配置项为什么必须在 ROM 阶段完成引脚复用UART TX/RX、SD 卡 CMD/CLK/DAT0-3复用不配好后面的控制器配了也出不了信号时钟主 PLL 倍频、外设门控、UART 分频SPL 的代码要靠时钟跑起来串口打印也依赖它按键扫描扫描周期、去抖、组合键判定决定是否强制进入 UART/USB 下载模式SD/eMMC 控制器总线宽度、初始时钟低速、电压ROM 只做单块读取高速模式留给 SPL看门狗关闭或延长超时否则 SPL 稍慢一点就被复位表现为反复重启这张表里最容易被忽略的是看门狗那一行。不少板子第一次上电时表现为“串口打印两行然后重来”看日志像是 SPL 崩了实际上是 ROM 打开的看门狗没被 SPL 及时喂上到点直接复位。碰到这种节律性很强的重启先怀疑看门狗比一行行看代码快得多。2.3 启动介质判定SD/eMMC、USB/UART 与 AON SRAM 搬运窗口介质判定通常由 strap 引脚电平、eFuse 里的一次性配置、以及前面说的按键组合共同决定优先级各家不同但常见顺序是 SD 卡优先、其次 eMMC、然后才是 UART/USB 下载。文档给的 SD 卡路径最典型ROM 配好 SD 控制器后按固定偏移读取卡上的头部扇区校验 magic 和长度字段再把指定长度的内容通过 DMA 搬到 AON SRAM 的固定基址最后关掉自己的异常跳到 SRAM 入口。AON 是 always-on 域这块 SRAM 在深睡时也不掉电ROM 选它做落脚点是因为它在最早期就可用、不需要 DDR 参与。代价是容量小所以 ROM 只会搬有限长度——文档里给的是 70KB 这个量级。这个数字不是随便定的它等于“片内可用 SRAM 减去 ROM 自己占用的栈和变量区”之后剩下的窗口改链接脚本时这个上限就是硬约束。把这一段拆成可执行的检查步骤顺序如下确认 strap 或按键把介质固定成 SD避免板子跑到别的分支上。用读卡器把卡的前若干个扇区 dump 出来确认头部 magic 和长度字段符合 SoC 手册的定义。用示波器或逻辑分析仪看 CMD 线上有没有初始化时序确认 ROM 真的去读了卡。对照串口日志里的跳转地址和链接脚本的 ORIGIN两者必须一致差一个字节都会跑飞。# 把卡的前 512 字节 dump 出来核对镜像头偏移按具体 SoC 手册改 dd if/dev/sdX ofcard_head.bin bs512 count1 hexdump -C card_head.bin | head -n 8if换成你实际的块设备名count1只读一个扇区不要图省事整卡 dump。头部里通常有 magic、镜像长度、校验和、入口地址这几个字段magic 对不上说明烧录偏移错了长度对不上说明打包脚本算错了大小这两种错在现场占的比例最高。2.4 ptset 测试模式一条容易被误触发的旁路分支ptset 是产线测试模式进入后不走正常的启动判定流程而是等上位机下测试指令做引脚连通性、内存读写、功耗这些项目。它的存在让整个 boot 流程复杂了一档因为 strap 组合和正常启动很接近产线上用的治具和开发板上的电阻配置稍有出入就可能误进。误进之后的现象特别有迷惑性串口有反应甚至能看到提示符但就是不启动系统新手很容易往 kernel 或文件系统方向去找越找越偏。我一般的判断顺序是先看串口那一行提示的具体内容测试模式的提示和正常 ROM 日志措辞完全不同再用万用表量 strap 电阻确认和原理图标注一致最后检查板上有没有预留的测试点被短接或焊锡粘连。第三点在返修板上出现过不止一次量一遍五分钟比翻一下午代码划算。文档把 ptset 单独列成一条分支是有道理的它不属于启动链路但它是启动失败的常见干扰源脑子里有这根弦遇到“能打印但不启动”时会先想到它。3. SPL 阶段70KB 片内 SRAM 里怎么装下一个加载器ROM 跳过去之后控制权就到了 SPL。这一段的本质是一道容量数学题片内 SRAM 只有 70KB 级别的可用窗口完整的 U-Boot 动辄几百 KB 到 1MBkernel 更是几 MB 起谁都不可能塞进去。于是只能分两段走——先用 70KB 把 DDR 点起来再把剩下的东西搬进 DDR 跑。所有 SPL 的代码组织、链接脚本、裁剪策略都是围着这道数学题转的。3.1 为什么必须有 SPL容量鸿沟与两段式搬运片内 SRAM 快、常开、不需要训练但小且贵DDR 大、便宜但要初始化、要训练时序、跑起来之前不能直接用。SPL 的定位就是把“能操作 DDR 的最小代码集”塞进片内 SRAM跑起来之后立刻初始化 DDR然后把更大的镜像搬到 DDR自己功成身退。文档里那句“先搬运 SD 卡前 70KB 的程序过来进行运行然后运行配置完毕后再搬运剩下的 sd 内容kernel到 ddr 中进行运行”说的就是这个两段式结构。这里有个容易混淆的点第二段搬的到底是 U-Boot 还是 kernel取决于你的镜像组织方式。常见做法是 SPL 搬完整的 U-Boot 到 DDRU-Boot 再搬 kernel也有把 U-Boot 的后续阶段和 kernel 打包在一起、由 SPL 一次搬完的省事做法。不管哪种判断依据都是 SPL 结尾那一次跳转的目标地址落在哪落在 DDR 区间说明搬的是后续阶段落在 SRAM 说明还在片内。查这个比读文档快。两段式带来的直接后果是启动时间被拉长了一截尤其是 SD 卡在低速模式下的单块读取每一块都是几十微秒起。所以量产版本通常会把 SPL 里的 SD 时钟尽快切到高速模式同时把镜像做紧凑去掉用不上的驱动。这块的优化空间比很多人想的大我见过把 SPL 阶段的读卡时间从 200ms 压到 40ms 的改法动作无非是提高时钟、加大单次读取块数、减少重复校验。3.2 70KB 是怎么切出来的链接脚本与段布局70KB 不是目标是上限。SPL 编译出来的所有段——代码、只读数据、已初始化数据、BSS、栈——全部要落在这个窗口里溢出一点链接时不一定报错跑起来就在别人的地盘上踩踏表现为随机死机或打印乱码属于最难查的一类问题。我的习惯是在链接脚本里直接写 ASSERT让它在编译期就把问题暴露出来/* SPL 链接脚本的关键约束所有段必须落在片内 SRAM 窗口内 */ MEMORY { sram (rwx) : ORIGIN 0x0000A000, LENGTH 0x11800 /* 70KB 搬运窗口 */ } SECTIONS { .text : { *(.text.entry) *(.text*) } sram .rodata : { *(.rodata*) } sram .data : { *(.data*) } sram .bss (NOLOAD) : { *(.bss*) *(COMMON) } sram _spl_end .; ASSERT(_spl_end - ORIGIN(sram) LENGTH(sram), SPL exceeds SRAM window) }ORIGIN必须和 ROM 日志里那个跳转地址完全一致LENGTH必须和 ROM 实际搬运的长度一致两个数任何一个对不上第一跳就飞了。*(.text.entry)单独拎出来放最前面是为了保证入口函数一定在窗口起始位置不依赖链接顺序的运气。BSS 段标NOLOAD表示它不占镜像体积但运行时必须由启动代码手动清零——这一步漏了全局变量初值就是随机的表现出来是“同一份固件有时候能起有时候不能起”的玄学问题。SPL 里还有几条硬规矩值得写进注释不要用malloc不要开大数组不要在栈上放缓冲区不要在中断里做耗时操作。这几条不是风格问题是 70KB 窗口逼出来的生存法则。真需要缓冲区就借用 DDR 初始化之后的那一段空间并且明确它不属于 SPL 的链接范围。3.3 搬运链路从 SD 卡到 AON SRAM 再到 DDR把整条搬运链路按时间顺序摊开大致是这么几步ROM 读卡头部按长度搬前 70KB 到 SRAMSPL 起来后先做最简的串口和时钟初始化接着初始化 DDR 控制器并训练时序训练通过后按镜像头里记录的剩余长度把后续内容分块读到 DDR 的目标地址全部读完做一次校验通过后跳到 DDR 入口。每一步失败现象都不一样这也是排查能分段的原因。打包环节经常被低估。手工拼镜像容易出现“切出来的长度和头部写的长度不一致”ROM 校验不过就直接不启动串口一片安静。我一般用脚本切切完立刻核对大小# 从完整镜像里切出前 70KB 单独烧到卡的裸偏移上 dd iffirmware.img ofspl.bin bs1024 count70 ls -l spl.bin # 确认切出来的头部 magic 与长度字段 hexdump -C spl.bin | head -n 4bs1024 count70就是 70KB改窗口大小时这两个数要一起改只改一个必然出错。切完用ls -l看一眼实际字节数如果不到 71680说明源镜像本身就不够长这时候该查的是打包脚本而不是烧录工具。烧录偏移也要对齐到块边界多数 ROM 只认按块对齐的读取偏移错一个字节读到的头部就是垃圾。3.4 SPL 的验证点打印、时钟与 DDR 训练SPL 是整条链路上第一个“你能改”的阶段所以它也是最好的观测点。我在 SPL 里通常会埋三类探针第一类是早期串口打印只输出几个字符用来确认代码真的跑起来了注意这时候 PLL 可能还没切波特率不一定准字符乱码也算成功第二类是 GPIO 翻转在关键节点拉高拉低用示波器看时序比串口更可靠因为它不依赖任何通信协议第三类是 DDR 训练结果的回读把训练得到的延迟参数打出来后面调稳定性时这就是基线。DDR 训练通过不代表 DDR 稳。SPL 说“初始化成功”只能说明那一次读写校验过了温度变化、电压波动、批次差异都可能让边缘参数失效。所以量产版本一般会把训练参数写进保留区下次启动直接复用省时间也省得每次都在边缘试探。这块我踩过的坑是只做一次训练、不做回读校验结果一批板子里有百分之几在高温下启动失败返修时才发现训练参数根本没落盘。另一个常被忽略的验证点是搬运完整性。SPL 把内容读到 DDR 之后最好对整段做一次 CRC 或哈希比对和镜像头里存的校验值对齐。多花的那几十毫秒换来的是“DDR 里的内容和卡上的内容一致”这个确定性后面 U-Boot 跑飞时可以直接排除搬运错误这个方向。没有这一步排查会变成两头猜。4. U-Boot 与 Kernel 交接DDR 里的接力棒怎么递到 U-Boot 这一层环境宽松多了DDR 可用、驱动齐全、命令行可交互出问题也能停下来看。但宽松不等于简单U-Boot 到 kernel 这一段是地址和参数最容易出错的地方因为两边对内存布局的假设必须严格对齐任何一方挪了位置另一方就得跟着改。4.1 U-Boot 的重定位为什么它要把自己搬一次U-Boot 分两个阶段跑board_init_f阶段还在片内 SRAM 或低地址区域只做最必要的初始化并把内存布局规划好board_init_r阶段则把自己整体重定位到 DDR 的高地址端然后在那里跑完整功能。为什么要多此一举因为 kernel 通常要求加载到 DDR 的低地址连续区域如果 U-Boot 一直占着低地址就得在启动前把自己挪开否则跳过去之后互相踩。重定位的另一个好处是把片内 SRAM 完全让出来减少和后续阶段的资源冲突。这段机制带来的典型故障是“U-Boot 前半段正常、重定位之后就没输出了”。原因多半是重定位目标地址算错或者 DDR 那一段实际不可用——训练参数看着通过了但高地址区域有坏块或时序边缘。查法是打印重定位前后的地址和大小再对目标区间做一次读写测试能读能写再往下走。4.2 bootargs 与镜像地址kernel、DTB、rootfs 的三方对齐U-Boot 到 kernel 的交接靠三样东西镜像本身、设备树、启动参数。三者里任何一个地址或内容不对现象都是“Starting kernel ...”之后没下文而且没有任何报错——这是最让人抓狂的一种失败因为串口什么都不说。参数常见取值示例写错后的典型现象consolettyS0,115200kernel 起来了但看不到打印误判为没启动root/dev/mmcblk0p2挂载根文件系统失败panic 后停在提示rootfstypeext4/squashfs同上但报错信息里能看到类型不匹配mem512M内核识别到的内存小于实际高地址申请失败earlyconuart8250,mmio32,0x...早期打印缺失前面几百毫秒是黑匣子earlycon那一行值得单独说。kernel 自己的串口驱动加载之前日志是打不出来的中间这段空窗期恰恰是问题最集中的地方。加上earlycon就能把这段补上等于给启动过程装了个黑匣子。我现在的习惯是调试版本一律带earlycon量产再去掉或者改成静默。# 在 U-Boot 命令行里核对地址别背参数直接看环境变量 printenv bootargs printenv loadaddr # 手动走一遍流程定位到底断在哪一步 mmc dev 0 fatload mmc 0:1 ${loadaddr} Image fatload mmc 0:1 ${fdt_addr} board.dtb booti ${loadaddr} - ${fdt_addr}loadaddr是 kernel 加载地址fdt_addr是设备树地址booti的第二个参数传-表示没有 initrd。手动走一遍的好处是每一步都有返回值哪一步失败一目了然比直接敲boot然后等黑屏强得多。设备树地址尤其要注意它不能被 kernel 的解压过程覆盖所以通常放在 kernel 镜像上方留出足够间隔的位置。4.3 UART/USB 下载模式量产与救砖通道文档里提到“如果是 uart/usb 下载模式则进行 download 下载”这是 ROM 里保留的另一条路径。它绕过所有存储介质直接通过串口或 USB 把镜像灌进片内 SRAM 再跳转用途有两个产线首次烧录板上还没有任何可启动内容以及救砖存储介质里的镜像坏了正常启动进不去。两种通道的差别主要在速度。UART 简单、接线少但受波特率限制灌一个几百 KB 的 SPL 要等十几秒USB 快得多但需要主机端驱动配合协议握手也更复杂。常见做法是产线用 USB 提效率研发调试用 UART 图省事。不管哪种主机端工具都必须严格匹配 SoC 内置的 ROM 协议——命令码、握手序列、校验方式任何一项不一致都会卡在第一步。判断下载模式有没有进对看的是主机端工具能不能完成握手。能读到芯片返回的版本或状态字说明 ROM 在正常响应一直超时先查线USB 的 D/D- 有没有接反、UART 的 TX/RX 有没有交叉再查按键或 strap 有没有正确进入下载分支。这一步不需要看代码纯硬件排查。4.4 跳进 kernel 前的最后一公里bootm或booti执行的那一瞬间U-Boot 做的事情比看上去多校验镜像头部、按需解压、关掉自己的中断和外设、设置好 CPU 寄存器里的三个关键入参机器类型或设备树地址、启动参数地址、入口地址、刷 cache、最后跳转。这中间任何一步出问题都会表现为“跳过去就没动静”。最常见的两个错误一个是镜像加载地址和 kernel 编译时假设的物理偏移冲突解压过程中把自己覆盖了另一个是设备树地址和 kernel 的保留区重叠早期内存分配把 DTB 写坏了。前者的现象是解压到一半死掉后者是启动过程中随机 panic。两者都不报错只能靠提前规划内存布局来避免。我一般的做法是在 U-Boot 里把整段内存布局打印出来——kernel、DTB、initrd、保留区各占哪一段——贴在调试记录里后面每次改配置都对着这张图核一遍。5. 避坑与排查启动失败的五个典型现场启动失败和普通驱动问题最大的区别是可观测信息极少。串口可能一个字都没有JTAG 不一定接得上日志可能停在半句话。所以排查的核心不是“读代码”而是“按阶段切分先确定死在哪一段”把搜索空间从一个系统压缩到一个函数。下面五条是我在不同板子上反复遇到的现场按出现频率排。5.1 上电后串口一片安静现象按下复位串口工具里一个字符都没有示波器看 TX 引脚也没有任何翻转。原因这类情况绝大多数不在软件。可能是 ROM 阶段就没跑起来供电、复位时序、晶振不起振也可能跑了但串口这条路不通引脚复用没配对、波特率不匹配、电平标准不对比如板上是 1.8V 而转换芯片按 3.3V 判逻辑。还有一种不太常见但确实存在的板子进了 ptset 测试模式走的不是正常打印路径。解决按供电→复位→晶振→引脚→波特率的顺序量一遍别跳步。晶振那一项尤其要看起振波形频率偏了 ROM 会卡在时钟初始化。如果怀疑是测试模式把 strap 电阻量一遍再看串口有没有极短的提示字符——测试模式的提示往往只有几个字节。5.2 SPL 打印到一半就断了现象串口能看到 SPL 的头几行然后停住隔一会儿可能自己重启也可能彻底不动。原因最常见的是 SRAM 溢出。加了点调试代码.text或.bss撑破了 70KB 窗口链接脚本没写 ASSERT 就不会报错跑起来直接踩到栈或者别的数据区。其次是看门狗SPL 里的 DDR 训练耗时较长如果没在关键节点喂狗到点就复位表现出来的节律性重启很容易被误判成代码崩溃。第三种是栈开得太小深一层的函数调用就溢出。解决先看 map 文件里各段的实际大小和窗口上限的差值余量不足 4KB 就要警惕再确认看门狗在 SPL 入口有没有关掉或延长最后把栈指针初始值和栈大小打出来核对。这三步做完绝大多数“打印到一半”都能定位。5.3 SPL 说 DDR 好了U-Boot 却在 DDR 里跑飞现象SPL 日志显示 DDR 初始化成功、搬运完成、跳转地址正确U-Boot 一进 DDR 就异常可能是乱码、可能是死循环、也可能直接复位。原因DDR 训练只验证了某几个地址的读写不代表整个区间可用。高地址区域时序边缘、容量识别错误把 512MB 认成 1GB、或者训练参数在温度变化后失效都会造成“局部能跑、整体跑飞”。另一个原因是搬运长度算错镜像只搬了一半就跳过去。解决在 SPL 里对 DDR 的目标区间做一次全段读写测试别只测头尾把训练参数打出来和厂商给的参考值对比核对搬运长度和镜像头里记录的长度是否一致最好加一次整段 CRC。这一步花的时间远比后面在 U-Boot 里瞎猜省。5.4 USB 下载模式识别不到设备现象按流程进了下载模式主机端工具一直超时设备管理器里看不到任何新设备。原因分三层。物理层是线序和供电USB 的 D/D- 接反、VBUS 没供上、地没共协议层是主机工具和 ROM 的命令码不匹配版本对不上就握不上手模式层是根本没进下载分支strap 或按键判定失败ROM 还在老老实实找 SD 卡。解决先换一台主机、换一根已知良好的线排除物理层再用总线分析仪看有没有枚举过程有枚举说明物理层通了问题在协议没有枚举就回到模式层量按键电平和 strap。这三层按顺序排比乱试工具版本有效。5.5 U-Boot 打印 Starting kernel 之后没了下文现象U-Boot 一切正常最后一行Starting kernel ...打完串口彻底安静既不报错也不重启。原因这段是典型的黑匣子区间。可能是 kernel 镜像本身有问题解压失败、入口地址不对可能是设备树地址被覆盖早期内存分配写坏 DTB可能是console参数写错导致 kernel 起来了但打印不出来也可能是 kernel 的内存布局假设和实际 DDR 容量不符一进页表初始化就崩。解决先加earlycon把这段空窗期的日志捞出来这一招能解决一半以上的此类问题再看bootargs里的console和mem两个参数最后核对 kernel 加载地址、DTB 地址、initrd 地址三者有没有重叠。三者都不重叠还不行就上 JTAG 单步看第一条指令执行到哪停的。6. 把 boot 流程固化成清单我每次 bring-up 必走的三步上面这些内容散着看容易记混我最后收在一个动作上把整条链路压成一张可执行的检查清单每次拿到新板子或者改完配置从头走一遍不跳步。这个习惯是被坑出来的——有次改完 DDR 参数直接烧板子结果卡在 SPL查了两天才发现是链接脚本里窗口上限改了但搬运长度没跟着改本来三分钟能发现的事花了两天。第一步是静态核对。链接脚本的ORIGIN、LENGTH、镜像头里的长度字段、烧录脚本里的偏移这四个数必须两两一致我一般在打包脚本末尾加一句断言让它在生成镜像时就报错而不是等上板。第二步是分段观测。ROM、SPL、U-Boot、kernel 四段各设一个最小的可观测点ROM 看串口那两三行SPL 看 GPIO 翻转U-Boot 看命令行能不能进kernel 看earlycon的第一行打印。哪个观测点没有输出问题就在它和前一个观测点之间搜索范围立刻收敛。第三步是记录基线。每次成功启动把 DDR 训练参数、内存布局、bootargs原文、镜像大小这几个值抄进调试记录后面出问题时第一件事是拿当前值和基线比差异项就是嫌疑项。# 打包脚本末尾的自检长度、偏移、窗口三者一致性检查 IMG_LEN$(stat -c %s firmware.img) SPL_LEN71680 if [ $IMG_LEN -lt $SPL_LEN ]; then echo fatal: firmware shorter than SPL window; exit 1 fi # 头部声明的长度必须等于实际切出来的长度否则 ROM 校验不过 echo spl window$SPL_LEN image$IMG_LEN这段脚本里的SPL_LEN要和链接脚本的LENGTH用同一个来源最好是同一个变量传两处别手工抄。stat -c %s取实际字节数比人眼估算可靠。断言失败就中断打包不让有问题的镜像流到烧录环节——这个后悔药成本极低收益极高。从那以后我每次改内存布局或者搬运参数都强制走一遍这三步哪怕只是改了一个数字。启动链路的特点是环环相扣任何一环的数字变了上下游都得跟着复核凭记忆跳步的代价通常是半天以上的排查。希望帮到你。本文还有配套的精品资源点击获取