3. TRACE32多核调试基础

多核调试与单核调试最大的区别在于:调试器面对的不再是“一个CPU”,而是“一组可独立运行、又可能相互耦合的CPU”。TRACE32 通过一套以核(core)为中心的视图与命令体系来管理这种复杂性。理解这套体系的关键,是分清三个层次:窗口层负责“看什么”,核选择层负责“对谁操作”,脚本命令层负责“如何批量、可复现地操作”。

一、多核窗口:把每个核的状态同时摆到台面上

在 TRACE32 中,每个被调试的核通常对应一个独立的 SYStem.CPU 配置与一个独立的调试通道。多核窗口的核心价值,是让工程师不必反复切换就能同时观察多个核的运行状态。常用的窗口包括:

  • Register 窗口:显示当前选中核的通用寄存器、状态寄存器(如 xPSR、CPSR)和 PC 值。多核场景下,每个核的 Register 窗口是独立的,标题栏会带上核标识。
  • Frame / Stack 窗口:查看当前核的调用栈。多核系统中,不同核可能运行不同任务,栈回溯必须绑定到具体核。
  • List / Source 窗口:显示反汇编或源码。它跟随当前选中的核,切换核时内容会变化。
  • Perf / Trace 窗口:在多核 trace 场景下,可按核过滤显示指令流或性能计数。

实际使用中,建议把多个核的 Register 窗口并排布局,形成“核状态面板”。这样在单步或运行后,能一眼看出哪个核停在了断点、哪个核还在跑、哪个核进入了异常。

二、核选择:明确“当前操作对象”

TRACE32 中所有交互式命令都作用于“当前选中的核”。核选择有两种典型方式:

  1. 通过窗口焦点隐式选择:点击某个核的 Register 或 List 窗口,该核即成为当前核。此时在命令区输入 Register、Step 等命令,都作用于该核。
  2. 通过 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停在异常向量时,可以:

  1. 在 Register 窗口确认核1的 PC 和 xPSR;
  2. 用 CORE.select 1 确保后续命令作用于核1;
  3. 用 Register 和 Frame 查看异常上下文;
  4. 用 Break.Set 在异常处理入口设断点,重新运行复现。

下面这张图概括了多核调试中“窗口—核选择—脚本命令”三者的关系,以及核状态在启动过程中的典型变化路径。

TRACE32 多核调试:窗口、核选择与脚本命令关系 多核窗口(观察层) Register 窗口(核0 / 核1) Frame / Stack 窗口 List / Source 窗口 核选择(操作层) CORE.select 0 / 1 窗口焦点隐式选择 SYStem.CPU 配置 脚本命令(执行层) Go / Break / Step Data.LOAD.Elf Break.Set / Wait 发现异常核 作用于当前核 核状态典型变化路径(以双核启动为例) Reset / Wait 核未启动 Halted 已停止,可读写 Running 执行中 Breakpoint / Fault 停在断点或异常 核0:上电复位 核0:加载镜像 核0:运行到 main 核1:停在异常向量 核间状态不一致时,应分别查看各核 PC 与状态寄存器,再决定下一步操作

掌握这套体系后,多核调试就不再是“碰运气”。窗口提供全局视野,核选择保证操作对象正确,脚本命令让过程可复现。三者结合,才能在多核启动与同步调试中稳定定位问题。