2. RISC-V指令集基础
JTAG 这玩意儿,说白了就是调试器跟芯片内部沟通的“门禁系统”。你想想看,芯片跑起来之后,CPU 内部那些寄存器、存储器,对外部来说就是个黑盒子。JTAG 协议就是用来打开这个盒子的钥匙。我个人习惯把 JTAG 的核心分成三块:TAP 状态机、指令寄存器(IR)和数据寄存器(DR)。搞懂这三块,调试接口就算入门了。
2.1 TAP 控制器状态机:JTAG 的大脑
TAP(Test Access Port)控制器是一个 16 状态的有限状态机。它由 TCK(时钟)和 TMS(模式选择)两个信号驱动。每次 TCK 上升沿,TAP 根据 TMS 的电平跳转到下一个状态。嗯,这里要注意:TMS 高电平走“1”路径,低电平走“0”路径。
我在项目中遇到过一个问题:刚接触 JTAG 时,总觉得状态机很复杂,记不住。后来我发现一个规律——所有操作都围绕两个“核心循环”:
- 指令循环:从
Test-Logic-Reset开始,经过Select-DR-Scan→Select-IR-Scan,进入Capture-IR→Shift-IR→Update-IR。 - 数据循环:从
Select-DR-Scan直接进入Capture-DR→Shift-DR→Update-DR。
说白了,你只需要记住怎么进入 Shift-IR 和 Shift-DR 这两个状态,因为所有数据交换都在这里发生。
核心记忆口诀:TMS 连续 5 个 1 强制回到 Test-Logic-Reset。这是 JTAG 的“硬复位”操作,任何时候都能用。
2.2 指令寄存器(IR):告诉 JTAG 要做什么
指令寄存器的作用,就是告诉 TAP 控制器“接下来要操作哪个数据寄存器”。IR 的长度由芯片设计决定,蜂鸟 E203 的 IR 长度是 5 位。为什么是 5 位?因为 2^5=32,足够编码所有调试指令了。
IR 的操作流程很简单:
- 进入
Shift-IR状态。 - 在 TCK 驱动下,通过 TDI 逐位移入指令码(LSB first)。
- 进入
Update-IR状态,指令生效。
我曾经在调试一个第三方调试器时,发现指令总是写不进去。查了半天,原来是 Update-IR 这个状态没走到。很多人以为 Shift 完就完事了,其实指令是在 Update 阶段才锁存的。这是个常见的坑。
| 指令名称 | 指令码(5位) | 选中的数据寄存器 | 说明 |
|---|---|---|---|
| BYPASS | 11111 | Bypass(1位) | 旁路当前 TAP,用于链式扫描 |
| IDCODE | 00001 | IDCODE(32位) | 读取芯片 ID |
| SAMPLE/PRELOAD | 00010 | Boundary-Scan | 采样/预加载边界扫描数据 |
| EXTEST | 00100 | Boundary-Scan | 外部互连测试 |
| DEBUG(E203 自定义) | 10000 | Debug Data Register | RISC-V 调试专用 |
个人技巧:调试时,先用 IDCODE 指令读一下芯片 ID。如果 ID 能正确读出,说明 TAP 状态机和 IR 通路是好的。这是最快速的“冒烟测试”。
2.3 数据寄存器(DR):JTAG 的数据通道
数据寄存器是 JTAG 真正干活的地方。每个指令都会选中一个特定的 DR。DR 的长度可以是 1 位、32 位,甚至几百位。操作流程跟 IR 类似:
- 进入
Shift-DR状态。 - 通过 TDI 逐位移入数据,同时从 TDO 移出数据。
- 进入
Update-DR状态,数据生效。
这里有个关键点:Shift-DR 是双向的。你每移入 1 位,就会移出 1 位。所以 JTAG 的读写操作其实是同时完成的。我刚开始做验证时,总以为读和写是分开的,后来才发现——你想读数据,必须先写一个 dummy 数据进去,把目标数据“推”出来。
2.4 JTAG 链式扫描与边界扫描
链式扫描(Chain Scan)是 JTAG 最强大的特性之一。多个 TAP 可以串联成一条链,共用 TDI 和 TDO。调试器通过 BYPASS 指令旁路掉不需要的 TAP,只操作目标 TAP。
边界扫描(Boundary Scan)则是 JTAG 的“硬件探针”。每个芯片引脚都对应一个边界扫描单元(BSC),这些单元串联成一个移位寄存器。通过 EXTEST 指令,你可以:
- 强制输出:给某个引脚设置高/低电平。
- 采样输入:读取某个引脚当前的逻辑电平。
我记得有一次,板卡上的 FPGA 和 CPU 之间的连线疑似虚焊。用 JTAG 边界扫描,直接给 FPGA 侧引脚输出一个方波,然后在 CPU 侧采样。结果发现有个引脚始终为高——果然是虚焊。这种问题用示波器很难抓,但 JTAG 边界扫描几分钟就定位了。
避坑指南:链式扫描时,TAP 的 IR 长度可能不同。调试器必须知道每个 TAP 的 IR 长度,否则指令会“串位”。我曾经因为忘记配置 IR 长度,导致整个链上的 TAP 都失控了。解决办法是:先发 BYPASS 指令(全 1),让所有 TAP 进入 1 位旁路模式,再逐个操作。
2.5 蜂鸟 E203 的 JTAG 实现要点
蜂鸟 E203 的 JTAG 接口遵循 IEEE 1149.1 标准,但做了 RISC-V 调试相关的扩展。核心差异点:
- IR 长度:5 位,支持最多 32 条指令。
- 自定义指令:
DEBUG(10000)用于访问调试模块(DM)的内部寄存器。 - 数据寄存器:Debug Data Register 是 32 位宽,用于传输 RISC-V 调试命令和返回数据。
在实际仿真中,我建议你重点关注 Shift-IR 和 Shift-DR 这两个状态的波形。用 Verdi 或 GTKWave 抓一下 TDI、TDO、TMS 和 TCK 的时序,看看指令和数据是否正确移位。这是验证 JTAG 接口最直接的方法。
一句话总结:JTAG 的核心就是“状态机选路,指令选寄存器,数据移位传输”。搞懂这三步,调试接口就通了。