2. TRACE32 多核调试基础操作
多核调试与单核调试最大的差别,不在于“能不能连上目标”,而在于调试器必须同时管理多个执行单元,并且让工程师清楚地知道“当前操作作用在哪个核上”。TRACE32 通过一套统一的窗口体系与核选择机制来处理这件事:所有窗口都绑定一个“当前核上下文”,切换核时窗口内容随之刷新,而断点、单步、运行控制则可以选择作用于单核还是全核。理解这套上下文机制,是后续多核同步、核间通信分析、共享内存竞争排查的基础。
多核窗口布局与核上下文
连接好多核目标后,TRACE32 通常会在 SYStem.CPU 或 SYStem.CONFIG 中声明核的数量与拓扑,例如一个双核 Cortex-A9 加一个 Cortex-M4 的异构系统,或四个同构 RISC-V 核。声明完成后,主界面会出现核选择下拉框(Core Selector),列出 Core 0、Core 1 等条目。这个下拉框就是全局的“当前核上下文”开关。
典型的多核窗口布局建议按“全局—单核—共享”三层组织:
- 全局层:
SYStem窗口显示各核运行状态(running/halt/unknown)、复位状态与时钟;Breakpoint列表显示所有核的断点归属。 - 单核层:每个核各开一个
Register窗口、一个Frame调用栈窗口、一个List反汇编窗口。这些窗口标题栏会标注核号,例如Register / Core1。 - 共享层:
Data.Dump内存窗口、Var.View变量窗口、Perf性能窗口,它们跟随当前核上下文,但可以手动锁定到某个核或某个地址空间。
需要特别注意的是,多核系统中每个核可能有独立的地址映射。例如 Core0 的 0x0000_0000 指向片内 ROM,而 Core1 的同一地址可能指向本地 TCM。因此内存窗口必须明确“以哪个核的视角访问”,否则读出的数据会张冠李戴。TRACE32 在 Data.Dump 中通过 /Core 选项或窗口属性指定访问核。
核间切换与寄存器、内存查看
切换核最直接的方式是在 Core Selector 中选择目标核,或在命令区执行:
SYStem.CPU.Select Core1
Register
Data.Dump 0x20000000 /Core Core1
执行后,Register 窗口立即显示 Core1 的 PC、SP、LR 及通用寄存器。若 Core1 处于运行状态,寄存器值可能是上一次停止时的快照,窗口会以灰色或标注提示“stale”。此时应先 Break 再读取,才能得到一致的状态。
内存查看在多核场景下要区分三类区域:
- 私有内存:如各核的 TCM、私有 Cache 配置区,只有对应核能访问,切换核后地址含义不同。
- 共享内存:如 DDR、共享 SRAM,多核看到同一物理地址,但需注意 Cache 一致性与内存屏障。
- 外设寄存器:通常全局唯一,但访问权限可能受核间互斥锁保护。
一个实用的做法是:在 Data.Dump 中同时打开两个窗口,分别锁定 Core0 与 Core1 的视角,对比同一地址的读出结果。若两者不一致,往往说明该地址属于私有映射,或 Cache 未同步。示例中,假设共享 SRAM 位于 0x2000_0000,Core0 写入 0xDEADBEEF 后,Core1 若因 Cache 未失效而读到旧值,就应执行 Data.Cache.Flush 或对应核的 Cache 维护命令后再读。
断点设置与命中控制
多核断点的核心问题是“断点属于谁”。TRACE32 的断点分为硬件断点和软件断点,多核环境下还需区分“核内断点”与“全局断点”。设置时通过 /Core 限定作用核:
Break.Set 0x08001234 /Core Core0
Break.Set func_shared_init /Core Core1 /Program
Break.List
第一条在 Core0 的 0x08001234 设置断点;第二条在 Core1 上按符号设置程序断点;Break.List 会列出所有断点及其归属核、类型、命中计数。若不加 /Core,默认作用于当前核上下文,这一点在切换核后极易出错——工程师以为在 Core1 下断点,实际却下到了 Core0。
命中控制方面,TRACE32 支持条件断点与命中计数:
Break.Set 0x08001234 /Core Core0 /Condition Register.R0==0x5A
Break.Set 0x08001234 /Core Core0 /Count 100
条件断点只在 R0 等于 0x5A 时停止,适合过滤循环中的特定迭代;命中计数则在第 100 次经过该地址时停止,适合定位偶发问题。多核下还要注意:若两个核都执行到同一共享代码地址,硬件断点资源可能不足,此时应优先使用条件断点缩小范围,或改用 Break.Set /Program 的软件断点并配合 MMU 设置。
多核同步停止与单核运行
多核调试最常用的两种运行模式是“同步停止”和“单核运行”。同步停止指一个核命中断点后,调试器自动暂停其他核,便于观察全局一致状态;单核运行则只让目标核继续,其他核保持暂停,适合隔离分析某个核的行为。
TRACE32 通过 SYStem.Mode 与 Go 的选项控制:
SYStem.Mode Go ; 全核同步运行
Go /Core Core0 ; 仅 Core0 运行,其余保持 halt
Break ; 全核停止
SYStem.Mode Down ; 全部停止并断开运行控制
同步停止的典型配置是 SYStem.Option.SyncHalt ON,这样任一核触发断点,其他核会被硬件或调试逻辑同步拉停。若系统不支持硬件同步,可退化为“断点触发后由调试器依次暂停其他核”,但会引入微秒级时间偏差,分析核间竞争时需谨慎。
单核运行在排查“某核卡死”时非常有用:先全核停止,再让可疑核单独 Go,其余核保持 halt,观察该核能否走到预期断点。若单核能跑通而全核同步时失败,问题往往出在核间同步原语、共享资源竞争或中断路由上,而不是单核代码本身。
实操要点与常见陷阱
把上述操作串起来,一个典型的多核调试流程是:连接目标 → 声明核拓扑 → 用 Core Selector 逐核确认 PC 与 SP 合理 → 在共享代码入口设置带 /Core 的断点 → 开启 SyncHalt → 全核 Go → 命中后检查各核寄存器与共享内存 → 需要隔离时用 Go /Core 单核运行。
常见陷阱有三个。第一,忘记指定 /Core,断点下到了当前核而非目标核,表现为“断点不命中”或“命中在错误的核”。第二,在核处于 running 状态时读寄存器,得到的是过期快照,误判为程序跑飞。第三,共享内存读取未考虑 Cache,Core0 已写入而 Core1 读到旧值,误判为通信失败。规避方法分别是:设置断点后立即用 Break.List 核对归属核;读寄存器前先 Break;跨核读共享数据前执行 Cache 维护或使用非缓存地址别名。
STORE / SETUP 机制),切换核时直接加载对应布局,减少手动调整窗口的时间,也避免看错核号。掌握核上下文、断点归属与同步/单核运行这三组概念后,多核调试就从“同时看多个单核”变成了“有全局视角的系统级调试”。后续的核间同步分析、共享资源竞争排查和实时性测量,都建立在本节这套操作基础之上。