Electronic – Stack error when jumping from bootloader to application

armbootloadercregistertexas instruments

I have been trying to jump from my bootloader to an application for a while now but I can not figure out what is going wrong. I am hoping someone here might be able to help me.
I am using Texas Instrument's CC2652 and Texas Instrument's Code Composer Studio to develop and flash the bootloader. The code below is the code I use to jump to the application.

void startApp(uint32_t prgEntry) {
    static uint32_t temp;
    temp = prgEntry;
    // Reset the stack pointer,
    temp +=4;
    asm(" LDR SP, [R0, #0x0] ");
    ((void (*)(void))(*((uint32_t*)temp)))();
}

When I run this code I will end up in the FaultISR. Here I can use the debugger to look at the registers. In the CFSR (Configurable Fault Status Register) I can see that the STKERR and the IBUSERR bits are set.

prgEntry Is 0x2E000 in this case. I defined this address as the start of the application in the linker command files of both the bootloader and the application as well. I copied the command files further below.

The application I am trying to jump to is first uploaded to the device in intel hex format and looks like this https://pastebin.com/DAerFkXr. Texas Instruments has a Flash Programmer application for Windows with which I can read a range of addresses from the internal flash of the device. I went through the intel hex file by hand and compared it to what I saw in the Flash Programmer application and I believe everything is put in the correct location. I posted a screenshot of the output of the Flash Programmer below the command files.

When I flash the application to the device using Code Composer Studio without my bootloader I can see that the application runs normally so I know it is not a problem with the application.

Application command file:

--stack_size=1024   /* C stack is also used for ISR stack */

--heap_size=256

/* Retain interrupt vector table variable                                    */
--retain=g_pfnVectors
/* Override default entry point.                                             */
--entry_point resetISR
/* Allow main() to take args                                                 */
--args 0x8
/* Suppress warnings and errors:                                             */
/* - 10063: Warning about entry point not being _c_int00                     */
/* - 16011, 16012: 8-byte alignment errors. Observed when linking in object  */
/*   files compiled using Keil (ARM compiler)                                */
--diag_suppress=10063,16011,16012

#define BOOT_BASE               0x0
#define BOOT_SIZE               0x8000
#define FLASH_BASE              0x2E000
#define FLASH_SIZE              0x2A000
#define RAM_BASE                0x20000000
#define RAM_SIZE                0x14000
#define GPRAM_BASE              0x11000000
#define GPRAM_SIZE              0x2000


/* System memory map */

MEMORY
{
    /* Application stored in and executes from internal flash */
    FLASH (RX) : origin = FLASH_BASE, length = FLASH_SIZE
    /* Application uses internal RAM for data */
    SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
    /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
    (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
    GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
}

/* Section allocation in memory */

SECTIONS
{
    .intvecs        :   > FLASH_BASE
    .text           :   > FLASH
    .TI.ramfunc     : {} load=FLASH, run=SRAM, table(BINIT)
    .const          :   > FLASH
    .constdata      :   > FLASH
    .rodata         :   > FLASH
    .binit          :   > FLASH
    .cinit          :   > FLASH
    .pinit          :   > FLASH
    .init_array     :   > FLASH
    .emb_text       :   > FLASH
    .ccfg           :   > FLASH (HIGH)

    .vtable         :   > SRAM
    .vtable_ram     :   > SRAM
     vtable_ram     :   > SRAM
    .data           :   > SRAM
    .bss            :   > SRAM
    .sysmem         :   > SRAM
    .stack          :   > SRAM (HIGH)
    .nonretenvar    :   > SRAM

    .gpram          :   > GPRAM
}

Bootloader command file:

--stack_size=1024   /* C stack is also used for ISR stack */

--heap_size=256

/* Retain interrupt vector table variable                                    */
--retain=g_pfnVectors
/* Override default entry point.                                             */
--entry_point resetISR
/* Allow main() to take args                                                 */
--args 0x8
/* Suppress warnings and errors:                                             */
/* - 10063: Warning about entry point not being _c_int00                     */
/* - 16011, 16012: 8-byte alignment errors. Observed when linking in object  */
/*   files compiled using Keil (ARM compiler)                                */
--diag_suppress=10063,16011,16012

#define BOOT_BASE               0x0
#define BOOT_SIZE               0x8000
#define FLASH_BASE              0x2E000
#define FLASH_SIZE              0x2A000
#define RAM_BASE                0x20000000
#define RAM_SIZE                0x14000
#define GPRAM_BASE              0x11000000
#define GPRAM_SIZE              0x2000


/* System memory map */

MEMORY
{
    /* The bootloader will be stored and executed from this location in internal flash */
    BOOT (RX)  : origin = BOOT_BASE, length = BOOT_SIZE
    /* Application stored in and executes from internal flash */
    FLASH (RX) : origin = FLASH_BASE, length = FLASH_SIZE
    /* Application uses internal RAM for data */
    SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
    /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
    (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
    GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
}


/* Section allocation in memory */

SECTIONS
{
    .intvecs        :   > BOOT_BASE
    .text           :   > BOOT
    .TI.ramfunc     : {} load=BOOT, run=SRAM, table(BINIT)
    .const          :   > BOOT
    .constdata      :   > BOOT
    .rodata         :   > BOOT
    .binit          :   > BOOT
    .cinit          :   > BOOT
    .pinit          :   > BOOT
    .init_array     :   > BOOT
    .emb_text       :   > BOOT
    .ccfg           :   > BOOT (HIGH)

    .vtable         :   > SRAM
    .vtable_ram     :   > SRAM
     vtable_ram     :   > SRAM
    .data           :   > SRAM
    .bss            :   > SRAM
    .sysmem         :   > SRAM
    .stack          :   > SRAM (HIGH)
    .nonretenvar    :   > SRAM

    .gpram          :   > GPRAM
}

Screenshot from Flash Programmer:

Description of STKERR:
Stacking from exception has caused one or more bus faults. The SP is still adjusted and the values in the context area on the stack might be incorrect. BFAR is not written.

Description of IBUSERR:
Instruction bus error flag. This flag is set by a prefetch error. The fault stops on the instruction, so if the error occurs under a branch shadow, no fault occurs. BFAR is not written.

Edit:
This is the disassembly of the startApp function:

void startApp(uint32_t prgEntry) {
startApp():
    push       {r3, r14}
    str        r0, [r13]
temp = prgEntry;
    ldr        r1, [pc, #0x18]
    ldr        r0, [r13]
    str        r0, [r1]
temp +=4;
    ldr        r1, [pc, #0x14]
    ldr        r0, [r1]
    adds       r0, r0, #4
    str        r0, [r1]
asm(" LDR SP, [R0, #0x0] ");
    ldr.w      r13, [r0]
((void (*)(void))(*((uint32_t*)temp)))();
    ldr        r0, [pc, #8]
    ldr        r0, [r0]
    ldr        r0, [r0]
    blx        r0
}

Best Answer

I think you should single-step through this code to see what is in R0 just before the LDR SP, [R0]. Remember that the value in R0 is interpreted as an address, and the instruction fetches whatever is at that address and shoves it into the stack pointer.

It appears to me that your code is taking the value 0x0002E004 and using it as an address. Whatever value is stored at 0x0002E004 is then stored in the stack pointer. Is that what you intended?

Depending on endianess, it looks like the SP gets 0x1F1F1F00 or 0x001F1F1F.