3. JTAG 状态机:核心状态详解

JTAG 状态机,说白了就是 TAP 控制器的“大脑”。它一共 16 个状态,但真正干活的核心状态就那么几个。我刚开始学 JTAG 时,总觉得这 16 个状态绕来绕去记不住。后来发现,你只要抓住几个关键节点,整个状态机就清晰了。

先看一张整体状态转移图,心里有个谱。

JTAG TAP 状态机核心状态转移图 Test-Logic-Reset Run-Test/Idle Select-DR-Scan Select-IR-Scan Capture-DR Shift-DR Capture-IR Shift-IR TCK=1 TMS=1 TMS=1 TMS=0 TMS=0 TMS=0 TMS=0 说明:TCK 上升沿采样 TMS,决定下一状态。Shift-DR 和 Shift-IR 是数据移位的核心工作状态。

3.1 Test-Logic-Reset:上电后的“安全屋”

这个状态是 TAP 控制器的默认状态。上电后,TAP 控制器会自动进入这里。它的特点是什么?——什么都不干。所有测试逻辑都被复位,边界扫描寄存器处于旁路模式,不影响芯片正常工作。

我个人习惯在每次调试会话开始时,先发 5 个 TCK 周期的 TMS=1,确保状态机回到 Reset。为什么是 5 个?因为从任何状态,连续 5 个 TMS=1 都能回到 Reset。这是 IEEE 1149.1 标准规定的“安全网”。

小技巧: 如果你发现 JTAG 链路不通,第一步就是发 5 个 TMS=1 复位状态机。我曾经在调试一块 FPGA 板时,因为上电时序问题导致 TAP 状态机跑飞,就是靠这招拉回来的。

3.2 Run-Test/Idle:等待指令的“待机室”

从 Reset 发一个 TMS=0,就进入 Idle。这个状态是状态机的“中间站”。你在这里等待,TAP 控制器什么都不做,但一旦收到 TMS=1,就会进入 Select-DR 或 Select-IR 分支。

说白了,Idle 就是让你喘口气的地方。在两次扫描操作之间,通常会把状态机停在 Idle。比如你要先发一条指令,再传数据,中间就会经过 Idle。

嗯,这里要注意:Idle 状态下,如果 TMS 保持为 0,状态机会一直停在 Idle。所以调试器通常会在 Idle 等待,直到准备好下一次操作。

3.3 Shift-DR:数据移位的“主战场”

Shift-DR 是 JTAG 最核心的工作状态之一。在这个状态下,每个 TCK 上升沿,TDI 上的数据会移入数据寄存器,同时 TDO 会移出数据寄存器的当前位。

它的工作流程是这样的:

  1. Select-DR-Scan:从 Idle 发 TMS=1 进入
  2. Capture-DR:TMS=0,数据寄存器捕获当前值
  3. Shift-DR:TMS=0,开始移位操作
  4. Exit1-DR:TMS=1,结束移位
  5. Update-DR:TMS=1,将移位后的数据更新到寄存器

我在项目中遇到过一个问题:某 ARM 芯片的边界扫描链有 1000 多位,每次移位都要精确计数。如果少移一位,整个数据就错位了。后来我写了个脚本,自动计算移位长度,再也没出过问题。

关键点: Shift-DR 状态下,TDO 在每个 TCK 上升沿输出当前移位寄存器的最低位。调试器必须同步读取 TDO,否则数据会丢失。

3.4 Shift-IR:指令移位的“指挥中心”

Shift-IR 和 Shift-DR 类似,但操作的是指令寄存器。指令寄存器通常比数据寄存器短得多——ARM 的 JTAG 指令寄存器一般是 4 位或 5 位。

流程上,Shift-IR 分支和 Shift-DR 分支是对称的:

步骤 状态 TMS 说明
1 Select-IR-Scan 1 从 Idle 进入 IR 分支
2 Capture-IR 0 指令寄存器捕获固定值(通常是 0b01)
3 Shift-IR 0 逐位移入新指令,同时移出旧指令
4 Exit1-IR 1 结束移位
5 Update-IR 1 指令生效,后续操作使用新指令

你想想看,为什么 Capture-IR 要捕获一个固定值?这是标准规定的,用于验证指令寄存器链路是否正常。如果读回来的不是 0b01,说明链路有问题。

避坑指南: 我曾经在调试一块多核 ARM 芯片时,发现 Shift-IR 后指令不生效。查了半天,原来是 Update-IR 状态没走到——TMS 序列少了一个 1。记住:移位完成后,必须经过 Update-IR 指令才会生效。

3.5 状态机实战:一个完整的扫描序列

说了这么多,不如看一个实际例子。假设我们要读取 ARM 芯片的 IDCODE(设备识别码),完整的 TMS 序列如下:

// 复位状态机
TMS = 1, 1, 1, 1, 1  // 进入 Test-Logic-Reset

// 进入 Idle
TMS = 0               // 进入 Run-Test/Idle

// 选择 IR 分支,移入 IDCODE 指令
TMS = 1               // Select-IR-Scan
TMS = 0               // Capture-IR
TMS = 0, 0, 0, 0      // Shift-IR:移入 4 位指令(假设 IDCODE = 0b0010)
TMS = 1               // Exit1-IR
TMS = 1               // Update-IR

// 选择 DR 分支,读取 IDCODE 数据
TMS = 1               // Select-DR-Scan
TMS = 0               // Capture-DR
TMS = 0 (重复 32 次)  // Shift-DR:移出 32 位 IDCODE,同时移入 32 位 0
TMS = 1               // Exit1-DR
TMS = 1               // Update-DR

// 回到 Idle,准备下一次操作
TMS = 0               // Run-Test/Idle

这个序列看起来长,但实际执行很快——每个 TCK 周期也就几十纳秒。调试器通常会把这种序列封装成函数,你只需要调用 jtag_shift_ir() 和 jtag_shift_dr() 就行。

3.6 状态机常见问题与排查

最后,分享几个我踩过的坑:

  • 状态机卡死:TMS 信号受干扰,导致状态机进入未定义状态。解决办法:发 5 个 TMS=1 强制复位。
  • 移位长度不对:Shift-DR 或 Shift-IR 时,TCK 周期数不等于寄存器长度。后果是数据错位或指令错误。
  • Update 缺失:移位完成后没走到 Update 状态,寄存器内容不变。检查 TMS 序列是否完整。
  • TCK 毛刺:时钟线上有毛刺,导致状态机多跳一步。建议在 TCK 上加 RC 滤波。
我的经验: 调试 JTAG 状态机时,用逻辑分析仪抓 TMS、TCK、TDI、TDO 四根线,一眼就能看出问题。我曾经靠这个在半小时内定位了一块 PCB 的 JTAG 链路故障——原来是 TMS 走线过长导致信号反射。

好了,JTAG 状态机的核心状态就这些。记住:Reset 是安全屋,Idle 是待机室,Shift-DR 和 Shift-IR 是干活的地方。掌握了这几个状态,你就掌握了 JTAG 的“交通规则”。