Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Debugging in nRF Connect for VS Code

Debugging, in the context of embedded development, is the process of identifying, analyzing and resolving issues related to software and hardware. Debugging is an iterative process where the developer analyzes the code or hardware and makes small adjustments before they test their application again. Common problems you might face during development are syntax errors, logical errors, memory leaks, timing issues, and hardware-related issues.

The nRF Connect SDK has multiple tools that can help when debugging your application. In this chapter, we will go through some of the most useful debugging tools and how to use them.

Preparing for debugging

To be able to debug your nRF Connect SDK application, you must configure it correctly before building.

In the build configuration menu, under Optimization level, select the option Optimize for debugging. This option enables additional extension-specific debugging features after the application is built.

When this optimization is set, the following Kconfigs are enabled in the build:

Other Kconfigs that can be useful when debugging:

  • CONFIG_I2C_DUMP_MESSAGES – Enables the possibility to log all I2C messages. See Debugging I2C communication for more information.
  • CONFIG_ASSERT – This enables the __ASSERT() macro in the kernel code. If an assertion fails, the policy for what to do is controlled by the implementation of the assert_post_action() function, which by default will trigger a fatal error.

It’s worth taking into consideration that enabling optimization for debugging will affect the size of the code, and this can cause errors. If this is a problem, enabling only what is needed from the default optimization for debugging might be used; for example CONFIG_THREAD_NAME or CONFIG_DEBUG. There are also additional Kconfigs for different network stacks.

Pristine Build

Outputs are created at CMake configuration time and are not always regenerated when one of their inputs changes. As a first step in getting the build compiled, it is recommended to delete the build folder and do a pristine build manually.

Breakpoints

A breakpoint is a place in the code where you want the instruction to pause the execution of the application when the selected point is reached. This allows you to examine the program state and variable values or look at the call stack at that point of execution. The breakpoints are normally set at the line where you would like the application to halt its execution.

Breakpoint added at line 39
Application stopped before line 39

In VS Code, one can set conditional breakpoints, so the breakpoint occurs when reaching a specific expression or function. It is also possible to print a log statement instead of stopping at the breakpoint. To change the type of breakpoint, either right-click on an existing breakpoint and select Edit breakpoint or right-click on the line number and add a desired type of breakpoint.

Change existing breakpoint
Add new breakpoint

Monitor mode debugging

The debugger can run in two modes: halt mode and monitor mode.

In halt mode, the debugger halts the CPU when a debug request occurs, while in monitor mode, the debugger lets the CPU debug parts of an application while crucial functions continue. Monitor mode is useful for timing-critical applications like Bluetooth Low Energy or PWM, where halting the entire application will affect the timing-critical aspects. The CPU takes debug interrupts, running a monitor code for J-Link communication and user-defined functions. The Logpoint can be used together with monitor mode to provide debug information without crashing the application.

To enable monitor mode for the application, do the following:

  1. In the application configuration file, enable the Kconfig options CONFIG_CORTEX_M_DEBUG_MONITOR_HOOK and CONFIG_SEGGER_DEBUGMON.
  2. Enable monitor mode for the debugger you are using. For nRF Debug in nRF Connect for VS Code extension, enter -exec monitor exec SetMonModeDebug=1 in the debug console to enable monitor mode.
Entering the SetMonModeDebug command

Variables view

The Variables view shows variables in the current scope and organizes them into local, global, and static sections. The variables are only updated when the application is halted.

Example view of the variables overview in VS Code

Watch View

The Watch View shows the output of variables you have selected to watch.

To start watching a variable:

  1. Click and drag the mouse cursor to highlight the variable in your code.
  2. Right-click and choose Add to Watch.
  3. Click the Start/Continue button in the debugging control bar to watch the debugger go through the code until it stops at the variable breakpoint.
    The output of the code displays in the Watch View
Example view of user-inputted variables you wish to keep an eye on

Peripherals View

When debugging starts, the debugger shows a memory map of peripherals from your device’s SVD file and displays the values of their registers and fields. You can use this information to monitor and troubleshoot your hardware behavior.

Short example view of the peripherals of an nRF52840

Call stack

The call stack lists all of the available threads and indicates which ones are running. It also includes the call stack for each thread that is running. The call stack is a data structure that keeps track of function calls within an application. A stack is a kernel object that implements a Last-In-First-Out(LIFO) queue. This allows threads and ISRs to add and remove a limited number of integer data values. More about stacks can be found here.

Call stack for an example application

Thread Viewer

Thread overview of an example application

The Thread Viewer shows you information about the specific threads in the application. Their state uses Zephyr’s thread states and is only updated when the debugger is stopped. While the device is running, the View is frozen. Any state in the table is stale until execution is stopped again.

  • Name – Name of the thread.
  • Priority – Priority of the thread configured by the user, based on Zephyr’s thread priorities.
  • Entry – Name of the thread entry point function in the code.
  • Stack Usage – Number of bytes used by the thread and the maximum number of bytes that can be allocated to the thread.
  • User Option – Binary value associated with the thread. The numbers in this column represent bit fields which are based on Zephyr’s thread options. Colors are for readability only.

Memory Explorer

The Memory Explorer shows the memory content on your device. You can review different regions of memory data in its Flash and RAM tabs. More information about what you can do in this view can be found here.

Memory Explorer view from an example application
Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.