1. 项目概述与核心价值在嵌入式系统开发领域尤其是基于TI OMAP这类复杂应用处理器的项目中系统上电后的第一行代码执行路径即启动引导过程是整个项目稳定性的基石。这个过程并非由我们编写的应用程序直接控制而是由芯片内部预先烧录好的一段固件——ROM代码BootROM——全权负责。很多开发者尤其是刚接触底层驱动的朋友往往对这段“黑盒”代码感到神秘又棘手设备为什么能从一张空白的SD卡启动为什么修改了时钟配置后系统反而“变砖”了其根本原因就在于对ROM代码的初始化逻辑和引导机制理解不够深入。ROM代码的核心使命是在芯片脱离复位状态后从一个“零知识”的起点出发完成最基础的硬件环境搭建并找到、加载并跳转到下一阶段的引导程序如U-Boot。为了实现这一目标它必须遵循一套严格、通用且可靠的协议。其中利用FAT文件系统作为引导介质就是一种被广泛采用的工业级方案。它巧妙地将存储设备的复杂物理寻址抽象为文件名的逻辑查找使得我们无需关心镜像文件具体存放在SD卡的哪个物理扇区只需确保其以正确的文件名如MLO存在于FAT格式的卷中即可。这极大地简化了生产烧录和现场升级的流程。本文将从一个资深嵌入式开发者的视角彻底拆解ROM代码的初始化流程与FAT文件系统引导机制。我不会仅仅复述技术手册的条目而是结合我过去在多个OMAP平台项目中的实际调试经验重点剖析那些手册中一笔带过、但在实际开发中却可能让你耗费数日排查的“魔鬼细节”。例如FAT表项中那些特殊的魔数Magic Number究竟有何深意ROM代码在解析簇链时其缓冲区管理策略是怎样的更为关键的是TI引入的“配置头”Configuration Header, CH机制为我们提供了在ROM代码阶段定制化硬件参数如SDRAM时序、系统时钟的黄金机会这往往是优化系统启动速度、提升稳定性的关键所在。理解并善用这些机制你就能从被动的“问题排查者”转变为主动的“系统架构者”。2. ROM代码初始化流程全景解析当处理器上电或复位后硬件逻辑会强制将程序计数器PC指向一个固定的、由芯片制造商预设的地址这个地址通常映射到片内ROM的起始位置。从此ROM代码开始接管系统。它的执行可以看作一个高度有序、环环相扣的侦探破案过程目标是在纷繁复杂的硬件环境中找到通往“主程序”的钥匙。2.1 冷启动与热启动的路径抉择ROM代码首先需要判断本次启动的“上下文”。是设备首次上电冷启动还是从某种低功耗睡眠状态被唤醒热启动这两种场景的初始化任务量是天差地别的。对于冷启动一切从零开始所有硬件模块都处于未知的复位状态。而对于从CORE OFF等深度睡眠状态的唤醒即Wake-Up Booting许多关键硬件配置如SDRC控制器寄存器、PLL时钟设置在进入睡眠前已被保存到芯片的Scratchpad MemorySCR中。ROM代码的职责就变成了“现场恢复”。这里涉及一个关键的数据结构CONTROL_SAVE_RESTORE_MEM。它位于SCR中一个固定的物理地址例如OMAP3系列是0x48002910。在系统进入深度睡眠前用户编写的电源管理代码必须将SDRC和PRCM电源、复位、时钟管理模块的关键寄存器值按照Table 26-50和Table 26-51定义的格式保存到这个内存区域的对应位置并设置好Public Restore Pointer指向你自定义的恢复函数。ROM代码在唤醒引导时会先检查这个区域如果数据有效则会自动恢复SDRC和时钟配置然后直接跳转到你的恢复函数从而跳过耗时的完整硬件初始化流程实现毫秒级快速唤醒。这是一个极易出错的地方保存的寄存器列表必须完整且顺序正确任何一个字段错位都可能导致恢复后内存访问错误系统崩溃。2.2 引导设备探测与排序确定启动场景后对于冷启动ROM代码便开始了“寻宝”之旅——按照一个预设的、与芯片绑定的设备列表逐个尝试可能的引导设备。这个列表通常是硬件固定的例如XIP NOR Flash - NAND Flash - MMC/SD1 - MMC/SD2 - UART - USB。注意这个探测顺序是芯片设计时固化在ROM中的无法通过软件更改。这意味着如果你的产品设计选择从SD卡启动就必须确保排在SD卡之前的设备如NAND是空的或未被识别为有效引导设备否则ROM代码会“卡”在更早的设备上。我曾在一个项目中因为NAND Flash芯片未彻底擦除残留了旧的引导信息导致ROM始终尝试从NAND启动而失败忽略了SD卡。解决方案是使用编程器彻底擦除NAND或临时将其从电路上断开。对于每个设备ROM代码会执行一套简短的“握手”协议初始化该设备控制器以最小化、最通用的配置初始化接口如MMC/SD控制器设置为低速识别模式。读取设备特定扇区对于存储设备尝试读取第一个扇区LBA 0寻找引导签名或特定的数据结构。验证有效性检查读取到的数据是否符合预期的格式如图像头、FAT引导扇区签名0xAA55。这个过程是顺序且阻塞的。ROM代码没有多线程或超时中断的概念它会在一个设备上花费数个毫秒进行尝试如果失败再切换到下一个。因此引导时间与列表中有效但实际无引导内容的设备数量成正比。2.3 配置头CH的发现与处理机制当ROM代码从一个存储设备如MMC/SD、NAND成功读取到第一个扇区后它并非直接将其当作可执行代码而是首先检查其是否包含一个可选的配置头Configuration Header, CH。CH是TI ROM代码设计中的一个精妙之处它允许开发者在引导镜像前端嵌入一小段配置数据用以覆盖ROM代码的默认硬件初始化参数。CH的存在与否是通过解析其内部的TOCTable of Contents来判定的。TOC位于这个512字节扇区的起始部分由若干个TOC Item条目组成每个条目指向一个具体的配置段如CHSETTINGS,CHRAM。ROM代码会遍历TOC直到遇到一个起始偏移为0xFFFFFFFF的条目标志TOC结束。每个TOC Item结构如下对应Table 26-41typedef struct { uint32_t start; // 从TOC起始地址到该配置段实际数据的偏移量 uint32_t size; // 该配置段的大小 uint32_t rsvd[3]; // 保留字段必须为0 char filename[12]; // 配置段名称如“CHSETTINGS\0” } toc_item_t;关键点在于filename字段。ROM代码会将其与内部已知的字符串CHSETTINGS,CHRAM,CHFLASH,CHMMCSD进行比对。只有完全匹配才会处理该配置段。这意味着你必须确保字符串的拼写和大小写完全正确且以\0结尾占用不超过12字节。如果发现了有效的CHROM代码会在加载和执行主引导镜像之前依次应用这些配置。例如CHRAM中的SDRC寄存器配置会被写入硬件从而在跳转到你的引导加载器如U-Boot之前内存控制器已经按照你的定制参数完成了初始化。这解决了一个“先有鸡还是先有蛋”的难题在外部SDRAM可用之前如何配置它答案是通过CH在ROM代码还在内部SRAM中运行时就完成对SDRAM控制器的配置。3. FAT文件系统引导的深度剖析对于MMC/SD这类块设备ROM代码通常支持从FAT12/16/32文件系统引导。这是一种“文件级”引导相比直接从固定扇区读取的“原始模式”提供了更好的灵活性和可管理性。3.1 FAT卷的识别与BPB解析ROM代码首先读取设备的0扇区即主引导记录MBR。它查找有效的MBR分区表并定位到活动分区的起始扇区。然后读取该分区的第一个扇区即DOS引导记录DBR或称FAT引导扇区。引导扇区中包含了至关重要的BIOS参数块BPB。ROM代码解析BPB以获取文件系统的关键几何参数BPB_BytsPerSec每扇区字节数通常为512。BPB_SecPerClus每簇扇区数1, 2, 4, 8...是文件系统分配空间的最小单位。BPB_RsvdSecCnt保留扇区数FAT表开始前的扇区数。BPB_NumFATsFAT表份数通常为2一份主用一份备份。BPB_FATSz32对于FAT32每个FAT表占用的扇区数。BPB_RootClus对于FAT32根目录的起始簇号。这是FAT32与FAT12/16的关键区别之一FAT12/16的根目录位于固定位置紧接FAT表之后且有固定大小的条目限制而FAT32的根目录是一个普通的簇链可以位于数据区的任何位置且大小可变。ROM代码根据这些参数可以计算出FAT表区的起始扇区、根目录区的起始扇区对于FAT12/16或根目录起始簇号对于FAT32以及数据区的起始扇区。3.2 FAT表项解码与簇链遍历这是ROM代码定位引导文件的核心算法。FAT表本质上是一个大型的簇号数组数组索引对应数据区的簇号数组元素的值指明了该簇的下一个簇号从而形成了文件的簇链。Table 26-40详细定义了FAT表项的特殊值这些是ROM代码逻辑中的“路标”必须深刻理解FAT类型值 (十六进制)描述对引导过程的影响公共0x000(FAT12:0x000)空闲簇ROM代码在分配空间时会跳过。公共0x001(FAT12:0x001)保留簇ROM代码不会使用通常为FAT表本身保留。公共0x002-0xFEF(等)已用簇值指向文件的下一个簇号ROM代码据此遍历。FAT12特有0xFF0-0xFF6保留值ROM代码应视其为坏簇或错误停止遍历。FAT16特有0xFFF0-0xFFF6保留值同上。FAT32特有0xFFFFFF0-0xFFFFFF6保留值同上。特别注意FAT32仅使用低28位高4位必须保持为0。公共0xFF7(FAT12:0xFF7)坏簇关键如果引导文件的簇链中包含坏簇标记ROM代码会认为介质损坏引导失败。公共0xFF8-0xFFF(等)文件结束簇标记簇链的末尾。ROM代码读到此类值即知文件结束。ROM代码在查找名为MLO对于OMAP或其他预设文件名如u-boot.img的引导文件时其过程如下定位根目录根据BPB信息读取根目录区FAT12/16或根据BPB_RootClus找到根目录簇链FAT32。线性搜索目录项遍历根目录下的每一个32字节的目录项比较文件名8.3格式和扩展名。获取起始簇号在找到匹配的目录项后从该目录项中提取文件的起始簇号DIR_FstClusLo和DIR_FstClusHi。遍历簇链以起始簇号为索引读取FAT表对应项获取下一个簇号如此反复直到遇到“文件结束簇”标记。同时它需要将簇号转换为物理扇区号物理扇区号 数据区起始扇区 (簇号 - 2) * BPB_SecPerClus这里-2是因为数据区的簇编号从2开始簇0和簇1有特殊用途。缓冲与加载如原文所述ROM代码会以扇区为单位缓冲FAT表项和文件数据。它并非一次性将整个文件读入内存而是按需读取逐步将文件内容加载到目标地址对于XIP设备可能是直接映射地址对于非XIP设备则是拷贝到RAM中。3.3 实际调试中的陷阱与技巧簇号计算错误这是最常见的文件系统相关启动失败原因。务必确保数据区起始扇区计算正确。公式为数据区起始扇区 分区起始扇区 BPB_RsvdSecCnt (BPB_NumFATs * FAT大小扇区数)对于FAT12/16还需加上根目录占用的扇区数(BPB_RootEntCnt * 32) / BPB_BytsPerSec。FAT32高4位处理如原文NOTE强调FAT32表项仅低28位有效。在手动计算或工具查看FAT时如果看到表项值为0x0FFFFFFF这表示文件结束而不是0xFFFFFFFF。ROM代码会屏蔽高4位后进行判断。文件碎片化虽然引导文件通常连续存放但ROM代码的簇链遍历逻辑天然支持不连续的文件。然而过度的碎片化会降低加载速度并增加FAT表读取次数。使用fsck或chkdsk在将镜像写入SD卡前最好在Linux下用fsck.vfat或在Windows下用chkdsk /f检查并修复FAT文件系统结构的不一致避免坏簇或错误的簇链干扰ROM代码。4. 镜像格式与执行交接当ROM代码通过FAT文件系统找到引导文件例如MLO后它需要按照约定的格式解析这个二进制镜像并将其内容放置到正确的位置最后将CPU的控制权移交出去。4.1 GP设备镜像格式详解Table 26-46定义了通用GP设备的镜像格式。这里存在两种主要类型选择哪种取决于你的引导设备是否支持就地执行XIP。1. 非XIP内存引导镜像用于需拷贝到RAM执行的设备如NAND、MMC/SD这种镜像的头部包含一个简单的8字节头结构紧接着才是真正的可执行代码。typedef struct { uint32_t image_size; // 需要加载的镜像大小字节数 uint32_t dest_addr; // 目标加载地址通常是SDRAM的地址 } gp_noxip_header_t; // 之后紧接着就是 image_data[image_size]ROM代码的工作是读取这个头得知需要拷贝多少数据image_size以及拷贝到哪里dest_addr然后将紧随其后的image_size字节数据搬运到dest_addr指向的内存中。这里有一个至关重要的细节dest_addr必须是目标内存的物理地址并且该内存区域在拷贝发生时必须已经完成初始化例如通过CHRAM配置好了SDRAM控制器。否则会导致数据写入失败或写入到错误位置。2. XIP内存引导镜像用于NOR Flash等可直接寻址执行的设备这种镜像最为简单它没有前面的8字节头。镜像的第一个字节就是可执行代码。ROM代码会直接将CPU的PC指针跳转到该存储设备的映射地址例如NOR Flash连接在GPMC CS0地址为0x08000000并开始执行。在这种情况下CHFLASH配置头就变得尤为重要因为它定义了GPMC访问NOR Flash的时序参数直接影响代码读取的速度和稳定性。4.2 配置头CH各章节精讲CH是连接ROM代码默认行为和用户定制需求的桥梁。它由多个独立的“节Section”组成每个节负责配置一个特定的硬件模块。4.2.1 CHSETTINGS时钟系统配置这是最常用也最危险的配置节。它许你覆盖ROM代码默认的PLL和时钟分频设置从而在引导早期就提升系统运行频率。Section Key必须为0xC0C0C0C1这是ROM代码识别该节的魔法数字。Valid非零则启用该节配置。Clocking settings指向一个复杂的钟配置结构体其字段定义在Table 26-12虽未在提供片段中展开但通常包含PLL倍频系数、分频器设置等。警告错误的时钟配置如过高的频率、不稳定的PLL锁定会导致系统在引导的最初几微秒内彻底死锁且无法通过软件调试。建议初期使用保守值或直接从ROM代码默认配置开始。4.2.2 CHRAMSDRAM控制器配置这是必须为使用外部SDRAM/DDR的系统提供的配置节。ROM代码对SDRAM一无所知它不知道你的板子上用的是哪家厂商、什么型号、多大容量、什么时序的内存芯片。核心寄存器你需要填充SDRC_MCFG内存配置、SDRC_MR模式寄存器、SDRC_ACTIM_CTRLA/B时序参数、SDRC_RFRCTRL刷新控制等。这些值必须严格参照你所使用的SDRAM芯片数据手册和处理器TRM技术参考手册中的计算示例得出。Memory type字段明确告知ROM代码内存类型是SDR、DDR还是Mobile DDR。Flags字段指示CS0和/或CS1内存片选是否已配置。务必准确设置否则可能导致只有一半内存可用或配置不生效。实操心得获取这些寄存器值最可靠的方法是参考你所使用的评估板EVM的U-Boot源码或相关配置文件。TI的PSPPlatform Support Package通常会提供针对不同内存型号的预配置头文件。4.2.3 CHFLASHGPMC接口配置当你的引导设备如NOR Flash或其它外设通过GPMC接口连接时需要此节。默认配置ROM代码为GPMC CS0配置了一个非常保守的异步时序适用于大多数低速NOR Flash。优化需求如果你希望获得更快的读取速度以加速XIP执行或者你的Flash需要特殊的时序如地址/数据复用模式就需要通过CHFLASH提供精确的GPMC_CONFIG1_0~GPMC_CONFIG7_0等时序参数。这些参数的计算同样需要参考Flash数据手册和TRM。4.2.4 CHMMCSDMMC/SD控制器配置用于优化MMC/SD卡的访问速度。Bus width字段可以覆盖ROM代码默认的1位模式设置为4位或8位总线宽度显著提升数据传输率。MMCHS_SYSCTRL可以设置更快的时钟频率如26MHz、52MHz前提是你的SD卡支持且硬件走线良好。重要提示如原文NOTE指出如果该节所有字段都是0xFFFFFFFF即使Valid位被设置ROM代码也会认为此节未执行。这提供了一种“条件配置”的可能性。4.3 执行交接与启动参数传递当镜像被成功加载或XIP准备就绪后ROM代码执行一条分支指令BX或BLX跳转到镜像的入口点。对于非XIP镜像入口点是dest_addr对于XIP镜像入口点是存储器的映射基址。在跳转之前ROM代码会做一件至关重要的事情将R0寄存器设置为一个“启动参数结构体”的指针如Table 26-47所示。这个结构体是ROM代码留给后续引导程序如U-Boot的“交接备忘录”包含了丰富的上下文信息Current booting device告诉你系统是从哪个设备启动的MMC/SD1, MMC/SD2, NAND等。这对于U-Boot决定从哪个设备加载内核至关重要。CH flags一个位图指示了哪些CH节被成功应用。你的引导程序可以检查这个标志确认硬件如SDRAM是否已按CH的配置初始化完毕。Device descriptor pointer一个指向设备描述符结构体的指针包含了当前引导设备的详细信息如块大小、容量等U-Boot可以利用它来初始化该设备的驱动而无需重新探测。一个标准的U-Boot SPLSecondary Program Loader的入口汇编代码通常如下.globl _start _start: b reset /* 复位向量 */ ... reset: /* 1. 设置CPU为SVC模式关闭中断 */ /* 2. 设置栈指针指向内部SRAM*/ /* 3. 检查R0是否为0判断是否由ROM代码调用*/ cmp r0, #0 beq normal_boot /* 如果为0可能是直接加载跳过参数处理 */ /* 4. 保存启动参数结构体指针到全局变量 */ ldr r4, boot_params_ptr str r0, [r4] /* 5. 根据参数进行进一步的硬件初始化或直接跳转到U-Boot主体 */通过解析这个启动参数你的引导加载器就能与ROM代码无缝衔接构建一个完整的启动链。5. 调试技巧与常见问题排查即使理解了所有原理在实际开发中引导失败仍是家常便饭。ROM代码本身如同一个沉默的黑匣子出错时通常只是表现为设备“变砖”——无任何输出。这时就需要借助一些高级调试手段和系统的排查思路。5.1 利用追踪向量Tracing VectorROM代码内部维护了一个64位的追踪向量Table 26-48每一位对应一个关键的执行步骤。这个向量在启动初期被写入到SRAM的某个固定位置具体地址需查TRM。虽然我们无法在运行时实时读取但可以通过以下方式利用它编写一个最小调试加载器这个加载器不干别的只做一件事——通过UART将SRAM中追踪向量的值打印出来。你可以通过仿真器如JTAG将其加载到内部SRAM并运行。分析位图通过比对打印出的位图与Table 26-48可以精确判断ROM代码死在了哪个环节。例如位3置位Booting started说明ROM开始引导流程。位4置位Memory booting started说明进入了存储设备引导流程。位34置位NAND说明尝试了NAND引导。位47置位No known NAND was detected说明NAND识别失败。如果位图在某个步骤之后再也没有变化那么死锁就发生在那一步之后。5.2 串口调试输出UART更直接的方法是让ROM代码自己“说话”。许多TI的ROM代码在编译时包含了有限的调试输出功能可以通过UART0输出一些状态信息。你需要确认芯片支持查阅你的芯片数据手册确认ROM代码是否支持UART调试输出。正确连接硬件将处理器的UART0 TX引脚连接到USB转串口工具的RX引脚并确保电平匹配通常是3.3V TTL。配置终端软件使用Putty、Tera Term或minicom设置正确的波特率常见的有115200、57600、38400具体需查TRM、8位数据、无校验、1位停止位。解读输出如果成功你可能会看到类似CCCC等待下载或Texas Instruments ...之类的启动标识以及设备枚举和引导尝试的日志。没有输出则可能意味着时钟或引脚复用配置错误导致UART外设未能正常工作。5.3 常见问题排查速查表现象可能原因排查步骤设备完全无响应仿真器也无法连接1. 电源/时钟问题。2. CHSETTINGS配置错误导致PLL锁相失败。3. 启动设备引脚配置冲突。1. 测量核心电压、PLL输出时钟是否正常。2. 暂时移除CH中的CHSETTINGS节使用ROM默认时钟。3. 检查启动模式配置引脚BOOT[4:0]的上拉/下拉电阻是否正确。串口有输出但卡在某个设备检测1. 该启动设备硬件连接故障。2. 设备初始化参数不匹配如SD卡时钟过快。3. 设备中有残留数据被误认为有效引导签名。1. 测量设备接口的电源、时钟、数据线。2. 降低CHMMCSD中的时钟频率。3. 对存储设备进行低级格式化或完全擦除。提示找到MLO但加载失败1. FAT文件系统损坏或格式不正确。2.MLO镜像的dest_addr设置错误指向未初始化或不可访问的内存。3.MLO镜像本身编译错误或与芯片不兼容。1. 在PC上用十六进制编辑器检查SD卡FAT表和导扇区。2. 检查CHRAM配置确保SDRAM已正确初始化且dest_addr在SDRAM有效范围内。3. 使用objdump或仿真器检查MLO的入口地址和代码。系统运行不稳定随机崩溃1. SDRAM时序CHRAM配置过于激进存在边际效应。2. 电源完整性差在高速运行时电压跌落。3. 时钟CHSETTINGS配置存在抖动或倍频过高。1. 放宽SDRAM的tRCD,tRP,tRAS,CL等时序参数。2. 检查电源网络增加去耦电容。3. 降低系统主频或检查PLL滤波环路配置。从睡眠唤醒后无法恢复1.CONTROL_SAVE_RESTORE_MEM中的数据在睡眠前未正确保存。2. 保存的寄存器列表不完整或格式错误。3.Public Restore Pointer指向了错误的函数地址。1. 在进入睡眠前将SCR中保存的数据通过仿真器或UART dump出来与预期值对比。2. 严格对照TRM中Table 26-50和Table 26-51的偏移量保存数据。3. 确保恢复函数用纯汇编编写且位于永远不会被掉电的内存中如内部SRAM。5.4 高级工具JTAG仿真器当所有软件手段都失效时JTAG是最后的王牌。通过连接JTAG仿真器如TI的XDS系列你可以停止CPU在ROM代码一开始就 halt 住CPU单步执行观察寄存器和内存的变化。检查内存和外设寄存器直接读取SDRAM控制器、GPMC、MMC/SD控制器的寄存器确认其配置是否与你的CH设置一致。加载并运行自定义调试脚本在SRAM中加载一个小的调试程序用于探测硬件状态。注意事项如原文26.6.2节警告CAUTION所述在调试前必须确保DEBUG电源域已激活。通常仿真器软件会通过nTRST复位后发送至少10个TCK脉冲来完成这个操作。如果JTAG无法连接检查EMU0和EMU1引脚的上电状态它们通常需要上拉到高电平以进入正确的调试扫描链模式。6. 工程实践从零构建一个可引导的SD卡镜像理论最终要服务于实践。下面我将以一个典型的OMAP3系统从SD卡FAT32启动为例详细拆解构建引导镜像MLO和u-boot.img的全过程并嵌入CH配置。6.1 步骤一准备U-Boot源码与工具链首先获取适合你芯片的U-Boot源码如u-boot-omap3。配置交叉编译工具链例如arm-none-eabi-或arm-linux-gnueabihf-。6.2 步骤二配置与编译SPL (MLO)U-Boot的SPLSecondary Program Loader就是最终要生成的MLO文件。它需要包含CH信息。# 进入U-Boot源码目录 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- omap3_beagle_defconfig # 关键在.config或板级配置头文件中确保以下配置被启用 # CONFIG_SPLy # CONFIG_SPL_FRAMEWORKy # CONFIG_SPL_OMAP3_ID_NANDy (如果从NAND启动) # CONFIG_SPL_OMAP3_ID_MMCy (如果从MMC/SD启动通常需要) # CONFIG_SPL_SYS_MALLOC_SIMPLEy (简化内存分配) # CONFIG_SPL_SERIAL_SUPPORTy (可选使SPL支持串口输出)更重要的配置在于板级文件。以beagleboard为例你需要修改include/configs/omap3_beagle.h或对应的板级C文件/* 定义CH配置 */ #define CONFIG_SYS_SDRAM_BASE 0x80000000 /* SDRAM起始物理地址 */ #define CONFIG_SYS_INIT_RAM_ADDR 0x4020f800 /* 内部SRAM地址用于临时栈和全局数据 */ #define CONFIG_SYS_INIT_RAM_SIZE 0x800 /* 内部SRAM可用大小 */ /* 通过CONFIG_SPL_BUILD宏在编译SPL时包含CH配置 */ #ifdef CONFIG_SPL_BUILD /* 定义CHRAM配置数据这些值需要根据你的具体SDRAM芯片计算 */ #define SDRC_MCFG_0_VAL 0x01881114 #define SDRC_ACTIM_CTRLA_0_VAL 0x6B5A4851 #define SDRC_ACTIM_CTRLB_0_VAL 0x00020317 #define SDRC_RFR_CTRL_0_VAL 0x0000045E /* ... 其他SDRC寄存器值 */ /* U-Boot的SPL框架通常会自动生成一个‘ch’节并将这些数据链接进去。 * 你需要查阅U-Boot源码中关于你具体平台的‘spl.c’或‘mux.c’文件 * 看它是如何组织并传递这些配置数据的。通常是通过一个特定的数据结构 * 并在链接脚本中指定其输出段。 */ #endif /* CONFIG_SPL_BUILD */编译SPLmake ARCHarm CROSS_COMPILEarm-linux-gnueabihf- spl/u-boot-spl.bin生成的u-boot-spl.bin就是我们的裸二进制文件但还不是最终的MLO。6.3 步骤三为SPL镜像添加GP头生成MLOu-boot-spl.bin是纯二进制代码ROM代码无法识别。我们需要为其添加Table 26-46定义的GP头大小和目的地地址。TI提供了一个工具mkimage在U-Boot的tools/目录下但更常用的是其变体signGP或芯片厂商提供的专用工具。实际上U-Boot的构建系统通常会自动完成这一步。对于OMAP3编译后会在spl/目录下生成MLO文件。如果没有你可能需要手动操作# 假设我们已经计算好大小和地址 SPL_SIZE$(stat -c%s spl/u-boot-spl.bin) # 目的地地址通常是SRAM的地址例如0x40200000 LOAD_ADDR0x40200000 # 使用一个小程序或脚本创建带头的镜像 # 伪代码示例 echo -ne $(printf %08x $SPL_SIZE | sed s/\(..\)/\\x\1/g) MLO.tmp # 小端序写入大小 echo -ne $(printf %08x $LOAD_ADDR | sed s/\(..\)/\\x\1/g) MLO.tmp # 小端序写入地址 cat spl/u-boot-spl.bin MLO.tmp mv MLO.tmp MLO重要LOAD_ADDR必须是芯片内部SRAM如OMAP3的SRAM的地址因为此时SDRAM可能还未初始化。ROM代码会将MLO加载到这个地址。6.4 步骤四编译主U-Boot镜像 (u-boot.img)主U-Boot镜像不需要GP头因为它将由SPLMLO来加载。make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- u-boot.imgu-boot.img通常是一个包含U-Boot自身格式头的镜像SPL知道如何解析它。6.5 步骤五准备SD卡并烧录识别SD卡设备在Linux下使用lsblk或sudo fdisk -l找到你的SD卡设备例如/dev/sdb。创建分区并格式化为FAT32sudo fdisk /dev/sdb # 在fdisk中输入 o 创建新的DOS分区表输入 n 创建新分区选择主分区默认起始扇区输入 64M 设置大小足够存放MLO和u-boot.img即可输入 t 选择分区类型为 c (W95 FAT32 LBA)输入 a 设置可启动标志输入 w 保存并退出。 sudo mkfs.vfat -F 32 -n BOOT /dev/sdb1挂载并拷贝文件sudo mount /dev/sdb1 /mnt sudo cp MLO /mnt/ sudo cp u-boot.img /mnt/ # 确保文件名为全大写且MLO是第一个拷贝的文件这对某些ROM代码的读取顺序有优化 sudo umount /mnt弹出SD卡插入目标板上电启动。6.6 验证与调试如果启动成功你应该能在串口终端看到U-Boot的启动信息。如果失败请回到第5节利用串口输出、追踪向量或JTAG进行排查。重点关注SPLMLO是否被正确加载和执行串口是否有SPL的早期输出SDRAM初始化是否成功可以在SPL代码中加入点灯或串口打印在初始化SDRAM前后输出信息u-boot.img是否被成功加载SPL通常会打印加载进度整个过程是对ROM代码、FAT文件系统、硬件配置和引导加载器协同工作的深度整合。每一个环节都必须严丝合缝任何一个字节的错误都可能导致整个链条断裂。耐心、细致的调试和对原理的透彻理解是攻克嵌入式系统引导难题的唯一途径。