4. TRACE32 脚本与命令基础

TRACE32 的调试能力并不只体现在图形界面上,真正可复现、可批处理、可交接给团队的部分,几乎都落在 PRACTICE 脚本与命令系统上。启动链调试尤其如此:从上电复位到内核解压、再到 init 进程拉起,中间跨越 BootROM、SPL、U-Boot、Kernel 多个阶段,如果每次都靠手工点按钮,既慢又难以保证一致性。本节围绕启动链调试中最常用的一组命令,说明它们各自解决什么问题、参数如何组织,以及如何组合成一段可执行的调试脚本。

PRACTICE 脚本:把调试动作固化成可重复执行的文件

PRACTICE 是 TRACE32 的脚本语言,扩展名通常为 .cmm,通过 DO <file>.cmm 执行。它的价值在于把“连接目标—加载符号—设置断点—运行—观察”这一串动作写成文本,从而支持版本管理、参数化和无人值守运行。启动链调试中,最典型的骨架如下:

; boot_debug.cmm —— 启动链调试基础脚本(示例)
SYStem.RESet                    ; 复位调试系统状态
SYStem.CPU CORTEXA53             ; 声明目标 CPU 类型
SYStem.JtagClock 10MHz           ; 设置 JTAG 时钟
SYStem.Up                        ; 连接并停止目标
Data.LOAD.Elf u-boot.elf /NoCode ; 只加载符号,不写内存
Break.Set 0x80000000 /Program    ; 在 U-Boot 入口下断点
Go                               ; 运行到断点
Register.view /SpotLight        ; 观察寄存器现场

脚本中 ; 起始为注释,命令大小写不敏感,但参数关键字通常有约定写法。脚本支持 &参数 形式的入参,例如 DO boot_debug.cmm 0x80000000,脚本内用 &1 引用,这让同一份脚本可以适配不同板卡的加载地址。

SYStem 与 CPU:先建立可信的连接与目标描述

启动链调试最容易出问题的地方不是断点,而是“目标根本没连对”。SYStem 命令族负责调试系统的整体状态:SYStem.RESet 清空当前会话配置,SYStem.CPU 声明目标核类型(如 CORTEXA53、CORTEXA7),SYStem.JtagClock 设定 TCK 频率,SYStem.Up 完成连接并使目标停止。若目标处于多核或 AMP 场景,还需用 SYStem.CONFIG 指定核间拓扑与调试通道。

需要特别注意的是 SYStem.Up 与 SYStem.Mode 的区别:前者是“连接并停住”,后者用于切换 Attach、Go、StandBy 等模式。启动链调试常需要从复位向量开始,因此更常用的是 SYStem.Mode Go 配合复位,或直接 SYStem.Up 后手动控制运行。CPU 相关命令还包括 CPU 用于切换当前调试核,CPU.CHECK 用于校验核状态,多核启动链中切换核是常见操作。

Data:加载符号、读写内存与观察启动参数

Data 命令族是启动链调试中使用频率最高的工具之一。它既负责把 ELF 符号表加载进来,也负责直接访问物理内存。常用形式包括:

  • Data.LOAD.Elf <file> /NoCode:只加载符号与调试信息,不把代码段写入目标内存,适合调试已在 Flash 中运行的 U-Boot。
  • Data.LOAD.Elf <file>:完整加载,常用于把 SPL 或内核镜像下载到 DDR 后调试。
  • Data.dump 0x80000000:以默认格式显示内存内容,可加 /Long、/Word 控制宽度。
  • Data.Set 0x80000000 %Long 0x12345678:向指定地址写入数据,用于构造启动参数或修补异常值。

在启动链中,Data 的一个关键用途是检查设备树与启动参数。例如内核启动时 x0 寄存器常指向 DTB 地址,可以用 Data.dump <dtb_addr> 查看魔数 0xd00dfeed 是否就位,从而判断 bootloader 是否正确传递了设备树。

Break、Go、Step:控制执行流与定位启动卡点

这三条命令构成执行控制的核心。Break.Set 用于设置断点,支持按地址、按符号、按行号三种方式:

Break.Set 0x80000000 /Program          ; 按物理地址
Break.Set board_init_f /Program        ; 按符号
Break.Set main.c:120 /Program          ; 按源码行
Break.List                             ; 查看已设断点
Break.Delete 0x80000000                ; 删除指定断点

Go 让目标全速运行,遇到断点或异常时停止;Go.Up 则常用于从当前函数返回。Step 是单步执行,Step.Over 跳过函数调用,Step.ASM 按汇编单步,Step.Hll 按高级语言单步。启动链早期往往没有完整符号,此时应使用 Step.ASM 逐条跟踪复位向量后的指令,观察 PC 是否按预期跳转到 SPL 入口。

一个实用的启动链定位套路是:先在复位向量处 Break.Set,Go 后确认 PC 正确;再在 SPL 入口、U-Boot 入口、内核入口分别设断点,逐段 Go,哪一段没有命中,问题就落在该段之前。若命中后 Step 几步就进入异常向量,则结合 Register.view 查看 ESR_EL3、FAR_EL3 等寄存器判断异常类型。

TRACE32 启动链调试命令协作关系 SYStem / CPU 建立可信连接 SYStem.RESet SYStem.CPU CORTEXA53 SYStem.JtagClock 10MHz SYStem.Up CPU <core> Data 符号加载与内存观察 Data.LOAD.Elf u-boot.elf Data.LOAD.Elf /NoCode Data.dump 0x80000000 Data.Set 0x... %Long 0x... 检查 DTB 魔数 0xd00dfeed Break / Go / Step 执行流控制 Break.Set 0x80000000 Break.Set board_init_f Go Step.ASM / Step.Over Register.view 启动链阶段与断点落点(示例地址) BootROM 0x00000000 SPL 0x00020000 U-Boot 0x80000000 Kernel 0x80080000 init 用户态 复位向量断点 SPL 入口断点 board_init_f 断点 start_kernel 断点 init 断点 命中即停 → 观察寄存器/内存 → Step 定位 → 继续 Go 到下一阶段

把命令组合成启动链调试脚本

单条命令只是工具,真正提升效率的是把它们组织成有判断逻辑的脚本。PRACTICE 支持 IF、WHILE、GOTO 等控制结构,也支持 PRINT 输出与 WAIT 延时。下面是一段带阶段判断的启动链调试脚本示例:

; boot_chain.cmm —— 分阶段验证启动链(示例)
SYStem.RESet
SYStem.CPU CORTEXA53
SYStem.JtagClock 10MHz
SYStem.Up

Data.LOAD.Elf spl.elf /NoCode
Break.Set 0x00020000 /Program
Go
IF Register(PC) == 0x00020000
  PRINT "SPL 入口命中"
ELSE
  PRINT "SPL 未命中,检查 BootROM 跳转"
  ENDDO
ENDIF

Break.Delete
Data.LOAD.Elf u-boot.elf /NoCode
Break.Set board_init_f /Program
Go
Data.dump 0x80000000 /Long
Register.view

这段脚本体现了启动链调试的基本节奏:先建立连接,再按阶段加载符号、设断点、运行、判断命中位置,最后观察内存与寄存器。若某阶段未命中,脚本直接输出提示并结束,避免继续在错误状态下运行。

常见问题与判断依据

启动链调试中,命令执行失败往往有明确信号。SYStem.Up 报错通常意味着 JTAG 时钟过高、目标未上电或复位未释放;Data.LOAD.Elf 报地址冲突,说明加载地址与目标实际运行地址不一致,应改用 /NoCode 只加载符号;Break.Set 后 Go 立即停在异常向量,说明断点地址处代码尚未就绪或 MMU 映射未建立,此时应改用物理地址断点并确认地址映射。

判断启动链是否正常推进,可以看三个信号:PC 是否按预期阶段跳转、关键寄存器(如 x0 指向 DTB)是否合理、内存中关键结构(DTB 魔数、内核头部)是否存在。三者一致,才说明该阶段真正通过。命令只是手段,最终要落到这些可观察、可判断的证据上。