1. 项目概述与核心价值
在嵌入式系统开发中,尤其是基于TI AM335x这类复杂应用处理器的项目,最让人头疼的往往不是应用逻辑的编写,而是系统“第一脚”怎么踢出去——也就是上电启动。很多工程师都遇到过这样的场景:精心设计的板子焊接完毕,满怀期待地通电,结果串口一片死寂,或者卡在某个神秘的阶段。问题的根源,十有八九出在对ROM Code和SYSBOOT配置的理解不够透彻上。
ROM Code,你可以把它理解为芯片出厂时自带的“生存本能”。它固化在芯片内部的只读存储器中,是系统上电或复位后,CPU脱离复位状态执行的第一段代码。它的任务非常明确:在几乎“一无所有”(没有初始化内存、没有配置时钟、外设都处于默认状态)的硬件环境下,完成最基础的初始化,然后根据我们预先设定的“寻人启事”(即SYSBOOT引脚配置),去指定的地方(如Flash、SD卡、网络)找到并加载我们真正的应用程序(Initial Software, ISW)。这个过程,就是启动引导(Booting)。
AM335x的ROM Code之所以强大且复杂,在于它支持多达十几种启动设备,并允许通过引脚灵活配置启动顺序和参数。但这份强大也带来了复杂性:SYSBOOT[4:0]这5个引脚的不同电平组合,对应着TI手册里那张令人望而生畏的庞大配置表,决定了启动设备的优先级、接口的工作模式、甚至外部晶振的频率。理解这张表,是驯服AM335x启动过程的关键。
本文将从一个一线嵌入式开发者的视角,带你彻底拆解AM335x的ROM Code启动流程。我们不会止步于翻译数据手册,而是结合实际的调试经验、常见的配置“坑点”以及内存布局的深层逻辑,让你不仅知道SYSBOOT每个比特位是干什么的,更明白为什么要这么设计,以及在你的板子上应该如何配置才能确保万无一失。无论你是正在评估AM335x进行新硬件设计,还是正在为一块“变砖”的开发板寻找救砖方案,这篇文章都将提供可直接参考的路径和避坑指南。
2. ROM Code启动流程全景解析
AM335x的启动不是一个简单的“读取-运行”过程,而是一个分层递进、包含安全切换的状态机。理解这个全景图,是后续进行任何细节调试的基础。
2.1 启动阶段划分:从Secure到Public
AM335x的启动流程严格分为两个阶段:安全模式(Secure Mode)和公共模式(Public Mode)。这是一种基于TrustZone安全扩展的设计。
2.1.1 安全启动阶段(Secure Startup)
芯片上电或冷复位后,CPU首先运行的是安全ROM代码(Secure ROM Code)。这部分代码对用户是完全不可见的,存储在芯片内部的安全区域。它的核心任务有三个:
- 芯片自检与安全初始化:进行最底层的芯片完整性检查,初始化安全相关的硬件模块。
- 运行环境建立:为后续公共代码的执行准备一个最小的、安全的环境。
- 模式切换:完成其使命后,将CPU的操作模式从安全世界(Secure World)切换到普通世界(Normal World, 即Public Mode),并将执行权交给公共ROM代码。
这个阶段开发者无法干预,但其结果直接影响后续流程。例如,如果安全启动失败(如芯片熔丝被错误编程),设备可能无法进入公共模式。
2.1.2 公共启动阶段(Public Startup)
公共ROM代码(Public ROM Code)就是我们通常所说的“ROM Code”,也是本文讨论的核心。它的入口地址固定在0x30000。一旦CPU跳转到此地址,便开始了我们可观测和配置的启动流程。其主要职责包括:
- 基础硬件初始化:配置MPU(Cortex-A8内核)的看门狗(超时默认为3分钟),初始化必要的时钟和DPLL。
- 解析SYSBOOT配置:读取控制模块(Control Module)中SYSBOOT引脚锁存的状态,生成启动设备列表。
- 执行引导循环:按照列表顺序,尝试从各个设备引导。
- 加载与验证镜像:找到有效镜像后,将其加载到内部SRAM,并进行必要的验证(如校验和)。
- 跳转执行:最终将CPU的执行权交给加载到SRAM中的ISW。
关键点:公共ROM Code运行时,MMU和L1缓存都是关闭的。这意味着所有内存访问都是物理地址、非缓存的。这解释了为什么ROM Code的初始化代码对性能要求不高,以及为什么ISW通常需要先被加载到SRAM(确保稳定执行)而非直接在外存XIP(Execute In Place)。
2.2 核心引导流程详解
公共ROM Code的主引导流程是一个典型的“尝试-失败-下一个”循环,其逻辑可以用以下步骤概括,这也是调试时分析串口输出或指示灯状态的理论依据:
- 平台初始化:配置看门狗、根据SYSBOOT[15:14]配置系统主晶振频率、初始化核心DPLL(CORE, MPU, PER)以产生各模块所需的工作时钟。
- 构建设备列表:根据SYSBOOT[4:0]的值,查表确定最多4个备选启动设备及其顺序。例如,
SYSBOOT[4:0] = 00101b对应着NOR -> USB_MS (USB1) -> EMAC1 -> USB_CL (USB0)这个搜索序列。 - 遍历设备列表: a.取设备:从列表中取出当前要尝试的设备。 b.判断类型:
- 内存设备(NOR/NAND/MMC/SPI/QSPI/USB_MS):执行“内存引导流程”。ROM Code会尝试读取该存储设备的特定偏移地址(如MMC的扇区0),寻找有效的镜像头。
- 外设设备(UART/USB_CL/EMAC):执行“外设引导流程”。ROM Code会初始化该接口,然后等待主机(如PC)通过XMODEM、RNDIS、TFTP等协议发送镜像。 c.尝试引导:
- 如果在该设备上找到了有效且完整的镜像,则将其拷贝到内部SRAM的指定区域(
0x402F0400或0x402F4000开始,取决于芯片版本)。 - 如果失败(超时、通信错误、镜像无效),则递增该设备的“引导错误计数器”(位于
0x40337DE0开始的地址),并返回步骤3a尝试列表中的下一个设备。
- 镜像验证与跳转:成功加载镜像到SRAM后,ROM Code会进行必要的验证(对于安全启动,可能包括数字签名验证)。验证通过后,它会准备一个引导参数结构体的指针放入R0寄存器,然后跳转到SRAM中镜像的入口点,将控制权彻底移交给ISW(通常是U-Boot或类似的二级引导程序)。
这个流程的精华在于其容错性和灵活性。通过一个有序的列表,系统可以自动尝试多种启动方案,例如优先从板载Flash启动,失败后自动降级到通过网络或USB烧录模式启动,这极大地方便了工厂生产和现场维护。
3. SYSBOOT配置引脚深度解读与实战配置
SYSBOOT配置是连接硬件设计与软件启动行为的桥梁。AM335x提供了多达19个SYSBOOT引脚(SYSBOOT[18:0]),但最常用、也最复杂的是其中一部分。我们将其分为两类:全局配置位和设备相关配置位。
3.1 全局配置位:影响整个启动环境
这些位的设置对后续所有启动尝试都有影响,必须在设计硬件时首要确定。
3.1.1 SYSBOOT[15:14] - 系统时钟源选择
这是最重要的配置之一,它决定了输入到芯片的主振荡器(Main Oscillator)频率。
00b: 19.2 MHz01b: 24 MHz10b: 25 MHz11b: 26 MHz
为什么是这几个频率?因为内部DPLL的倍频系数是针对这些基频优化设计的,可以产生无误差的标准时钟(如25MHz的基频可以方便地倍频出100MHz、125MHz等常用频率)。配置错误后果:如果硬���焊接的是24MHz晶振,但配置为
00b(19.2MHz),那么DPLL计算出的所有时钟(CPU、外设)频率都会偏差约20%,可能导致内存控制器、USB PHY等对时钟精度敏感的模块工作异常,表现为启动失败或运行不稳定。
3.1.2 SYSBOOT[18] - CLKOUT2输出控制(仅PG1.2及以后版本)
0b: 不将EXTDEV_PLL时钟路由到CLKOUT2引脚。1b: 将EXTDEV_PLL时钟路由到CLKOUT2引脚输出。
3.1.3 SYSBOOT[17] - CLKOUT1输出使能
0b: 禁用CLKOUT1引脚时钟输出。1b: 使能CLKOUT1引脚时钟输出。
3.1.4 SYSBOOT[16] - USB DP/DM引脚交换(仅PG1.2及以后版本)
0b: USB差分数据线不交换(正常连接)。1b: USB DP/DM信号交换。 这个功能用于纠正PCB布线时可能出现的差分线反接错误,无需改动硬件即可通过软件配置修复。
3.1.5 SYSBOOT[13:12] - 保留位必须设置为00b,用于正常操作。
3.2 设备相关配置位与启动顺序解码
SYSBOOT[4:0]这5位是启动配置的“灵魂”,它直接映射到数据手册中那个庞大的表格(Table 5-10)。我们不需要死记硬背,但要理解其编码逻辑。
3.2.1 启动顺序的编码逻辑
SYSBOOT[4:0]的值(0b00000 到 0b11111)作为一个索引,查表得到对应的4个启动设备序列。ROM Code会严格按照这个顺序尝试。例如:
00101b(十进制5):NOR -> USB_MS (USB1) -> EMAC1 -> USB_CL (USB0)01100b(十进制12):MMC0 -> (无) -> (无) -> (无)(注意:很多配置的2、3、4号设备是“无效”或“不关心”的,ROM Code尝试完第一个就会失败或停止)
3.2.2 设备特定参数解析
同一启动设备(如NAND),在不同的SYSBOOT[4:0]配置行中,可能还需要其他SYSBOOT位来定义其工作模式:
- NAND Flash相关:
SYSBOOT[6]: Wait引脚复用选项。决定NAND的R/B#(Ready/Busy)信号使用哪个引脚。SYSBOOT[5]: ECC校验责任方。0b: ECC由ROM Code计算和校验。这是最常用的设置,因为ROM Code在读取NAND时会自动处理ECC。1b: ECC由NAND控制器硬件完成。通常用于高级场景或使用不支持标准ECC的NAND芯片时。
- NOR Flash相关:
SYSBOOT[11]: 复用与非复用模式选择。决定地址/数据总线是复用(共用)还是独立。SYSBOOT[9]: WAIT信号监控使能。如果NOR Flash速度较慢,需要使能此功能,ROM Code会在读写时采样WAIT信号以插入等待周期。SYSBOOT[8]: Wait引脚复用选项(类似NAND)。SYSBOOT[7:6]: NOR Flash引脚复用选项。决定NOR Flash接口占用哪组GPIO引脚。
- QSPI Flash相关:
SYSBOOT[7]: 数据线宽度选择。0b: 四线读取(Quad Read),速度更快。1b: 单线读取(Single Read),兼容性更好。
SYSBOOT[6]: QSPI引脚复用选项。
- 以太网(EMAC)相关:
SYSBOOT[5]: PHY接口类型选择。0b: MII接口。需要更多引脚(约16个),常用于板载PHY。1b: RMII接口。引脚数减半(约7个),常用于外接PHY模块或节省布线空间。
SYSBOOT[18]与SYSBOOT[5]配合:为外部PHY提供时钟。当SYSBOOT[5]=0,EXTDEV_PLL配置为25MHz;SYSBOOT[5]=1则配置为50MHz,并通过CLKOUT2输出(如果SYSBOOT[18]=1)。
3.3 实战配置案例与硬件设计要点
假设我们要设计一个典型的工业控制器,启动方案为:首选从板载eMMC(MMC0)启动,备用方案为通过以太网(EMAC1)进行TFTP网络启动。
确定SYSBOOT[4:0]值:查阅手册Table 5-10,寻找第一设备为MMC0,第二设备为EMAC1的行。我们发现
SYSBOOT[4:0] = 10110b(十进制22)对应EMAC1 -> NAND_I2C -> NAND -> MMC0,顺序不符。我们需要的是MMC0在第一顺位。继续查找,01100b(十进制12)是MMC0,但无EMAC1。10110b顺序不对。实际上,手册的表格是固定的,可能没有完全符合我们“MMC0第一,EMAC1第二”的条目。这时需要做出权衡:- 方案A(推荐):选择
00100b(十进制4):MMC1 -> MMC0 -> USB_MS -> USB_CL。我们将eMMC接到MMC0端口,但配置为从MMC1启动?这不对。实际上,我们可以将eMMC硬件连接到MMC1接口,然后选择此配置。这样,第一启动设备是MMC1(我们的eMMC),第二是MMC0(可接SD卡座作为备用),第三、四是USB。网络启动不在列表中。 - 方案B(使用网络):如果我们必须保留网络启动作为备用,可能需要选择以EMAC1为第一启动设备的配置,如
11011b(十进制27):EMAC1 -> UART0 -> NOR。但这意味着放弃了MMC0作为第一启动项。
结论:AM335x的ROM Code启动顺序表是预定义的,灵活性有一定限制。常见的做法是:
- 生产环境:配置为从最可靠的存储(如eMMC)启动,不设复杂备用。
- 开发/烧录环境:配置为从UART或USB启动,方便通过串口或USB下载程序到Flash。
- 方案A(推荐):选择
配置其他引脚:
- 假设我们使用24MHz晶振,则
SYSBOOT[15:14] = 01b。 - 不使用CLKOUT1,则
SYSBOOT[17] = 0b。 - 不使用USB交换,则
SYSBOOT[16] = 0b(如果芯片版本支持)。 - 保留位
SYSBOOT[13:12] = 00b。 - 我们的启动设备是MMC,不涉及NAND/NOR/QSPI/EMAC的特殊配置位(
SYSBOOT[11], [9], [8], [7], [6], [5]),这些位在该配置行中通常标记为“don‘t care”,但硬件设计时最好给这些引脚接固定电平(上拉或下拉),避免悬空导致状态不确定。
- 假设我们使用24MHz晶振,则
硬件设计:根据以上决定,我们需要在PCB上通过电阻将
SYSBOOT[4:0]、SYSBOOT[15:14]等引脚拉高或拉低。强烈建议使用0欧姆电阻或跳线帽来配置这些引脚,而不是直接焊接死。这为后续调试和启动模式切换留下了余地。
4. 内存映射与关键数据结构剖析
理解ROM Code运行时的内存布局,对于高级调试、分析启动失败原因以及编写自己的引导程序都至关重要。
4.1 ROM与RAM内存地图
4.1.1 片上ROM内存布局
AM335x的片上ROM总容量为240KB,其中前176KB为Secure ROM,后64KB为Public ROM。Public ROM的映射如下:
0x30000 - 0x3001F:异常向量表。这是CPU发生复位、中断、 abort等异常时首先跳转的地址。0x30020:Public ROM CRC值。用于校验ROM代码本身的完整性。0x30080 - 0x300BC:死循环(Dead Loops)集合。这是ROM Code预定义的一系列无限循环分支指令,用作默认的异常处理或调试状态指示。例如,0x3009C是“验证测试通过”循环,0x300A0是“验证测试失败”循环。通过让PC指针停在这些地址,可以间接判断代码执行到了哪个状态。0x30100 - 0x3FFFC:ROM代码与常量数据区。0x3FFFC(PG1.2):ROM版本号。低8位是主版本,次8位是次版本。
4.1.2 公共L3 RAM内存布局(关键工作区)
这是ROM Code和初始软件(ISW)运行时的“舞台”,范围是0x402F0000到0x4033FFFF。
0x402F0400(PG1.1) 或0x402F4000(PG1.2) 到0x40337C00:下载镜像区。这是ROM Code从外部设备(Flash, SD卡,网络等)拷贝进来的初始软件(如U-Boot)的存放位置。大小约为271KB(GP设备)。重要限制:如果通过USB大容量存储(USB_MS)模式启动,镜像只能从0x40300000���始存放,最大尺寸缩减到约220KB。0x40337DE0 - 0x40337DEC:启动错误计数器。四个32位寄存器,分别记录第1到第4个启动设备尝试失败的次数。这是一个非常实用的调试信息。如果系统反复重启,可以连接调试器读取这些地址,看看是哪个设备卡住了。0x40337E00 - 0x40338E1F:公共栈空间。8KB大小,用于ROM Code的函数调用。0x40338E00 - 0x40338E1F:RAM异常向量表。ROM异常向量最终会跳转到这里。0x40338E20 - 0x40338E3F:异常处理程序地址表(用户可修改)。这是关键!默认情况下,这些地址指向ROM中的死循环。我们的ISW可以在早期初始化中,修改这些地址(例如0x40338E38的IRQ句柄),使其指向我们自己的中断服务程序,从而接管异常处理。0x40338E40 - 0x40338E63:跟踪数据区。ROM Code在执行过程中会在此处写入跟踪向量,记录执行路径,用于深度调试。0x40338E64以上:ROM静态变量区。
4.2 异常处理机制:ROM到RAM的接力
AM335x的异常处理设计得很巧妙,实现了从ROM固定处理到用户自定义处理的平滑过渡。
- 异常发生:例如,发生了一个“未定义指令”异常。
- 跳转至ROM向量:CPU硬件自动跳转到
0x30004(Undefined Instruction异常向量地址)。 - ROM向量引导:
0x30004处存放的指令是LDR PC, [PC, #0x20](假设)。这条指令的效果是:将程序计数器PC设置为内存地址[0x30004 + 8 + 0x20] = 0x3002C处存放的值。但根据ROM映射,0x3002C并不是一个直接存储地址的向量表项。实际上,ROM Code的向量表指令是精心计算的,它会跳转到RAM异常向量表的对应位置,即0x40338E04。 - RAM向量接力:
0x40338E04处存放的指令是LDR PC, [PC, #0x20]。这条指令会读取0x40338E28地址处的值到PC。 - 用户自定义处理:
0x40338E28这个地址位于“异常处理程序地址表”中。它的默认值是0x30084(ROM中的SWI死循环)。但是,我们的ISW在启动后,可以修改0x40338E28这个内存位置,将其值改为我们自己的SWI异常处理函数的地址。这样,当下次发生SWI异常时,流程就会变成:0x30008 -> 0x40338E08 -> [0x40338E2C] -> 我们的处理函数。
这种机制保证了ROM Code的异常向量表是固定且不可写的,但通过一层RAM中的间接跳转,为用户程序提供了覆盖默认异常处理的灵活性。
4.3 引导参数结构体:ROM留给ISW的“信件”
当ROM Code成功找到并验证镜像后,在跳转到ISW之前,它会将一个重要的数据结构的地址放入R0寄存器。这个数据结构就是引导参数结构体,它是ROM Code传递给下一阶段软件的“交接文档”。
其结构定义如下(基于常见理解,具体偏移需核对最新手册):
typedef struct { uint32_t reserved; // 0x00: 保留 void* device_descriptor; // 0x04: 指向启动设备描述符的指针(ROM内部使用) uint8_t boot_device; // 0x08: 当前使用的启动设备代码 uint8_t reset_reason; // 0x09: 复位原因位图 uint8_t reserved2[10]; // 0x0A: 保留 } boot_params_t;boot_device:这是一个枚举值,告诉ISW它是从哪个设备被加载的。例如,0x07代表MMC/SD port 0,0x47代表CPGMAC0(以太网)。ISW可以根据这个信息初始化对应的驱动,或者显示启动来源。reset_reason:一个位掩码,指示本次启动是由什么事件触发的。例如,bit 0表示上电复位,bit 4表示看门狗复位。这对于诊断系统意外重启的原因极其有用。
在U-Boot等引导程序中,通常会在一开始就保存R0的值,然后解析这个结构体,从而获得关键的硬件上下文信息。
5. 时钟与电源初始化细节
ROM Code在公共启动阶段,需要建立一个让自身和后续基本外设(如UART用于调试输出,MMC用于读取镜像)能够运行的最小时钟系统。
5.1 DPLL配置策略
ROM Code不会配置所有的DPLL和时钟,它只初始化启动所必需的部分:
- MPU ADPLLS:锁定到300MHz,为Cortex-A8内核提供时钟。这是CPU运行的主频。
- CORE ADPLLS:锁定到2GHz,然后通过分频器产生:
- L3_FCLK: 100MHz。这是L3互连总线的功能时钟,许多高速外设挂载于此。
- Ethernet Module Clocks: 100MHz, 50MHz, 5MHz。用于以太网控制器和PHY。
- PER ADPLLJ:锁定到960MHz,分频后产生:
- PER_CLKOUTM2: 192MHz。作为多个低速外设的时钟源。
- SPI_CLK: 48MHz (192MHz / 4)。
- MMC_CLK: 96MHz (192MHz / 2)。用于SD/MMC控制器。
- UART_CLK: 48MHz (192MHz / 4)。
- I2C_CLK: 48MHz (192MHz / 4)。
- QSPI_CLK: 12MHz (192MHz / 16)。用于QSPI Flash接口。
- USB PHY时钟:直接使用PER_CLKDCOLDO的960MHz。
关键点:这个时钟配置是一个最小化、安全的配置。它保证了ROM Code自身和基本启动外设能工作,但性能并非最优。例如,MMC时钟只有96MHz,可能无法发挥eMMC 5.1的高速性能。因此,我们的ISW(如U-Boot)在启动后,通常需要根据实际硬件(如DDR3的速度、外设需求)重新配置DPLL和时钟分频器,以达到最佳性能。
5.2 看门狗与超时管理
ROM Code在公共初始化阶段会配置并启动MPU的看门狗定时器,超时时间设置为3分钟。这是一个重要的安全机制。
作用:防止系统在启动过程中卡死。例如,如果SYSBOOT配置错误,导致ROM Code一直在尝试一个不存在的设备(如错误的NAND时序),或者在等待主机发送网络镜像时主机无响应,看门狗会在3分钟后触发复位,让系统有机会尝试列表中的下一个启动设备。
对ISW的影响:我们的ISW在获得控制权后,必须尽快重新配置或禁用这个看门狗。否则,如果ISW的初始化或加载操作系统的时间超过3分钟,系统会被看门狗复位,陷入重启循环。在U-Boot中,通常是在board_init_f或board_init_r的早期调用看门狗相关函数进行处理。
6. 常见启动问题排查与调试技巧实录
基于AM335x的启动问题排查,是一个从硬件到软件、从静态配置到动态交互的过程。以下是我在实际项目中总结的排查流程和技巧。
6.1 问题排查流程图与速查表
当一块AM335x板卡无法启动时,可以遵循以下流程图进行系统性排查:
graph TD A[板卡上电无反应] --> B{测量核心电压与时钟}; B -->|正常| C{检查SYSBOOT引脚电平}; B -->|异常| Z[检查电源电路与晶振]; C -->|与设计相符| D{连接JTAG调试器}; C -->|不符| Y[检查上下拉电阻与PCB短路/开路]; D -->|可连接| E{暂停CPU, 查看PC指针}; D -->|无法连接| X[检查JTAG接口、 复位电路、 芯片是否损坏]; E -->|PC在ROM区间 0x30000附近| F{查看启动错误计数器 0x40337DE0}; E -->|PC在其他地方或杂乱| W[可能DDR/时钟初始化失败, 或代码跑飞]; F -->|计数器1递增| G[第一启动设备失败, 检查该设备硬件与连接]; F -->|计数器2递增| H[第二启动设备失败]; F -->|计数器均为0| I[未进入设备尝试循环, 检查ROM Code早期初始化]; G --> J[具体设备排查: Flash/MMC/USB/UART/ETH];表:启动失败常见症状与可能原因
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
| 完全无反应,无电流 | 电源故障,核心电压缺失,晶振未起振 | 测量电源轨电压,用示波器测晶振引脚 |
| 有电流,但串口无任何输出 | SYSBOOT配置错误,UART引脚复用错误,时钟配置错误 | 1. 确认SYSBOOT[15:14]与板上晶振匹配。 2. 确认UART0引脚未被其他功能占用。 3. 测量UART_TXD引脚是否有波形(波特率可能与预期不同)。 |
| 串口输出乱码 | 波特率不匹配,时钟频率错误 | 1. 尝试常用波特率(115200, 57600, 38400)。 2. 确认SYSBOOT[15:14]配置正确。 |
输出CCC后停止 | ROM Code在尝试UART下载模式(XMODEM) | 这是正常行为,表示ROM Code正在等待主机通过串口发送镜像。如果不想用UART启动,检查SYSBOOT[4:0]配置,确保UART不在启动列表前几位。 |
输出C后停止 | 看门狗超时复位 | 系统在3分钟内未找到有效镜像。检查所有启动设备。可通过JTAG读取0x40337DE0开始的错误计数器判断卡在哪个设备。 |
| 仅输出少量字符后停止 | DDR初始化失败,或ISW镜像损坏 | 1. 使用JTAG检查DDR控制器配置。 2. 检查烧写到存储设备的镜像是否完整(校验和)。 3. 确认镜像格式(如TI的GP Header)是否正确。 |
6.2 高级调试手段:利用跟踪向量与错误计数器
当常规串口日志不足时,需要动用更底层的调试工具。
6.2.1 读取跟踪向量(Tracing Data)
地址0x40338E40开始的20个字节(5个字)保存了ROM Code执行路径的跟踪码。这些代码是ROM Code内部定义的,TI通常不公开完整列表,但通过对比正常和异常启动时的值,可以定位问题发生在哪个阶段。例如,如果跟踪向量在某个值后不再变化,可能意味着代码在该处卡死。
6.2.2 读取启动错误计数器
这是最实用的调试信息之一。通过JTAG或挂载到运行中的ISW(如果它能部分运行),读取0x40337DE0(Boot Mode 1),0x40337DE4(Boot Mode 2)等地址的值。
- 如果
0x40337DE0的值很大,说明系统反复尝试第一个启动设备都失败了,然后看门狗复位,循环往复。问题肯定出在第一个设备上。 - 如果
0x40337DE0为0,但0x40337DE4有值,说明第一个设备尝试成功(或快速失败),但卡在了第二个设备上。
6.2.3 使用JTAG进行指令级调试
连接JTAG调试器(如TI的XDS系列),在CCS或GDB中暂停CPU。
- 检查PC指针:如果PC在
0x30000到0x3FFFF之间,说明CPU还在执行ROM Code。 - 检查寄存器:查看R0-R15、CPSR寄存器。R0在跳转到ISW前应包含引导参数结构体地址。
- 检查内存:查看
0x402F0400或0x40300000区域,确认从启动设备读取的镜像数据是否被正确加载到此。可以对比原始镜像文件进行验证。 - 单步执行:在ROM Code区域小心地进行单步,观察执行流是否按预期跳转。
6.3 特定设备启动失败排查要点
6.3.1 MMC/SD卡启动失败
- 现象:错误计数器1(如果MMC是第一个设备)持续增加。
- 排查:
- 电气:用示波器检查MMC_CLK(引脚
M19/L18)是否有96MHz时钟输出?CMD和DAT线是否有上拉? - 配置:确认SYSBOOT[4:0]正确选择了MMC0或MMC1。
- 镜像:SD卡是否格式化为FAT?镜像是否放在名为
MLO的文件中(对于RAW模式,是否烧写到了正确的扇区)?MLO文件是否包含TI特有的GP Header? - 时序:对于eMMC,ROM Code使用的是默认的、较保守的时序。如果eMMC芯片型号较新或要求特殊时序,可能需要通过eMMC的扩展寄存器进行配置,这超出了ROM Code的能力,可能导致初始化失败。
- 电气:用示波器检查MMC_CLK(引脚
6.3.2 UART启动无响应
- 现象:串口输出
CCC后停止,但主机发送XMODEM协议无反应。 - 排查:
- 波特率:ROM Code的UART波特率是固定的115200,8N1。确认主机终端软件设置正确。
- 流控:ROM Code的UART启动不使用硬件流控(RTS/CTS)。确保连接线只用了TX、RX、GND三根线。
- 协议:ROM Code期望的是XMODEM协议。确保使用的下载工具(如
kermit的send命令,或lrzsz的sb命令)支持并正确使用了XMODEM。 - 镜像头:通过UART下载的镜像同样需要包含TI的GP Header。
6.3.3 以太网(EMAC)启动失败
- 现象:系统停留在等待网络状态,错误计数器增加。
- 排查:
- SYSBOOT[5]:确认配置为MII还是RMII,与PHY芯片的接口类型匹配。
- SYSBOOT[18]与时钟:如果使用RMII且需要50MHz时钟参考,确认
SYSBOOT[18]=1且SYSBOOT[5]=1,并用示波器测量CLKOUT2引脚是否有50MHz输出。 - PHY地址:ROM Code默认尝试的PHY地址是1。确保你的PHY芯片的硬件地址(通过PHYAD引脚设置)是1。
- 网络环境:确保开发板与主机在同一网段,主机开启了DHCP服务器(用于BOOTP获取IP)和TFTP服务器,并且TFTP服务器目录下有正确的镜像文件(如
u-boot.img)。
启动问题的排查往往需要耐心和系统性思维。从最基础的电源、时钟、配置引脚查起,逐步深入到接口信号、协议交互。充分利用错误计数器和跟踪向量这些ROM Code提供的内建诊断工具,能极大缩短定位问题的时间。记住,一个稳定的启动是嵌入式产品可靠性的基石,在SYSBOOT配置和启动镜像制作上多花些功夫,能为后续开发省去无数麻烦。