
1. 嵌入式系统启动流程全景解析每次给一块全新的嵌入式板卡上电看着串口终端里一行行启动日志滚动出来最终进入熟悉的命令行或者应用程序界面这个过程看似理所当然背后却是一套精密而复杂的“开机自检”流程在默默工作。这套流程的起点就是固化在芯片内部只读存储器ROM中的那一段代码我们称之为ROM代码或BootROM。它就像是设备的“出厂预设本能”在CPU复位后第一个获得执行权负责唤醒沉睡的硬件并找到存储在外部的“操作系统说明书”——也就是引导加载程序Bootloader将其搬运到内存中执行。对于嵌入式开发者尤其是做底层驱动和系统移植的工程师来说深入理解ROM代码的启动流程尤其是它对NAND Flash、eMMC、SD卡这些非易失性存储设备的初始化与检测过程是解决各种“板子不启动”、“镜像烧录失败”问题的关键。这不仅仅是理论更是实战中排查问题的地图。今天我们就以TI德州仪器某款处理器的ROM代码为例拆解从芯片上电到成功加载第一段外部代码的完整旅程重点聚焦在NAND Flash和MMC/SD卡的设备检测与初始化上。你会发现那些看似神秘的“启动失败”背后无非是时序不对、参数不匹配或者坏块没处理好。2. ROM代码的使命与启动设备选择逻辑2.1 ROM代码的核心职责与工作环境ROM代码是芯片设计阶段就烧录进去的无法修改。它的首要任务是在一个“一无所有”的环境里为后续软件的运行搭建起最基础的舞台。这个环境有多原始呢CPU刚复位内部RAM可能还未初始化所有外设都处于复位或未知状态更没有文件系统、驱动这些概念。ROM代码就是在这样的“蛮荒之地”开始工作的。它的核心职责可以概括为三点最小化硬件初始化配置最基础的系统时钟、必要的电源管理域以及初始化用于与外部存储通信的控制器比如通用内存控制器GPMC用于NOR/NANDMMC/SD控制器用于存储卡。它不会初始化所有外设只做启动所必需的那部分。枚举与检测启动设备按照预设的顺序通常由芯片的启动模式引脚SYSBOOT[15:0]的电平决定去尝试与各种可能的启动设备通信。常见的顺序可能是XIP NOR Flash - NAND Flash - MMC/SD - UART - Ethernet等。一旦在某个设备上找到了有效的启动镜像流程就会继续否则尝试下一个。加载并验证初始软件从检测成功的设备中读取预先约定格式的镜像文件例如TI的.bin文件包含大小、校验和、加载地址等信息将其拷贝到指定的内存地址通常是内部RAM或已初始化的外部DDR并进行简单的校验如校验和最后跳转到该地址执行。注意ROM代码加载的通常不是完整的操作系统而是一个被称为“第一阶段引导加载程序”的小程序比如U-Boot的SPLSecondary Program Loader或MLO文件。这个程序会完成更复杂的硬件初始化如DDR然后加载更大的第二段引导程序或直接加载内核。2.2 启动模式引脚告诉ROM代码从哪里开始找芯片如何知道该先去检查哪个设备呢答案就在SYSBOOT引脚上。这些引脚在上电复位时的电平状态会被硬件锁存到一个特定的寄存器中ROM代码读取这个寄存器来决定启动顺序。以NAND启动为例芯片手册中通常会有一个类似下表的定义SYSBOOT[n]引脚名称描述[14:13]CS0MUX[1:0]必须设置为00b表示非复用设备对于NAND。[12]CS0BW必须设置为0如果使用8位NAND必须设置为1如果使用16位NAND。[15]CS0WAIT对于NAND启动必须设置为0。为什么这些引脚配置如此重要CS0MUX[1:0]这告诉GPMC控制器连接到CS0片选信号的设备是哪种地址/数据线复用模式。NAND Flash是典型的非复用设备地址和数据通过不同的周期传输所以设为00。CS0BW这是硬件设计的关键。它直接决定了GPMC控制器在初始化时是以8位还是16位宽度去尝试与NAND通信。如果你板子上焊的是16位NAND但这个引脚被错误地上拉或下拉成了0ROM代码就会以8位模式去读ID很可能读不到正确的设备ID导致检测失败。这个错误非常隐蔽因为软件无法在ROM阶段更改这个配置。CS0WAITNAND Flash操作如擦除、编程需要时间会通过R/B#Ready/Busy引脚告知控制器。这个引脚通常连接到GPMC的WAIT信号。但在ROM代码最初的设备检测阶段可能采用延时等待或轮询状态寄存器的方式所以这里通常设为0不使用WAIT功能或者根据具体硬件设计决定。实操心得在画原理图时务必根据你选用的NAND Flash型号8位或16位正确配置SYSBOOT[12]引脚的上拉或下拉电阻。这是硬件层面的“硬编码”一旦出错软件无法补救。3. NAND Flash设备检测与初始化的深度剖析NAND Flash因其高容量、低成本的优势成为嵌入式系统大容量存储的首选。但它并非“即插即用”的设备其初始化过程比NOR Flash复杂得多主要挑战在于接口时序复杂、存在坏块、需要ECC纠错、以及设备参数页大小、块大小不统一。3.1 GPMC控制器初始化为通信铺平道路ROM代码要操作NAND首先需要初始化与它连接的桥梁——GPMC控制器。GPMC是一个非常灵活的接口可以模拟多种异步存储器的时序。对于NANDROM代码会将其配置为异步模式并设置一套相对保守、兼容性较好的初始时序参数。根据参考文档中的时序图Figure 4-11和参数表Table 4-11我们可以理解这些参数的意义。假设GPMC时钟为55MHz周期约18.18nsROM代码可能会设置如下时序单位时钟周期参数描述值时钟周期计算出的时间nstwr写周期周期30545.4trd读周期周期30545.4tCEon片选(CE)有效时间00tOEonCE有效到输出使能(OE)有效的时间7127.3tWEonCE有效到写使能(WE)有效的时间590.9trddataCE有效到数据锁存的时间21381.8tOEoffCE有效到OE无效的时间24436.4tWEoffCE有效到WE无效的时间22400.0为什么是这些值这些是初始化的安全值确保能与市面上大多数速度不高的NAND Flash进行最基本的通信如发复位命令、读ID。它们比NAND Flash数据手册中要求的最小值要大得多以保证可靠性。在后续的Bootloader如U-Boot中我们会根据读取到的NAND型号查询其数据手册配置一套更精确、性能更优的时序参数。关键点ROM代码在初始化GPMC时会释放地址总线因为NAND使用复用的I/O口来传输命令、地址和数据而不是独立的地址线。数据总线宽度初始设为8位如果后续检测到是16位设备会再动态调整。3.2 设备检测与参数获取ONFI与备选方案这是NAND启动最核心也最容易出错的环节。ROM代码需要知道面对的NAND是“谁”制造商、容量以及“什么样”页大小、块大小、OOB大小、ECC要求。它通过两级探测机制来实现。第一步尝试ONFI标准识别ONFIOpen NAND Flash Interface是一个标准化组织定义了NAND Flash的通用接口和识别方法。支持ONFI的NAND会在特定地址返回一个签名。复位与等待发送复位命令FFh并等待最多250ms让NAND完成内部初始化轮询其状态直到就绪。发送ONFI Read ID命令发送命令90h地址20h。这是ONFI标准规定的“读ID”命令地址。检查签名读取4字节数据。如果它们是4Fh 4Eh 46h 49h即“ONFI”的ASCII码则表明这是一个ONFI设备。读取参数页发送命令ECh读取完整的参数页通常256字节。从这个参数页中可以解析出页大小如2048或4096字节、OOB/备用区大小如64或128字节、每块的页数、所需的地址周期数等关键信息。这些信息是准确访问NAND的基础。第二步回退到旧式ID表查询如果ONFI识别失败旧款或不兼容的NANDROM代码会回退到传统方法。再次复位。发送标准Read ID命令发送命令90h地址00h。这是绝大多数NAND都支持的标准命令。查询内置ID表读取返回的ID字节流。通常第二个字节是“设备ID”。ROM代码内部维护了一个庞大的设备ID对照表如参考文档中的Table 4-13通过匹配这个ID来查找对应的设备参数容量、总线宽度、页大小。例如读到设备ID为DAh查表可知这是一个2Gb容量、8位总线、页大小为2048字节的NAND。第三步特殊模式——从I2C EEPROM读取几何参数对于一些非常规或自定义的NAND模块其几何参数可能无法通过上述两种方式获得。ROM代码支持一种特殊的NANDI2C启动模式。在此模式下它会尝试从一个I2C EEPROM从地址50h的特定偏移80h开始读取7字节的NAND几何信息。这7个字节定义了页大小、块大小、总线宽度、ECC类型等。这为定制化硬件提供了灵活性。常见问题与排查问题板子从NAND启动失败串口无任何输出。排查思路检查硬件确认SYSBOOT引脚配置是否正确特别是总线宽度。测量NAND的电源、复位信号是否正常。检查ID读取如果可能在ROM代码运行前用示波器或逻辑分析仪抓取GPMC总线在发送90h 00h命令时的波形看NAND是否有数据返回。没有返回可能是硬件连接、上拉电阻或时序问题。核对ID表将读取到的设备ID与芯片手册中的ROM支持列表对比。如果你的NAND不在列表中ROM代码将无法识别这是最直接的“不支持”错误。时序问题虽然ROM代码使用保守时序但在某些高速或特定型号NAND上仍可能失败。这需要结合具体芯片数据手册分析。3.3 坏块管理与ECC纠错保障数据可靠性NAND Flash由于物理特性出厂时就会存在一些“坏块”并且在生命周期中还会产生新的坏块。ROM代码在寻找启动镜像前必须避开它们。坏块检测方法 ROM代码通常只检查前几个块例如前4个块因为Bootloader镜像就存储在这里。它通过读取每个块**第一页和第二页的OOBOut-Of-Band备用区的第一个字节8位设备或字16位设备**来判断。好块该位置的数据为FFh8位或FFFFh16位。坏块该位置的数据不是FFh/FFFFh。这个标记是由NAND生产商或烧录工具在出厂/编程时写好的。 ROM代码会跳过被标记为坏块的区域只在好块中搜索启动镜像。ECC纠错——数据的守护者 NAND Flash在读写过程中可能发生位翻转。ROM代码使用硬件ECC纠错码引擎如BCH算法来检测和纠正这些错误。ECC计算与存储当数据被写入NAND的一个页时GPMC控制器会为每512字节的扇区计算出一段ECC校验码并写入该页对应的OOB区域中。ECC校验与纠错当从NAND读取数据时GPMC会再次计算读取数据的ECC校验码并与OOB中存储的原始ECC码进行比较。如果一致数据无误。如果不一致且错误位数在ECC纠错能力之内如BCH8可纠8位/扇区则ELMError Location Module等硬件模块会尝试纠正错误返回正确数据。如果错误位数超出纠错能力则扇区读取失败ROM代码会认为该扇区损坏。ECC类型选择对于不同容量和类型的NAND所需的ECC强度不同。ROM代码会根据设备ID特别是第4个ID字节的某些位自动选择使用BCH8还是更强的BCH16纠错。实操心得烧录工具至关重要用于烧写Bootloader到NAND的工具如flash_erase,nandwrite必须支持跳过坏块并且能正确计算和写入ECC信息。如果工具不写ECC或者ECC格式与ROM代码预期不符即使数据烧进去了ROM代码读取时ECC校验也会失败。镜像位置确保你的Bootloader镜像烧录在NAND的起始好块中。通常烧录工具会自动处理但你需要知道镜像实际被写到了哪个物理块这在调试时很有用。OOB布局不同页大小2KB vs 4KB的NAND其OOB区域内ECC数据的存放偏移是不同的参考Figure 4-15, 4-16。ROM代码和你的烧录工具、后续的Linux内核驱动必须使用一致的OOB布局否则ECC数据对不上必然导致读取失败。4. MMC/SD卡启动与FAT文件系统解析相较于NAND从MMC/SD包括eMMC卡启动是另一种常见方式尤其在开发阶段非常方便因为可以通过读卡器在PC上直接准备镜像。4.1 设备检测与初始化区分MMC与SDROM代码通过MMC/SD控制器如MMC1接口与设备通信。初始化流程如下电压设置首先尝试用标准高电压3.0V与设备通信。发送CMD0使设备进入空闲状态。鉴别设备类型发送CMD1发送操作条件。如果设备响应则识别为MMC设备。如果不响应则发送CMD55应用特定命令后跟ACMD41发送操作条件。如果设备响应则识别为SD卡包括SDHC/SDXC。如果两者均无响应则认为该接口上无设备启动失败。获取RCA为检测到的设备分配一个相对卡地址RCA用于后续通信。4.2 两种启动模式Raw模式 vs FAT文件系统模式ROM代码支持从MMC/SD设备以两种方式加载镜像1. Raw模式原始扇区模式在这种模式下ROM代码将存储设备视为一个原始的、按扇区寻址的块设备。它会在固定的几个扇区偏移量例如0 128KB 256KB 384KB处寻找一个特殊的结构——TOCTable of Contents和配置头CH以确认镜像的存在并获取加载信息。优点简单直接无需解析文件系统常用于eMMC等嵌入式存储。限制镜像必须放置在精确的扇区地址且大小通常受限如不超过128KB否则会覆盖下一个可能的镜像位置。2. FAT文件系统模式这是更常用的方式尤其对于可插拔的SD卡。ROM代码内置了一个轻量级的FAT12/16/32文件系统解析器。镜像文件ROM代码会在存储设备上寻找一个名为MLO的特定文件对于TI平台。这个文件就是第一阶段引导加载程序。支持的结构无MBR软盘模式整个卡就是一个FAT分区。有MBR硬盘模式卡的第一个扇区是主引导记录MBR其中包含分区表。ROM代码会查找一个活动的、主分区类型为FAT12/16/32的分区。流程读取第一个扇区检查末尾签名是否为0xAA55判断是否存在MBR。如果存在MBR遍历其4个分区表项找到活动且类型为FAT的主分区。读取该分区的第一个扇区DBRDOS Boot Record解析BPBBIOS Parameter Block信息确认是有效的FAT分区并计算出FAT表、根目录区的位置。读取根目录区查找名为MLO的文件条目只支持8.3短文件名格式。找到文件后从目录条目中获取文件的起始簇号。读取FAT表将文件所占用的所有簇号链缓存到内存中的一个“FAT缓冲区”。这样ROM代码在后续读取文件数据时就可以通过这个映射表直接由文件内的逻辑扇区号转换到存储设备上的物理扇区号LBA而无需每次都解析FAT。4.3 FAT文件系统解析的关键步骤ROM代码中的FAT解析器是精简而高效的它只关心如何找到MLO文件。以下是几个关键检查点也是制作可启动SD卡时容易出错的地方BPB有效性检查ROM代码会验证DBR中的关键字段例如BPB_BytsPerSec必须是512ROM代码通常固定处理512字节扇区。BPB_SecPerClus必须是2的幂次方如1248...。BPB_RsvdSecCnt保留扇区数FAT32通常为32。BPB_NumFATsFAT表副本数通常为2。BPB_RootEntCntFAT12/16的根目录条目数FAT32此处为0。分区大小计算必须自洽。计算FAT类型ROM代码通过计算数据区的簇数来确定FAT类型簇数 4085 - FAT124085 ≤ 簇数 65525 - FAT16簇数 ≥ 65525 - FAT32 这个计算必须与BS_FilSysType字段如“FAT32”匹配但ROM代码更相信计算出来的结果。查找MLO文件在根目录区FAT12/16或由BPB_RootClus指定的簇链FAT32中线性搜索目录项。目录项的文件名字段必须是“MLO”大写后跟空格填充属性为普通文件非卷标、目录、隐藏、系统。制作可启动SD卡的避坑指南格式化工具在Windows下不要使用默认的“快速格式化”建议使用diskpart的clean和create partition primaryformat fsfat32 quick命令或使用第三方工具如rufus、Win32DiskImager。在Linux下使用fdisk分区后用mkfs.vfat -F 32格式化。文件命名确保文件系统上的文件名就是大写的MLO。在Linux下复制后可以用dosfsck -v检查。避免使用长文件名。分区与活动标志如果使用MBR分区确保目标分区是主分区类型是0x0B或0x0CFAT32并且使用fdisk或diskpart将其设置为**活动active**分区。文件放置直接将MLO文件拷贝到分区的根目录不要放在任何子文件夹里。大小写敏感FAT文件系统本身不区分大小写但ROM代码的查找函数可能是大小写敏感的严格按照MLO来匹配所以使用大写最保险。5. 启动失败常见问题与高级调试技巧当你的板子无法启动串口一片寂静时不要慌张。我们可以通过一些方法和思路层层递进地定位问题。5.1 问题分类与初步定位完全无输出死寂最可能ROM代码本身未运行。检查电源、复位电路、时钟、启动模式引脚电平。其次ROM代码运行了但在初始化最基本硬件如PLL、时钟时就失败了。这需要结合芯片的勘误表和硬件设计检查。可能ROM代码尝试从默认启动设备失败且没有切换到下一个设备或没有触发任何错误指示如点亮LED。检查你的目标启动设备是否在支持列表中硬件连接是否可靠。有部分输出后停止如果芯片支持ROM代码可能会通过某个UART端口输出简单的调试信息或错误码例如通过UART0输出字符C、8等具体需查芯片手册。捕获并解析这些字符是黄金手段。输出可能表明它卡在了某个阶段如“NAND ID read failed”或“MMC init failed”。输出乱码或错误信息UART配置错误Bootloader和ROM代码的UART波特率、数据位、停止位设置不一致。DDR初始化失败如果ROM代码负责初始化DDR但参数配置错误可能导致向DDR拷贝数据或执行时崩溃。5.2 基于ROM代码流程的专项排查针对NAND启动失败“Device not found”查硬件确认SYSBOOT引脚特别是总线宽度。用万用表量电压。查连接检查NAND芯片的CE#、WE#、RE#、ALE、CLE、R/B#、WP#等控制信号线是否连接正确上拉电阻是否合适。测波形用示波器在复位后抓CLE、ALE、WE#、RE#和I/O线。你应该能看到ROM代码发送复位命令FFh和读ID命令90h 00h的波形。如果命令波形正确但无数据返回问题在NAND侧电源、复位、芯片损坏。如果命令波形都没有问题在SoC或GPMC配置侧。“Bad block found” / “ECC error”确认烧录你使用的烧录工具和命令是否正确是否支持坏块管理是否写入了正确的ECC检查OOB用编程器或能直接读NAND原始数据的工具读出前几个块的页数据和OOB区手动检查坏块标记和ECC数据是否正确。更换存储位置尝试将Bootloader镜像烧录到NAND更靠后的位置避开前几个物理块并在Bootloader中修改加载地址。这可以排除前几个块恰好是坏块或物理损坏的情况。针对MMC/SD启动失败“No MMC card present”检查卡槽的电源、检测脚CD、命令和数据线的连接。尝试更换一张不同的卡品牌、容量。确认ROM代码支持的卡类型是否支持SDHC/SDXC。“FAT fs error” / “MLO not found”确认文件系统将SD卡插入电脑确认分区是主分区、活动、FAT32格式。确认文件名在磁盘根目录查看文件名必须是大写MLO没有隐藏的扩展名。使用二进制查看工具如hexdump或WinHex直接查看SD卡的第一个扇区MBR和FAT分区的第一个扇区DBR手动验证BPB字段和0xAA55签名。简化测试尝试使用Raw模式用dd命令将Bootloader镜像直接写入SD卡的固定偏移扇区如sudo dd ifMLO of/dev/sdX bs512 seek2048偏移量需查手册并配置为Raw模式启动以绕过FAT文件系统解析的复杂性。5.3 利用硬件调试器进行深度追踪如果以上软件方法都无法定位就需要祭出终极武器——硬件调试器如JTAG/SWD。连接调试器在板子上电前连接好调试探头。暂停CPU上电后立即暂停CPU或设置复位后立即暂停。查看ROM代码虽然ROM代码本身不可读但你可以单步执行从复位向量地址开始单步观察程序流。这需要芯片厂商提供ROM代码的符号文件或大致流程图通常不提供难度极高。设置数据观察点在关键地址设断点。例如在GPMC的数据寄存器地址设写断点观察ROM代码是否在尝试发送NAND命令。在内部RAM的加载地址设读/写断点观察ROM代码是否成功将数据加载到了内存。查看寄存器检查GPMC、MMC控制器的置寄存器看其是否被ROM代码正确初始化。检查SYSBOOT状态寄存器确认启动模式是否如预期。查看内存在ROM代码执行完毕后如果可能查看它应该加载了Bootloader的内部RAM区域。如果那里全是0x00或0xFF说明加载失败。如果数据看起来像有效的代码有可识别的指令头说明加载成功但执行失败。调试ROM代码启动问题是对工程师硬件知识、软件理解和调试耐心的综合考验。最有效的方法永远是预防在硬件设计阶段就严格参考芯片厂商的推荐原理图在软件层面使用经过验证的烧录工具和方法在遇到问题时遵循从简单到复杂、从硬件到软件的排查顺序善用一切可用的日志和调试工具。理解本文剖析的每一个步骤就等于拥有了解决大部分启动问题的路线图。