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

Custom bootloader: Can't reach interrupt handler

Hello,

We're trying to create a custom bootloader from scratch and we're struggling with a weird behavior. This is for the nRF52840 and the bootloader doesn't use the softdevice. 

The bootloader is located at address 0xF8000 which is written in UICR.NRFFW[0]. Right now, we are able to execute code from the bootloader, including successfully jumping to the application (normally located after the softdevice). The problem is that we want to use some interrupts, but whenever we'd expect to jump to an interrupt handler, instead our debugger stops at address 0x00. At this point it seems we're in hardfault (ICSR->VECTATIVE is 3) but for some reason we never reach a breakpoint placed in our Hardfault_Handler.  

The only SDK files we're building are: gcc_startup_nrf52840.S, nrfx_gpiote.c and system_nrf52840.c. We also have a linker script very similar to the one of the secure_dfu_uart example. 

The bootloader's main'code looks like this, and its goal is simply to jump to the app when we toggle the GPIO 16:

#include <stdbool.h>
#include "nrfx_gpiote.h"

static volatile bool jump_to_app = false;

void HardFault_Handler(void) __attribute__((naked));
void HardFault_Handler(void)
{
    NVIC_SystemReset();
}

void pin_handler(uint32_t pin, nrf_gpiote_polarity_t action)
{
    if ((action == NRF_GPIOTE_POLARITY_TOGGLE) && (pin == 16))
    {
        jump_to_app = true;
    }
}

int main(void)
{
    // Some code to set GPIO as input/output
    
    nrfx_gpiote_in_config_t nrfx_gpiote_cfg = {.sense           = NRF_GPIOTE_POLARITY_TOGGLE,
                                               .pull            = NRF_GPIO_PIN_PULLUP,
                                               .is_watcher      = false,
                                               .hi_accuracy     = false,
                                               .skip_gpio_setup = true};
    nrfx_gpiote_init();
    nrfx_gpiote_in_init(HWP_P1, &nrfx_gpiote_cfg, pin_handler);
    nrfx_gpiote_in_event_enable(16, true);
    
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

    while (1)
    {
        __SEV(); // Set the internal event register
        __WFE(); // Clear event register
        __WFE(); // Enter sleep

        if (jump_to_app)
        {
            // Boot the application
            bootloader_start_app();
        }

}

I checked the MAP file and I can see that isr_vector is correctly located at address 0xF8000. I can also see from my debug session that at address 0xF8058 (isr_vector + 0x58 is the GPIOTE Handler address) there is 0xFA7D9, which is the address of the GPIOTE_Handler (in my MAP file it's 0xFA7D8). If I put a breakpoint at this address, it's never reached. 

I also checked if interrupts were enabled or blocked. BASEPRI is 0, PRIMASK is 0, NVIC_ICER0.SETENA_6 is 1 (should be GPIOTE) and GPIOTE.INTENSET.PORT is 1, 

Do you have any idea what could cause this behavior ? I have the feeling that this could be simple, or that my analysis is wrong somewhere, but I'm really stuck on that one. 

Work environment details:

Windows 10 64-bit, build with arm gcc  7.2.1 20170904, based on SDK 15.1 and using SD s140. The nRF52840 are engineering C. 

Related