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);
}

  • I am confused by your post. You stated that you added LED controls to the bootloader and application code and both execute correctly but didn't state the operating condition.  Was this done by starting the bootloader and having it launch the application or was this done on each project in a stand alone manner?

  • Well, the application starts at 0x10000 right, so there is no way for that to run on its own. I did not modify the start address.

    I will send you the projects I used for testing, to see if there is any difference when you use those.

  • Did you actually change something in the project or the code (beyond adding the LED commands)?  This does seem to work now but I have a couple of issues:

    1.) adding a break point on the call to nrf_bootloader_app_start() results in a hard fault.  Why is that?

    2.) There seems to be some connection between calling functions before nrf_bootloader_app_start() and a hard fault result.  For example, a call to my CRC function to validate the application always results in a hard fault but replacing it with a simple query of a flag in flash memory (for example, the application size) seems to work all the time.

    3.) Are there any conditions on using the nrf_bootloader_app_start() call?

    Thank you

  • I commented out a few header files that was not included in the packets you provided, for instance crc.h in util.c and nvmem.c, and radio.h in mvmem.c.

    1. I'm not able to reproduce this. I just tested, and I hit the breakpoint on nrf_bootloader_app_start(), and I'm able to continue to a breakpoint on nrf_bootloader_app_start_final() which is hit twice before I get a HardFault. I assume this HardFault is expected as the bootloader have branched outside of the debugged application at this point.

    2. Do this happen with every functions, or just the CRC functions? Is the HardFault happening immediately, or when jumping to the application. Could this be related to that the application works with crc header commented out on my test applications?

    3. Not that I can think of, It should just take care of interrupt handling and then jump to the application.

  • I have resolved these issues and moved onto the full bootloader, which uses a USB driver based on one of your examples.  The bootloader is setup to reside in flash at 0x0000-0xFFFF (64 KB) and the application at 0x20000-0x3FFFF (128 KB).  I used this setup to test downloading the application into flash over USB and it worked correctly, including updating the CRC in a parameters block up at 0xFF000.  I then download a new boot loader using the emulator in SES and it seems to be placing some NRF functions in the SD space identified in the Nordic literature (below 0x27000).  I specifically noted that the USB driver did not function after programming flash, even though it was used in the image download.  The section placement macros were set for FLASH_START=0x0 and FLASH_SIZE=0x10000 so I would expect SES to force the boot loader image in that region. I resolved this by moving the application start up to 0x30000 (inside the region reserved for the application the Nordic memory map) but am concerned that I don't fully understand how the sections are placed in flash.   Is there another factor controlling the placement of these NRF functions in flash memory? Do I need to adhere to the Nordic memory map defined in the boot loader user guide?

Related