1、JTAG协议基础:JTAG历史、IEEE 1149.1标准、TAP控制器状态机详解

各位同学,咱们今天正式开讲JTAG。说实话,这玩意儿在嵌入式领域就像空气一样——你天天在用,但很少有人真正停下来琢磨它到底是怎么工作的。

我记得刚入行那会儿,拿着一个JLINK给板子烧程序,插上就能用,从来没想过背后是什么协议在支撑。直到有一次,一块新板子死活连不上调试器,我才被迫去啃JTAG的spec。嗯,从那以后,我再也不敢小看这5根线了。

1.1 JTAG的诞生:一段不得不提的历史

JTAG的全称是Joint Test Action Group,中文叫联合测试行动组。这名字听起来像某个特工组织,其实它是一群工程师在1985年凑在一起搞出来的标准。

当时PCB越来越密,芯片引脚越来越多,传统的飞针测试和床板测试已经搞不定了。你想啊,一个BGA封装的芯片,几百个引脚全藏在肚子底下,测试探针根本碰不到。怎么办?这帮人想了个绝妙的点子:干脆在芯片内部做一套测试电路,通过串行方式访问每个引脚的状态。

1990年,这个方案被IEEE采纳,成了IEEE 1149.1标准,也叫Standard Test Access Port and Boundary-Scan Architecture。说白了,就是边界扫描标准。

核心思想:在芯片的每个I/O引脚和内部逻辑之间,插入一个移位寄存器单元(边界扫描单元)。这些单元串联成一条链,通过一个专用的测试访问端口(TAP)来控制。这样,即使物理上够不到引脚,也能通过串行方式读取或设置每个引脚的状态。

1.2 IEEE 1149.1标准:到底规定了什么?

这个标准其实就干了三件事:

  1. 定义了TAP(Test Access Port)的接口信号——就是那5根线
  2. 定义了TAP控制器的状态机——16个状态,来回跳
  3. 定义了指令寄存器和数据寄存器的操作方式——你想让芯片干啥,就往指令寄存器里写啥

咱们先看接口信号。标准规定了5根必备线,外加1根可选的:

信号名 方向 功能描述
TCK 输入 测试时钟。所有操作都在这个时钟的上升沿或下降沿采样
TMS 输入 测试模式选择。控制TAP状态机的跳转
TDI 输入 测试数据输入。串行数据从这里进入芯片
TDO 输出 测试数据输出。串行数据从这里流出芯片
TRST 输入 测试复位(可选)。异步复位TAP控制器

个人经验:我建议你在设计板子时,一定要把TRST拉出来,哪怕只用个GPIO模拟。有一次我在调试一块量产板,TAP控制器莫名其妙卡死在未知状态,TRST拉一下立刻恢复,省了我半天查线的时间。

1.3 TAP控制器状态机详解

好,重头戏来了。TAP控制器状态机是整个JTAG的核心。你想想看,5根线里只有TMS是控制信号,怎么实现那么多复杂操作?答案就是——靠状态机。

这个状态机一共有16个状态,每个状态在TCK的上升沿根据TMS的电平决定下一个状态跳到哪里。说白了,你只要在TCK的每个上升沿之前,把TMS拉高或拉低,就能让状态机走到你想要的任何位置。

我画了一张图,把状态机的全貌展示出来:

TAP控制器状态机(IEEE 1149.1) TAP控制器状态机(IEEE 1149.1) Test-Logic-Reset TMS=0 Run-Test/Idle TMS=1 Select-DR-Scan TMS=0 TMS=1 Select-IR-Scan Capture-DR TMS=0 Shift-DR TMS=1 TMS=0 Exit1-DR TMS=0 TMS=1 Pause-DR TMS=1 TMS=0 Exit2-DR TMS=0 TMS=1 Update-DR TMS=1 TMS=1 说明: 每个状态在TCK上升沿根据TMS电平跳转。TMS=0通常沿路径向下,TMS=1则向右或向上退出。 IR扫描路径与DR扫描路径完全对称,只是将"DR"替换为"IR"。

这张图看着复杂,其实规律很简单。我总结了一个口诀:

状态机口诀:

  • TMS=0:沿着当前路径往下走(Capture → Shift → Exit1 → Pause → Exit2)
  • TMS=1:退出当前路径(回到Select或Update)
  • 连续5个TMS=1:无论你在哪个状态,都会回到Test-Logic-Reset

为什么会设计成连续5个1就复位?我猜是为了容错。你想啊,如果调试器跟目标芯片的时钟不同步了,你只要连续拉高TMS 5个周期,状态机肯定回到已知状态。这个设计在实战中救过我很多次。

1.4 两条核心路径:DR扫描 vs IR扫描

状态机里其实只有两条核心路径:

  • DR扫描路径:Select-DR-Scan → Capture-DR → Shift-DR → Exit1-DR → Update-DR
  • IR扫描路径:Select-IR-Scan → Capture-IR → Shift-IR → Exit1-IR → Update-IR

两条路径完全对称,只是操作的对象不同。DR路径操作的是数据寄存器,IR路径操作的是指令寄存器。

我举个例子你就明白了。假设你想读取芯片的IDCODE:

  1. 先走IR路径,把指令"IDCODE"(比如二进制1001)移入指令寄存器
  2. 再走DR路径,把芯片ID移出来

就这么简单。每次操作都是先送指令,再送数据。

避坑指南:我曾经在一个项目里,发现读出来的IDCODE总是全0。查了半天,原来是TDO引脚虚焊了。JTAG的TDO是唯一的数据输出线,如果它断了,你读到的永远是0。所以调试时,先确认TDO有没有波形,这是最基本的排查步骤。

1.5 边界扫描寄存器:JTAG的"眼睛"

前面提到边界扫描单元,这里展开说一下。每个I/O引脚对应一个边界扫描单元(BSC),这些单元串联成边界扫描寄存器(BSR)。

每个BSC可以:

  • 捕获(Capture):把引脚上的电平值锁存进来
  • 移位(Shift):把数据沿着链传给下一个BSC
  • 更新(Update):把数据输出到引脚上

说白了,BSR就是芯片的"眼睛"和"手"——能看引脚状态,也能控制引脚输出。

操作 对应状态 作用
Capture Capture-DR 把引脚当前电平锁存到BSR
Shift Shift-DR 把BSR数据串行移出(同时移入新数据)
Update Update-DR 把BSR数据输出到引脚

1.6 常见的JTAG指令

IEEE 1149.1规定了几条强制性指令,所有兼容芯片都必须支持:

指令 指令码(示例) 功能
BYPASS 1111 旁路模式,数据直接通过1位寄存器传到TDO
SAMPLE/PRELOAD 0001 采样引脚状态或预加载数据到BSR
EXTEST 0000 外部测试,通过BSR控制引脚输出,检测板级互联
IDCODE 1001(可选) 读取芯片ID(可选,但几乎所有芯片都支持)

注意:BYPASS指令在JTAG链有多颗芯片时特别有用。你想访问链上的某颗芯片,就把其他芯片都设成BYPASS,这样数据只经过它们1位寄存器就出来了,不会浪费时钟周期。

1.7 实战中的JTAG时序

说了这么多理论,咱们看一段实际的JTAG操作时序。假设我们要读取芯片的IDCODE:

// 伪代码:读取IDCODE
// 假设指令寄存器长度=4位,IDCODE指令=0b1001

// 第一步:进入Select-IR-Scan状态
// 从Run-Test/Idle开始,TMS=1,1,0
// 状态跳转:Run-Test/Idle → Select-DR-Scan → Select-IR-Scan

// 第二步:走IR路径,移入指令
// Select-IR-Scan → Capture-IR (TMS=0)
// Capture-IR → Shift-IR (TMS=0)
// Shift-IR: 移入4位指令 1001 (LSB first)
//   TCK上升沿: TDI=1, TMS=0 (移入第1位)
//   TCK上升沿: TDI=0, TMS=0 (移入第2位)
//   TCK上升沿: TDI=0, TMS=0 (移入第3位)
//   TCK上升沿: TDI=1, TMS=1 (移入第4位,同时退出)
// Shift-IR → Exit1-IR (TMS=1)
// Exit1-IR → Update-IR (TMS=1)
// Update-IR: 指令生效

// 第三步:进入Select-DR-Scan状态
// Update-IR → Select-DR-Scan (TMS=1)

// 第四步:走DR路径,读出ID
// Select-DR-Scan → Capture-DR (TMS=0)
// Capture-DR → Shift-DR (TMS=0)
// Shift-DR: 移出32位ID (LSB first)
//   连续32个TCK上升沿,TMS=0,从TDO读取数据
//   第32个TCK上升沿,TMS=1,退出
// Shift-DR → Exit1-DR (TMS=1)
// Exit1-DR → Update-DR (TMS=1)

// 第五步:回到Run-Test/Idle
// Update-DR → Select-DR-Scan (TMS=1)
// Select-DR-Scan → Run-Test/Idle (TMS=0)

这段代码看着长,其实核心就几步:送指令 → 送数据 → 读数据。我刚开始写JTAG驱动时,总是搞不清TMS应该在TCK上升沿之前还是之后设置。后来养成一个习惯:在TCK上升沿之前,先把TMS和TDI准备好,这样时序就不会乱。

关键时序要点:

  • 所有状态跳转发生在TCK上升沿
  • TMS和TDI在TCK上升沿之前建立稳定
  • TDO在TCK下降沿之后采样(通常在下降沿读取)
  • 数据移位是LSB first(最低位在前)

1.8 本章小结

好了,咱们把JTAG的基础捋了一遍。核心就三件事:

  • 5根线:TCK、TMS、TDI、TDO、TRST(可选)
  • 16个状态的状态机:两条路径(IR和DR),TMS控制跳转
  • 边界扫描寄存器:芯片的"眼睛"和"手"