3. TRACE32多核调试基础
多核调试与单核调试最大的区别在于:调试器面对的不再是“一个CPU”,而是“一组可独立运行、又可能相互耦合的CPU”。TRACE32 通过一套以核(core)为中心的视图与命令体系来管理这种复杂性。理解这套体系的关键,是分清三个层次:窗口层负责“看什么”,核选择层负责“对谁操作”,脚本命令层负责“如何批量、可复现地操作”。
一、多核窗口:把每个核的状态同时摆到台面上
在 TRACE32 中,每个被调试的核通常对应一个独立的 SYStem.CPU 配置与一个独立的调试通道。多核窗口的核心价值,是让工程师不必反复切换就能同时观察多个核的运行状态。常用的窗口包括:
- Register 窗口:显示当前选中核的通用寄存器、状态寄存器(如 xPSR、CPSR)和 PC 值。多核场景下,每个核的 Register 窗口是独立的,标题栏会带上核标识。
- Frame / Stack 窗口:查看当前核的调用栈。多核系统中,不同核可能运行不同任务,栈回溯必须绑定到具体核。
- List / Source 窗口:显示反汇编或源码。它跟随当前选中的核,切换核时内容会变化。
- Perf / Trace 窗口:在多核 trace 场景下,可按核过滤显示指令流或性能计数。
实际使用中,建议把多个核的 Register 窗口并排布局,形成“核状态面板”。这样在单步或运行后,能一眼看出哪个核停在了断点、哪个核还在跑、哪个核进入了异常。
二、核选择:明确“当前操作对象”
TRACE32 中所有交互式命令都作用于“当前选中的核”。核选择有两种典型方式:
- 通过窗口焦点隐式选择:点击某个核的 Register 或 List 窗口,该核即成为当前核。此时在命令区输入
Register、Step等命令,都作用于该核。 - 通过
SYStem.CPU或CORE.select显式选择:在脚本中,必须显式指定核,否则命令会落到默认核上,导致“改了另一个核”的隐蔽错误。
一个常见的教学示例是双核 Cortex-A9 系统,核0运行 Linux,核1运行裸机实时任务。若在脚本中不写核选择,直接执行 Go,可能只让核0运行,核1仍处于 halt 状态,现象与预期不符。因此,“先选核,再操作”应成为多核脚本的第一条纪律。
三、核状态查看:从“停没停”到“为什么停”
核状态查看不只是看 running/halt,更要看 halt 的原因。TRACE32 提供 SYStem.Mode、STATE 以及各核的 Register 窗口来呈现状态。典型状态包括:
- Running:核正在执行,调试器无法直接读寄存器,需先
Break。 - Halted:核已停止,可读写寄存器和内存。
- Breakpoint hit:因断点停止,PC 指向断点地址。
- Exception / Fault:因异常或错误停止,需查看 xPSR 或 fault 寄存器。
- Reset / Wait:核处于复位或等待状态,常见于启动阶段。
在多核启动调试中,最值得关注的是“核间状态不一致”:例如核0已运行到 main,核1仍停在复位向量。这通常意味着核1的启动地址、时钟或电源域尚未配置正确。此时应分别查看两个核的 PC 和状态寄存器,而不是只看一个核。
四、多核脚本命令体系:从交互到可复现
TRACE32 的脚本(.cmm)是多核调试自动化的核心。与多核相关的命令大致分为四类:
| 类别 | 典型命令 | 作用 |
|---|---|---|
| 系统配置 | SYStem.CPU、SYStem.Mode | 声明核类型与调试模式 |
| 核选择 | CORE.select、SYStem.CPU 切换 | 指定后续命令作用的核 |
| 运行控制 | Go、Break、Step、Wait | 控制核的运行与停止 |
| 状态查询 | Register、STATE、PRINT | 读取核状态与寄存器 |
下面是一个双核启动脚本的示例片段,用于演示“先配置、再选核、后运行”的顺序:
; 配置双核系统
SYStem.CPU CortexA9
SYStem.Mode Prepare
; 选择核0,加载并运行
CORE.select 0
Data.LOAD.Elf app_core0.elf
Go
; 选择核1,加载并运行
CORE.select 1
Data.LOAD.Elf app_core1.elf
Go
; 等待两个核都到达 main
CORE.select 0
Break.Set main
Go
Wait !STATE.RUN()
CORE.select 1
Break.Set main
Go
Wait !STATE.RUN()
这段脚本的关键点在于:每次切换核后,后续的 Data.LOAD、Go、Break.Set 都只作用于当前核。若省略 CORE.select,加载和运行都会落到默认核上,另一个核不会被正确初始化。
CORE.select n,即使当前核恰好是目标核。这样脚本在核顺序调整后仍能正确执行,也便于他人阅读。
五、把窗口、核选择与脚本串起来
实际调试时,这三者是配合使用的:先用多核窗口观察各核状态,发现异常核后用核选择切换到该核,再用脚本命令批量复现操作。例如,当发现核1停在异常向量时,可以:
- 在 Register 窗口确认核1的 PC 和 xPSR;
- 用
CORE.select 1确保后续命令作用于核1; - 用
Register和Frame查看异常上下文; - 用
Break.Set在异常处理入口设断点,重新运行复现。
下面这张图概括了多核调试中“窗口—核选择—脚本命令”三者的关系,以及核状态在启动过程中的典型变化路径。
掌握这套体系后,多核调试就不再是“碰运气”。窗口提供全局视野,核选择保证操作对象正确,脚本命令让过程可复现。三者结合,才能在多核启动与同步调试中稳定定位问题。