RISC-V is an open standard instruction set architecture based on established RISC (Reduced Instruction Set Computer) principles.
Comparing to ARM and x86, a RISC-V CPU has the following advantages:
To create a minimum viable debugger on a RISC-V Processor, you need the following capabilities:
Memory Access: The ability to peek and poke memory. This means reading from and writing to specific memory locations. Alternatively, the ability to push instructions (through the program buffer) can also suffice if direct memory access is not possible or is restricted.
CPU Register Access: The ability to read and write CPU registers. Registers are special storage locations within the CPU that are used for calculations, control, and status information.
CPU Control: The ability to control the CPU. This involves several key actions:
Here’s a simplified diagram representing these core components:
+---------------------+ +-----------------------+
| Debugger Host | | Target Device |
+---------------------+ +-----------------------+
| |
| (Debug Interface) |
v v
+-----------------------+ +-----------------------+
| Memory Access |----->| Memory |
| (Read/Write) | | |
+-----------------------+ +-----------------------+
| |
| |
+-----------------------+ +-----------------------+
| CPU Register Access |----->| CPU Registers |
| (Read/Write) | | |
+-----------------------+ +-----------------------+
| |
| |
+-----------------------+ +-----------------------+
| CPU Control |----->| CPU (Halt, Step, etc.)|
| (Halt, Step, Reset) | | |
+-----------------------+ +-----------------------+
VexRiscv is an FPGA-friendly CPU core that implements the RISC-V instruction set architecture (ISA). Designed with flexibility and scalability in mind, it caters to a wide range of applications, from compact microcontroller-based systems to more complex multi-core configurations. The project is open-source under the MIT license, promoting community collaboration and adaptation.
VexRiscv is developed using SpinalHDL, a high-level hardware description language that enhances design modularity and reusability. This approach allows for a plugin-based architecture, enabling users to customize and extend the CPU’s capabilities to meet specific project requirements.
The VexRiscv soft core supports minimal custom debugging by utilizing a debug plugin, which has two registers that allow for complete control of the CPU.
With these two registers, the core has all the necessary functions for a complete debugger, thus meeting the minimum requirements of a viable debugger.
JTAG stands for Joint Test Action Group. JTAG support in VexRiscv is implemented through a combination of hardware and software components that adhere to the JTAG standard (IEEE 1149.1).
The diagram below describe how a host computer (with GDB) uses a JTAG interface to debug a VexRiscv CPU.
+-----------------+
| GDB (Host PC)|
+-------+---------+
| (GDB remote target protocol)
v
+---------------+
| OpenOCD |
| (VexRiscv |
| Driver) |
+-------+-------+
| (JTAG transactions)
v
+----------+ +-----------------+ +-------------------+
| JTAG | <---> | JTAG Bridge |--->| VexRiscv Debug |
| Dongle | |(TAP, IR, DR, | | Plugin |
|(TCK, TMS,| |State Machine) | |(SystemDebugBus) |
|TDI, TDO) | +-----------------+ +--------+----------+
+----------+ | (CPU Control, Registers)
|
v
+--------------------------+
| VexRiscv CPU |
+--------------------------+
Debugging VexRiscv using JTAG involves connecting a JTAG dongle to the FPGA, and then using OpenOCD with a VexRiscv specific driver, to communicate with the JTAG bridge, which accesses the CPU debug module, all while GDB executes on the host machine. A custom version of OpenOCD acts as the glue between GDB and the VexRiscv debug logic, handling the low-level JTAG communication.
The VexRiscv CPU has its own specific debugging implementation that is not compatible with the standard RISC-V Debug Specification. Current deviation from the ratified RISC-V debug specification is listed below :
| Ratified Debug Spec Feature | VexRiscv Support Status |
|---|---|
| Halt/Resume/Singlestep | ✅ Supported |
| Register (GPR/CSR) Read/Write | ✅ Via instruction injection |
| Memory Access via Abstract Commands | ❌ Not standard; done via instruction injection |
| Hardware Breakpoints / Triggers | ⚠️ Supports PC and load/store triggers, other modes not supported |
| Program Buffer | ✅ Supported |
| Multi-hart / Multi-DM support | ⚠️ Unable to halt/resume multiple harts with a single command |
| Alternate Transport Modules (non-JTAG) | ❌ Not implemented |
| DM/DTM layer per spec | ❌ Partial; custom version used, need to extend to supprt SWD |
Other than above features , below feature needs to be implemented :
SWD stands for Serial Wire Debug. The key differences between JTAG and SWD are :
To add support for the RISC-V debug standard over SWD in VexRiscv below steps need to be followed:
To be compliant, the implementation must have at least one of the memory access mechanisms: Program Buffer, Abstract Access Memory or System Bus Access (SBA).
Create a GDB server that can communicate with the DTM over the SWD interface. This server would use the standard RISC-V debug commands.
Tools like OpenOCD or probe-rs can be used as a basis for the GDB server, given that they support RISC-V debug specification. Currently a custom version of OpenOCD works with VexRiscv.
The following conceptual diagram explains the general components and data flow involved in debugging via SWD:

In the diagram:
Below is the structured breakdown of tasks with estimated effort .
Goal: Align VexRiscv CPU-side debug logic to the RISC-V Debug Spec.
Tasks:
Effort: 30 person-days
Goal: Make the transport layer spec-compliant.
Tasks:
dtmcs, abits, idle cycles.Effort: 30 person-days
Goal: Add Serial Wire Debug support alongside JTAG.
Tasks:
Effort: 30 person-days
Goal: Integrate the new debug architecture into the top-level FPGA/System design.
Tasks:
Effort: 15 person-days
Goal: Fully debug the system using OpenOCD + GDB.
Tasks:
Effort: 15 person-days
Goal: Ensure the entire debug pipeline behaves predictably.
Tasks:
Effort: 15 person-days
Goal: Make integration/usage clear for future development.
Tasks:
Effort: 15 person-days
Goal: Add support for multiple cores and advanced triggers.
Tasks:
Effort: 30 person-days
Equivalent to 180 person-days .