2. TRACE32脚本语言基础

TRACE32 的 PRACTICE 脚本是调试自动化的核心载体。它不像 C 语言那样需要编译,而是由 TRACE32 的脚本解释器逐行执行,因此非常适合在调试会话中快速构建可复用的测试序列、寄存器检查、内存扫描和批量命令封装。理解 PRACTICE 的语法、变量、运算符、流程控制以及它与 CMM 命令的交互方式,是写出可靠自动化脚本的前提。

脚本结构与基本语法

PRACTICE 脚本通常以 .cmm 为扩展名,由若干行命令组成。每行可以是一条 CMM 命令,也可以是一条 PRACTICE 语句。脚本中大小写不敏感,但为了可读性,建议命令使用大写、变量使用小写。注释使用 // 或 ;,块注释使用 /* ... */。一个最简单的脚本如下:

// 初始化并读取内核寄存器
SYStem.Up
Register.Set PC 0x8000_0000
PRINT "PC = " Register(PC)

其中 SYStem.Up 和 Register.Set 是 CMM 命令,PRINT 是 PRACTICE 的输出语句。PRACTICE 脚本的执行是顺序的,遇到错误默认会中止,除非使用 ON ERROR 或 GOTO 进行错误处理。

变量与数据类型

PRACTICE 的变量不需要显式声明类型,赋值即创建。变量名以字母开头,可包含字母、数字和下划线。常见的数据类型包括:

  • 整数:默认 64 位有符号整数,支持十进制、十六进制(0x)、二进制(0b)和字符常量。
  • 浮点数:带小数点的数值,用于计算频率、电压等物理量。
  • 字符串:用双引号括起,支持转义字符,常用于拼接命令和输出信息。
  • 布尔值:PRACTICE 没有独立布尔类型,通常用 0 表示假、非 0 表示真。

变量赋值示例:

base = 0x8000_0000
offset = 0x100
addr = base + offset
name = "core0"
PRINT "addr = " FORMAT("%08X", addr)

PRACTICE 还支持数组和结构体。数组用 DIM 声明,例如 DIM buf[16] 创建一个 16 元素的数组。结构体则通过 STRUCT 定义,适合组织寄存器组或测试参数。

运算符与表达式

PRACTICE 支持常见的算术、关系和逻辑运算符。算术运算符包括 +、-、*、/、%,以及位运算符 &、|、^、~、<<、>>。关系运算符有 ==、!=、<、>、<=、>=,逻辑运算符为 &&、||、!。表达式可以混合使用,优先级与 C 语言类似。

一个典型的位操作示例:读取状态寄存器,判断第 3 位是否置位:

status = Register(STATUS)
if (status & 0x08) != 0
(
    PRINT "Bit3 is set"
)
else
(
    PRINT "Bit3 is clear"
)

流程控制

PRACTICE 提供 IF/ELSE、WHILE、REPEAT、FOR 和 GOTO 等流程控制结构。注意 PRACTICE 的块使用圆括号 ( ... ) 而不是花括号,这是初学者最容易出错的地方。

IF 语句用于条件分支,WHILE 在条件为真时循环,REPEAT 至少执行一次直到条件满足,FOR 则适合已知次数的循环。例如,批量读取 8 个连续寄存器的值:

i = 0
while i < 8
(
    value = Register(i * 4)
    PRINT "Reg[" i "] = " FORMAT("%08X", value)
    i = i + 1
)

如果需要在循环中提前退出,可以使用 BREAK;跳过本次循环剩余部分则用 CONTINUE。对于复杂的跳转逻辑,GOTO 配合标签仍然有效,但建议优先使用结构化控制语句,以保持脚本可读性。

与 CMM 命令的交互方式

PRACTICE 脚本与 CMM 命令的交互是双向的:脚本可以调用 CMM 命令,CMM 命令也可以读取脚本变量。最常见的交互方式有三种:

  1. 直接调用:在脚本中直接写 CMM 命令,如 Data.Set、Register.Set、Go 等。
  2. 函数式调用:使用 Register()、Data.Long()、Address() 等函数读取目标状态,返回值可直接赋给变量。
  3. 参数传递:通过 & 前缀将脚本变量传递给 CMM 命令,例如 Data.Set &addr %Long 0x12345678。

下面是一个综合示例,演示如何用脚本控制一次内存写入并回读验证:

addr = 0x8000_1000
expected = 0xDEADBEEF
Data.Set &addr %Long expected
actual = Data.Long(addr)
if actual == expected
(
    PRINT "Write/Read OK"
)
else
(
    PRINT "Mismatch: " FORMAT("%08X", actual)
)

需要注意的是,CMM 命令中的地址和数值通常以十六进制解析,而脚本变量在传递时由 PRACTICE 解释器负责转换。使用 FORMAT() 可以控制输出格式,ADDRESS() 可以获取符号地址,Register() 可以读取寄存器值。这些函数让脚本能够动态适应不同的调试目标。

PRACTICE 脚本中的变量作用域默认是全局的。如果脚本被多次调用,建议在开头显式初始化关键变量,避免残留值影响后续执行。对于复杂脚本,可以使用 LOCAL 声明局部变量,减少命名冲突。

脚本执行与调试

在 TRACE32 中执行脚本有三种常用方式:在命令行输入 DO script.cmm、通过 AREA 窗口加载、或在 PRACTICE 对话框中运行。脚本执行过程中,可以使用 PRINT 输出中间变量,也可以使用 Break 命令暂停执行。如果脚本出错,TRACE32 会显示错误行号和错误信息,常见的错误包括括号不匹配、变量未定义、CMM 命令参数类型错误等。

下面这张图展示了 PRACTICE 脚本从编写到执行的完整数据流,以及脚本变量与 CMM 命令之间的交互关系:

PRACTICE 脚本与 CMM 命令交互数据流 PRACTICE 脚本 (.cmm) 变量:addr, expected, actual 运算符:+ - & | << == 流程:IF / WHILE / FOR 函数:FORMAT(), Register() 输出:PRINT, ERROR CMM 命令层 Data.Set / Data.Long Register.Set / Register() Go / Break / Step SYStem.Up / SYStem.Down 参数传递:&addr %Long 调试目标 CPU 寄存器组 内存 / 外设寄存器 断点 / 跟踪单元 JTAG / SWD 接口 目标状态:运行/停止 调用 访问 返回 结果 输出与判断 PRINT 格式化输出 比较 expected == actual 错误处理 ON ERROR / GOTO 执行结果 示例: addr = 0x8000_1000 → Data.Set &addr %Long 0xDEADBEEF → actual = Data.Long(addr) → IF actual == expected PRINT "OK" 脚本变量经 PRACTICE 解释器转换为 CMM 参数,CMM 命令访问目标后返回结果,再由脚本进行判断和输出。

从图中可以看出,PRACTICE 脚本负责逻辑组织和变量管理,CMM 命令负责与调试目标交互,两者通过参数传递和函数返回值形成闭环。掌握这个交互模型后,就可以把重复的寄存器检查、内存扫描、批量测试封装成脚本,显著提升调试效率。

在脚本中调用 CMM 命令时,务必确认目标已处于正确状态。例如,在 SYStem.Up 之前执行 Register.Set 会失败。建议在脚本开头加入状态检查,如 IF !STATE.RUN() PRINT "Target not running",避免因目标状态异常导致脚本中断。

最后,建议把常用操作封装成带参数的 PRACTICE 函数。PRACTICE 支持 ENTRY 和 RETURN 定义子程序,参数通过 & 传递。这样可以把“读寄存器—比较—打印”这样的逻辑复用多次,让脚本更接近工程化的调试工具,而不是一次性命令堆叠。