4. 多核启动脚本编写
多核调试与单核调试最大的差别,不在于“能不能连上”,而在于“连上之后,调试器是否知道每个核此刻处于什么状态、由谁控制、下一步该对谁下命令”。TRACE32 的启动脚本(通常保存为 .cmm)正是用来把这件事一次说清楚的地方。本节围绕 SYStem.CPU、SYStem.MultiCore、CORE.select 三条核心命令,讲清它们各自解决什么问题,以及如何按正确顺序组织成一段可复用的启动序列。
三条命令的职责边界
先明确分工,避免把命令堆在一起却不知道谁在起作用。
SYStem.CPU:声明当前调试会话面向的处理器类型。它决定调试器使用哪套内核寄存器模型、哪套断点/观察点资源描述、以及默认的复位与运行控制方式。多核场景下,它通常先声明主核(或首个被调试的核)的 CPU 类型。SYStem.MultiCore:声明这是一个多核系统,并给出核间拓扑与同步方式。它告诉调试器“这里不止一个核,它们之间如何被统一控制”,是后续按核选择、按核复位、按核运行的前提。CORE.select:在已经建立的多核会话中,把“当前操作对象”切换到指定核。之后执行的寄存器读写、断点设置、单步、运行/停止,默认都作用于被选中的核。
三者的关系可以概括为:SYStem.CPU 定义“是什么核”,SYStem.MultiCore 定义“有几个核、怎么协同”,CORE.select 定义“现在操作哪一个核”。顺序颠倒或缺失,往往表现为“能连上但选不中核”“断点只在一个核生效”“复位后其他核跑飞”等典型问题。
启动序列的组织原则
把三条命令串起来时,遵循“先声明、后选择、再操作”的层次。一个可参考的骨架如下(参数为示例,需按实际芯片手册替换):
; 1. 声明处理器类型
SYStem.CPU CortexA53
; 2. 声明多核拓扑与同步方式
SYStem.MultiCore SMP4 ; 示例:4 核 SMP 结构
; 3. 建立调试会话
SYStem.Up
; 4. 选择主核,完成系统级初始化
CORE.select 0
SYStem.Up
Data.LOAD.Elf boot.elf /NoClear
; 5. 逐核接管,设置各自断点
CORE.select 1
Break.Set core1_main
CORE.select 2
Break.Set core2_main
CORE.select 3
Break.Set core3_main
; 6. 回到主核统一运行
CORE.select 0
Go
这里有几个容易踩的坑。第一,SYStem.MultiCore 必须在 SYStem.Up 之前声明,否则调试器会按单核会话建立连接,之后再补声明往往需要重新初始化。第二,CORE.select 只改变“当前操作对象”,不会自动让该核停止或运行;如果目标核还在运行,直接读寄存器可能读到不一致的值,通常先 Break 或 Halt 再操作。第三,主核与从核的复位入口不同,从核常处于等待唤醒状态,脚本里应明确“先主核、后从核”的接管顺序,而不是一次性对所有核下同一命令。
参数与模式的选择依据
SYStem.MultiCore 的模式参数决定了核间同步的粒度。常见取值包括 SMP(对称多处理,核间共享一致视图)与 AMP(非对称多处理,各核独立运行不同镜像)。选择依据是软件架构:如果多个核跑同一份 OS 且共享内存,用 SMP 模式更便于统一断点和统一步进;如果各核跑独立固件、互不共享地址空间,用 AMP 模式分别管理更清晰。选错模式不会立刻报错,但会在“单步时其他核是否跟随停止”“断点是否在所有核生效”上表现出差异,调试时容易误判为硬件问题。
CORE.select 的编号与芯片的物理核编号一致,但要注意某些架构存在“核簇(cluster)”概念,编号可能是“簇号+核号”的组合。脚本中建议用注释标明每个编号对应的物理核,避免换芯片后编号错位。
init.cmm 负责 SYStem.CPU 与 SYStem.MultiCore 的声明,attach.cmm 负责 CORE.select 与断点设置。这样换芯片时只改 init,调试逻辑复用 attach。结果如何判断
脚本执行后,不要只看“没有报错”就认为成功。应逐项确认:SYStem.CPU 后,寄存器窗口显示的内核名称与目标一致;SYStem.MultiCore 后,核列表中出现预期数量的核且状态可读;每次 CORE.select n 后,寄存器窗口标题或核指示器切换到对应编号;在各核设置的断点,能在对应核运行时命中,且不会误触发到其他核。若出现“断点只在 CORE 0 命中”,优先检查 MultiCore 模式是否声明正确、以及断点是否在正确的核上下文中设置。