1 |
69 |
zero_gravi |
<<<
|
2 |
|
|
:sectnums:
|
3 |
|
|
== Debugging using the On-Chip Debugger
|
4 |
|
|
|
5 |
|
|
The NEORV32 on-chip debugger allows _online_ in-system debugging via an external JTAG access port from a
|
6 |
|
|
host machine. The general flow is independent of the host machine's operating system. However, this tutorial uses
|
7 |
72 |
zero_gravi |
Windows and Linux (Ubuntu on Windows / WSL) in parallel running the upstream version of OpenOCD and the
|
8 |
|
|
RISC-V _GNU debugger_ `gdb`.
|
9 |
69 |
zero_gravi |
|
10 |
72 |
zero_gravi |
[NOTE]
|
11 |
69 |
zero_gravi |
See datasheet section https://stnolting.github.io/neorv32/#_on_chip_debugger_ocd[On Chip Debugger (OCD)]
|
12 |
72 |
zero_gravi |
for more information regarding the actual hardware.
|
13 |
69 |
zero_gravi |
|
14 |
|
|
[NOTE]
|
15 |
|
|
The on-chip debugger is only implemented if the _ON_CHIP_DEBUGGER_EN_ generic is set _true_. Furthermore, it requires
|
16 |
72 |
zero_gravi |
the `Zicsr` and `Zifencei` CPU extension to be implemented (top generics _CPU_EXTENSION_RISCV_Zicsr_ = _true_
|
17 |
|
|
and _CPU_EXTENSION_RISCV_Zifencei_ = _true_).
|
18 |
69 |
zero_gravi |
|
19 |
|
|
|
20 |
|
|
:sectnums:
|
21 |
|
|
=== Hardware Requirements
|
22 |
|
|
|
23 |
72 |
zero_gravi |
Make sure the on-chip debugger of your NEORV32 setup is implemented (_ON_CHIP_DEBUGGER_EN_ generic = true). This
|
24 |
|
|
tutorial uses `gdb` to **directly upload an executable** to the processor. If you are using the default
|
25 |
|
|
processor setup _with_ internal instruction memory (IMEM) make sure it is implemented as RAM
|
26 |
|
|
(_INT_BOOTLOADER_EN_ generic = true).
|
27 |
|
|
|
28 |
69 |
zero_gravi |
Connect a JTAG adapter to the NEORV32 `jtag_*` interface signals. If you do not have a full-scale JTAG adapter, you can
|
29 |
|
|
also use a FTDI-based adapter like the "FT2232H-56Q Mini Module", which is a simple and inexpensive FTDI breakout board.
|
30 |
|
|
|
31 |
|
|
.JTAG pin mapping
|
32 |
|
|
[cols="^3,^2,^2"]
|
33 |
|
|
[options="header",grid="rows"]
|
34 |
|
|
|=======================
|
35 |
|
|
| NEORV32 top signal | JTAG signal | FTDI port
|
36 |
|
|
| `jtag_tck_i` | TCK | D0
|
37 |
|
|
| `jtag_tdi_i` | TDI | D1
|
38 |
|
|
| `jtag_tdo_o` | TDO | D2
|
39 |
|
|
| `jtag_tms_i` | TMS | D3
|
40 |
|
|
| `jtag_trst_i` | TRST | D4
|
41 |
|
|
|=======================
|
42 |
|
|
|
43 |
|
|
[TIP]
|
44 |
72 |
zero_gravi |
The low-active JTAG tap reset `jtag_trst_i` signals is _optional_ as a reset can also be triggered via the TAP controller
|
45 |
|
|
issuing special commands. If `jtag_trst_i` is not connected make sure to pull the signal _high_.
|
46 |
69 |
zero_gravi |
|
47 |
|
|
|
48 |
|
|
:sectnums:
|
49 |
|
|
=== OpenOCD
|
50 |
|
|
|
51 |
72 |
zero_gravi |
The NEORV32 on-chip debugger can be accessed using the upstream version of OpenOCD. A pre-configured OpenOCD configuration
|
52 |
|
|
file is provided (`sw/openocd/openocd_neorv32.cfg`) that allows an easy access to the NEORV32 CPU.
|
53 |
69 |
zero_gravi |
|
54 |
|
|
[NOTE]
|
55 |
72 |
zero_gravi |
You might need to adapt `ftdi vid_pid`, `ftdi channel` and `ftdi layout_init` in `sw/openocd/openocd_neorv32.cfg`
|
56 |
69 |
zero_gravi |
according to your interface chip and your operating system.
|
57 |
|
|
|
58 |
|
|
[TIP]
|
59 |
|
|
If you want to modify the JTAG clock speed (via `adapter speed` in `sw/openocd/openocd_neorv32.cfg`) make sure to meet
|
60 |
|
|
the clock requirements noted in https://stnolting.github.io/neorv32/#_debug_module_dm[Documentation: Debug Transport Module (DTM)].
|
61 |
|
|
|
62 |
|
|
To access the processor using OpenOCD, open a terminal and start OpenOCD with the pre-configured configuration file.
|
63 |
|
|
|
64 |
72 |
zero_gravi |
.Connecting via OpenOCD (on Windows) using the default `openocd_neorv32.cfg` script
|
65 |
69 |
zero_gravi |
[source, bash]
|
66 |
|
|
--------------------------
|
67 |
|
|
N:\Projects\neorv32\sw\openocd>openocd -f openocd_neorv32.cfg
|
68 |
72 |
zero_gravi |
Open On-Chip Debugger 0.11.0 (2021-11-18) [https://github.com/sysprogs/openocd]
|
69 |
69 |
zero_gravi |
Licensed under GNU GPL v2
|
70 |
72 |
zero_gravi |
libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3
|
71 |
|
|
For bug reports, read
|
72 |
|
|
http://openocd.org/doc/doxygen/bugs.html
|
73 |
69 |
zero_gravi |
Info : clock speed 1000 kHz
|
74 |
|
|
Info : JTAG tap: neorv32.cpu tap/device found: 0x0cafe001 (mfg: 0x000 (), part: 0xcafe, ver: 0x0)
|
75 |
|
|
Info : datacount=1 progbufsize=2
|
76 |
|
|
Info : Disabling abstract command reads from CSRs.
|
77 |
|
|
Info : Examined RISC-V core; found 1 harts
|
78 |
72 |
zero_gravi |
Info : hart 0: XLEN=32, misa=0x40901107
|
79 |
69 |
zero_gravi |
Info : starting gdb server for neorv32.cpu.0 on 3333
|
80 |
|
|
Info : Listening on port 3333 for gdb connections
|
81 |
72 |
zero_gravi |
Target HALTED.
|
82 |
|
|
Ready for remote connections.
|
83 |
|
|
Info : Listening on port 6666 for tcl connections
|
84 |
|
|
Info : Listening on port 4444 for telnet connections
|
85 |
69 |
zero_gravi |
--------------------------
|
86 |
|
|
|
87 |
|
|
OpenOCD has successfully connected to the NEORV32 on-chip debugger and has examined the CPU (showing the content of
|
88 |
72 |
zero_gravi |
the `misa` CSRs). The processor is halted and OpenOCD waits fot `gdb` to connect via port 3333.
|
89 |
69 |
zero_gravi |
|
90 |
|
|
|
91 |
|
|
:sectnums:
|
92 |
|
|
=== Debugging with GDB
|
93 |
|
|
|
94 |
|
|
This guide uses the simple "blink example" from `sw/example/blink_led` as simplified test application to
|
95 |
|
|
show the basics of in-system debugging.
|
96 |
|
|
|
97 |
|
|
At first, the application needs to be compiled. We will use the minimal machine architecture configuration
|
98 |
|
|
(`rv32i`) here to be independent of the actual processor/CPU configuration.
|
99 |
|
|
Navigate to `sw/example/blink_led` and compile the application:
|
100 |
|
|
|
101 |
|
|
.Compile the test application
|
102 |
|
|
[source, bash]
|
103 |
|
|
--------------------------
|
104 |
|
|
.../neorv32/sw/example/blink_led$ make MARCH=rv32i USER_FLAGS+=-g clean_all all
|
105 |
|
|
--------------------------
|
106 |
|
|
|
107 |
|
|
.Adding debug symbols to the executable
|
108 |
|
|
[NOTE]
|
109 |
|
|
`USER_FLAGS+=-g` passes the `-g` flag to the compiler so it adds debug information/symbols
|
110 |
72 |
zero_gravi |
to the generated ELF file. This is optional but will provide more sophisticated debugging information
|
111 |
69 |
zero_gravi |
(like source file line numbers).
|
112 |
|
|
|
113 |
|
|
This will generate an ELF file `main.elf` that contains all the symbols required for debugging.
|
114 |
|
|
Furthermore, an assembly listing file `main.asm` is generated that we will use to define breakpoints.
|
115 |
|
|
|
116 |
|
|
Open another terminal in `sw/example/blink_led` and start `gdb`.
|
117 |
|
|
The GNU debugger is part of the toolchain (see <<_software_toolchain_setup>>).
|
118 |
|
|
|
119 |
|
|
.Starting GDB (on Linux (Ubuntu on Windows))
|
120 |
|
|
[source, bash]
|
121 |
|
|
--------------------------
|
122 |
|
|
.../neorv32/sw/example/blink_led$ riscv32-unknown-elf-gdb
|
123 |
|
|
GNU gdb (GDB) 10.1
|
124 |
|
|
Copyright (C) 2020 Free Software Foundation, Inc.
|
125 |
|
|
License GPLv3+: GNU GPL version 3 or later
|
126 |
|
|
This is free software: you are free to change and redistribute it.
|
127 |
|
|
There is NO WARRANTY, to the extent permitted by law.
|
128 |
|
|
Type "show copying" and "show warranty" for details.
|
129 |
|
|
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv32-unknown-elf".
|
130 |
|
|
Type "show configuration" for configuration details.
|
131 |
|
|
For bug reporting instructions, please see:
|
132 |
|
|
.
|
133 |
|
|
Find the GDB manual and other documentation resources online at:
|
134 |
|
|
.
|
135 |
|
|
|
136 |
|
|
For help, type "help".
|
137 |
|
|
Type "apropos word" to search for commands related to "word".
|
138 |
|
|
(gdb)
|
139 |
|
|
--------------------------
|
140 |
|
|
|
141 |
|
|
Now connect to OpenOCD using the default port 3333 on your machine.
|
142 |
|
|
We will use the previously generated ELF file `main.elf` from the `blink_led` example.
|
143 |
|
|
Finally, upload the program to the processor and start debugging.
|
144 |
|
|
|
145 |
|
|
[NOTE]
|
146 |
|
|
The executable that is uploaded to the processor is **not** the default NEORV32 executable (`neorv32_exe.bin`) that
|
147 |
|
|
is used for uploading via the bootloader. Instead, all the required sections (like `.text`) are extracted from `mail.elf`
|
148 |
|
|
by GDB and uploaded via the debugger's indirect memory access.
|
149 |
|
|
|
150 |
|
|
.Running GDB
|
151 |
|
|
[source, bash]
|
152 |
|
|
--------------------------
|
153 |
|
|
(gdb) target extended-remote localhost:3333 <1>
|
154 |
|
|
Remote debugging using localhost:3333
|
155 |
|
|
warning: No executable has been specified and target does not support
|
156 |
|
|
determining executable automatically. Try using the "file" command.
|
157 |
|
|
0xffff0c94 in ?? () <2>
|
158 |
|
|
(gdb) file main.elf <3>
|
159 |
|
|
A program is being debugged already.
|
160 |
|
|
Are you sure you want to change the file? (y or n) y
|
161 |
|
|
Reading symbols from main.elf...
|
162 |
|
|
(gdb) load <4>
|
163 |
|
|
Loading section .text, size 0xd0c lma 0x0
|
164 |
|
|
Loading section .rodata, size 0x39c lma 0xd0c
|
165 |
|
|
Start address 0x00000000, load size 4264
|
166 |
|
|
Transfer rate: 43 KB/sec, 2132 bytes/write.
|
167 |
|
|
(gdb)
|
168 |
|
|
--------------------------
|
169 |
|
|
<1> Connect to OpenOCD
|
170 |
|
|
<2> The CPU was still executing code from the bootloader ROM - but that does not matter here
|
171 |
|
|
<3> Select `mail.elf` from the `blink_led` example
|
172 |
|
|
<4> Upload the executable
|
173 |
|
|
|
174 |
|
|
After the upload, GDB will make the processor jump to the beginning of the uploaded executable
|
175 |
|
|
(by default, this is the beginning of the instruction memory at `0x00000000`) skipping the bootloader
|
176 |
|
|
and halting the CPU right before executing the `blink_led` application.
|
177 |
|
|
|
178 |
74 |
zero_gravi |
[IMPORTANT]
|
179 |
|
|
After gdb has connected to the CPU, it is recommended to disable the CPU's global interrupt flag
|
180 |
|
|
(`mstatus.mie`, = bit #3) to prevent unintended calls of potentially outdated trap handlers. The global
|
181 |
|
|
interrupt flag can be cleared using the following gdb command:
|
182 |
|
|
`set $mstatus = ($mstatus & ~(1<<3))`. Interrupts can be enabled globally again by the following command:
|
183 |
|
|
`set $mstatus = ($mstatus | (1<<3))`.
|
184 |
69 |
zero_gravi |
|
185 |
74 |
zero_gravi |
|
186 |
69 |
zero_gravi |
:sectnums:
|
187 |
72 |
zero_gravi |
==== Software Breakpoints
|
188 |
69 |
zero_gravi |
|
189 |
|
|
The following steps are just a small showcase that illustrate a simple debugging scheme.
|
190 |
|
|
|
191 |
|
|
While compiling `blink_led`, an assembly listing file `main.asm` was generated.
|
192 |
|
|
Open this file with a text editor to check out what the CPU is going to do when resumed.
|
193 |
|
|
|
194 |
|
|
The `blink_led` example implements a simple counter on the 8 lowest GPIO output ports. The program uses
|
195 |
|
|
"busy wait" to have a visible delay between increments. This waiting is done by calling the `neorv32_cpu_delay_ms`
|
196 |
|
|
function. We will add a _breakpoint_ right at the end of this wait function so we can step through the iterations
|
197 |
|
|
of the counter.
|
198 |
|
|
|
199 |
|
|
.Cut-out from `main.asm` generated from the `blink_led` example
|
200 |
|
|
[source, assembly]
|
201 |
|
|
--------------------------
|
202 |
|
|
00000688 <__neorv32_cpu_delay_ms_end>:
|
203 |
|
|
688: 01c12083 lw ra,28(sp)
|
204 |
|
|
68c: 02010113 addi sp,sp,32
|
205 |
|
|
690: 00008067 ret
|
206 |
|
|
--------------------------
|
207 |
|
|
|
208 |
|
|
The very last instruction of the `neorv32_cpu_delay_ms` function is `ret` (= return)
|
209 |
|
|
at hexadecimal `690` in this example. Add this address as _breakpoint_ to GDB.
|
210 |
|
|
|
211 |
|
|
[NOTE]
|
212 |
|
|
The address might be different if you use a different version of the software framework or
|
213 |
|
|
if different ISA options are configured.
|
214 |
|
|
|
215 |
72 |
zero_gravi |
.Adding a GDB software breakpoint
|
216 |
69 |
zero_gravi |
[source, bash]
|
217 |
|
|
--------------------------
|
218 |
72 |
zero_gravi |
(gdb) b * 0x690 <1>
|
219 |
69 |
zero_gravi |
Breakpoint 1 at 0x690
|
220 |
|
|
--------------------------
|
221 |
72 |
zero_gravi |
<1> `b` is an alias for `break`, which adds a _software_ breakpoint.
|
222 |
69 |
zero_gravi |
|
223 |
72 |
zero_gravi |
.How do _software_ breakpoints work?
|
224 |
69 |
zero_gravi |
[TIP]
|
225 |
72 |
zero_gravi |
Software breakpoints are used for debugging programs that are accessed from read/write memory (RAM) like IMEM. The debugger
|
226 |
|
|
temporarily replaces the instruction word of the instruction, where the breakpoint shall be inserted, by a `ebreak` / `c.ebreak`
|
227 |
|
|
instruction. Whenever execution reaches this instruction, debug mode is entered and the debugger restores the original
|
228 |
|
|
instruction at this address to maintain original program behavior. +
|
229 |
|
|
+
|
230 |
|
|
When debugging programs executed from ROM _hardware-assisted_ breakpoints using the core's trigger module have to be used.
|
231 |
|
|
See section <<_hardware_breakpoints>> for more information.
|
232 |
69 |
zero_gravi |
|
233 |
|
|
Now execute `c` (= continue). The CPU will resume operation until it hits the break-point.
|
234 |
72 |
zero_gravi |
By this we can move from one counter increment to another.
|
235 |
69 |
zero_gravi |
|
236 |
|
|
.Iterating from breakpoint to breakpoint
|
237 |
|
|
[source, bash]
|
238 |
|
|
--------------------------
|
239 |
|
|
Breakpoint 1 at 0x690
|
240 |
|
|
(gdb) c
|
241 |
|
|
Continuing.
|
242 |
|
|
|
243 |
|
|
Breakpoint 1, 0x00000690 in neorv32_cpu_delay_ms ()
|
244 |
|
|
(gdb) c
|
245 |
|
|
Continuing.
|
246 |
|
|
|
247 |
|
|
Breakpoint 1, 0x00000690 in neorv32_cpu_delay_ms ()
|
248 |
|
|
(gdb) c
|
249 |
|
|
Continuing.
|
250 |
|
|
--------------------------
|
251 |
72 |
zero_gravi |
|
252 |
|
|
.BREAK instructions in your program code
|
253 |
|
|
[TIP]
|
254 |
|
|
If your original application code uses the BREAK instruction (for example for some OS calls/signaling) this
|
255 |
|
|
instruction will cause an enter to debug mode when executed. These situation cannot be continued using gdb's
|
256 |
|
|
`c` command and have to be "stepped-over" using the single-step command `s`.
|
257 |
|
|
|
258 |
|
|
|
259 |
|
|
:sectnums:
|
260 |
|
|
==== Hardware Breakpoints
|
261 |
|
|
|
262 |
|
|
Hardware-assisted breakpoints using the CPU's trigger module are required when debugging code that is executed from
|
263 |
|
|
read-only memory (ROM) as GDB cannot temporarily replace instructions by BREAK instructions.
|
264 |
|
|
|
265 |
|
|
From a user point of view hardware breakpoints behave like software breakpoints. GDB provides a command to setup
|
266 |
|
|
a hardware-assisted breakpoint:
|
267 |
|
|
|
268 |
|
|
.Adding a GDB hardware breakpoint
|
269 |
|
|
[source, bash]
|
270 |
|
|
--------------------------
|
271 |
|
|
(gdb) hb * 0x690 <1>
|
272 |
|
|
Breakpoint 1 at 0x690
|
273 |
|
|
--------------------------
|
274 |
|
|
<1> `hb` is an alias for `hbreak`, which adds a _hardware_ breakpoint.
|
275 |
|
|
|
276 |
|
|
[NOTE]
|
277 |
|
|
The CPU's trigger module only provides a single _instruction address match_ type trigger. Hence, only
|
278 |
|
|
a single `hb` hardware-assisted breakpoint can be used.
|