Error de pila al saltar del gestor de arranque a la aplicación

He estado tratando de pasar de mi cargador de arranque a una aplicación por un tiempo, pero no puedo entender qué es lo que está mal. Espero que alguien aquí pueda ayudarme.
Estoy usando CC2652 de Texas Instrument y Code Composer Studio de Texas Instrument para desarrollar y actualizar el gestor de arranque. El siguiente código es el código que uso para saltar a la aplicación.

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

Cuando ejecute este código, terminaré en FaultISR. Aquí puedo usar el depurador para mirar los registros. En el CFSR (Registro de estado de falla configurable) puedo ver que los bits STKERR e IBUSERR están establecidos.

prgEntry es 0x2E000 en este caso. Definí esta dirección como el inicio de la aplicación en los archivos de comando del enlazador tanto del gestor de arranque como de la aplicación. Copié los archivos de comando más abajo.

La aplicación a la que intento acceder primero se carga en el dispositivo en formato hexadecimal de Intel y tiene este aspecto: https://pastebin.com/DAerFkXr . Texas Instruments tiene una aplicación Flash Programmer para Windows con la que puedo leer un rango de direcciones desde el flash interno del dispositivo. Revisé el archivo hexadecimal de Intel a mano y lo comparé con lo que vi en la aplicación Flash Programmer y creo que todo está colocado en la ubicación correcta. Publiqué una captura de pantalla de la salida del Programador Flash debajo de los archivos de comando.

Cuando actualizo la aplicación en el dispositivo usando Code Composer Studio sin mi cargador de arranque, puedo ver que la aplicación se ejecuta normalmente, así que sé que no es un problema con la aplicación.

Archivo de comandos de la aplicación:

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

Archivo de comando del cargador de arranque:

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

Captura de pantalla de Flash Programmer:

Descripción de STKERR: el apilamiento desde una excepción ha causado una o más fallas de bus. El SP todavía está ajustado y los valores en el área de contexto en la pila pueden ser incorrectos. BFAR no está escrito.

Descripción de IBUSERR: indicador de error del bus de instrucciones. Este indicador se establece mediante un error de captación previa. La falla se detiene en la instrucción, por lo que si el error ocurre bajo una sombra de rama, no ocurre ninguna falla. BFAR no está escrito.

Editar:
Este es el desmontaje de la función startApp:

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
}
Tiene que desensamblar startAppy ver qué está pasando, luego ver si el código de máquina generado coincide con la convención de llamadas de su compilador en particular. En general, el código C no existe en un estado predecible hasta que se inicializa el SP, ni tampoco coexiste en la misma función con el código que configura manualmente el SP.
@lundin Agregué el desmontaje de la función startApp, ¿ves si hay algún problema? Soy nuevo en esto y no sé mucho sobre montaje.
No conozco esta convención de llamadas. Si tampoco lo sabe, entonces no puede hacerlo; debe estudiar esto en la documentación del compilador. No puede simplemente convertir un número entero a un puntero de función y llamarlo.
@lundin Esta función se tomó directamente del ejemplo de Boot Image Manager proporcionado por Texas Instruments para esta placa. Entonces, creo que esta función debería funcionar, ¿verdad? Modifiqué prgEntry, por supuesto, para que apuntara a la primera dirección del firmware.

Respuestas (1)

Creo que deberías pasar un solo paso por este código para ver qué hay R0justo antes del archivo LDR SP, [R0]. Recuerde que el valor in R0se interpreta como una dirección, y la instrucción obtiene lo que sea que esté en esa dirección y lo coloca en el puntero de la pila.

Me parece que su código toma el valor 0x0002E004 y lo usa como dirección. Cualquier valor que se almacene en 0x0002E004 se almacena en el puntero de pila. ¿Es eso lo que pretendías?

Dependiendo de la frecuencia, parece que el SP obtiene 0x1F1F1F00 o 0x001F1F1F.

De hecho, eso es lo que está sucediendo, pero no lo que pretendía. ¿Podría decirme cómo puedo cambiarlo para que apunte a la dirección en lugar del valor almacenado en la dirección? Soy bastante nuevo en el montaje.
El proceso habitual para obtener un valor almacenado en una dirección arbitraria en la memoria requiere dos instrucciones LDR. El primer LDR usa el modo de direccionamiento literal para obtener la dirección de los datos. La dirección se almacenaría en la memoria del programa, entre funciones, en algún lugar cerca de la instrucción LDR. El direccionamiento literal se convierte entonces en un direccionamiento relativo a la PC. Una vez que haya obtenido la dirección de los datos, un segundo LDR que use esa dirección obtendrá el valor real de los datos.
Pude arreglarlo usando "mov SP, R0" en su lugar y también lo usé para PC. Esto parece solucionar el problema STKERR e IBUSERR y, al usar el depurador, ahora puedo repasar todos los pasos en la aplicación que estoy cargando, sin embargo, todavía termino en FaultISR() eventualmente con PRECISERR y con 0xFFFFFFE0 en BFAR. Así que no está completamente arreglado pero me ha ayudado.