-
Notifications
You must be signed in to change notification settings - Fork 577
JTAG GDB Debugging with VexRiscv SMP NaxRiscv VexiiRiscv CPUs
This guide explores how to leverage JTAG debugging with LiteX for RISC-V CPUs such as VexRiscv-SMP, NaxRiscv or future VexiiRiscv CPUs which are commonly used within LiteX SoCs/projects. We'll look at setting up JTAG for both simulations and physical hardware implementations, using exposed/external JTAG IO pins but also internal features like BSCANE2 on Xilinx FPGAs.
- GDB with RISC-V
- Debugging in LiteX Simulation
- Debugging on Physical Hardware with an External USB/JTAG Cable
- Debugging on Physical Hardware Directly Through BSCANE2 JTAG Primitive
GNU Debugger (GDB) is a powerful tool for debugging programs written in C, C++, and other languages. For RISC-V CPUs, GDB allows developers to control the execution of their program, inspect memory and register states, and perform other critical debugging tasks. GDB interacts with RISC-V through a debugging interface which can be supported via JTAG, enabling step-by-step execution and real-time inspection of the processor state.
To use GDB with RISC-V in a LiteX environment, the processor must be compiled with debugging symbols and the hardware or simulation environment must support debugging capabilities. This setup often involves configuring OpenOCD as a bridge between GDB and the target RISC-V CPU.
LiteX Sim provides a versatile environment for testing and debugging system designs before deploying them to actual hardware. To initiate a simulation with JTAG debugging:
Required Files: Before starting, ensure you have the necessary OpenOCD scripts available by downloading and extracting them from openocd_scripts.zip.
- Initialize the Simulation Environment and generate software files
The first step involves initializing the LiteX simulation environment with the specific CPU configuration and debug options. This setup does not yet compile the gateware, as it's primarily focused on preparing the simulation parameters and environment.
litex_sim --cpu-type=vexriscv_smp --with-sdram --with-rvc --with-privileged-debug --hardware-breakpoints 4 --jtag-tap --with-jtagremote --no-compile-gateware
Where:
-
--cpu-type=vexriscv_smp
: Specifies the CPU type to simulate. -
--with-sdram
: Includes SDRAM in the simulation, used here to store our firmware. -
--with-rvc
: Enables RISC-V compressed instructions. -
--with-privileged-debug
: Includes support for privileged debugging features. -
--hardware-breakpoints 4
: Specifies the number of hardware breakpoints available for use. -
--jtag-tap
: Includes a JTAG Test Access Port in the simulation. -
--with-jtagremote
: Allows remove JTAG connection in simulation. -
--no-compile-gateware
: Skips the compilation of FPGA gateware, focusing on software preparation.
- Compile the LiteX Bare Metal Demo
After setting up the initial simulation environment, the next step involves compiling a simple bare metal demo program. This demo serves as the primary code base for demonstrating the debugging process.
litex_bare_metal_demo --build-path=build/sim/
Where:
-
--build-path=build/sim/
: Specifies the directory where the simulation environment has been set up.
- Run the Simulation with the LiteX Bare Metal Demo
Finally, the compiled demo program is used to run the simulation, allowing for real-time debugging and examination of the CPU's behavior under simulated conditions.
litex_sim --cpu-type=vexriscv_smp --with-sdram --sdram-init demo.bin --with-rvc --with-privileged-debug --hardware-breakpoints 4 --jtag-tap --with-jtagremote
Where:
-
--sdram-init demo.bin
: Initializes the SDRAM with demo.bin, the binary file generated from the bare metal demo, effectively loading the program into the simulated system's memory.
To initiate OpenOCD:
openocd -f openocd/jtag_remote.cfg -f openocd/riscv_jtag_tunneled.tcl
GDB To debug using GDB:
riscv64-unknown-elf-gdb demo/demo.elf
(gdb) target extended-remote localhost:3333
(gdb) load
(gdb) continue
Note: You may encounter timeouts and slow command executions; adjusting GDB's set remotetimeout may be necessary.
For debugging on actual hardware, such as an FPGA board, you can expose JTAG pins and connect them to an external USB-to-JTAG adapter. This method involves specifying JTAG pins on the FPGA and routing them to accessible connectors.
The following example targets a Digilent Arty board but can easily be adapted to other hardware:
To set up JTAG debugging, you first need to update your SoC configuration to expose JTAG pins through one of the PMOD connectors on the Arty board. This is accomplished by adding a new PMOD extension to your SoC configuration and wiring it up to the JTAG interface of the CPU.
# Add the JTAG PMOD extension to the FPGA platform
soc.platform.add_extension([
("pmod_jtag", 0,
Subsignal("tms", Pins("pmodd:0")),
Subsignal("tdi", Pins("pmodd:1")),
Subsignal("tdo", Pins("pmodd:3")),
Subsignal("tck", Pins("pmodd:2")),
IOStandard("LVCMOS33")
)
])
# Request and connect the JTAG interface
jtag = soc.platform.request("pmod_jtag")
soc.comb += [
soc.cpu.jtag_clk.eq(jtag.tck),
soc.cpu.jtag_tms.eq(jtag.tms),
soc.cpu.jtag_tdi.eq(jtag.tdi),
jtag.tdo.eq(soc.cpu.jtag_tdo)
]
# Timing constraints for JTAG clock
soc.platform.add_platform_command("create_clock -period 10.000 -name jtag_tck [get_nets jtag_tck_IBUF]")
soc.platform.add_platform_command("create_clock -name jtag_tck -period 20.0 [get_nets jtag_tck]")
soc.platform.add_false_path_constraints(soc.crg.cd_sys.clk, jtag.tck)
Compile and load the gateware/firmware onto the FPGA with the JTAG debugging features enabled. This step ensures that the FPGA is correctly programmed and ready for JTAG interaction.
./digilent_arty.py --cpu-type=vexriscv_smp --with-rvc --with-privileged-debug --hardware-breakpoints 4 --jtag-tap --build --load
Configure OpenOCD to interface with the FPGA using the appropriate configuration files for the Digilent JTAG-HS2.
Interface configuration (digilent-hs2.cfg
):
# Digilent JTAG-HS2 configuration for OpenOCD
adapter driver ftdi
ftdi vid_pid 0x0403 0x6014
ftdi channel 0
ftdi layout_init 0x00e8 0x60eb
reset_config none
RISC-V JTAG configuration (riscv_jtag_tunneled.tcl
):
# RISC-V JTAG configuration for OpenOCD
set _CHIPNAME riscv
set _TARGETNAME $_CHIPNAME.cpu
set cpu_count 1
if [info exists env(RISCV_COUNT)] {
set cpu_count $::env(RISCV_COUNT)
}
adapter speed 500
jtag newtap $_CHIPNAME cpu -irlen 6 -expected-id 0x10003FFF
for {set i 0} {$i < $cpu_count} {incr i} {
target create $_TARGETNAME.$i riscv -coreid $i -chain-position $_TARGETNAME
riscv use_bscan_tunnel 6 1
}
for {set i 0} {$i < $cpu_count} {incr i} {
targets $_TARGETNAME.$i
init
halt
}
echo "Ready for Remote Connections"
Run OpenOCD with the specified configurations:
openocd -f digilent-hs2.cfg -f riscv_jtag_tunneled.tcl
Finally, connect GDB to the OpenOCD server to start debugging the FPGA. Use the following commands to initiate a GDB session targeting the remote JTAG interface:
gdb-multiarch -q demo/demo.elf -ex "target extended-remote localhost:3333"
Want to go even further an external USB/JTAG cable, we also got you covered :)
Using BSCANE2, a built-in JTAG capability of some Xilinx FPGAs, eliminates the need for external wiring. BSCANE2 integrates the JTAG functionality internally, providing a direct interface for debugging tools without consuming physical I/O pins:
from litex.soc.cores.jtag import XilinxJTAG
soc.jtag = jtag = XilinxJTAG(XilinxJTAG.get_primitive(soc.platform.device), chain=4)
soc.comb += [
soc.cpu.jtag_reset.eq(jtag.reset),
soc.cpu.jtag_capture.eq(jtag.capture),
soc.cpu.jtag_shift.eq(jtag.shift),
soc.cpu.jtag_update.eq(jtag.update),
soc.cpu.jtag_clk.eq(jtag),
]
Compile and load the gateware/firmware onto the FPGA with the JTAG debugging features enabled. This step ensures that the FPGA is correctly programmed and ready for JTAG interaction.
./digilent_arty.py --cpu-type=vexriscv_smp --with-rvc --with-privileged-debug --hardware-breakpoints 4 --build --load
Configure OpenOCD to interface with the FPGA using the appropriate configuration files for the Digilent onboard interface.
Interface configuration (digilent_arty.cfg
):
# SPDX-FileCopyrightText: 2023 "Everybody"
#
# SPDX-License-Identifier: MIT
adapter driver ftdi
ftdi_vid_pid 0x0403 0x6010
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
ftdi_tdo_sample_edge falling
reset_config none
adapter speed 5000
source [find cpld/xilinx-xc7.cfg]
source [find cpld/jtagspi.cfg]
set TAP_NAME xc7.tap
RISC-V JTAG configuration (riscv_jtag_tunneled.tcl
):
# SPDX-FileCopyrightText: 2023 "Everybody"
#
# SPDX-License-Identifier: MIT
set _CHIPNAME riscv
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $TAP_NAME
riscv use_bscan_tunnel 6 1
#riscv set_bscan_tunnel_ir 0x23 #In riscv-openocd upstream
init
halt
echo "Ready for Remote Connections"
Run OpenOCD with the specified configurations:
openocd -f digilent_arty.cfg -f riscv_jtag_tunneled.tcl
Finally, connect GDB to the OpenOCD server to start debugging the FPGA. Use the following commands to initiate a GDB session targeting the remote JTAG interface:
gdb-multiarch -q demo/demo.elf -ex "target extended-remote localhost:3333"
Have a question or want to get in touch? Our IRC channel is #litex at irc.libera.chat.
- Welcome to LiteX
- LiteX's internals
- How to
- Create a minimal SoC-TODO
- Add a new Board-TODO
- Add a new Core-WIP
- Add a new CPU-WIP
- Reuse-a-(System)Verilog,-VHDL,-Amaranth,-Spinal-HDL,-Chisel-core
- Use LiteX on the Acorn CLE 215+
- Load application code the CPU(s)
- Use Host Bridges to control/debug a SoC
- Use LiteScope to debug a SoC
- JTAG/GDB Debugging with VexRiscv CPU
- JTAG/GDB Debugging with VexRiscv-SMP, NaxRiscv and VexiiRiscv CPUs
- Document a SoC
- How to (Advanced)