This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP432E401Y: custom bootloader cannot jump to app

Part Number: MSP432E401Y


Tool/software:

We need to implement firmware upgrades via UART6 without using the BSL Rocket. To achieve this, we built a custom BSL starting from an empty CCS project.

To support dual-image storage, we modified the flash origin in the linker script so that:

  • BSL starts at 0x00000000
  • Application starts at 0x00004000

To jump from the BSL to the application, we use the following call:

        NVIC->ICER[0] = 0xffffffff;
        NVIC->ICER[1] = 0xffffffff;
        NVIC->ICER[2] = 0xffffffff;
        NVIC->ICER[3] = 0xffffffff;

        SCB->VTOR = APP_START_ADDRESS;
        uint32_t sp = *((uint32_t*)APP_START_ADDRESS);
        __set_MSP(sp);
        ((void (*)(void)) APP_START_ADDRESS)();

However, we’re currently unable to jump from BSL to the application, or from the application back to the BSL.

Both images execute correctly when placed at 0x00000000, but once we adjust the origin using the linker script, the images fail to run. We’d like to understand why and determine what steps are needed to resolve the issue. Thank you.

  • To support dual-image storage, we modified the flash origin in the linker script so that:

    • BSL starts at 0x00000000
    • Application starts at 0x00004000

    Hi,

      Are saying the application is already programmed to 0x4000? Can you double check the application is indeed programmed there? You can use the CCS memory browser to view the flash at 0x4000.

     Another thing to check is how did you build your application? Did you build your application to start at 0x4000? You said the application works at 0x0. I suppose you change the linker for the application so that it is allocated to 0x4000. 

    To jump from the BSL to the application, we use the following call:

            NVIC->ICER[0] = 0xffffffff;
            NVIC->ICER[1] = 0xffffffff;
            NVIC->ICER[2] = 0xffffffff;
            NVIC->ICER[3] = 0xffffffff;

            SCB->VTOR = APP_START_ADDRESS;
            uint32_t sp = *((uint32_t*)APP_START_ADDRESS);
            __set_MSP(sp);
            ((void (*)(void)) APP_START_ADDRESS)();

    Looks you did not use C:\ti\simplelink_msp432e4_sdk_4_20_00_12\source\ti\devices\msp432e4\boot_loader\bl_startup_ccs.s to jump from the BSL to the application. Looks like your bootloader is written in C rather in assembly as in bl_startup_ccs.s.

    Here is one example that jumps from the bootloader (C code based) to the application starting at 0x8100. 

    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    #include <stdio.h>
    
    /* driverlib header files */
    #include <inc/hw_memmap.h>
    #include <driverlib/uart.h>
    #include <inc/hw_types.h>
    #include <driverlib/sysctl.h>
    #include <driverlib/gpio.h>
    #include <driverlib/pin_map.h>
    #include <inc/hw_nvic.h>
    
    static char message_buffer[128];
    
    /* Address of the reset vector of the application control is transferred to */
    #define APP_RESET_VECTOR_ADDR 0x8100
    const uint32_t *const app_reset_vector = (const uint32_t *) APP_RESET_VECTOR_ADDR;
    
    /*
     * Transfer control to application in flash, given a pointer to the reset vector which gives the initial stack pointer
     * and the program counter to transfer to.
     *
     * Defined as a naked function as doesn't return and uses inline assembler assuming the reset_vector argument is passed in r0
     */
    __attribute__((naked)) static void transfer_to_app (const uint32_t *const reset_vector)
    {
        asm (" ldr r1,[r0]");      /* Initial stack pointer */
        asm (" mov sp,r1");
        asm (" ldr r0, [r0, #4]"); /* Initial program counter */
        asm (" bx r0");
    }
    
    
    int main(void)
    {
        uint32_t SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |SYSCTL_CFG_VCO_480), 120000000);
    
        /* Enable UART to allow an indication that the boot loader is running */
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        while (!SysCtlPeripheralReady (SYSCTL_PERIPH_GPIOA))
        {
        }
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        while (!SysCtlPeripheralReady (SYSCTL_PERIPH_UART0))
        {
        }
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
        UARTConfigSetExpClk(UART0_BASE, SysClock, 115200,
                            UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE);
    
        /* Report that the bootloader is running */
        snprintf (message_buffer, sizeof (message_buffer),
                  "\n\rBootloader running\n\rTransfering to application with SP=0x%08x PC=0x%08x\n\r",
                  app_reset_vector[0], app_reset_vector[1]);
        const size_t message_len = strlen (message_buffer);
        size_t message_index;
        for (message_index = 0; message_index < message_len; message_index++)
        {
            UARTCharPut (UART0_BASE, message_buffer[message_index]);
        }
    
        /* Wait until all the message has completed transmission before resetting the UART */
        while (UARTBusy (UART0_BASE))
        {
        }
    
        /* Reset the peripherals used to output the above message */
        SysCtlPeripheralReset (SYSCTL_PERIPH_UART0);
        SysCtlPeripheralDisable (SYSCTL_PERIPH_UART0);
        SysCtlPeripheralReset (SYSCTL_PERIPH_GPIOA);
        SysCtlPeripheralDisable (SYSCTL_PERIPH_GPIOA);
    
        /* Set the vector table to the beginning of the application in flash.
         * For the TI-RTOS based application this doesn't seem necessary for the application to run, since TI-RTOS
         * defaults to a minimum set of exception vectors in flash before moving the vector table to RAM and installing
         * device specific interrupt handlers. */
        HWREG(NVIC_VTABLE) = (int32_t) app_reset_vector;
    
        /* Transfer control to the application - doesn't return */
        transfer_to_app (app_reset_vector);
    
    	return 0;
    }

  • Hi,

    Thank you for the response.

    Yes, we’ve verified that the application is correctly programmed at the specified location using the CCS Memory Browser.
    To change location of the application, we updated the linker script to set the flash origin to 0x4000. Is there anything else we should modify?

    We tested the example and set APP_RESET_VECTOR_ADDR to 0x4000, but unfortunately, the application still doesn’t run as expected. This time, it gets stuck at the default handler in startup_msp432e401y_ccs.c when running transfer_to_app(app_reset_vector).

    By the way, we didn't find inc/hw_memmap.h and inc/hw_types.h.

  • Hi,

    it gets stuck at the default handler in startup_msp432e401y_ccs.c when running transfer_to_app(app_reset_vector).

    Where is it stuck?  Can you check if NVIC_VTABLE at address 0xE000ED08 is updated with 0x4000?

    HWREG(NVIC_VTABLE) = (int32_t) app_reset_vector;

    /* Transfer control to the application - doesn't return */
    transfer_to_app (app_reset_vector);

    By the way, we didn't find inc/hw_memmap.h and inc/hw_types.h

    The example code is for TM4C129 which is the same silicon as MSP432E but using TivaWare rather than MSP432E SDK. For MSP432E. For MSP432E, you can replace the #includes section with the below.

    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    #include <stdio.h>
    #include "ti/devices/msp432e4/driverlib/driverlib.h"

  • Hi,

    The example you provided is now running successfully; however, our own code is still not functioning as expected.
    We suspect the issue might be related to the flash programming process.
    We'll continue investigating—thank you so much for your support!