4. JTAG 指令寄存器(IR)与数据寄存器(DR):BYPASS、IDCODE、SAMPLE/PRELOAD、EXTEST 指令

好,咱们进入 JTAG 的核心地带了。指令寄存器(IR)和数据寄存器(DR),这两个东西搞明白了,JTAG 协议你就掌握了一半。

我个人习惯把 JTAG 比作一个「多路开关」。IR 就是那个开关的档位选择器,你拨到哪个档位,TDI 到 TDO 之间就接通哪条数据通路(DR)。说白了,IR 决定了你要干什么,DR 才是真正干活的地方。

4.1 指令寄存器(IR)的工作原理

IR 是一个移位寄存器,它的长度取决于芯片设计。ARM 芯片通常用 4 位或 5 位,但 IEEE 1149.1 标准规定最少要 2 位。

IR 的操作流程很简单,就两步:

  1. 移位阶段(Shift-IR):TAP 控制器进入 Shift-IR 状态,每个 TCK 上升沿,TDI 上的数据移入 IR,同时 IR 的当前内容从 TDO 移出。
  2. 更新阶段(Update-IR):TAP 进入 Update-IR 状态,IR 移位寄存器里的内容被锁存到并行输出端,变成真正的指令。
小技巧: 我调试时习惯在 Shift-IR 阶段多留几个 TCK 周期,确保移位稳定。有些廉价调试器时序不稳,容易丢位。

这里有个关键点:IR 的移位长度是固定的。比如 4 位 IR,你必须在 Shift-IR 状态送 4 个 TCK 脉冲,多一个少一个都不行。我曾经见过一个同事,代码里把 IR 长度配错了,结果死活读不到 IDCODE,折腾了两天。

4.2 数据寄存器(DR)的多样性

DR 就灵活多了。每条 JTAG 指令对应一个 DR 通路,长度从 1 位到几百位不等。TAP 控制器进入 Shift-DR 状态时,TDI 和 TDO 之间就接上了当前指令选中的那个 DR。

DR 的移位操作和 IR 类似,也是先移位再更新。但 DR 的更新时机更讲究——有些 DR 在 Update-DR 时更新,有些在 Capture-DR 时就采样了。

注意: 千万别在 DR 移位过程中改变 IR 的值!TAP 状态机有严格的状态转换规则,乱来会导致 JTAG 链路锁死。我遇到过一块板子,就是因为在 Shift-DR 时误触了 Select-IR-Scan,结果整个链路上的芯片都傻了。

4.3 四大核心指令详解

IEEE 1149.1 规定了几个 mandatory 指令,咱们挑四个最常用的来讲。

指令 IR 编码(4位示例) DR 长度 主要用途
BYPASS 1111 1 位 旁路当前芯片
IDCODE 0010 32 位 读取芯片 ID
SAMPLE/PRELOAD 0001 边界扫描链长度 采样/预装载
EXTEST 0000 边界扫描链长度 外部互连测试

4.3.1 BYPASS 指令

BYPASS 是最简单的指令。它把 DR 切换成一个 1 位的旁路寄存器。TDI 进来的数据,一个 TCK 周期后就从 TDO 出去了。

为什么要用 BYPASS?你想想看,JTAG 链上可能串了十几颗芯片。如果你只想调试其中一颗,其他芯片设成 BYPASS,数据就能快速穿过它们,直达目标芯片。否则,每颗芯片的边界扫描链可能有几百位长,移位一次得等半天。

实战经验: 我调试多核 ARM 芯片时,经常把不用的核心设成 BYPASS。这样 JTAG 通信速度能提升 10 倍以上。特别是用低速调试器时,这个技巧特别管用。

4.3.2 IDCODE 指令

IDCODE 是 JTAG 的「身份证」。它是一个 32 位的只读寄存器,芯片上电后默认就选中它。所以,你连上 JTAG 后不用发任何指令,直接进 Shift-DR 就能读到 IDCODE。

IDCODE 的 32 位结构如下:

  • 位 31:28:版本号(Version)
  • 位 27:12:厂商代码(Part Number),16 位
  • 位 11:1:制造商代码(Manufacturer ID),11 位,JEDEC 标准
  • 位 0:固定为 1

我记得有一次,客户说板子上的 ARM 芯片不工作。我连上 JTAG 读 IDCODE,发现读出来全是 0xFFFFFFFF。这说明 JTAG 链路不通,或者芯片根本没上电。如果读出来是 0x00000000,那可能是 TMS 时序有问题。这些经验,你多调几次就熟了。

4.3.3 SAMPLE/PRELOAD 指令

这个指令有两个功能,名字就写明白了:

  • SAMPLE:在 Capture-DR 时刻,把芯片引脚上的当前值采样进边界扫描寄存器。不影响芯片正常工作。
  • PRELOAD:在 Update-DR 时刻,把数据预装载到边界扫描寄存器的输出端。为后续的 EXTEST 做准备。

为什么要把这两个功能合在一个指令里?因为 JTAG 标准的设计者希望:你先用 SAMPLE 抓一下当前状态,然后用 PRELOAD 设好下一个测试向量,最后切到 EXTEST 去驱动引脚。一气呵成,不用切换指令。

调试技巧: 我写驱动时,经常用 SAMPLE 来观察芯片引脚的实时电平。比如检查某个 GPIO 是否被正确拉高,不用示波器,JTAG 就能搞定。当然,前提是你的 JTAG 时钟够快,能抓到瞬态信号。

4.3.4 EXTEST 指令

EXTEST 是边界扫描的「重头戏」。它让边界扫描寄存器驱动芯片引脚,同时采样引脚上的输入值。说白了,就是你可以通过 JTAG 来控制芯片的每个引脚输出高或低,然后读回引脚上的实际电平。

EXTEST 的典型应用场景:

  • 板级互连测试:检查 PCB 上的焊点是否虚焊、短路。
  • 引脚电平验证:确认芯片之间的信号电平是否匹配。
  • 故障定位:隔离出是芯片问题还是外围电路问题。

我曾经用 EXTEST 抓过一个很隐蔽的 bug。一块板子上有两颗 ARM 芯片通过 SPI 通信,但偶尔会丢数据。我用 EXTEST 把 SPI 的 MOSI 和 MISO 引脚强制拉高拉低,发现 MISO 线上有个毛刺,追查下去是 PCB 走线太长,信号反射导致的。加了个终端电阻就解决了。

4.4 指令与数据寄存器的协同流程

下面这张图展示了 IR 和 DR 在 TAP 状态机中的协同工作流程:

JTAG 指令与数据寄存器协同流程 JTAG 指令与数据寄存器协同流程 1. 选择指令:进入 Shift-IR 2. 移位 IR:TDI → IR → TDO 3. 更新 IR:锁存指令到并行输出 4. 选择数据:进入 Shift-DR 5. 移位 DR:TDI → DR → TDO 6. 更新 DR:数据生效/采样完成 指令决定 DR 通路 关键说明 • IR 移位长度固定(如 4 位),DR 长度由指令决定 • BYPASS 指令的 DR 只有 1 位,IDCODE 的 DR 是 32 位 • SAMPLE/PRELOAD 和 EXTEST 共用边界扫描链,长度相同 • 每次操作必须先选指令(IR),再操作数据(DR) • 多芯片链路上,BYPASS 可加速非目标芯片的数据穿透

这个流程看起来简单,但实际调试时容易出问题。我见过最典型的错误是:有人想在 Shift-DR 状态下直接切换指令,结果 TAP 状态机跑飞了。记住,每次指令切换都必须经过 Run-Test/Idle 状态,这是标准规定的。

4.5 避坑指南

我曾经踩过的坑:
  • IR 长度不匹配:不同芯片的 IR 长度可能不同。链路上混用 4 位和 5 位 IR 的芯片时,必须分别处理。我一开始没注意,结果指令总是发错。
  • DR 移位位数算错:边界扫描链的长度不是固定的。换一颗芯片就要重新算。我写过一个脚本自动解析 BSDL 文件,省了不少事。
  • TCK 频率太高:有些芯片的 JTAG 接口有最大频率限制。我遇到过一块板子,TCK 超过 10MHz 就丢数据,降到 5MHz 就稳了。
我的习惯: 每次连上新板子,第一件事就是读 IDCODE。读到了,说明 JTAG 链路基本正常。读不到,先查供电、复位、TCK/TMS 连接。这个习惯帮我省了无数排查时间。