This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF52 doesn't start without debugger (Jlink) on GCC toolchain

Okay this got us going around in circles and it looks stupidly simple. It seems there is a knowledge gap since we saw many posts similar to ours but without clarity as of what to do in practice.

-- Problem statement:

Boards with NRF52 do not start when a debugger is not plugged to it. This is impossible to see if one is using a DK board. It might be a blind spot in the setup/info that the Nordic Team has worked on or something that everyone creating software with NRF52 will have to deal with. At any rate it would be great to get a sensible answer for the community.

-- Hardware/software setup:

  • The debugger is a JLink from Segger with all latest drivers. Works a treat.
  • Our toolchain is ARM GCC
  • We work on Mac (we're not perfect)
  • SDK 14.0
  • SD 132 v5.0.0

-- What we did:

Tested on Dev kit board (10040), since JLink is always attached to it, it works. Tested on 4(!) different versions of our custom board with Jlink debugger attached, works but when removing the debugger and power cycling, the NRF52 doesn't start at all (no BT, no test LED turning on, no test GPIO turning on, nothing).

-- What we are aware of:

Something called semihosting, which prevents the nrf52 from starting if there is no debugger attached to it. Seems like the most likely issue here. The thing is, there is nowhere we could find a semihosting config. Is it in the sdk_config.h? We couldn't find that info.

Linker file. We have one like everyone else and it's unclear if that could play a role. Unlikely since the board works well when attached with the debugger. here is the linker file just in case:

/* Linker script to configure memory regions. */

SEARCH_DIR(.)
GROUP(-lgcc -lc -lnosys)

MEMORY
{
  FLASH (rx) : ORIGIN = 0x23000, LENGTH = 0x55000
  RAM (rwx) :  ORIGIN = 0x200020F0, LENGTH = 0xDF10 /*is normally 0xDF10*/
}

SECTIONS
{
  . = ALIGN(4);
  .svc_data :
  {
    PROVIDE(__start_svc_data = .);
    KEEP(*(.svc_data))
    PROVIDE(__stop_svc_data = .);
  } > RAM
  
  .fs_data :
  {
    PROVIDE(__start_fs_data = .);
    KEEP(*(.fs_data))
    PROVIDE(__stop_fs_data = .);
  } > RAM
   .log_dynamic_data :
  {
    PROVIDE(__start_log_dynamic_data = .);
    KEEP(*(.log_dynamic_data))
    PROVIDE(__stop_log_dynamic_data = .);
  } > RAM
  .cli_sorted_cmd_ptrs :
  {
    PROVIDE(__start_cli_sorted_cmd_ptrs = .);
    KEEP(*(.cli_sorted_cmd_ptrs))
    PROVIDE(__stop_cli_sorted_cmd_ptrs = .);
  } > RAM
} INSERT AFTER .data;

SECTIONS
{
  .pwr_mgmt_data :
  {
    PROVIDE(__start_pwr_mgmt_data = .);
    KEEP(*(SORT(.pwr_mgmt_data*)))
    PROVIDE(__stop_pwr_mgmt_data = .);
  } > FLASH
  
  .log_const_data :
  {
    PROVIDE(__start_log_const_data = .);
    KEEP(*(.log_const_data))
    PROVIDE(__stop_log_const_data = .);
  } > FLASH
  
  
  .cli_command :
  {
    PROVIDE(__start_cli_command = .);
    KEEP(*(.cli_command))
    PROVIDE(__stop_cli_command = .);
  } > FLASH

  .sdh_stack_observers :
  {
    PROVIDE(__start_sdh_stack_observers = .);
    KEEP(*(SORT(.sdh_stack_observers*)))
    PROVIDE(__stop_sdh_stack_observers = .);
  } > FLASH

  .sdh_req_observers :
  {
    PROVIDE(__start_sdh_req_observers = .);
    KEEP(*(SORT(.sdh_req_observers*)))
    PROVIDE(__stop_sdh_req_observers = .);
  } > FLASH

  .sdh_state_observers :
  {
    PROVIDE(__start_sdh_state_observers = .);
    KEEP(*(SORT(.sdh_state_observers*)))
    PROVIDE(__stop_sdh_state_observers = .);
  } > FLASH

  .sdh_ant_observers :
  {
    PROVIDE(__start_sdh_ant_observers = .);
    KEEP(*(SORT(.sdh_ant_observers*)))
    PROVIDE(__stop_sdh_ant_observers = .);
  } > FLASH

  .sdh_ble_observers :
  {
    PROVIDE(__start_sdh_ble_observers = .);
    KEEP(*(SORT(.sdh_ble_observers*)))
    PROVIDE(__stop_sdh_ble_observers = .);
  } > FLASH

  .sdh_soc_observers :
  {
    PROVIDE(__start_sdh_soc_observers = .);
    KEEP(*(SORT(.sdh_soc_observers*)))
    PROVIDE(__stop_sdh_soc_observers = .);
  } > FLASH
} INSERT AFTER .text

INCLUDE "nrf5x_common.ld"

Also here are the flags we use:

#================================================================
# Libraries common to all targets
LIB_FILES += \
  $(SDK_ROOT)/components/nfc/t2t_lib/nfc_t2t_lib_gcc.a


# Optimization flags
OPT = -O3 -g3
# Uncomment the line below to enable link time optimization
#OPT += -flto


# C flags common to all targets

CFLAGS += $(OPT)
CFLAGS += -DBL_SETTINGS_ACCESS_ONLY
#CFLAGS += -DCONFIG_GPIO_AS_PINRESET
CFLAGS += -DNRF52
CFLAGS += -DNRF52832_XXAA
CFLAGS += -DNRF52_PAN_74
CFLAGS += -DNRF_SD_BLE_API_VERSION=5
CFLAGS += -DS132
CFLAGS += -DSOFTDEVICE_PRESENT
CFLAGS += -DSWI_DISABLE0
CFLAGS += -mcpu=cortex-m4
CFLAGS += -mthumb -mabi=aapcs
CFLAGS +=  -Wall -Werror
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
# keep every function in a separate section, this allows linker to discard unused ones
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin -fshort-enums 

#----------------------- PROJECT SPECIFIC --------------
CFLAGS += $(PROJECT_BOARD)
CFLAGS += $(NFC_DISABLE)
CFLAGS += $(LOGGING_MESSAGES)
CFLAGS += $(WHICH_TEST_TO_RUN)
CFLAGS += -DNRF_DFU_HW_VERSION=$(NRF_DFU_HW_VERSION)


# C++ flags common to all targets
CXXFLAGS += $(OPT)

# Assembler flags common to all targets
ASMFLAGS += -g3
ASMFLAGS += -mcpu=cortex-m4
ASMFLAGS += -mthumb -mabi=aapcs
ASMFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
ASMFLAGS += -DBL_SETTINGS_ACCESS_ONLY
#ASMFLAGS += -DCONFIG_GPIO_AS_PINRESET
ASMFLAGS += -DNRF52
ASMFLAGS += -DNRF52832_XXAA
ASMFLAGS += -DNRF52_PAN_74
ASMFLAGS += -DNRF_SD_BLE_API_VERSION=5
ASMFLAGS += -DS132
ASMFLAGS += -DSOFTDEVICE_PRESENT
ASMFLAGS += -DSWI_DISABLE0

#----------------------- PROJECT SPECIFIC --------------
ASMFLAGS += $(PROJECT_BOARD)
ASMFLAGS += $(NFC_DISABLE)
ASMFLAGS += -DNRF_DFU_HW_VERSION=$(NRF_DFU_HW_VERSION)



# Linker flags
LDFLAGS += $(OPT)
LDFLAGS += -mthumb -mabi=aapcs -L $(TEMPLATE_PATH) -T$(LINKER_SCRIPT)
LDFLAGS += -mcpu=cortex-m4
LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
# let linker to dump unused sections
LDFLAGS += -Wl,--gc-sections

# use newlib in nano version
#SDK13 was like thatLDFLAGS += --specs=nano.specs -lc -lnosys
LDFLAGS += --specs=nano.specs 


# Add standard libraries at the very end of the linker input, after all objects
# that may need symbols provided by these libraries.
LIB_FILES += -lc -lnosys -lm

Anyone could articulate a reason and a solution for this behavior? We sorted all the shenanigans like size of RAM, hardware failure etc... We are completely stuck.

  • have you tried power cycling the board and THEN plugging the JLink in. JLink can be connected to a running board. Hit break. Where are you?

  • We also did connect the debugger only after power cycling (connect to the running board) and what happens is that upon connection the board starts. that's why we are really puzzled since even before we get to hit break or otherwise stop the execution on the board, it has started normally and does what it should.

  • There were several issues, intertwined. One of them raises interesting questions.

    1. Easy stuff, we left the RTT setup in the sdk_config.h to blocking, so yea it was blocking of course. LOG frontend was waiting forever that a backend picks the logs. duh...

    2. Something Nordic might want to look into: For people with multiple hardware like us, some of our systems will have a low frequency crystal and some others won't. In the current setup (SDK14), it is impossible to use the board files to define that (clock source and softdevice clock source). That creates very subtle bugs:

      imagine you are using a clock source for LF crystal on a configuration that doesn't have such crystal. It will look like it is working. If you then use a bootloader and DFU it will stop working with no apparent reason. Inside the bootloader is a routine to disable and re-enable the clock source that will fail in an infinite loop waiting for the crystal system to stop running.

    It took us multiple days to find this out. We saw 2 reasons for that:

    • As far as we know there is no way to tell that a board has or not a hardware LF crystal in the board file for SDK14+ (and prior I believe)
    • Since the bug appears far removed from its origin, it is very hard to find and reproduce. Having a #define or something like that in the board file would eliminate this issue. It would also allow to detect bugs at compile time by refusing to compile if the board file says there is no hardware LF crystal and the sdk_config.h says there is one.
  • Hi Thomas, good to hear that you figured it out! I will mention it to my boss and hopefully this will be changed in the future. You might want to take a look at this case, as it is somewhat similar.

Related