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

nRF52 softdevice hangs at sd_softdevice_enable when using custom bootloader

I have a problem initializing the ble stack when I'm starting my application via a custom bootloader. I'm using the nrf52832 device on a custom board.

My application has been working fine for a while on its own. But now I want to add a bootloader to the mix...

The memory layout in flash is like this:

0x00000000   - softdevice (s132_nrf52_4.0.2)
0x0001F000   - Primary bootloader
0x00022000   - Main application

There is no overlap in RAM

The point where the execution stops is at sd_softdevice_enable in softdevice_handler.c. I have seen several other questions about this where the solution often seemed to be related to the clock source. However I don't think this is my problem since the application works fine on its own. But I've tried both external and internal clock sources, with the same results:

// external clock source
nrf_clock_lf_cfg_t clock_lf_cfg =
{
    .source        = NRF_CLOCK_LF_SRC_XTAL,
    .rc_ctiv       = 0,
    .rc_temp_ctiv  = 0,
    .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_30_PPM
};

// internal clock source
nrf_clock_lf_cfg_t clock_lf_cfg =
{
    .source = NRF_CLOCK_LF_SRC_RC,
    .rc_ctiv = 16, // Interval in 0.25 s, 16 * 0.25 = 4 sec
    .rc_temp_ctiv = 2, // Check temperature every .rc_ctiv, but calibrate every .rc_temp_ctiv
    .xtal_accuracy = 0,
};

I'm currently out of ideas as to what could be wrong. Can anyone here point me in the right direction?

Parents
  • Never had to do this before either. And I will look for a better reference but the way the legacy DFU handles this is:

    void bootloader_app_start(uint32_t app_addr)
    {
        // If the applications CRC has been checked and passed, the magic number will be written and we
        // can start the application safely.
        uint32_t err_code = sd_softdevice_disable();
        APP_ERROR_CHECK(err_code);
    
        interrupts_disable();
    
        err_code = sd_softdevice_vector_table_base_set(CODE_REGION_1_START);
        APP_ERROR_CHECK(err_code);
    
        bootloader_util_app_start(CODE_REGION_1_START);
    }
    

    Bootloader util is a bunch of assembly code:

    static inline void bootloader_util_reset(uint32_t start_addr)
    {
        asm("ldr   r5, [%0]\n"                    // Get App initial MSP for bootloader.
            "msr   msp, r5\n"                     // Set the main stack pointer to the applications MSP.
            "ldr   r0, [%0, #0x04]\n"             // Load Reset handler into R0.
    
            "movs  r4, #0x00\n"                   // Load zero into R4.
            "mvns  r4, r4\n"                      // Invert R4 to ensure it contain ones.
    
            "mrs   r5, IPSR\n"                    // Load IPSR to R5 to check for handler or thread mode 
            "cmp   r5, #0x00\n"                   // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
            "bne.n isr_abort\n"                   // If not zero we need to exit current ISR and jump to reset handler of bootloader.
    
            "mov   lr, r4\n"                      // Clear the link register and set to ones to ensure no return.
            "bx    r0\n"                          // Branch to reset handler of bootloader.
    
            "isr_abort: \n"
                                                  // R4 contains ones from line above. We be popped as R12 when exiting ISR (Cleaning up the registers).
            "mov   r5, r4\n"                      // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application.
            "mov   r6, r0\n"                      // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR.
            "movs  r7, #0x21\n"                   // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21.
            "rev   r7, r7\n"                      // Reverse byte order to put 0x21 as MSB.
            "push  {r4-r7}\n"                     // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
    
            "movs  r4, #0x00\n"                   // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers).
            "movs  r5, #0x00\n"                   // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers).
            "movs  r6, #0x00\n"                   // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers).
            "movs  r7, #0x00\n"                   // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers).
            "push  {r4-r7}\n"                     // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
    
            "movs  r0, #0x06\n"                   // Load 0x06 into R6 to prepare for exec return command.
            "mvns  r0, r0\n"                      // Invert 0x06 to obtain EXEC_RETURN, 0xFFFFFFF9.
            "bx    r0\n"                          // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
            :: "r" (start_addr)                   // Argument list for the IAR assembly. start_addr is %0.
            :  "r0", "r4", "r5", "r6", "r7");     // List of register maintained manually.
    }
    
  • After a bit of struggling I got everything to work properly. Will update this answer with some more info tomorrow...

Reply Children
No Data
Related