STM32 and GDB

12 Jan 2019

Work has started on the next big project, using one of the ST Nucleo dev boards. I got one a while ago but never started playing with it, and it turns out they’re pretty cool.

For starters, as well as the chip you’re targeting, they also have a STM32F103C8 on the board, which they make into a STLink v2.1 interface, so you don’t need a programmer. They also let you switch some jumpers and use it to program other boards.

USART2 on the main chip is wired to the programmer and set-up as a virtual COM port, so you get both programming and basic comes over a single USB cable.

Better debugging

The main point of this post is to share the setup for getting gdb working with the board so you can see what’s going on. I looked at this in the past but all you seem to find is guides of how to use it with Eclipse, which I really don’t like as an IDE. My setup is VS Code and with my own build tool that uses gcc for the compiling (I’m not a fan of make files either).

Enabling debug info

The first thing you need to do is turn on debug symbols in your builds, or it’s not going to be very useful. In gcc this is the -g option. You might want to turn off optimizing, otherwise variables you’re just wiring too could be removed (e.g. return values your not actually using), -O0 does this. Once you’re compiled with that you can flash the code onto your board.

OpenOCD

GDB doesn’t know how to talk to the STM MCUs directly, so you need something to interface the two. OpenOCD seems to work fine for this and isn’t that hard to get going.

First grab a build, I used the ones from the Eclipse GNU MCU tools project: https://github.com/gnu-mcu-eclipse/openocd/releases

Once you’ve got it extracted and on path, opening a session requires:

openocd.exe -f board\st_nucleo_f4.cfg

It should now scroll some messages saying that it’s connecting and then it’ll just sit there waiting for a client.

GDB

To start debugging you give gdb the raw elf file you’ve compile (not the hex version) and tell it to connect to a remote debug target.

arm-eabi-gdb.exe --eval-command="target remote localhost:3333" "somewhere\build\target.elf"

You should see it connect and show the current state. “(gdb)” is the prompt for gdb. If you run the following command the board will be hard-reset and wait at the start of flash for instructions.

(gdb) mon reset halt
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080019c4 msp: 0x20020000

You can now set break points and do stuff like…

(gdb) b main.c:110
Breakpoint 1 at 0x8001c74: file src\main.c, line 110.
(gdb) c
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at src\main.c:110
110       SetupDebugUART();
(gdb) s
halted: PC: 0x08001afc
SetupDebugUART () at src\debug_uart.c:68
68          debugUart.Instance = USART2;
(gdb) s
halted: PC: 0x08001b00
halted: PC: 0x08001b02
halted: PC: 0x08001b04
69          debugUart.Init.BaudRate = 115200; //921600;
(gdb) s
halted: PC: 0x08001b08
halted: PC: 0x08001b0a
70          debugUart.Init.WordLength = UART_WORDLENGTH_8B;
(gdb) p debugUart.Init.BaudRate
$2 = 115200

So here I set a breakpoint on line 110 of main.c and started it running. When it stopped I stepped through three instructions then checked a value that had been set.

Here’s a few useful gdb commands:

mon reg :- Shows the state of all the registers
mon reset halt :- Restart the board and wait at the beginning
c :- Run
s :- Single step
n :- next step, but don't go into functions (e.g. step over)
f : Show the current frame (e.g. line)
up : See the calling function
list :- Show the source code around the current frame
p VARIABLE_NAME :- Show the value of the variable
i b :- List break points
break LINENUM :- Make a break point in the current file at the given line number
break FILENAME:LINENUM :- Make a breakpoint of a file
break FILENAME:FUNCTION

This post has been taged as STM32