-
Notifications
You must be signed in to change notification settings - Fork 356
JTAG On Chip Debugging Support
This is currently experimental and a work in progress. The external JTAG (IEEE 1149) interface allows limited software debugging support. It supports a few JTAG instruction types:
Code | Name | Width | Description |
---|---|---|---|
0000 | IDCODE | 32 | Device type identification (standard JTAG instruction) |
0011 | CONTROL | 7 | Debug Control register |
0100 | INJECT_INST | 32 | When this is selected, the shifted in value will be sent into the pipeline as an instruction, running on the thread and core indicated in the control register. |
0101 | TRANSFER_DATA | 32 | Allows bi-directional data transfer between host and target. There is a control register visible to all threads called "JTAG data." This instruction will shift the current value out of that register and shift a new value into it. |
0110 | STATUS | 2 | Status of injected instruction |
1111 | BYPASS | 1 | Pass data through with no side effects (standard JTAG instruction) |
The debug control register consists of the following fields:
6 | 5 | 5 | 3 | 2 | 1 | 0 |
core | thread | halt |
The core and thread fields control which thread the INJECT_INST will execute in the context of. When the halt field is 1, the processor will stop fetching instructions. This must be true to execute the INJECT_INST instruction.
The status register can have the following values:
Value | Meaning |
---|---|
00 | READY - This will be the state if an instruction has completed successfully |
01 | ISSUED - An instruction has been issued, but has not completed yet |
10 | ROLLED_BACK - The instruction did not complete, but was rolled back. This is normal for a branch instruction (which can be used to change the program counter), but for a memory instruction, indicates a cache miss occurred, and that the debugger should reissue the instruction |
These instructions are sufficient to inspect processor state. Here are a few examples of how they can be used:
To read a register (assuming processor is already halted), first transfer it to the control register, then read out:
INJECT_INST 0xac0000d2 (getcr s6, 18)
TRANSFER_DATA out: <data value>
TRANSFER_DATA in: <data value>
INJECT_INST 0x8c0000f2 (setcr s7, 18)
The next instructions require scratchpad registers, which can be stored on the host using the read register operation, then restored before resuming the program with the write register operation.
TRANSFER_DATA in: <address>
INJECT_INST getcr s0, 18
INJECT_INST load_32 s0, (s0)
INJECT_INST setcr s0, 18
TRANSFER_DATA out: data
May need to retry, as described in the section above about the status register.
TRANSFER_DATA in: <address>
INJECT_INST getcr s0, 18
TRANSFER_DATA in: <data>
INJECT_INST getcr s1, 18
INJECT_INST store_32 s1, (s0)
The program counter is not directly accessible as a register, but can be read by using a call instruction. This will clobber the ra register, so that must have already been saved on the host:
INJECT_INST call 0 (will stay on same instruction)
INJECT_INST getcr ra, 18
TRANSFER_DATA out: program counter
TRANSFER_DATA in: <dest addr>
INJECT_INST getcr s0, 18
INJECT_INST b s0
Information about limitations is documented in the file hardware/core/on_chip_debugger.sv
See https://github.com/jbush001/NyuziProcessor/issues/149
- Instrumenting register file and caches with bypasses that allow direct manipulation.
- Creating a small RAM that can be selectively enabled and loaded by JTAG, and adding special interrupts to jump to it.