3. 硬件调试接口:JTAG、SWD、CJTAG原理与对比

调试接口,说白了,就是芯片和我们开发工具沟通的物理通道。你写好的代码要下载进去,芯片跑到一半要停下来给你看寄存器状态,全得靠它。

早期江湖上基本是JTAG一统天下。后来ARM公司嫌JTAG引脚太多,搞了个SWD出来,省了两根线。再后来,有些厂家觉得“我既要SWD的省引脚,又想兼容JTAG的菊花链”,于是CJTAG就登场了。

我个人习惯把这三者的关系想象成:JTAG是标准VGA线,SWD是HDMI精简版,CJTAG则是那种“你插Type-C也能用、插老USB也能用”的转接头。

3.1 JTAG——老祖宗级的边界扫描协议

JTAG(Joint Test Action Group)最初是为了测试PCB板级连接而生的。它定义了一套边界扫描(Boundary Scan)机制,后来大家发现这东西不仅能测板子,还能用来调试CPU,于是就成了事实标准。

3.1.1 核心信号

信号 方向 作用
TMS 输出 模式选择——决定TAP状态机下一步去哪
TCK 输出 时钟——所有操作在它的上升沿或下降沿触发
TDI 输出 数据输入——把指令或数据串行送进芯片
TDO 输入 数据输出——芯片把结果串行吐出来
nTRST(可选) 输出 复位TAP控制器,不接的话可以用TMS硬复位

这里要记住一个关键点:JTAG是一个串行协议。你往里写数据,是1位1位往里塞的。TDI和TDO可以同时操作,相当于一条流水线。

3.1.2 TAP状态机

TAP(Test Access Port)是JTAG的大脑。它本质上是个16状态的有限状态机,通过TMS在TCK边沿上的电平组合来驱动状态跳转。

我见过有人调试JTAG连不上,卡了大半天,结果发现是TMS时序搞错了。TMS必须在TCK的上升沿之前稳定下来,否则状态机就跳到了不知道哪个州(State,谐音梗)。

核心口诀:JTAG调试的基本流程就是“先进入Shift-IR装指令 → 进入Shift-DR喂数据 → 回到Run-Test/Idle等结果 → 通过Shift-DR把TDO读回来”。

3.1.3 菊花链

JTAG一个很酷的特性是菊花链(Daisy Chain)。你可以把多个支持JTAG的芯片串在一起,只用一组JTAG接口就能访问所有芯片。

  • 怎么串? 把上一颗芯片的TDO接到下一颗芯片的TDI。
  • 怎么区分? 通过TMS控制TAP状态机,让不关心的芯片进入Bypass模式(只用1比特延迟就传过去),只让目标芯片进入数据模式。

我曾经在一款服务器主板上调试BMC和CPU之间的链路,板上串了5颗JTAG器件。最开始菊花链顺序我搞反了,结果TDI和TDO数据死活对不上。后来老老实实拿示波器看TDO信号,发现第一颗芯片的TDO根本什么都没吐,这才意识到链子接错了。

小技巧:调试菊花链时,先让所有芯片进Bypass,看看TDO能不能把TDI的数据延迟N个时钟后完整透传出来。能透传,说明链子物理上通了。

3.2 SWD——ARM阵营的精简革命

SWD(Serial Wire Debug)是ARM公司推出的调试接口。它只用了两根线,就能完成JTAG四根线才能干的事。

3.2.1 为什么只需两根线?

SWD把时钟和数据分开,但把TDI和TDO合并成了双向的SWDIO。这样省掉了一根线。同时,SWD也用SWCLK做时钟。状态机控制也编进了数据包里,不再需要独立的TMS。

信号 方向 作用
SWCLK 输出 时钟,一般4~10MHz
SWDIO 双向 数据和控制复用

3.2.2 包格式

SWD的通信是有固定帧格式的。你对SWDIO发一串比特,格式如下:

起始位(1) + APnDP(1) + RnW(1) + 地址A[2:3](2) + 奇偶校验(1) + 停止位(1) +  park位(1)
—— 主机发完这8位后,切换方向,等待目标返回 确认位(3) + 数据(32) + 奇偶校验(1)

为什么这么复杂?因为只有两根线,所有控制信息、地址信息、读写标志全得塞进一个串行序列里。

注意:SWDIO是双向线。主机发完请求包后,必须把IO口从输出切回输入,然后等目标返回响应。这个切换时机如果没处理好,轻则通信错乱,重则烧坏IO口——因为两边同时输出,就会VCC对GND直通。

3.2.3 速度与抗干扰

SWD的典型速率能跑到4~10MHz,比JTAG的10~50MHz低一些,但胜在引脚少。实际调试中,SWDIO线的容性负载比JTAG的独立TDI+TDO要敏感一些,线长了容易丢包。

我个人建议:SWD走线别超过15cm。如果一定要长线传输,考虑加个电平转换/缓冲芯片,或者降低SWCLK频率到2MHz以下。

3.3 CJTAG——兼容与切换的艺术

CJTAG(Compact JTAG)也叫SWJ-DP(Serial Wire JTAG Debug Port)。它是ARM的一种混合接口,物理上同时支持JTAG和SWD。上电时默认工作在JTAG模式,调试器通过一段特定的序列,就能让芯片切到SWD模式。

3.3.1 切换序列

CJTAG接口检测到SWD切换序列后,会把原本的JTAG引脚重新配置:

  • TCK → SWCLK(不变)
  • TMS → SWDIO(TMS被重映射成SWDIO)
  • TDI、TDO、nTRST 在这个模式下就不用了。

切换序列很简单(我凭记忆写一个典型的):

// 伪代码,来自ARM CoreSight架构
// 先让TMS输出至少50个周期的1(确保回到Test-Logic-Reset)
for (i = 0; i < 50; i++) { TCK高 → TMS=1 → TCK低 }
// 然后输出SWD切换序列: 0b01111100 (注意LSB先发)
send_bits(0b01111100, 8);
// 等待目标返回确认
response = read_3_bits();
if (response == 0b001) // 确认
    // 现在CJTAG进入SWD模式

切换成功后,SWCLK和SWDIO就接管了。你不需要重新接线,调试工具也没变,只是协议变了。

这个机制非常实用:同一套硬件调试器(比如J-Link、ST-Link)在CJTAG接口上,既能以JTAG方式连接传统ARM7/9,也能以SWD方式连接Cortex-M0。你不需要换电路板,换根线,只需要调试器发一段序列。

3.4 三者的横向对比

维度 JTAG SWD CJTAG
引脚数 4~5 2 4-5(但可切换)
最大速率 通常10~50MHz 通常4~10MHz 取决于当前模式
菊花链 原生支持 不支持 仅JTAG模式下支持
调试带宽 高(TDI/TDO并行) 中(半双工串行) 同SWD或JTAG
设计复杂度 中 低 中高
典型应用 CPU、FPGA、板级测试 Cortex-M全系列 Cortex-A/R/M系列,一板多用
引脚成本 较高 最低 适中

3.5 如何选择?

  • 如果你用的是MCU(Cortex-M0/3/4/7等),直接用SWD。省引脚,够用,成本低。
  • 如果你调试的是应用处理器(Cortex-A系列),或者需要同时调试多核、FPGA与CPU协同,选CJTAG,预留JTAG接口,实际用SWD模式。
  • 如果你在做板级测试(比如验证PCB上所有BGA焊点是否连接正常),那JTAG的边界扫描功能是无可替代的。SWD干不了这个。

我一个常用的设计惯例:在原理图上把JTAG的TMS和TCK与SWDIO、SWCLK合并成一个信号网络。板子上保留5针排针(SWCLK、SWDIO、GND、Vref、nSRST)。这样同一个连接器,既支持纯SWD调试器,也支持CJTAG调试器在JTAG模式下连接。多出来的TDI/TDO用0欧电阻可选的接到排针或者不接。

3.6 知识体系框架

下面这张图把三者的全貌串起来了。你可以看到它们之间的切换关系、物理引脚映射以及典型的调试器协议栈。

硬件调试接口:JTAG / SWD / CJTAG 知识体系 调试协议层(ARM CoreSight / JTAG规范) JTAG 接口 SWD 接口 CJTAG 接口 物理信号 TCK, TMS, TDI, TDO, nTRST 支持菊花链 / 边界扫描 物理信号 SWCLK, SWDIO(双向) 无双向校验/无菊花链 物理信号 TCK+TMS映射SWC+SWDIO 切换序列进入SWD模式 数据包构成对比 JTAG: IR Scan / DR Scan | SWD: 8bit请求头 + 3bit确认 + 32bit数据 + 1bit校验 调试器实物:J-Link / ST-Link / DAP-Link 统一物理连接

再强调一次:实物连线时,SWDIO不能另外接上拉或下拉电阻,因为它双向驱动。接了可能会把IO电平钳住,导致通信失败。JTAG的TDI/TDO则可以加弱上拉保证空闲电平。

好了,以上就是JTAG、SWD、CJTAG的原理和对比。你在实际项目中,根据板子的管脚数量和调试需求来做选择就行。没有的好坏,只有合不合适的方案。