3、BootROM日志基础:日志输出机制(UART、JTAG)、日志级别(INFO、WARN、ERROR、FATAL)

各位好,欢迎来到第三讲。

上一讲我们聊了BootROM的启动流程,说白了就是芯片上电后那几行代码到底在干嘛。今天咱们深入一个非常实际的话题——日志。

你想想看,芯片刚上电,连操作系统都没跑起来,你怎么知道它内部发生了什么?靠猜吗?当然不是。靠的就是BootROM吐出来的那几行日志。我个人习惯,拿到一块新板子,第一件事就是抓BootROM日志。这玩意儿比任何文档都诚实。

3.1 日志输出机制:UART与JTAG

BootROM的日志输出,说白了就两条路:UART 和 JTAG。这两条路各有各的脾气,咱们一个一个说。

3.1.1 UART输出

UART是最常见的日志输出方式。为什么?因为它简单、便宜、两根线就能搞定。BootROM在初始化完时钟和引脚后,第一件事往往就是配置UART控制器。

我在项目中遇到过,有些芯片的BootROM默认UART波特率是115200,有些是921600。如果你用错波特率,看到的全是乱码。嗯,这里要注意,乱码不一定是硬件坏了,很可能是波特率没对上。

典型UART初始化流程(伪代码):
// BootROM中的UART初始化
void uart_init(void) {
    // 1. 使能UART模块时钟
    CLK_ENABLE(UART_CLK_GATE);
    
    // 2. 配置引脚复用为UART功能
    PINMUX_SELECT(UART_TX_PIN, PINMUX_MODE_UART);
    PINMUX_SELECT(UART_RX_PIN, PINMUX_MODE_UART);
    
    // 3. 设置波特率(以115200为例)
    UART_BAUD = CLK_FREQ / (16 * 115200);
    
    // 4. 配置数据格式:8N1
    UART_LCR = 0x03;  // 8位数据,1位停止位,无校验
    
    // 5. 使能发送器
    UART_CR |= TX_ENABLE;
}

这段代码看着简单,但坑不少。比如时钟频率算错了,波特率就不对。我曾经调试一块芯片,UART死活不出数据,查了半天发现是时钟门控没打开——代码里写了,但编译器优化掉了。你说气不气人?

3.1.2 JTAG输出

JTAG呢?它比UART复杂,但功能更强。JTAG本来是用来调试和边界扫描的,但BootROM也可以用它来输出日志。怎么做到的?

说白了,就是通过JTAG的数据寄存器(DR)来传输日志字符串。调试器那边轮询读取,然后显示出来。这种方式的好处是:不占用UART引脚,而且速度可以更快。

我的经验: 在量产阶段,很多板子根本没有引出UART引脚。这时候JTAG日志就是救命稻草。我记得有一次帮客户定位问题,他们的板子只有JTAG接口,我硬是通过JTAG把BootROM日志抓了出来,发现是DDR初始化时序不对。

不过JTAG也有缺点:它需要调试器硬件(比如J-Link、OpenOCD),不像UART那样一根USB转串口线就能搞定。所以,开发阶段用UART,调试阶段用JTAG,这是很多团队的标配。

3.2 日志级别:INFO、WARN、ERROR、FATAL

日志级别这个东西,说白了就是给日志贴个标签,告诉你这条消息有多严重。BootROM里常见的级别有四个:INFO、WARN、ERROR、FATAL。

级别 含义 典型场景
INFO 普通信息 启动进度、版本号、配置参数
WARN 警告 配置异常、降级运行、非致命错误
ERROR 错误 初始化失败、校验失败、超时
FATAL 致命错误 硬件故障、无法恢复的错误

3.2.1 INFO:告诉你我在干嘛

INFO级别的日志,就是BootROM在跟你汇报工作进度。比如:

[INFO] BootROM version: 1.2.3
[INFO] PLL locked at 800MHz
[INFO] DDR initialization started
[INFO] DDR calibration passed
[INFO] Boot device: eMMC (user area)

这些信息看着平淡无奇,但关键时刻能救命。比如DDR calibration failed,你一看就知道是内存有问题。

3.2.2 WARN:小心,有坑

WARN级别表示系统还能跑,但可能不是最优状态。比如:

[WARN] eFuse checksum mismatch, using default values
[WARN] Clock frequency lower than expected (396MHz vs 400MHz)

我曾经遇到一个案例:客户说他们的板子偶尔启动失败。我抓了日志,发现每次都有[WARN] eFuse checksum mismatch。后来查出来是eFuse烧录时电压不稳,导致部分位没烧进去。嗯,这个警告就是线索。

3.2.3 ERROR:出事了,但还能抢救

ERROR级别表示某个模块初始化失败了,但BootROM不会立即死掉。它会尝试其他方式,或者把错误信息告诉你,然后继续往下走。比如:

[ERROR] SPI NOR flash read failed at offset 0x1000
[ERROR] CRC32 check failed for boot image

这时候BootROM可能会尝试从备份启动。如果备份也失败,那就升级到FATAL了。

3.2.4 FATAL:没救了,重启吧

FATAL是最高级别。一旦出现FATAL日志,说明BootROM遇到了无法恢复的错误。它会停止执行,或者直接触发看门狗复位。比如:

[FATAL] CPU exception: undefined instruction at 0x00010020
[FATAL] No valid boot device found

看到FATAL,基本可以断定硬件有严重问题,或者固件刷错了。我建议你直接检查硬件连接和电源。

注意: 有些芯片的BootROM在FATAL之后会进入一个死循环,等待看门狗复位。这时候你只能断电重启。我曾经见过一个设计,FATAL之后连看门狗都没开,芯片直接卡死,只能硬复位。

3.3 日志输出示例:一个完整的启动日志

下面是一个典型的BootROM启动日志,我加了注释,方便你理解:

[INFO] BootROM v2.1.0 (Build 20240315)   // 版本信息
[INFO] CPU: Cortex-A72 @ 1.2GHz          // CPU信息
[INFO] PLL: locked, sys_clk = 1200MHz    // 时钟锁定
[INFO] DDR: 4GB LPDDR4, 3200MT/s         // DDR初始化成功
[WARN] DDR: timing relaxed due to temperature  // 温度高,时序放宽
[INFO] Boot device: SPI NOR (CS0)        // 从SPI NOR启动
[INFO] Image header found at 0x00000000  // 找到启动镜像头
[INFO] Image size: 256KB                 // 镜像大小
[ERROR] CRC32 mismatch! Expected 0xA5B6C7D8, got 0x12345678  // CRC校验失败
[INFO] Trying backup image at 0x00040000 // 尝试备份镜像
[INFO] Backup image CRC32 passed         // 备份镜像校验通过
[INFO] Loading image to DRAM at 0x80000000 // 加载到内存
[INFO] Jumping to entry point 0x80001000 // 跳转到应用

你看,这个日志里既有INFO,也有WARN和ERROR。但最终系统还是启动成功了,因为BootROM有容错机制。这就是日志的价值——让你知道系统经历了什么。

3.4 知识体系总览

下面这张图,帮你理清本章的核心逻辑:

BootROM日志输出机制 输出机制 日志级别 UART JTAG INFO WARN ERROR FATAL UART:简单、低成本,开发阶段首选 JTAG:功能强、不占引脚,调试阶段利器 日志级别:从INFO到FATAL,严重程度递增

好了,这一讲就到这里。日志这东西,平时你可能觉得它啰嗦,但出了问题它就是你的第一手证据。下次你拿到一块新板子,记得先抓BootROM日志看看。嗯,你会发现很多有趣的东西。

个人建议: 在项目初期,就把日志输出机制调通。别等到板子回来了再搞,那时候你会发现各种坑——引脚没拉出来、电平不匹配、波特率不对……我吃过这个亏,所以提醒你。

可可精品资料库