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。如果你用错波特率,看到的全是乱码。嗯,这里要注意,乱码不一定是硬件坏了,很可能是波特率没对上。
// 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引脚,而且速度可以更快。
不过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,基本可以断定硬件有严重问题,或者固件刷错了。我建议你直接检查硬件连接和电源。
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日志看看。嗯,你会发现很多有趣的东西。