Visual Studio Code for C/C++ with ARM Cortex-M: Part 4 – Debug

The previous parts were about installation, project setup and building. This one is about debugging an ARM Cortex-M Microcontroller with Visual Studio Code:

Cortex-M4 (NXP K22FN512) Debugging with Visual Studio Code


In this article I show how to debug an ARM Cortex (M4F, NXP K22FN512) microcontroller with the Microsoft Visual Studio Code. For this I need the tools and extensions installed in Part 1 of this tutorial series. Debugging is through a debug probe (J-Link), either external (standalone debug probe) or on-board (available with many development boards e.g. from NXP or STM). I’m using SWD here because the FRDM-K22F only has SWD available on the debug header, but JTAG could be used too.

Debug Probe

In this article I’m using a SEGGER J-Link EDU Mini debug probe. Technically it should be possible to use other probes too. The J-Link is widely available and the EDU only costs $20 or €20, is very small and universally works for many, many devices out of the box.


Additionally many silicon vendor boards (LPC4322 or FRDM OpenSDA) and debug probes can load the J-Link firmware, including the NXP LPC-Link2 or STLink. The J-Link can be used with GDB, LLDB, OpenOCD or CMSI-DAP.


Launch JSON File

Inside the .vscode folder, create a new file named launch.json, with the following content:

    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit:
    "version": "0.2.0",
    "configurations": [
            "cwd": "${workspaceRoot}",
            "executable": "./build/FRDM-K22F_Simple.elf",
            "name": "Debug Microcontroller",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "jlink",
            "serverpath": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe",
            "armToolchainPath": "C:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2020-q4-major/bin",
            "device": "MK22FN512xxx12",
            "interface": "swd",
            "serialNumber": "", // add J-Link serial number if having multiple attached the same time.
            "runToMain": true,
            "svdFile": "${workspaceRoot}/device/MK22F51212.svd",


You can find all files on GitHub too.

The most important settings are:

  • executable“: the binary to load and debug.
  • serverpath“: Path and file name of J-Link GDB Server executable. See Part 1.
  • armToolchainPath“: path to the arm-none-eabi-gdb executable. See Part 1.
  • device“: Device ID used for J-Link, see SEGGER J-Link website.
  • runToMain“: if debugger shall set a temporary breakpoint on main() und run until there.
  • svdFile“: Optional, this is a CMSIS-SVD file for register detail debugging.

SVD Files

The “svdFile” entry in the launch.json file is optional, but crucial to embedded system debugging because it describes the device peripheral registers. They are used for example in the Eclipse EmbSysRegView view and this is a good source to get the files. If using the NXP devices, then the SVD files are present in the NXP MCUXpresso SDK, but are harder to find as NXP is not using the standard *.svd extension.


For the launch file the extension does not matter, but I recommend to rename it to .svd for clarity.


Use F5 or the menu Run > Start Debugging to start a debug session:

This starts debug session. I case of troubles, check the Debug Console:

Please check OUTPUT tab (Adapter Output) for output from C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe
Launching server: "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" "-if" "swd" "-port" "50000" "-swoport" "50001" "-telnetport" "50002" "-device" "MK22FN512xxx12"
Launching GDB: "C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin\arm-none-eabi-gdb.exe" "-q" "--interpreter=mi2"
undefinedC:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin\arm-none-eabi-gdb.exe: warning: Couldn't determine a path for the index cache directory.
Reading symbols from C:\Users\erich\Data\GitRepos\McuOnEclipse\Examples\VisualStudioCode\FRDM-K22F\FRDM-K22F_Simple\build\FRDM-K22F_Simple.elf...
0x000006b0 in _exit ()
Not implemented stop reason (assuming exception): undefined
Resetting target
Resetting target

Temporary breakpoint 1, main () at ../src/main.c:7
7	    int local = 0;

I have a warning plus a ‘non-imlemented stop reason’ (not sure why), but it works and I’m debugging 🙂

Debugging ARM Cortex-M4F with Visual Studio Code

By default there are several debug views available. With the .svd file (see above) I have access to the peripheral register too:


There are the usual debug actions (continue, stepping, restart) plus a ‘run and debug’ toolbar on the left:

Assembly Stepping

For the Disassembly view or assembly code stepping use the Command Palette (CTRL+SHIFT+P) with ‘Cortex’:

Use ‘View Disassembly (Function’ with the name of a function to debug that function in assembly code. Use ‘Set Force Disassembly’ to switch to disassembly stepping for all functions.

It is possible to set breakpoints in the assembly code, but one cannot see high level (C/C++) side-by-side with the assembly code. And the disassembly view does not show any more information or symbolic a the Eclipse CDT disassembly view does.

Switch back to ‘auto’ only to show disassembly for code without debug/source information:


To view memory, use the Command Palette (CTRL+SHIFT+P) with Cortex:

Then specify the expression for the memory location and a length:

This then shows the memory:

Memory View

The memory view is very basic compared to the Eclipse (many) memory views, but for inspecting memory it is better than nothing.


With this, I hope this gets you up and running (well: debugging) with the Microsoft Visual Studio Code on ARM Cortex-M. The embedded debugging capabilities (memory, registers, assembly) are much more basic compared to the views in Eclipse or MCUXpresso IDE, so for sure not up to that level (yet?). But at least I can do basic hardware debugging on an ARM Cortex with this.

There are for sure many more things to add and improve, but these could be topics for future articles.

Happy Coding 🙂


39 thoughts on “Visual Studio Code for C/C++ with ARM Cortex-M: Part 4 – Debug

  1. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 2 | MCU on Eclipse

    • Hi Dave,

      >A question though: What specific missing features (as compared to Eclipse) do you think significant?
      In terms of debugging? Nothing. It only provides the very basics.

      The feature I would mention is IntelliSense (which I really like for Rust, but have not fully configured for C/C++ yet).
      To me it is not about a feature. It is that it is ‘modern’ (see other discussion) with a fast and cool looking user interface. I love the extension manager. The other point I would like to mention is that it is very exciting that Liviu is contributing with an extension to VSC (see and his comments on this series). His ‘GNU ARM Eclipse’ and ‘GNU MCU Eclipse’ and now ‘Eclipse Embedded CDT’ ( work and contributions are for me one big reason why Eclipse today is so good for embedded system development, beside of other CDT contributions: because his plugins made toolchain configuration (multi-host) and especially debugging very easy. I still need to dive into his VSC work, and I’ll cover that for sure in one of the next article(s).



  2. Hey Erich,

    Nicely done. Honestly, I was expecting more in terms of debug capability – maybe that will come out in time.

    What I would take out of this series is that Visual Studio is something to consider when you have to use a part where the vendor has not provided modern or adequate development tools. I thinking of my own work where I have a simple batch of (Java based) tools for testing operations including code and I wonder if Visual Studio would be something to consider going forwards.

    I found this series very useful.

    Liked by 1 person

    • Hi myke,
      Yes, I was hoping more in terms of embedded debugging capabilities in Visual Studio Code when I started my journey. The very, very basics are there, but mostly because of the work of Marus ( and his extension. His plugin supports SWO with data graphing (see but because many boards do not have SWO routed to the debug header it is not widely available. I plan to try it out with a different board and to come up with an article about it.

      RTOS (FreeRTOS) debugging is something else want to look into: this should be supported to some level according to the marus25.cortex-debug extension. And Semihosting is one of the planned features of the extension.

      SWO and Semihosting are some ‘nice to have’ for me: much more important to me (and probably everyone else) is FreeRTOS support in the IDE. To me FreeRTOS awareness as in the Eclipse MCUXpresso IDE are the key factor which vendor to use for a project, as about 98% of my current research projects for micro controllers are using FreeRTOS as kernel.

      >>What I would take out of this series is that Visual Studio is something to consider when you have to use a part where the vendor has not provided modern or adequate development tools.
      I agree with that. Visual Studio is currently no real replacement for the real embedded development tools. It is really interesting to play with Visual Studio Code for ARM Cortex-M, but probably not (yet) for a production level development. I hope this will change over time with more and more requesting Microsoft to move into that space. I think the key word here is ‘modern’: this is what Microsoft brings to the table.

      Liked by 1 person

  3. Hi Erich
    Thanks for this posts about vscode. I do almost all of my coding in vscode nowadays that means embedded C and also Python and Go. I also hope vscode will gain more features in the embedded space, although I can easily live with the current state as I do the debugging part mostly in Ozone. And I got used to have vscode as IDE, which is great (in my opinion even superior to eclipse, as e.g. Intellisense works perfectly for me, easier to update, better extensions, no java ;), ….) and Ozone as standalone debugger which also does a good job.
    In general vscode feels much snappier and less bloated than eclipse but still has tons of feature, which I really like about it…

    Liked by 1 person

    • Hi Martin,
      yes, VSC feels snappier to me too. That might change over time maybe if it gets more and more extensions added (I saw that with Eclipse: it was pretty good at the start and then I feel it got dragged down with all the plugins installed). But running native code than Java makes a difference too, even with JITC.
      Having an external debugger like Ozone sure makes sense until VSC gets more mature for embedded development. Like Ozone one can easily use Eclipse as the ‘debugger’ too: with this VSC excels in the domain where it is really good today: as an editor. I think it might take time until VSC could catch up on these aspects, and we have to see.


  4. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 3 | MCU on Eclipse

  5. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 1 | MCU on Eclipse

  6. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 5 | MCU on Eclipse

  7. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 6 | MCU on Eclipse

  8. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 7 – FreeRTOS | MCU on Eclipse

  9. Hey Erich!
    Thanks for these posts. They are very useful and I was looking for them for a while!
    I did all the steps, but when I run it, I get the error below, do you know what can be the reason for that?
    “Reading symbols from /home/repos/e2/build/Debug/apps/e2.elf…
    0xfffffffe in ?? ()
    Not implemented stop reason (assuming exception): undefined
    Resetting target

    received signal SIGTRAP, Trace/breakpoint trap.
    0xfffffffe in ?? ()”

    Liked by 1 person

  10. Hi,
    Dear Erich the article was excellent .Really VSC shines in terms of
    speed and functionality and your article has made clear how things work .
    Your article raised some questions related to usual IDE i operate
    How are custom build switches handled (debug-release-….) ?.
    Can somebody view-edit the project structure in a gui way ?
    In MARUS-debugger can we watch live global variables with adjustable time window
    without SWO .
    Thanks again for all the information you are providing .

    Liked by 1 person

  11. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 8 – xPack C/C++ Managed Build Tools | MCU on Eclipse

  12. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 9 – RTT | MCU on Eclipse

  13. Hi Erich,
    Thanks a lot for this inspiring article!
    There’s one question left to me, it concerns the single step debugging in VSCode together with the Cortex-Debug extension. Is it possible to perform single step debugging in the (Assembler) startup code before main() has been reached?
    E.g for the STM32L476 MCU this is in startup_stm32l476xx.s.
    So far I didn’t find a way or any documentation how to do this.
    Cheers, Hubert

    Liked by 1 person

      • Hi Eric,
        I think in the meantime I can answer my own question.
        Bottom line the problem occured because VSCode by default simply refuses to set breakpoints in files like *.s, *.asm, *.txt, …
        To enable this, go to VSCode: File: Preferences: Debug and set the checkbox for “Allow breakpoints everywhere”. Then it is possible to set a breakpoint as usual by mouseclick.
        Another alternative is to manually add a breakpoint in the debugger “BREAKPOINT” area. For assembler code you can type in a label instead of a function. Label+number (e.g. MyLabel+2) sets the breakpoint at the second line after the label. Without having set the “Allow breakpoints everywhere” checkbox, the breakpoint works but will not be shown in the editor by the red dot.

        Cheers, Hubert

        Liked by 1 person

  14. Pingback: New “MCU-Link Pro”: Debug Probe with Energy Measurement | MCU on Eclipse

  15. Pingback: Visual Studio Code for C/C++ with ARM Cortex-M: Part 10 – Assembly Stepping | MCU on Eclipse

  16. Hi Erich,
    in Eclipse there is something called “Dynamic Printfs”, which can sometimes be useful when debugging. Visual studio code has something similar called “log points”. Have you come across this/know more about it?

    For me the log points are working fine in a normal C project using gdb, but using it when cross debugging doesn’t work, instead they are treated like normal breakpoints.

    Have you any idea which plugin would need to support this feature? Is it the Cortex-Debug plugin?

    Liked by 1 person

  17. Hi Erich, thanks again for this series of articles, they are very helpful. I’m looking for a Cortex-M4 development board but the NXP FRDM-K22F Board that you mentioned here is on a long lead-time. Do you use any other Cortex-M4 boards?

    Liked by 1 person

    • Hi David,
      glad you like that series! About the FRDM-K22F: at least seems to get some very soon ( 113 Expected 14/02/2022 ), if this can be trusted. I’m using the FRDM-K64F too, but this one does not even seem to have delivery date. Other than tat, you might consider the LPC55S69 or LPC55S16 EVK: I do use them too, they are (at least right now) on stock, but they are Cortex-M33.


  18. Hi Erich,

    Many thanks. I use an old LPC device ARM7 with semihosting to telnet using JLink JTAG. I was wondering how I can manage to create an additional terminal window in VScode after the GDB server has started.


What do you think?

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.