STM32 and GDB12 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.
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.
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.
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