VSCode ARM Cortex-M debugging

If you’re an embedded developer working with ARM Cortex-M microcontrollers you’re most likely familiar with Eclipse based IDEs. The great number of flavors is simply astonishing. There is ARMs own DS-5 Development Studio, Atollics Truestudio, STMs System Workbench, NXPs MCUXpress or plugin based options like GNU MCU Eclipse and probably many more I haven’t even thought about. Over the years most of them conformed to offer more or less the same experience having almost the same features apart from a couple of specialties maybe (e.g. trace support). They’ve all come a long way since I first used GNU MCU (formerly GNU ARM) back in 2011. Setting up projects and a proper debugging connection has never been as easy and straight forward as today. All of those IDEs support multiple debug adapters (ST-Link, J-Link, use of OpenOCD, etc.), special-function-register and memory views, redirecting debugger output of some form (e.g. through semihosting or SWO) and so on and so forth. So why even bother with VSCode?

Well… the only thing I really dislike about Eclipse is actually writing code in it. I’ve spent the majority of last year with the indexer disabled because it kept crashing on me. Without an indexer browsing your code becomes a nightmare. You’re practically unable to find anything. You can’t jump to declarations or definitions, can’t view constants or expend macros. You constantly feel lost within your own code base. Also the current state of C++ syntax analysis is somewhere between buggy and nonexistent. Anything from init-statements in if clauses to delimiters in numbers seemed to confuse the analyzer…

With VSCode you don’t have to put up with any of this crap. Pretty much everything works out of the box and what little does not is simply configured within a JSON file. Actually JSON is everywhere in VSCode. Workspace configuration? JSON. Project configuration? JSON. Debug configuration? JSON. Compiling? JSON. Any other task? JSON.

Speaking of debug configurations… Apparently I wasn’t the only one convinced by VSCode simplicity and wanting to work exclusively in it. Thats probably why Marcel Ball created an extension called Cortex-Debug. Cortex-Debug is pretty much what GNU MCU is for Eclipse. It allows us to debug Cortex-M controllers from within VSCode. The extension can be installed from the marketplace.

Installing Cortex-Debug from the marketplace

Cortex-Debug currently offers support for J-Link, OpenOCD, ST-Util, PyOCD and Black Magic Probe. Personally I’m using SEGGERs J-Link so that’s what I’ll be showing here as well but if you’re interested in other launch configurations check out Marcel Balls page.

Getting Cortex-Debug to work is very simple. At first I had to add J-Links GDB server path to my workspace settings.

"cortex-debug.JLinkGDBServerPath": "/opt/SEGGER/JLink/JLinkGDBServerCLExe"

After that I added a Cortex-Debug J-Link configuration which I extended a bit to satisfy my needs. Apart from the obvious executable option its mandatory to specify the interface type (JTAG or SWD) and the device. In my case I wanted to use SWD and a STM32L431VC processor. Cortex-Debug already comes with a lot of supported devices and their related SVD description but in case you find your processor unsupported you can still manually add a path to some SVD file.

Since my application does not start at the beginning of flash memory I’ve also added a couple of “Pre-Start Commands” which load a bootloader prior to the main application. Regardless of whether you need further commands or not its nice to know that Cortex-Debug supports such things. Another convenience parameter is “runToMain” which can automatically add a breakpoint at main and stops the debugger there once it finished loading the application.

  "version": "0.2.0",
  "configurations": [
      "name": "Release",
      "cwd": "${workspaceRoot}",
      "executable": "./build/release/release.elf",
      "request": "launch",
      "type": "cortex-debug",
      "servertype": "jlink",
      "interface": "swd",
      "device": "STM32L431VC",
      "runToMain": true,
      "preRestartCommands": [
        "file ./bootloader.elf",
        "add-symbol-file ./build/release/release.elf 0x08002030",
        "enable breakpoint",
        "monitor reset"

Now you can simply go ahead and start the debug session by launching the configuration. Apart from the usual variables and watch window Cortex-Debug adds a register view and if you’ve specified a device and/or a SVD file even a peripheral view.

Variable, watch, peripheral and register view