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

Issues with nrf_bootloader_app_start and the SDK 16.0.0

Hello. I am trying to implement a application jump from my normal firmware located at memory 0x19000 per recommendations based on the soft device used (s112) to a different application located at memory address 0x70000. I have implemented a version of the application jump as used by the SDK, but have had no luck. The SDK had code that forced the jump to start at the end of the MBR (which I understand to be the soft device). However, I wanted to do a manual jump to a separate application, so I changed the start address to the application memory location. I also added the additional step to set the soft device vector table for later (0x70000), but the jump didn't work before this addition, nor after. 

Each time I jump, I end up in the soft device (I think) and eventually hit some kind of hard fault. 

My code looks like the following. Is there something obviously wrong I am not getting?

/**@brief Function that sets the stack pointer and link register, and starts executing a particular address.
 *
 * @param[in]  new_msp  The new value to set in the main stack pointer.
 * @param[in]  new_lr   The new value to set in the link register.
 * @param[in]  addr     The address to execute.
 */
#if defined ( __CC_ARM )
__ASM __STATIC_INLINE void jump_to_addr(uint32_t new_msp, uint32_t new_lr, uint32_t addr)
{
    MSR MSP, R0;
    MOV LR,  R1;
    BX       R2;
}
#else
__STATIC_INLINE void jump_to_addr(uint32_t new_msp, uint32_t new_lr, uint32_t addr)
{
    __ASM volatile ("MSR MSP, %[arg]" : : [arg] "r" (new_msp));
    __ASM volatile ("MOV LR,  %[arg]" : : [arg] "r" (new_lr) : "lr");
    __ASM volatile ("BX       %[arg]" : : [arg] "r" (addr));
}
#endif

/**@brief Function for booting an app as if the chip was reset.
 *
 * @param[in]  vector_table_addr  The address of the app's vector table.
 */
__STATIC_INLINE void app_start(uint32_t vector_table_addr)
{
    const uint32_t current_isr_num = (__get_IPSR() & IPSR_ISR_Msk);
    const uint32_t new_msp         = *((uint32_t *)(vector_table_addr));                    // The app's Stack Pointer is found as the first word of the vector table.
    const uint32_t reset_handler   = *((uint32_t *)(vector_table_addr + sizeof(uint32_t))); // The app's Reset Handler is found as the second word of the vector table.
    const uint32_t new_lr          = 0xFFFFFFFF;

    __set_CONTROL(0x00000000);   // Set CONTROL to its reset value 0.
    __set_PRIMASK(0x00000000);   // Set PRIMASK to its reset value 0.
    __set_BASEPRI(0x00000000);   // Set BASEPRI to its reset value 0.
    __set_FAULTMASK(0x00000000); // Set FAULTMASK to its reset value 0.

    //ASSERT(current_isr_num == 0); // If this is triggered, the CPU is currently in an interrupt.

    // The CPU is in Thread mode (main context).
    jump_to_addr(new_msp, new_lr, reset_handler); // Jump directly to the App's Reset Handler.
}


void nrf_bootloader_app_start_final(uint32_t vector_table_addr)
{
    // Run application
    app_start(vector_table_addr);
}

void nrf_bootloader_app_start(void)
{
    uint32_t start_addr = 0x70000;//MBR_SIZE; // Always boot from end of MBR. If a SoftDevice is present, it will boot the app.
    uint32_t err_code;

    // Disable and clear interrupts
    // Notice that this disables only 'external' interrupts (positive IRQn).
    NVIC->ICER[0]=0xFFFFFFFF;
    NVIC->ICPR[0]=0xFFFFFFFF;
#if defined(__NRF_NVIC_ISER_COUNT) && __NRF_NVIC_ISER_COUNT == 2
    NVIC->ICER[1]=0xFFFFFFFF;
    NVIC->ICPR[1]=0xFFFFFFFF;
#endif

    uint32_t ret_val = NRF_ERROR_INVALID_PARAM;
    uint32_t address = MBR_SIZE;

#if !defined(BLE_STACK_SUPPORT_REQD) && !defined(ANT_STACK_SUPPORT_REQD)
    sd_mbr_command_t command =
    {
        .command = SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET,
        .params.irq_forward_address_set.address = address,
    };

    ret_val = sd_mbr_command(&command);
#endif

    if (ret_val == NRF_ERROR_INVALID_PARAM)
    {
        // Manually set the forward address if this MBR doesn't have the command.
        *(uint32_t *)(0x20000000) = address;

        ret_val = NRF_SUCCESS;
    }

    err_code = sd_softdevice_vector_table_base_set(0x70000);
    if (err_code != NRF_SUCCESS) { return false; }

    nrf_bootloader_app_start_final(start_addr);
}

  • Hi,

    You need to use a different MBR command in order to jump to another address than 0x1000. Specifically, In SDK >= 15.0.0, the boot process consists of forwarding interrupts to the SoftDevice using SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET (nrf_dfu_mbr_irq_forward_address_set()), and then always jumping to address 0x1000 (SoftDevice start) instead of the application start address.

    Another approach, which was used up to and including SDK 14.2 was to use the SD_MBR_COMMAND_INIT_SD and SD_MBR_COMMAND_VECTOR_TABLE_BASE_SET command and start the application at a specific address. This is what you should do when the application is not always immediately after the MBR.

    You can compare components\libraries\bootloader\nrf_bootloader_app_start.c in SDK 14.2 and a newer SDK to see both approaches.

    Update: Fixed mistake referring to bootloader instead of MBR at the end of the second paragraph.

    Br,

    Einar

  • Einar, thank you for the quick response. A follow up:

    You said 

    This is what you should do when the application is not always immediately after the bootloader.

    So if my memory is set up to be:

    Top of flash

    Bootloader             0x70000 -  0x7E000

    Application             0x19000 -  0x70000

    Soft Device            0x1000   -  0x19000

    MBR                      0x0000   -  0x1000

    Bottom of flash:     0x0000000

    Then this means my application is not immediately after the bootloader, andI should be following that approach? Is that what you mean? I ask because I am following the approach using SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET (nrf_dfu_mbr_irq_forward_address_set()) currently, with no avail. 

  • Hi,

    I am sorry, there was a mistake in the text. I meant to say "This is what you should do when the application is not always immediately after the MBR" (not bootloader). But the rest of the post is correct, and the key is that you need to use the approach you see in nrf_bootloader_app_start.c from SDK 14.2. In other words, you can not use SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET in your case, but instead, you should use SD_MBR_COMMAND_INIT_SD and SD_MBR_COMMAND_VECTOR_TABLE_BASE_SET.

  • Einar, thank you for the clarification. So I should be following the process laid out in SDK 14.2. I went ahead and implemented that, and the jump to application seems to be working like a charm.

    I am having faults on execution of the application after bootloader init, but this is most likely due to initialization carrying over between applications I assume...time for the next challenge!

Related