研究 Rocket Chip 的 BSCAN 调试原理¶
前言¶
最近 @jsteward 在研究如何通过 JTAG 对 FPGA 里的 Rocket Chip 进行调试。之前 @sequencer 已经做了一些实践,我们在重复他的工作,同时也研究了一下这是怎么工作的。
原理¶
我们从 @sequencer 得到了一份可用的 Scala 代码 和 OpenOCD 配置,并且了解到:
- 可以通过 openocd 找到并调试 Rocket Chip
- openocd 是通过 JTAG 向 FPGA 的 TAP 的 IR 写入 USER4,然后往 DR 写入特定格式的数据,然后控制 Rocket Chip 的 JTAG。
这里涉及到一个“封装”的过程,在一个仅可以控制 DR 的 JTAG 中控制另一个 JTAG。首先可以找到 OpenOCD 端的操作代码:
tunneled_ir[3].num_bits = 3;
tunneled_ir[3].out_value = bscan_zero;
tunneled_ir[3].in_value = NULL;
tunneled_ir[2].num_bits = bscan_tunnel_ir_width;
tunneled_ir[2].out_value = ir_dtmcontrol;
tunneled_ir[1].in_value = NULL;
tunneled_ir[1].num_bits = 7;
tunneled_ir[1].out_value = tunneled_ir_width;
tunneled_ir[2].in_value = NULL;
tunneled_ir[0].num_bits = 1;
tunneled_ir[0].out_value = bscan_zero;
tunneled_ir[0].in_value = NULL;
如果画成图,大概是这个样子(IR):
3 bits | IR Width bits | 7 bits | 1 bit | TDI | Data Register | TDO |
---|---|---|---|---|---|---|
0 | Payload | Tunneled IR Width | 0 | -> | Rocket Chip TAP | -> |
DR:
3 bits | DR Width bits | 7 bits | 1 bit | TDI | Data Register | TDO |
---|---|---|---|---|---|---|
0 | Payload | Tunneled DR Width | 1 | -> | Rocket Chip TAP | -> |
这里 TDI 和 TDO 是直接接到 Rocket Chip 的 JTAG 中的,所以我们期望,当 Rocket Chip TAP 在 Shift-IR/Shift-DR 阶段的时候,刚好通过的是 Payload 部分。而控制 TAP 状态机,需要控制 TMS,这个则是通过一段 HDL 来完成的:
always@(*) begin
if (counter_neg == 8'h04) begin
jtag_tms = TDI_REG;
end else if (counter_neg == 8'h05) begin
jtag_tms = 1'b1;
end else if ((counter_neg == (8'h08 + shiftreg_cnt)) || (counter_neg == (8'h08 + shiftreg_cnt - 8'h01))) begin
jtag_tms = 1'b1;
end else begin
jtag_tms = 1'b0;
end
end
这里 TDI_REG
取的是第一个 bit 的反(也就是上面 IR 对应 0,DR 对应 1 的那一位),shiftreg_cnf
则是之后 7 个 bit,对应上面的 Tunneled IR/DR Width。那么,在选择 IR 时 TMS 的序列为:
4 cycles | 1 cycle | 1 cycle | 2 cycles | shiftreg_cnt-1 cycles | 2 cycles | rest cycles |
---|---|---|---|---|---|---|
0 | 1 | 1 | 0 | 0 | 1 | 0 |
Run-Test/Idle | Select-DR-Scan | Select-IR-Scan | Capture-IR, Shift-IR | Shift-IR | Exit1-IR, Update-IR | Run-Test/Idle |
类似地,如果是选择 DR:
4 cycles | 1 cycle | 1 cycle | 2 cycles | shiftreg_cnt-1 cycles | 2 cycles | rest cycles |
---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 1 | 0 |
Run-Test/Idle | Run-Test/Idle | Select-DR-Scan | Capture-DR, Shift-DR | Shift-DR | Exit1-DR, Update-DR | Run-Test/Idle |
这样,刚好在 Shift-IR/DR 状态下,Payload 会被写入 IR/DR,从而完成了期望的操作。通过规定一个特定格式的 Data Register,可以实现嵌套的 TAP 的 IR 和 DR 的操作。
参考¶
- JTAG Standard
- sequencer/rocket-playground
- SiFive's JTAG Tunnel: https://github.com/sifive/fpga-shells/blob/c099bd9b4f916bc0ba88030939a9614d0b0daf2d/src/main/scala/ip/xilinx/Xilinx.scala#L13
- https://github.com/watz0n/arty_xjtag
- https://github.com/riscv/riscv-openocd/blob/7cb8843794a258380b7c37509e5c693977675b2a/src/target/riscv/riscv.c#L361
- UG740: 7 Series FPGAs Configuration