3. JTAG 状态机:核心状态详解
JTAG 状态机,说白了就是 TAP 控制器的“大脑”。它一共 16 个状态,但真正干活的核心状态就那么几个。我刚开始学 JTAG 时,总觉得这 16 个状态绕来绕去记不住。后来发现,你只要抓住几个关键节点,整个状态机就清晰了。
先看一张整体状态转移图,心里有个谱。
3.1 Test-Logic-Reset:上电后的“安全屋”
这个状态是 TAP 控制器的默认状态。上电后,TAP 控制器会自动进入这里。它的特点是什么?——什么都不干。所有测试逻辑都被复位,边界扫描寄存器处于旁路模式,不影响芯片正常工作。
我个人习惯在每次调试会话开始时,先发 5 个 TCK 周期的 TMS=1,确保状态机回到 Reset。为什么是 5 个?因为从任何状态,连续 5 个 TMS=1 都能回到 Reset。这是 IEEE 1149.1 标准规定的“安全网”。
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 会移出数据寄存器的当前位。
它的工作流程是这样的:
- Select-DR-Scan:从 Idle 发 TMS=1 进入
- Capture-DR:TMS=0,数据寄存器捕获当前值
- Shift-DR:TMS=0,开始移位操作
- Exit1-DR:TMS=1,结束移位
- Update-DR:TMS=1,将移位后的数据更新到寄存器
我在项目中遇到过一个问题:某 ARM 芯片的边界扫描链有 1000 多位,每次移位都要精确计数。如果少移一位,整个数据就错位了。后来我写了个脚本,自动计算移位长度,再也没出过问题。
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,说明链路有问题。
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 状态机的核心状态就这些。记住:Reset 是安全屋,Idle 是待机室,Shift-DR 和 Shift-IR 是干活的地方。掌握了这几个状态,你就掌握了 JTAG 的“交通规则”。