4. J-Link脚本语言与流程控制

在自动化测试工程中,J-Link Command File(以下简称 JCF)是连接测试主机与目标芯片之间的“可编程桥梁”。它把原本需要人工在 J-Link Commander 中逐条敲入的命令,固化成可重复执行的脚本。对于测试准备与恢复流程而言,脚本的价值不在于“能执行”,而在于“能判断、能分支、能复用、能容错”。本节围绕变量、条件、循环、子程序和错误处理五个要素,说明如何构建一套可复用的测试准备与恢复流程。

一、JCF 的执行模型与变量

JCF 由 J-Link Commander 通过 -CommandFile 参数加载,按行顺序解释执行。脚本中的变量以 $ 开头,分为两类:一类是 J-Link 内置变量,如 $DEVICE、$SPEED,由连接参数决定;另一类是用户自定义变量,通过 SetVar 或 Var 声明。变量在脚本内是全局可见的,因此命名应带前缀以避免冲突,例如 $TEST_RETRY、$PREP_OK。

SetVar TEST_RETRY = 3
SetVar PREP_OK = 0
SetVar TARGET_ADDR = 0x08000000
SetVar EXPECT_ID = 0x1BA01477

变量可以参与算术与比较运算,J-Link 支持 + - * / & | ^ ~ 等运算符,比较结果以 0/1 表示。这一点是后续条件判断的基础。需要注意的是,JCF 的变量本质是整数,字符串比较需借助 strcmp 类命令或直接比较数值编码。

二、条件判断:让脚本具备“决策”能力

测试准备流程中最常见的判断是“目标是否已连接、ID 是否正确、是否处于调试状态”。JCF 提供 If ... Else ... EndIf 结构,条件表达式通常由比较运算构成。例如读取 CPU ID 后与期望值比对,只有匹配才继续执行擦除或下载,否则进入恢复分支。

If $CPU_ID == $EXPECT_ID
  Print "ID matched, continue preparation"
  SetVar PREP_OK = 1
Else
  Print "ID mismatch, enter recovery"
  SetVar PREP_OK = 0
EndIf

条件可以嵌套,但建议嵌套不超过两层,否则脚本可读性急剧下降。对于多分支场景,可用连续的 If/ElseIf 或提前用变量记录状态码,再统一判断。判断依据应来自真实可观测的量,例如 ReadAP 返回值、Mem 读取结果、Connect 的返回状态,而不是主观假设。

三、循环:批量操作与重试机制

JCF 支持 While ... EndWhile 循环,配合变量自增/自减实现计数控制。循环在测试准备中有两类典型用途:一是批量擦除多个扇区或写入多段数据;二是连接重试,当目标上电时序不稳定时,允许有限次重连。

SetVar RETRY = 0
While $RETRY < $TEST_RETRY
  Connect
  If $CONNECT_STATUS == 1
    Break
  EndIf
  SetVar RETRY = $RETRY + 1
  Sleep 200
EndWhile

循环必须设置明确的退出条件,避免死循环。上例中 $TEST_RETRY 是上限,Break 用于提前跳出。若循环体内部有耗时操作,应加入 Sleep 让目标电源与复位信号稳定,否则重试可能连续失败。循环结束后应检查 $RETRY 是否达到上限,以此判断准备流程是否成功。

四、子程序:把准备与恢复拆成可复用单元

JCF 通过 GoSub 与 Return 实现子程序调用,配合标签 Label 定义入口。子程序的最大价值是把“准备”和“恢复”两个方向的操作分别封装,主流程只负责编排顺序与判断结果。例如 PrepareTarget 负责复位、连接、校验 ID、擦除;RecoverTarget 负责复位、断开、重新上电、恢复默认时钟。

GoSub PrepareTarget
If $PREP_OK == 0
  GoSub RecoverTarget
EndIf
...
PrepareTarget:
  Reset
  Connect
  ...
  Return

子程序应遵循“单一职责”:一个子程序只完成一类操作,并通过全局变量或约定寄存器返回状态。调用前应确保相关变量已初始化,调用后应立即检查返回状态,避免错误被后续步骤掩盖。对于需要在多个测试用例间共享的准备逻辑,可把子程序集中放在一个 common.jcf 中,由各用例脚本通过 GoSub 引用。

五、错误处理:从“能跑”到“可控”

JCF 没有现代语言的异常机制,错误处理依赖“状态检查 + 分支 + 恢复子程序”的组合。常见错误包括:连接失败、ID 不匹配、擦除超时、写入校验失败、目标掉电。处理策略分三层:第一层是就地重试,适用于瞬时故障;第二层是回退到恢复子程序,把目标拉回已知状态;第三层是记录错误码并终止脚本,避免污染后续测试。

If $ERASE_STATUS != 0
  Print "Erase failed, code=", $ERASE_STATUS
  GoSub RecoverTarget
  SetVar PREP_OK = 0
  Exit
EndIf

错误码应统一规划,例如 0 表示成功,1 表示连接失败,2 表示 ID 不匹配,3 表示擦除失败,4 表示校验失败。这样上位机解析日志时可以直接按码判断,而不必解析文本。所有 Print 输出建议带固定前缀,如 [PREP]、[RECOVER],便于日志过滤。

JCF 测试准备与恢复流程控制结构 主流程 Main SetVar 初始化变量 GoSub PrepareTarget If $PREP_OK == 1 ? 执行测试用例 GoSub RecoverTarget 记录错误码并退出 是 否 子程序 PrepareTarget Reset 目标复位 Connect 连接目标 While 重试 < 3 读取 CPU ID 校验 擦除 Flash 区域 SetVar PREP_OK = 1 Return 返回主流程 子程序 RecoverTarget Reset 复位目标 断开调试连接 Sleep 等待稳定 重新上电/复位 恢复默认时钟 SetVar PREP_OK = 0 Return 返回主流程 GoSub GoSub 变量驱动判断 · 循环控制重试 · 子程序封装准备/恢复 · 错误码统一记录

六、把五要素组合成可复用的准备与恢复流程

将上述要素组合,可以得到一个稳定的测试准备骨架:主流程先初始化变量,调用 PrepareTarget;子程序内部用循环处理连接重试,用条件校验 ID,用变量记录 $PREP_OK;主流程根据 $PREP_OK 决定进入测试还是调用 RecoverTarget;恢复子程序同样以子程序形式封装,确保无论测试用例如何变化,目标都能回到已知状态。这样,脚本不再是“一次性命令堆叠”,而是具备判断、重试、回退和状态记录能力的可复用资产。

建议把公共子程序、错误码定义和日志前缀统一放在 common.jcf 中,各测试用例脚本只保留差异化的准备参数与测试步骤,通过 GoSub 复用。这样修改一处即可影响全部用例,显著降低维护成本。
JCF 的循环没有自动超时保护,若目标始终无法连接,While 可能长时间占用调试通道。务必在循环内设置最大重试次数,并在每次重试之间加入 Sleep,同时在上位机侧设置脚本执行超时。

最后需要强调:JCF 的流程控制能力有限,它更适合做“确定性的准备与恢复”,而不是复杂业务逻辑。把判断条件限定在可观测的硬件状态上,把恢复动作做成幂等子程序,把错误码固定下来,才能让自动化测试在长时间运行中保持稳定与可追溯。