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.
For the official debug path, JTAG follows the RISC-V Debug Specification DTM
(IEEE 1149.1 TAP + DTMCS/DMI registers), not the custom Murax DebugPlugin protocol.
+-----------------+
| GDB (Host PC) |
+-------+---------+
|
v
+-----------------+ +--------------------+
| riscv-openocd | --> | JTAG adapter |
| (riscv-target) | | (TCK,TMS,TDI,TDO) |
+-----------------+ +--------------------+
|
v
+--------------------------------------------------------------+
| Target (FPGA) |
| DebugTransportModuleJtagTap |
| → DebugBus (DebugCmd/DebugRsp) |
| → DebugModule.scala |
| → DebugHartBus → CsrPlugin (withPrivilegedDebug) |
| → VexRiscv CPU |
+--------------------------------------------------------------+
RTL: SpinalHDL/lib/.../cpu/riscv/debug/DebugTransportModuleJtag.scala,
DebugModule.scala. Generator: sbt "runMain vexriscv.demo.GenFullWithOfficialRiscvDebug".
Terminal 1:
openocd -f interface/ftdi/olimex-jtag-tiny.cfg -f target/riscv.cfg
Terminal 2:
riscv32-unknown-elf-gdb hello.elf
(gdb) target extended-remote :3333
(gdb) load
(gdb) break main
(gdb) continue
SWD stands for Serial Wire Debug. The key differences between JTAG and SWD are :
The following conceptual diagram explains the general components and data flow involved in debugging via SWD:

In the diagram:
To add SWD transport for the official RISC-V debug stack in VexRiscv:
DebugModule.scala in SpinalHDL implements the RISC-V DM and connects via DebugBus(7).enableRiscvDebug(),
withPrivilegedDebug) and complete any missing spec features — not to build DM from scratch.DebugTransportModuleSwd implements ARM ADI SW-DP (Phases 2A/2B) plus an AP→DebugBus
bridge (Phases 2C/2D). This replaces JTAG-DTM on the wire only; the DM is unchanged.DebugBus (DebugCmd / DebugRsp). It is not memory-mapped in the CPU address space.Goal: Ensure the existing SpinalHDL DebugModule + JTAG DTM path is fully wired in VexRiscv SoC generators and meets project requirements.
Note: DebugModule.scala, DebugTransportModuleJtag.scala, and CsrPlugin debug support already exist under SpinalHDL/lib/.../cpu/riscv/debug/. This task completes integration and
any missing DM features — it does not duplicate Murax DebugPlugin work.
Detailed milestones: Standard Debug Spec Milestone.
Goal: Add ARM ADI SWD as an alternate DTM to the official DebugBus (alongside JTAG-DTM).
Reference Specs: ARM Debug Interface Architecture Specification ADIv6.0
The following Architecture diagram explains the components and data flow involved in debugging VexRiscv via SWD, showing both existing (implemented) and new (to be built) components with their implementation phases:
JTAG Path (EXISTING): SWD Path (NEW):
============================== ==============================
JTAG TAP + dtmcs/dmi SWD Interface (Phase 2A)
(DebugTransportModuleJtag.scala) + SW-DP registers (Phase 2B)
- JTAG state machine - SWD protocol state machine
| |
| both produce the |
| same interface | DMI Bus Adapter
v | (Phase 2C)
DebugBus (DebugCmd/DebugRsp) v
| DebugBus (DebugCmd/DebugRsp)
| |
+──────────────► same bus ◄────────────────+
|
v
Debug Module (DM) ◄── NO CHANGES NEEDED
(DebugModule.scala) (transport-agnostic)
|
DebugHartBus
|
v
VexRiscv CPU ◄── NO CHANGES NEEDED
(CsrPlugin.scala) (transport-agnostic)
Note:
The Debug Module and VexRiscv CPU are transport-agnostic i.e. they work identically whether accessed via JTAG or SWD. The Debug Module and VexRiscv CPU need NO changes — they only see DebugBus commands and have no knowledge of whether they came from JTAG or SWD.
Phase 2A (SWD Interface) + Phase 2B (SW-DP) together are the SWD equivalent of the existing JTAG DTM. They handle the wire protocol and produce DebugBus commands — the same DebugCmd/DebugRsp interface that the JTAG DTM already produces.
For SoC-level change: Add withSwdTransport() to DebugModuleFiber.scala alongside
withJtagTap(). Instantiate one transport per SoC build (via generator flag ).
DebugModuleFiber OR-combines debugBuses and assumes only one interface is active i.e. do not wire JTAG-DTM and SWD-DTM concurrently without explicit arbitration.
Why Phase 2C (DMI Bus Adapter) is needed: In the JTAG path, the dmi shift register directly carries DMI address+data+op, so the translation to DebugBus is straightforward. In SWD, access goes through DP registers — the debugger writes SELECT to pick an AP address, then issues AP read/write operations. The adapter translates this DP/AP register access model into DebugBus read/write commands.
Spec Reference: ADIv6.0 Sec B4.1-B4.2
File: DebugTransportModuleSwd.scala — line-layer component SwdPhy
Spec Reference: ADIv6.0 Sec B2.2 (DP Reference Information)
File: same DebugTransportModuleSwd.scala (SW-DP register component behind the 2A seam)
Spec Reference: Custom bridge layer
Files: DebugTransportModuleSwd.scala (bridge) + DebugModuleFiber.scala (withSwdTransport())
Phases 2A–2C can be implemented and verified with test bench entirely inside a VexRiscv project using Verilator — no SoC/LiteX/cluster generation required.
Goal: Integrate the new debug architecture into the top-level FPGA/System design.
Stock riscv-openocd riscv-target does not support RISC-V over ARM SW-DP
(OpenOCD #378).
| Strategy | Description |
|---|---|
| Custom OpenOCD target | New target/vexriscv_swd.cfg + Tcl helpers: transport select swd, adapter driver cmsis-dap, then dap info, dap apreg, scripted reads/writes to DMI_ADDR/DMI_DATA at AP0. Reuse existing riscv examination once DM is reachable. |
Goal: Ensure the entire debug pipeline behaves per spec.
Tasks for testing & verification.
Goal: Make integration/usage clear for future development.