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

Getting hard fault when launching application from custom boot loader

I am building a custom boot loader to work with out application on a custom board based on the UBLOX NINA-302 module.  The boot loader will use USB as the only connection and that will use a USB driver we already developed for the application.  The idea is to use a simple boot loader without a soft device or MBR that will interface directly to our host application over USB.   The boot loader will start at 0x0 (FLASH_START=0x0) and a length of 0x10000 (FLASH_SIZE=0x10000).  The application is built in SES with FLASH_START set to offset address (0x10000) and FLASH_SIZE set to 0x20000.  The application was loaded into flash using J-Link before starting up the boot loader solution.

I am launching the application using the same funcitons that the Nordic boot loader wit some modifications specific to the implementation (files included below).  I have tried this with the standard "start_app' with optimization and also by replacing the function with an updated version I found on the developer blog.  The application seems to be starting up since I can track it using breakpoints up to "start_app" and then single set through the application code after the jump to the new reset handler.  I can single step through code but hit a hard fault whenever I let it free run.  The application has been stripped down to just blinky code with no interrupt service.  I even stripped the boot loader down to just the launch code but the application still crashes.  Any ideas?

/**
 * Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <stdint.h>
#include "nrf.h"
#include "nrf_bootloader_app_start.h"
//#include "nrf_bootloader_info.h"
#include "nrf_log.h"
//#include "nrf_dfu_mbr.h"
#include "nrf_log_ctrl.h"

#include "g4_global_defs.h"

// Do the final stages of app_start. Protect flash and run app. See nrf_bootloader_app_start_final.c
void nrf_bootloader_app_start_final(uint32_t start_addr);

void nrf_bootloader_app_start(void)
{
//    uint32_t start_addr = MBR_SIZE; // Always boot from end of MBR. If a SoftDevice is present, it will boot the app.
    uint32_t start_addr = (uint32_t)FLASH_START_APP;
    NRF_LOG_DEBUG("Running nrf_bootloader_app_start with address: 0x%08x", start_addr);
    uint32_t err_code;

    // Disable and clear interrupts
    // Notice that this disables only 'external' interrupts (positive IRQn).
    NRF_LOG_DEBUG("Disabling interrupts. NVIC->ICER[0]: 0x%x", NVIC->ICER[0]);

    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

/* not using DFU for G4
    err_code = nrf_dfu_mbr_irq_forward_address_set();
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Failed running nrf_dfu_mbr_irq_forward_address_set()");
    }
*/

    NRF_LOG_FLUSH();
    nrf_bootloader_app_start_final(start_addr);
}
/**
 * Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include "sdk_config.h"
#include "nrf_bootloader_app_start.h"
#include <stdint.h>
#include "nrf.h"
#include "nrf_peripherals.h"
//#include "nrf_bootloader_info.h"
//#include "nrf_dfu_types.h"
//#include "nrf_dfu_utils.h"
//#include "nrf_dfu_settings.h"
#include "nrf_assert.h"
#include "nrf_log.h"
#include "sdk_config.h"


/*
// Enabling the NRF_BOOTLOADER_READ_PROTECT define is untested.
// Read-protecting the bootloader requires certain functions to run from RAM.
// In GCC and SES this is done automatically when the define is enabled. You will
// get warnings which can be ignored.
// In Keil you must change project settings to run the entire file from RAM.
#ifndef NRF_BOOTLOADER_READ_PROTECT
#define NRF_BOOTLOADER_READ_PROTECT 0
#endif
*/


#define HANDLER_MODE_EXIT 0xFFFFFFF9 // When this is jumped to, the CPU will exit interrupt context
                                     // (handler mode), and pop values from the stack into registers.
                                     // See ARM's documentation for "Exception entry and return".
#define EXCEPTION_STACK_WORD_COUNT 8 // The number of words popped from the stack when
                                     // HANDLER_MODE_EXIT is branched to.


/**@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));
}
*/

//replacement to avoid optimization bug with above version
void jump_to_addr(uint32_t new_msp, uint32_t new_lr, uint32_t addr)
{
    __set_MSP(new_msp);
    ((void (*)(void))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.
}

/*
#if NRF_BOOTLOADER_READ_PROTECT
#ifdef __ICCARM__
__ramfunc
#elif  defined ( __GNUC__ ) || defined ( __SES_ARM )
__attribute__((noinline, long_call, section(".data")))
#elif  defined ( __CC_ARM )
#warning "Keil requires changes to project settings to run this file from RAM. Ignore this warning if configuration has been made."
#endif
#endif
ret_code_t nrf_bootloader_flash_protect(uint32_t address, uint32_t size, bool read_protect)
{
    if ((size & (CODE_PAGE_SIZE - 1)) || (address > BOOTLOADER_SETTINGS_ADDRESS))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

#if defined(ACL_PRESENT)

    // Protect using ACL.
    static uint32_t acl_instance = 0;

    uint32_t const wmask  = (ACL_ACL_PERM_WRITE_Disable << ACL_ACL_PERM_WRITE_Pos);
    uint32_t const rwmask = wmask | (ACL_ACL_PERM_READ_Disable << ACL_ACL_PERM_READ_Pos);
    uint32_t const mask   = read_protect ? rwmask: wmask;

    do
    {
        if (acl_instance >= ACL_REGIONS_COUNT)
        {
            return NRF_ERROR_NO_MEM;
        }

        NRF_ACL->ACL[acl_instance].ADDR = address;
        NRF_ACL->ACL[acl_instance].SIZE = size;
        NRF_ACL->ACL[acl_instance].PERM = mask;

        acl_instance++;

    } while (NRF_ACL->ACL[acl_instance - 1].ADDR != address
          || NRF_ACL->ACL[acl_instance - 1].SIZE != size
          || NRF_ACL->ACL[acl_instance - 1].PERM != mask); // Check whether the acl_instance has been used before.

#elif defined (BPROT_PRESENT)

    // Protect using BPROT. BPROT does not support read protection.
    uint32_t pagenum_start = address / CODE_PAGE_SIZE;
    uint32_t pagenum_end   = pagenum_start + ((size - 1) / CODE_PAGE_SIZE);

    for (uint32_t i = pagenum_start; i <= pagenum_end; i++)
    {
        uint32_t config_index = i / 32;
        uint32_t mask         = (1 << (i - config_index * 32));

        switch (config_index)
        {
            case 0:
                NRF_BPROT->CONFIG0 = mask;
                break;
            case 1:
                NRF_BPROT->CONFIG1 = mask;
                break;
#if BPROT_REGIONS_NUM > 64
            case 2:
                NRF_BPROT->CONFIG2 = mask;
                break;
            case 3:
                NRF_BPROT->CONFIG3 = mask;
                break;
#endif
        }
    }

#endif

    return NRF_SUCCESS;
}
*/


#if NRF_BOOTLOADER_READ_PROTECT
#ifdef __ICCARM__
__ramfunc
#elif  defined ( __GNUC__ ) || defined ( __SES_ARM )
__attribute__((noinline, long_call, section(".data")))
#elif  defined ( __CC_ARM )
#warning "Keil requires changes to project settings to run this file from RAM. Ignore this warning if configuration has been made."
#endif
#endif
void nrf_bootloader_app_start_final(uint32_t vector_table_addr)
{
    ret_code_t ret_val;

/*
    // Protect MBR & bootloader code and params pages.
    if (NRF_BOOTLOADER_READ_PROTECT)
    {
        ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, NRF_BOOTLOADER_READ_PROTECT);
    }

    // Size of the flash area to protect.
    uint32_t area_size;

    area_size = BOOTLOADER_SIZE + NRF_MBR_PARAMS_PAGE_SIZE;
    if (!NRF_BL_DFU_ALLOW_UPDATE_FROM_APP && !NRF_BL_DFU_ENTER_METHOD_BUTTONLESS && !NRF_DFU_TRANSPORT_BLE)
    {
        area_size += BOOTLOADER_SETTINGS_PAGE_SIZE;
    }

    ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR,
                                           area_size,
                                           NRF_BOOTLOADER_READ_PROTECT);

    if (!NRF_BOOTLOADER_READ_PROTECT && (ret_val != NRF_SUCCESS))
    {
        NRF_LOG_ERROR("Could not protect bootloader and settings pages, 0x%x.", ret_val);
    }
    APP_ERROR_CHECK(ret_val);

    ret_val = nrf_bootloader_flash_protect(0,
                    nrf_dfu_bank0_start_addr() + ALIGN_TO_PAGE(s_dfu_settings.bank_0.image_size),
                    false);

    if (!NRF_BOOTLOADER_READ_PROTECT && (ret_val != NRF_SUCCESS))
    {
        NRF_LOG_ERROR("Could not protect SoftDevice and application, 0x%x.", ret_val);
    }
    APP_ERROR_CHECK(ret_val);
*/

    // Run application
    app_start(vector_table_addr);
}

Parents Reply Children
  • Any luck with the debug using the files I sent you?

  • Hi,

    I tried adding some LED controls to both bootloader and application code, and both seems to execute correctly. 

    In main_bl.c, I set LED4 on the nRF52840 DK:

    nrf_gpio_cfg_output(16);
    nrf_gpio_pin_clear(16);

    Similar, in main.c I set LED3:

    nrf_gpio_cfg_output(15);
    nrf_gpio_pin_clear(15);

    Both light up when I run the DK as normal (not in debug interface mode).

    Can you check if you are seeing similar behavior?

    Best regards,
    Jørgen

  • I'm running this on a custom board with SES using JLINK.  What do you mean by "DK as normal"?

  • I just implemented a similar "blinky" in both solutions, loaded the "app" at 0x10000 using SES and then ran the boot loader but  all I get is a reboot into the bootloader application.  I verified that the blinky worked in both applications before trying to boot the application from the bootloader.  The bootoader lights come on but the code seem to consistently reboot at 0x0 instead of 0x10000.

  • I have verified that my bootloader is placing 8 bytes of data outside of the flash range reserved in the "section placement macros" in SES. The boot loader was set to start at 0x00000 with a size of 0x10000 and the application at 0x10000 with a size of 0x20000.  This arrangement doesn't work since the boot_loader overwrites part of the application image if it is loaded second.  Interestingly the 8 bytes are not contiguous but seem to be related to functions calls from the boot loader to NRF routines (2 examples follow).

    static void lfclk_request(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();
    //    APP_ERROR_CHECK(err_code);
        nrf_drv_clock_lfclk_request(NULL);
    }

    static void init_power_clock(void)
    {
    ret_code_t ret;

    /* Initializing power and clock */
    ret = nrf_drv_clock_init();
    APP_ERROR_CHECK(ret);
    ret = nrf_drv_power_init(NULL);
    APP_ERROR_CHECK(ret);
    nrf_drv_clock_hfclk_request(NULL);
    nrf_drv_clock_lfclk_request(NULL);
    while (!(nrf_drv_clock_hfclk_is_running() && nrf_drv_clock_lfclk_is_running()))
    {
    /* Just waiting */
    }

    /* Avoid warnings if assertion is disabled */
    UNUSED_VARIABLE(ret);

    return;
    }//end function

    I moved the application flash to start at 0x30000 and verified that the boot loader does not interfere with that range.  Next I tried to load the full application with a "blinky" in the main line code.  This application initializes the system to use the radio (ESB stack), USB, timers, SPI and various software constructs like FIFO's before running the blinky in main.  I found that the system hits a hard fault when calling nrf_timer_task_trigger() in nrfx_timer_enable(), resulting in a constant reboot cycle.  The code runs correctly if nrf_timer_task_trigger() is commented out.  Any ideas what could be causing this?

    I can send you the updated projects by email but would rather not post them in the forum.

Related