aplicación de cargador de arranque en el controlador de brazo

6

Estoy usando un microcontrolador con procesador ARM Cortex M4 y estoy desarrollando una aplicación de cargador de arranque.

  1. Compilador: GCC- GNU ARM compiler
  2. Tamaño de flash de código: 1MB
  3. Interfaz para descargar el código de la aplicación: clase CDM ACM USB
  4. Un software de aplicación en la PC para transferir el archivo binario del código de la aplicación a MCU o USB
  5. Tengo dos códigos: a. código principal (código de inicio), b. Código de aplicación
  6. En el código de la aplicación, la función run_my_app () está escrita.
  7. El flujo del código principal es el siguiente:

        reset->
        initialize MCU
        initialize USB
        boot status = CLEAR;
        while(1)
        {
                wait for command from USB : write/ erase
                if(boot status == BOOT_COMPLETE)
                {
                    call a function name run_my_app() from application code;
                }
        }
    
        if erase command received : 
        erase the application code at a flash location 0x0020000
    
        if write command received : 
        Write the application code to flash location 0x0020000
        after completion successful write,
                 boot status == BOOT_COMPLETE;
    

Mi pregunta es ¿cómo debo asignar la función run_my_app () del código de la aplicación al código de inicio? ¿Cómo puedo obtener la dirección de la función run_my_code () para que, con el puntero a la función, pueda llamar a esa función?

Hola a todos, tengo que modificar el punto de entrada del código de la aplicación a la ubicación 0x0020000 en flash desde la ubicación 0x0000000 usando el script del vinculador. El archivo de script del enlazador generado actualmente es:

MEMORY
{
   FLASH (rx)         : ORIGIN = 0x00000000, LENGTH = 0x0100000  /*   1M */
   RAM (rwx)          : ORIGIN = 0x20000000, LENGTH = 0x0030000  /* 192K */
   DATA_FLASH (rx)    : ORIGIN = 0x40100000, LENGTH = 0x0004000  /*  16K */
   QSPI_FLASH (rx)    : ORIGIN = 0x60000000, LENGTH = 0x4000000  /*  64M, Change in QSPI section below also */
}

ENTRY(Reset_Handler)

SECTIONS
{
  .text :
  {
    __ROM_Start = .;
    /* Even though the vector table is not 256 entries (1KB) long, we still allocate that much
     * space because ROM registers are at address 0x400 and there is very little space
     * in between. */
    KEEP(*(.vectors))
    __Vectors_End = .;
    __Vectors_Size = __Vectors_End - __Vectors;
    __end__ = .;

    /* ROM Registers start at address 0x00000400 */
    . = __ROM_Start + 0x400;
    KEEP(*(.rom_registers*))

    /* Reserving 0x100 bytes of space for ROM registers. */
    . = __ROM_Start + 0x500;

    *(.text*)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    KEEP(*(.eh_frame*))

    __ROM_End = .;
  } > FLASH = 0xFF
   .............
   ..........

Agregando algunos detalles a mi implementación:

Se define una estructura 'CodeInfoStruct' tanto en el código de inicio como en el de la aplicación.

struct CodeInfoStruct

{

uint32_t RunCodeFunctionAddress;

};

En el código de la aplicación:

Aquí, se crea una instancia de estructura en una ubicación fija '0x0020100' y la dirección de la función 'run_my_app' se almacena allí.

const struct CodeInfoStruct CodeInformation __attribute__((section(".ARM.__at_0x0020100")));

const struct CodeInfoStruct CodeInformation =

{

(uint32_t)run_my_app

};

Además, en el archivo del vinculador, la dirección de inicio de la ROM se modifica a 0x0020000:

MEMORY 

{

   FLASH (rx)         : ORIGIN = 0x00020000, LENGTH = 0x0100000  /*   1M */

   RAM (rwx)          : ORIGIN = 0x20000000, LENGTH = 0x0030000  /* 192K */

   DATA_FLASH (rx)    : ORIGIN = 0x40100000, LENGTH = 0x0004000  /*  16K */

   QSPI_FLASH (rx)    : ORIGIN = 0x60000000, LENGTH = 0x4000000  /*  64M, Change in QSPI section below also */

}

En el código de arranque:

el puntero de estructura de 'COdeInfoStruct' está definido.

struct CodeInfoStruct* pCodeInfo = 0x0020100;

Y luego, la función 'run_my_code' se llama desde el código de inicio de la siguiente manera:

void (*trgt_fnc)(void);

trgt_fnc = (void (*)())(pCodeInfo->RunCodeFunctionAddress);
trgt_fnc(); 

Pero, obtengo un valor cero en este 'trgt_fnc'. ¿Es una forma correcta de compartir la dirección de la función run_my code? ¿Qué está mal aquí?

    
pregunta Nama

3 respuestas

1

Gracias por tu esfuerzo ...

Como he indicado en mi pregunta, he creado una instancia de una estructura en una ubicación fija 0x0020100. utilizando el siguiente método.

const struct CodeInfoStruct CodeInformation __attribute__((section(".ARM.__at_0x0020100")));

Pero, he observado que la estructura no está almacenada en esta ubicación en particular. Por lo tanto, el acceso a la dirección requerida del puntero de función que se almacenó en esta estructura en esta ubicación fue incorrecto. La lógica de acceder al código de la aplicación desde el código de inicio funciona perfectamente.

La forma correcta de escribir la estructura (cualquier dato / variable) en una ubicación específica es la siguiente:

struct CodeInfoStruct __attribute__((section (".ArrSectioninflash"))) CodeInformation =  
{
    &my_run_code     
};

Y agregue la sección "ArrSectioninflash" en el archivo del vinculador de la siguiente manera:

.myBufBlock 0x00021000 :    
{
            KEEP(*(.ArrSectioninflash))     
} > FLASH
    
respondido por el Nama
3

La forma en que lo hacemos en uno de nuestros productos es así (el código se basa en el compilador IAR, por lo que los pragmas pueden ser diferentes para usted):

Definimos una estructura que contiene información importante de la parte de la aplicación (que se llama mainpart en nuestros proyectos):

#pragma pack(push,1)
    struct softwareRevision_t {
        uint8_t VersionSystem;
        uint8_t VersionFunction;
        uint8_t VersionError;
        uint8_t VersionCustomer;
        uint8_t BuildNumber;
    };
#pragma pack(pop)

#pragma pack(push,1)
    struct mainInfoStruct_t {
        softwareRevision_t softwareRevision;
        uint32_t startFunctionAddress;
        uint8_t reserve[27];
        uint32_t bootControl;
    };
#pragma pack(pop)

Esto contiene entre otras cosas el uint32_t startFunctionAddress . Ahora para que esta estructura sea útil, tienes que hacer varias cosas:

  1. Inicialízalo con valores significativos en la aplicación
  2. Hágalo const para que pueda colocarse en flash
  3. Dígale al vinculador que coloque la estructura siempre exactamente en la misma ubicación cada vez que compile su proyecto
  4. comparte la ubicación y la estructura entre la aplicación y la parte de arranque

En la parte de la aplicación:

Para compartir la ubicación, creamos un archivo de encabezado que se incluye en ambas partes con una simple definición:

#define MAIN_INFO_MEMORY_POSITION (0x0807FFD4)

En un archivo .cpp de nuestra aplicación, creamos una instancia de la estructura que se coloca en consecuencia:

#pragma location = MAIN_INFO_MEMORY_POSITION
extern __root const mainInfoStruct_t mainInfoStruct =
{
    {
            1U,     // mainInfo.softwareRevision.VersionSystem
            0U,     // mainInfo.softwareRevision.VersionFunction
            0U,     // mainInfo.softwareRevision.VersionError
            0U,     // mainInfo.softwareRevision.VersionCustomer
            0U      // mainInfo.softwareRevision.BuildNumber
    },

    reinterpret_cast<uint32_t>((&_start)),  // mainInfo.startFunctionAddress
    {0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U},     // reserve

    0xF0F0FFFFU     // mainInfo.bootControl
};

Varias cosas suceden aquí: #pragma location = MAIN_INFO_MEMORY_POSITION le dice al enlazador dónde colocar la siguiente estructura.

El __root le dice a nuestro enlazador que este símbolo debe mantenerse, incluso si no está referenciado por el código de la aplicación (que no lo es). Tiene que ser const , de lo contrario no se colocaría en flash (si no termina en flash, intente agregar static ).

Así que con esto hemos hecho todo en la aplicación.

En la parte de arranque:

La parte de arranque necesita acceso a la estructura de información de la parte de la aplicación. Envolvimos una clase alrededor de todo esto, pero básicamente un puntero será suficiente:

volatile mainInfoStruct_t* pMainInfo = reinterpret_cast<mainInfoStruct_t*>(MAIN_INFO_MEMORY_POSITION);

Aquí usamos volatile para asegurarnos de que el compilador no optimizará el acceso al flash. Con esto ahora puedes saltar al código de la aplicación con un reparto feo:

(reinterpret_cast<void (*)(void)>(pMainInfo->startFunctionAddress))();

Notas :

En nuestra aplicación, realizamos una verificación de CRC en toda la parte de la aplicación que incluye mainInfoStruct, por lo que no saltamos a nirvana, algo salió mal con la actualización.

Lo primero que hacemos en nuestra aplicación es establecer el punto de pila en el valor que se espera que comience (puede obtenerlo del archivo de configuración del vinculador o de su tabla de vectores de interrupción). De lo contrario, podría terminar con un desbordamiento de pila y corromper sus otros datos.

Este no es un ejemplo mínimo, pero también podría dar una pista sobre lo que podría ser útil en una estructura compartida.

Usamos C ++, perdón por el reinterpret_cast s.

    
respondido por el Arsenal
0

Dos posibles enfoques:

  • Convierta el archivo descargado en un binario ELF y analice sus encabezados para determinar el "punto de entrada" o un símbolo específico.

  • Simplemente haga que el cargador de arranque salte a un punto fijo específico en el archivo descargado, generalmente el inicio. Escriba un script de vinculador para colocar el punto de entrada en el lugar correcto. (Por lo general, esto termina como un archivo "bin").

respondido por el pjc50

Lea otras preguntas en las etiquetas