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

Hold output pin during bootloader jump

I am trying to keep an output pin asserted while I jump to the DFU on the 52840.

We have a dead-man switch hooked up to pin 19.  I need to hold this pin high while I jump to the bootloader.  However, it seems like it is being reset to an input or toggle low when I jump.

Here is how I jump to the bootloader.

// Save update in restart register
NRF_POWER->GPREGRET = BOOTLOADER_DFU_START;

nrf_sdh_disable_request();

StartApplication(0xf1000);

void StartApplication(uint32_t start_addr)
{
    __ASM volatile(
        "LDR   R2, [%0]              \n" // Get App MSP.
        "MSR   MSP, R2               \n" // Set the main stack pointer to the applications MSP.
        "LDR   R3, [%0, #0x00000004] \n" // Get application reset vector address.
        "BX    R3                    \n" // No return - stack code is now activated only through SVC
                                         // and plain interrupts.
        ".ALIGN                      \n" ::"r"(
            start_addr) // Argument list for the gcc assembly. start_addr is %0.
    );
}

  • Hi Luke, 

    Which SDK version are you using?

    Are you using our default bootloader from the nRF5x SDK? Could it be that the the bootloader is re-initializing the pin in question ?

    I would perhaps take a look at the app_start() function the bootloader from SDK v15.3.0

    __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.
    
        if (current_isr_num == 0)
        {
            // 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.
        }
        else
        {
            // The CPU is in Handler mode (interrupt context).
    
            const uint32_t exception_stack[EXCEPTION_STACK_WORD_COUNT] = // To be copied onto the stack.
            {
                0x00000000,    // New value of R0. Cleared by setting to 0.
                0x00000000,    // New value of R1. Cleared by setting to 0.
                0x00000000,    // New value of R2. Cleared by setting to 0.
                0x00000000,    // New value of R3. Cleared by setting to 0.
                0x00000000,    // New value of R12. Cleared by setting to 0.
                0xFFFFFFFF,    // New value of LR. Cleared by setting to all 1s.
                reset_handler, // New value of PC. The CPU will continue by executing the App's Reset Handler.
                xPSR_T_Msk,    // New value of xPSR (Thumb mode set).
            };
            const uint32_t exception_sp = new_msp - sizeof(exception_stack);
    
            memcpy((uint32_t *)exception_sp, exception_stack, sizeof(exception_stack)); // 'Push' exception_stack onto the App's stack.
    
            jump_to_addr(exception_sp, new_lr, HANDLER_MODE_EXIT); // 'Jump' to the special value to exit handler mode. new_lr is superfluous here.
                                                                   // exception_stack will be popped from the stack, so the resulting SP will be the new_msp.
                                                                   // Execution will continue from the App's Reset Handler.
        }
    }

    Best regards

    Bjørn

  • Using SDK 15.3 and the standard secure dfu ble example project

    I tried using the code above, but it hard faulted.

    I tried disabling the soft device with  sd_softdevice_disable();, but I am still getting a hardfault.  Anything else I need to do in order to jump to the bootloader start address?

  • Hi Luke, 

    we did branch from the application to the bootloader back in SDK v11.0.0, but we moved away from thsi as it was sort of unnecessary complicated for most uses cases, but you can try to use that code, I have attached it below ( CC_ARM is for the Keil compiler and  __GNUC__ is for GCC/SES)

    #if defined ( __CC_ARM )
    __asm static void bootloader_util_reset(uint32_t start_addr)
    {
        LDR   R5, [R0]              ; Get App initial MSP for bootloader.
        MSR   MSP, R5               ; Set the main stack pointer to the applications MSP.
        LDR   R0, [R0, #0x04]       ; Load Reset handler into R0. This will be first argument to branch instruction (BX).
    
        MOVS  R4, #0xFF             ; Load ones to R4.
        SXTB  R4, R4                ; Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF.
        MRS   R5, IPSR              ; Load IPSR to R5 to check for handler or thread mode.
        CMP   R5, #0x00             ; Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
        BNE   isr_abort             ; If not zero we need to exit current ISR and jump to reset handler of bootloader.
    
        MOV   LR, R4                ; Clear the link register and set to ones to ensure no return, R4 = 0xFFFFFFFF.
        BX    R0                    ; Branch to reset handler of bootloader.
    
    isr_abort
                                    ; R4 contains ones from line above. Will be popped as R12 when exiting ISR (Cleaning up the registers).
        MOV   R5, R4                ; Fill with ones before jumping to reset handling. We be popped as LR when exiting ISR. Ensures no return to application.
        MOV   R6, R0                ; 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             ; 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                ; Reverse byte order to put 0x21 as MSB.
        PUSH  {r4-r7}               ; Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
    
        MOVS  R4, #0x00             ; Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers).
        MOVS  R5, #0x00             ; Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers).
        MOVS  R6, #0x00             ; Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers).
        MOVS  R7, #0x00             ; Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers).
        PUSH  {r4-r7}               ; Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
    
        MOVS  R0, #0xF9             ; Move the execution return command into register, 0xFFFFFFF9.
        SXTB  R0, R0                ; Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9.
        BX    R0                    ; No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
        ALIGN
    }
    #elif defined ( __GNUC__ )
    static inline void bootloader_util_reset(uint32_t start_addr)
    {
        __asm volatile(
            "ldr   r0, [%0]\t\n"            // Get App initial MSP for bootloader.
            "msr   msp, r0\t\n"             // Set the main stack pointer to the applications MSP.
            "ldr   r0, [%0, #0x04]\t\n"     // Load Reset handler into R0.
    
            "movs  r4, #0xFF\t\n"           // Move ones to R4.
            "sxtb  r4, r4\t\n"              // Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF.
    
            "mrs   r5, IPSR\t\n"            // Load IPSR to R5 to check for handler or thread mode.
            "cmp   r5, #0x00\t\n"           // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
            "bne   isr_abort\t\n"           // If not zero we need to exit current ISR and jump to reset handler of bootloader.
    
            "mov   lr, r4\t\n"              // Clear the link register and set to ones to ensure no return.
            "bx    r0\t\n"                  // Branch to reset handler of bootloader.
    
            "isr_abort:  \t\n"
    
            "mov   r5, r4\t\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\t\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\t\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\t\n"              // Reverse byte order to put 0x21 as MSB.
            "push  {r4-r7}\t\n"             // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
    
            "movs  r4, #0x00\t\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\t\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\t\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\t\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}\t\n"             // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
    
            "movs  r0, #0xF9\t\n"           // Move the execution return command into register, 0xFFFFFFF9.
            "sxtb  r0, r0\t\n"              // Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9.
            "bx    r0\t\n"                  // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
            ".align\t\n"
            :: "r" (start_addr)             // Argument list for the gcc assembly. start_addr is %0.
            :  "r0", "r4", "r5", "r6", "r7" // List of register maintained manually.
        );
    }

    Best regards

    bjørn

Related