Monitor Mode Debugging with J-Link and GDB/Eclipse

The biggest pain point when debugging an application on the nRF51 series is the inability to set breakpoints while the SoftDevice is active without triggering an assert. Although a workaround exists that allows the debugging process to limp along for a short amount of time after the SoftDevice complains, using breakpoints with the nRF51 is inevitably a disruptive process.

Thankfully, the nRF52 series includes support for a neat feature called Monitor Mode Debugging. When this feature is enabled, the processor will trigger a specific interrupt handler (DebugMon_Handler) instead of halting when a breakpoint is reached. This DebugMon_Handler then spins and executes some 'monitor' code until the debugger wishes to continue. Because the DebugMon_Handler is running in an interrupt context, it effectively halts the execution of all code except for code that runs in higher-priority contexts. The monitor code itself allows the debug probe (e.g. Segger J-Link) to maintain communication with the processor without actually halting it.

In short, using monitor mode instead of halt mode with the nRF52 allows the debugger to set breakpoints in low-priority contexts and perform normal debugging operations without impeding higher-priority code like the SoftDevice's time-critical routines.

Prereqs

Monitor mode debugging requires support in the processor (in this case a Cortex-M4F) as well as in the debug probe (in this case a J-Link). As long as the processor and debug probe are correctly configured no further cooperation is required from the debugger: this means that the GDB client and therefore Eclipse can use monitor mode without any specific support for it. Adding monitor mode support to a GDB/Eclipse project is straightfoward as long as the following requirements are met:

  • S132 v2 or newer
  • Segger J-Link drivers v5.10r or newer
  • J-Link PLUS, ULTRA+, or PRO

NOTE: The J-Link OB that is included on Nordic development kits does not include a license for monitor mode debugging. On Windows machines, the J-Link driver will allow evaluation of the monitor mode debugging feature if the user accepts a license pop-up. On Linux and OSX machines, the J-Link driver will accept the commands to enable monitor mode debugging but will silently fail to enable it.

Procedure

Segger supplies DebugMon_Handler code for several compilers. The files needed for using GDB/Eclipse are available in the 'Sample project' here. After downloading:

  1. Create a new dir e.g. '{SDK13_ROOT}/external/jlink_monitor_mode_debug/gcc'
  2. Copy 'JLINK_MONITOR.c', 'JLINK_MONITOR.h', and 'JLINK_MONITOR_ISR_SES.s' from the 'Sample project' to the new dir

These files need to be compiled into the application so the default DebugMon_Handler is replaced by Segger's implementation. Regardless of whether GDB or Eclipse is being used, the fastest way to get started is to simply add these files to an existing Makefile. For a typical example in SDK13 this might involve copy-and-pasting the following to a line just above the 'default:' target:

ifeq ($(MMD), 1)
CFLAGS += -O0 -ggdb -DDEBUG -DDEBUG_NRF
CFLAGS += -DMMD
ASMFLAGS += -mcpu=cortex-m4
ASMFLAGS += -mthumb
SRC_FILES += $(SDK_ROOT)/external/jlink_monitor_mode_debug/gcc/JLINK_MONITOR.c
SRC_FILES += $(SDK_ROOT)/external/jlink_monitor_mode_debug/gcc/JLINK_MONITOR_ISR_SES.s
INC_FOLDERS += $(SDK_ROOT)/external/jlink_monitor_mode_debug/gcc
$(info Building for monitor debug mode.)
endif

ifeq ($(HALT), 1)
CFLAGS += -O0 -ggdb -DDEBUG -DDEBUG_NRF
$(info Building for halt debug mode.)
endif

ifeq ($(RELEASE), 1)
CFLAGS += -O3 -g3
ASMFLAGS += -g3
$(info Building for release.)
endif

Then, calling make clean followed by make MMD=1 will cause these files to included in the build. From Eclipse, calling make MMD=1 can be accomplished by modifying the existing 'Build Configuration' (or creating a new one):

image description

Next, two commands are required to be executed by JLinkGDBServerCL to enable monitor mode debugging:

mon exec SetMonModeDebug=1
mon exec SetMonModeVTableAddr=0x1f000

The second command tells the J-Link where the application's vector table is located. This location varies between versions of a particular SoftDevice. The easiest way to locate it is to check a project's linker file. For example, in the ble_app_uart project in SDK13 the 'ble_app_uart_gcc_nrf52.ld' defines this offset as 0x1f000:

MEMORY
{
  FLASH (rx) : ORIGIN = 0x1f000, LENGTH = 0x61000
  RAM (rwx) :  ORIGIN = 0x200025f8, LENGTH = 0xda08
}

These two commands can be added to a command script or gdbinit file if GDB is being invoked directly from a Makefile:

.PHONY: gdb_cmd_file
gdb_cmd_file: default
    @echo "target remote localhost:$(GDB_PORT)" > $(GDB_CMD_PATH)
    @echo "mon speed 10000" >> $(GDB_CMD_PATH)
    @echo "mon flash download=1" >> $(GDB_CMD_PATH)
    @echo "load $(DBG_BUILD_DIR)/$(PROJECT_NAME).out" >> $(GDB_CMD_PATH)
    @echo "break main" >> $(GDB_CMD_PATH)
    @echo "mon reset 0" >> $(GDB_CMD_PATH)
    @echo "mon exec SetMonModeDebug=1" >> $(GDB_CMD_PATH)
    @echo "mon exec SetMonModeVTableAddr=0x1f000" >> $(GDB_CMD_PATH)

If GDB is being launched by Eclipse then these commands can be appended to the commands under the 'Startup' tab in the project's 'Debug Configuration':

image description

No other changes are required but if a standalone debug probe is used (i.e. a fancy J-Link) then the correct debug probe should be selected by entering its serial number in the 'Debugger' tab:

image description

Note that a second 'Debug Configuration' can be created that omits these two commands to allow for switching between halt mode and monitor mode debugging. Care should be taken to ensure that the correct 'Build Configuration' was used to build the application before starting the debugger in monitor mode because the JLinkGDBServerCL has no way to ensure that the application was built with a valid DebugMon_Handler.

A proper Makefile for switching between halt debugging, monitor debugging, and release builds is provided for the ble_app_uart project in SDK13 here.

Finally, the application needs to assign a reasonable priority to the DebugMon_Handler. This can be done at the top of the main function in 'main.c' with a call to NVIC_SetPriority(DebugMonitor_IRQn, 7UL);. Priority level 7 allows for setting breakpoints in code that executes in the Thread (or main) context. For reference, here is list of the interrupt priorities that are use by the S132:

image description

The priority level that is assigned to the DebugMon_Handler should be set to one level higher than the code that needs to be debugged. However, setting the DebugMon_Handler priority to level 0, 1, 2, 3, or 4 can lead to interference with the operation of the SoftDevice. Using priority level 6 allows breakpoints to be set in the on_ble_evt function and is the recommended level for most applications.

The JLinkGDBServerCL takes care of writing the required processor registers to enable monitor mode debugging. Note that if the application triggers a soft reset then there can be a brief window between when the processor resets and when the J-Link can write the registers to reenable monitor mode.

  • I had the same problem and found this : https://github.com/NordicPlayground/j-link-monitoring-mode-debugging/blob/master/pca10040/s132/ses/README.md

    In fact, the interrupt priority wasn't high enough. Here is the relevent line from the README : NVIC_SetPriority(DebugMonitor_IRQn, _PRIO_SD_LOW)

  • i was all the setting give in the tutorial, still i am getting new problem like after break point now the code is moving Hardfault handler in startup file . Can any one help on this

  • "NOTE: The J-Link OB that is included on Nordic development kits does not include a license for monitor mode debugging. On Windows machines, the J-Link driver will allow evaluation of the monitor mode debugging feature if the user accepts a license pop-up."

    Like this:

  • I got errors in JLINK_MONITOR_ISR_SES.s file:


    make: *** [_build/nrf52832_xxaa_JLINK_MONITOR_ISR_SES.s.o.o] Error 1
    recipe for target '_build/nrf52832_xxaa_JLINK_MONITOR_ISR_SES.s.o.o' failed
    symbol `_HandleReadRegDone_Veneer' is already defined
    undefined symbol _APP_SP_OFF_S0 used as an immediate value
    undefined symbol _APP_SP_OFF_S0 used as an immediate value
    undefined symbol _MON_VERSION used as an immediate value
    undefined symbol _NUM_BYTES_BASIC_STACKFRAME used as an immediate value
    undefined symbol _NUM_BYTES_EXTENDED_STACKFRAME used as an immediate value

  • Hi Daniel,

    I figured out that reading your Blog “Getting More Out of Make” is helpful since it mentions the otherwise empty GDB_PORT and DBG_BUILD_DIR. (Please, add a link to it) Nevertheless I don’t succeed in MMD working. Therefore, I asked for the exact things to do. Now in my Debug Configuration the 2 commands are: mon exec SetMonModeDebug=1 mon exec SetMonModeVTableAddr=0x22000

    It is unclear why in your screenshot there is the 3rd command mon reset 0 So, I thought the others from the created file of 8 commands are also needed in Eclipse.

    A debug session of ble_app_blinky can be started and keeps running in the busy/wait loop as long as no breakpoints are hit. However, when e.g. the breakpoint at the source code bsp_board_led_on(LEDBUTTON_LED) should be hit, then the smartphone app says after 20 seonds that the BLE connection is disconnected. And the intended breakpoint is not hit at all. (No connection is possible anymore: so, reset)

    Although I follow all the instructions, I do not manage to have MMD working. Please, any help is welcome!