Heads up! This is a guide targeted at macOS specifically! Build, installation and configuration instructions might differ for other UNIX based systems and especially for windows
Psst: you also might want to check out the blog post I have written on how to set up a (Neo)vim dev environment for C(++) with all bells and whistles: Modern C++ development in (Neo)vim
See it in action!
In my examples I will sometimes refer to the STM32 libraries and the STM32F3DISCOVERY board as that’s what I’m using to test all this.
We will need the GNU Arm Embedded Toolchain which includes the Arm version of
arm-none-eabi-gdb). You can easily install it via homebrew:
brew cask install gcc-arm-embedded`
Note on macOS Catalina: Catalina is blocking the
arm-none-eabi-gdb executable by default. Run it once in your console, then open Security & Privacy system settings, then, in the general tab allow gcc-arm-none-eabi to run.
Installing a gdb-server
gdb-server is the tool that actually connects to your microcontroller programmer via USB. For STM32 MCUs there are two options: stlink (
st-util) or OpenOCD. I will focus on OpenOCD here as this seems to be working much better for me and supports way more devices.
I highly recommend building openOCD from source as the homebrew formulas seem to be outdated. This is how I went about it:
brew install autoconf automake texinfo git clone https://git.code.sf.net/p/openocd/code openocd-code cd openocd-code ./bootstrap ./configure make sudo make install
See also OpenOCD - Open On-Chip Debugger / Code
Connect your board and run
openocd -f board/stm32f4discovery.cfg # replace with your corresponding board / interface
to see if it’s working. If it looks something like this, we’re in business!
Info : STLINK V2J27M15 (API v2) VID:PID 0483:374B Info : Target voltage: 2.906461 Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32f4x.cpu on 3333 Info : Listening on port 3333 for gdb connections
You can also specify a custom interface and/or mcu target:
openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
See here for all supported devices and targets.
Configuring your project for gdb
To connect our project to
gdb we need to configure a few things within our project directory. I am using a platformIO project as an example but if you know how to build your project in debug mode (
-g) this shouldn’t be a problem.
.gdbinit file in the project directory. This will be executed every time the
gdb client connects to the server:
file .pio/build/disco_f303vc/firmware.elf # point this to your firmware file compiled with the --debug (platformIO) or -g flag target remote localhost:3333 # points gdb to our OpenOCD server load # loads all the debug symbols
To start debugging you’d compile the project with the debug flag, then start the OpenOCD server and keep it running in the background. I created a Makefile entry for this, for convenience:
OPENOCD := /usr/local/bin/openocd # ... debug: platformio -f -c vim run --target debug && $(OPENOCD) -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
Using the (Neo)vim debugger
The debugger solution that we are going to use is actually baked into vim (from v8.1) and Neovim. It’s called
termdebug. See here for a more in-depth introduction and here for the official manual.
termdebug plugin is disabled by default, so we have to enable it in the
We are making further adjustments to point the
termdebug plugin to the arm
" Default is ARM debugging " In the future, consider using https://github.com/embear/vim-localvimrc for " project specific settings let g:termdebugger = "arm-none-eabi-gdb"
Then, if you like, add some lines for a nice split window view and some keymappings:
" C(++) debugging " See https://neovim.io/doc/user/nvim_terminal_emulator.html " For a nice split window view let g:termdebug_popup = 0 let g:termdebug_wide = 163 " Map ESC to exit terminal mode tnoremap <Esc> <C-\><C-n> nnoremap <silent> <leader>b :Break<CR> nnoremap <silent> <leader>bc :Clear<CR> nnoremap <silent> <leader>c :Continue<CR>
make debugin another console window
- Open file to debug, then
- Set and remove breakpoints, see evaluated values, manage control flow with commands below
- leader, b to set breakpoint on line under cursor
- leader, b, c to remove that breakpoint
- leader, c to continue code execution
- Shift + k for evaluation
See the manual for all the commands that are possible with the debugging terminal emulator
When in the debugger, normally the window in the bottom left would show the program output. As this is not the case for remote debugging I thought I could try to make better use of that and maybe even show the uart output of the device, if there is any. For that I had to fork the
termdebugger plugin and pour it into its own vim-plug compatible one.
To install add
and remove the
Then you are able to define the command that will be executed in the
program window of the debugger (in my case the
platformio device monitor command):
let g:termdebugger_program = "pio device monitor -b 38400" " only works with termdebugx.nvim
I hope I haven’t missed anything. If you’re having problems setting this up, feel free to reach out on one of the channels below.
Some links I stumbled upon during gathering the information in no particular order.
- GNU Toolchain | GNU Arm Embedded Toolchain Downloads – Arm Developer
- Debugging with GDB: Top
- GitHub - ntfreak/openocd: Spen’s Official OpenOCD Read-Only Mirror (no pull requests)
- GitHub - embear/vim-localvimrc: Search local vimrc files (“.lvimrc”) in the tree (root dir up to current dir) and load them.
- gdb(1): GNU Debugger - Linux man page
- neovim/termdebug.vim at master · neovim/neovim · GitHub
- Nvim documentation: nvim_terminal_emulator
- OpenOCD User’s Guide: GDB and OpenOCD
- stlink — Homebrew Formulae
- Debugging with GDB on STM32 — Dev documentation
- GitHub - stlink-org/stlink: Open source STM32 MCU programming toolset
- ST-LINK on-board | SEGGER - The Embedded Experts