4. TRACE32启动脚本与自动化配置

在量产项目里,TRACE32 的调试环境往往不是“打开软件、点几下按钮”就能复现的。不同项目、不同板卡、不同调试阶段(研发、试产、量产返修)需要的初始化动作差异很大:加载哪份配置文件、用哪个调试探头、目标芯片的复位方式、时钟初始化、Flash 烧写算法、符号文件路径、断点策略等。如果每次都靠人工操作,不仅效率低,还极易因为漏掉某个步骤导致“这次能连上、下次连不上”的随机问题。启动脚本与自动化配置要解决的,正是把这一整套环境搭建过程固化成可重复执行的脚本。

4.1 CMake 脚本基础:为什么用 CMake 组织 TRACE32 配置

TRACE32 自身使用 PRACTICE 脚本语言(.cmm 文件)来执行命令序列,而 CMake 在这里扮演的是“配置生成器”和“构建编排器”的角色。量产项目通常有多个目标板、多个固件版本、多个调试场景,直接用一堆 .cmm 文件手工维护会很快失控。用 CMake 可以把“选择哪个目标、加载哪份固件、使用哪套符号”这些变量集中管理,再通过 configure_file() 生成最终被 TRACE32 执行的 .cmm 脚本。

一个最小可用的 CMake 结构通常包含:顶层 CMakeLists.txt 定义项目与选项,cmake/ 目录存放模板文件(如 init.cmm.in),targets/ 目录按目标板存放各自的参数文件。核心思路是:CMake 负责“算参数”,PRACTICE 负责“执行动作”。例如下面这段 CMake 片段定义了一个目标选择变量,并把模板渲染成实际脚本:

set(TARGET_BOARD "board_a" CACHE STRING "目标板名称")
set(DEBUG_PROBE "USB" CACHE STRING "调试探头类型")
set(SYMBOL_ELF "${CMAKE_SOURCE_DIR}/build/${TARGET_BOARD}/app.elf")

configure_file(
  ${CMAKE_SOURCE_DIR}/cmake/init.cmm.in
  ${CMAKE_BINARY_DIR}/init_${TARGET_BOARD}.cmm
  @ONLY
)

模板 init.cmm.in 中则用 @TARGET_BOARD@、@SYMBOL_ELF@ 等占位符接收 CMake 传入的值。这样,切换目标板只需要在命令行加 -DTARGET_BOARD=board_b,而不需要改动任何脚本内容。

4.2 量产项目通用初始化脚本

通用初始化脚本的目标是:无论哪个项目,只要调用它,就能把 TRACE32 带到一个“已知的、可调试的”状态。典型的初始化顺序如下,每一步都有明确的判断依据:

  • 复位与连接:先执行 SYStem.RESet 让调试器与目标芯片进入确定状态,再用 SYStem.Up 建立连接。如果连接失败,脚本应输出错误码并停止,而不是继续执行后续命令。
  • 时钟与内存映射:通过 SYStem.Option 和 MMU 相关命令设置目标时钟、内存映射。量产板通常使用外部晶振,脚本中应写入实际频率值,例如 SYStem.Option.Frequency 24MHz。
  • 加载符号:用 Data.LOAD.Elf 加载 ELF 文件,并指定 /NoCode 或 /NoClear 等选项控制是否覆盖已有代码。符号加载后,用 Data.List 或 sYmbol.List 确认关键函数地址已解析。
  • Flash 烧写配置:如果项目需要烧写,脚本中应包含 FLASH.RESet、FLASH.CHANGEtype 和 FLASH.Program 的调用,并指定烧写算法文件路径。
  • 断点与观察点:量产调试通常只保留必要的硬件断点,脚本中可用 Break.Set 设置入口断点,用 Break.List 检查断点资源占用。

一个值得注意的细节是:初始化脚本应当具备“幂等性”。也就是说,重复执行同一份脚本,结果应当一致。为此,脚本开头应先执行清理动作(如 Break.Delete /ALL、SYStem.RESet),避免上一次调试残留的断点或状态影响本次执行。

4.3 多目标配置切换

量产项目经常需要在同一台电脑上调试多个硬件版本。多目标配置切换的核心是“参数与逻辑分离”:把每个目标板特有的参数(芯片型号、内存基址、Flash 算法、探头序列号)放在独立的配置文件中,而把通用逻辑放在共享脚本里。CMake 通过 include() 或 add_subdirectory() 按目标加载对应参数,再统一渲染。

下面这张图展示了从 CMake 配置到 TRACE32 执行的完整数据流,以及多目标切换时参数如何被注入到最终脚本中:

CMake 配置 → TRACE32 启动脚本 数据流 CMake 配置层 CMakeLists.txt -DTARGET_BOARD=board_a -DDEBUG_PROBE=USB configure_file() 模板层 init.cmm.in @TARGET_BOARD@ @SYMBOL_ELF@ @FLASH_ALGO@ 生成脚本 init_board_a.cmm init_board_b.cmm init_board_c.cmm (按目标生成) 目标参数文件 targets/board_a.cmake targets/board_b.cmake 芯片型号 / 内存基址 TRACE32 执行层(PRACTICE 脚本) SYStem.RESet → SYStem.Up → 时钟/内存映射 → Data.LOAD.Elf → FLASH.Program → Break.Set 每步失败即停止并输出错误码,保证状态可判断 多目标切换:同一套通用脚本 + 不同参数文件 board_a:Cortex-M7 / 0x08000000 board_b:Cortex-M4 / 0x00000000 board_c:RISC-V / 0x80000000

图中可以看到,CMake 层负责接收目标选择变量,模板层负责定义脚本骨架,目标参数文件提供具体数值,最终生成针对每个目标板的独立 .cmm 脚本。TRACE32 执行层只关心“拿到脚本后按顺序执行”,不关心参数从哪来。这种分层让新增一个目标板只需要增加一个参数文件,而不需要复制整套脚本。

4.4 脚本参数化与复用

参数化的关键是识别哪些值会变、哪些值不变。会变的值包括:目标芯片型号、内存基址、Flash 算法路径、符号文件路径、探头序列号、复位方式(硬件复位/软件复位)。不变的值包括:初始化顺序、错误处理逻辑、日志输出格式。把“变”的部分抽成参数,把“不变”的部分写成函数或宏,就能实现复用。

在 PRACTICE 脚本中,可以用 & 定义参数变量,用 GOSUB 或 DO 调用子脚本。例如,通用初始化脚本可以写成 init_common.cmm,接收三个参数:目标板名称、符号文件路径、Flash 算法路径。各目标板的启动脚本只需要调用它并传入自己的值:

; board_a_start.cmm
&board = "board_a"
&elf   = "C:\build\board_a\app.elf"
&flash = "C:\t32\flash\board_a.flash"
DO init_common.cmm &board &elf &flash

这样,当初始化逻辑需要调整(比如增加一步时钟校验),只需要改 init_common.cmm 一处,所有目标板同时生效。对于量产项目来说,这种复用方式能显著降低维护成本,也能减少“某个板子漏改脚本”导致的问题。

参数化脚本的一个实用技巧是:在脚本开头用 PRINT 输出当前使用的参数值,并写入日志文件。这样当调试出现异常时,可以快速确认“这次到底用了哪份符号、哪个 Flash 算法”,避免参数传错却难以察觉。
量产环境中,启动脚本不应包含任何交互式等待(如 WAIT 或需要人工确认的对话框)。所有判断都应基于命令返回码或状态变量自动完成,否则自动化流程会在无人值守时卡住。

最后需要强调的是,脚本的版本管理应与固件版本管理同步。建议把 CMake 配置、模板文件、目标参数文件全部纳入 Git 仓库,并在脚本中嵌入版本号或提交哈希。当量产现场出现“同一块板子在不同电脑上行为不一致”时,首先核对的就是脚本版本是否一致。把环境配置当成代码来管理,是量产调试从“手工活”走向“可复现工程”的关键一步。