Atributos del archivo de inicio de ARM frente a los argumentos de la línea de comandos de GCC

4

Los archivos de inicio para MCU de STM32 Cortex-M, para la mayoría de las cadenas de herramientas de GCC, a menudo combinan los archivos de ensamblaje de inicio de Atollic TrueStudio con bibliotecas HAL, como por ejemplo en mi caso, STM32CubeF4 .

Estoy viendo startup_stm32f407xx.s , y comienza con una sección con el siguiente aspecto

.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb

Quiero volver a escribir el script de inicio del ensamblador en C, como parte del aprendizaje del proceso de inicio de Cortex-M.

Al compilar con la GCC ARM Toolchain , o quizás con cualquier otra herramienta basada en GCC, significa que tengo que transferir estos argumentos, que se encuentran en el archivo de inicio del ensamblaje, a los argumentos de la línea de comando para arm-none-eabi-gcc :

arm-none-eabi-gcc -mcpu=cortex-m4 --mfpu=softvfp --mthumbz ...

¿Estas líneas de ensamblador se correlacionan con los argumentos de GCC respectivos, o se usan para algo completamente diferente?

    

1 respuesta

6

Esta página ofrece una buena descripción general de las directivas "especiales" de ensambladores de la GNU ARM ensamblador.

Como sospechaba, estas directivas se usan básicamente en la forma de los conmutadores del compilador y deberían tener su representación al compilar las fuentes.

Los utilizados:

  • %código%: Esta directiva establece la Sintaxis del conjunto de instrucciones como se describe en la sección ARM-Instruction-Set . (unificado: ARM y THUMB usan la misma sintaxis)
  • .syntax [unified | divided] : seleccione el procesador de destino. Los valores válidos para el nombre son los mismos que para la opción de línea de comandos -mcpu. Al especificar .cpu se eliminan las extensiones de arquitectura seleccionadas previamente.
  • .cpu cortex-m4 : seleccione la unidad de punto flotante para ensamblar. Los valores válidos para el nombre son los mismos que para la opción de línea de comandos -mfpu
  • .fpu softvfp : esto realiza la misma acción que .code 16.
  • .thumb : esta directiva selecciona el conjunto de instrucciones que se está generando. El valor 16 selecciona Pulgar, con el valor 32 seleccionando ARM.

Algunos, si no todos, también pueden configurarse a través de la interfaz de línea de comandos. Supongo que lo incluyeron en el archivo de ensamblaje para asegurarse de que se ensambla exactamente de la forma en que se pensaba. Las directivas en línea tienen mayor prioridad a medida que la línea de comando cambia.

En cuanto a la descripción del proceso de inicio, no sé si realmente me preguntaste esto, pero sentí ganas de escribirlo:

Desde el punto de vista del hardware, es parte del núcleo y se describe en ARMv7-M Architecture Reference Manual (disponible en el momento del registro). En la sección B1.5.5 se explica el comportamiento de restablecimiento.

  

La activación del restablecimiento hace que el procesador abandone el estado de ejecución actual sin guardarlo. En la anulación de   reinicio, todos los registros que tienen un valor de reinicio definido contienen ese valor y el procesador realiza las acciones descritas   por el pseudocódigo TakeReset ().

// TakeReset()
// ============
TakeReset()
CurrentMode = Mode_Thread;
PRIMASK<0> = '0'; /* priority mask cleared at reset */
FAULTMASK<0> = '0'; /* fault mask cleared at reset */
BASEPRI<7:0> = Zeros(8); /* base priority disabled at reset */
if HaveFPExt() then /* initialize the Floating Point Extn */
CONTROL<2:0> = '000'; /* FP inactive, stack is Main, thread is privileged */
CPACR.cp10 = '00';
CPACR.cp11 = '00';
FPDSCR.AHP = '0';
FPDSCR.DN = '0';
FPDSCR.FZ = '0';
FPDSCR.RMode = '00';
FPCCR.ASPEN = '1';
FPCCR.LSPEN = '1';
FPCCR.LSPACT = '0';
FPCAR = bits(32) UNKNOWN;
FPFSR = bits(32) UNKNOWN;
for i = 0 to 31
S[i] = bits(32) UNKNOWN;
else
CONTROL<1:0> = '00'; /* current stack is Main, thread is privileged */
for i = 0 to 511 /* all exceptions Inactive */
ExceptionActive[i] = '0';
ResetSCSRegs(); /* catch-all function for System Control Space reset */
ClearExclusiveLocal(ProcessorID()); /* Synchronization (LDREX* / STREX*) monitor support */
ClearEventRegister(); /* see WFE instruction for more details */
for i = 0 to 12
R[i] = bits(32) UNKNOWN;
bits(32) vectortable = VTOR<31:7>:'0000000';
SP_main = MemA_with_priv[vectortable, 4, AccType_VECTABLE] AND 0xFFFFFFFC<31:0>;
SP_process = ((bits(30) UNKNOWN):'00');
LR = 0xFFFFFFFF<31:0>; /* preset to an illegal exception return value */
tmp = MemA_with_priv[vectortable+4, 4, AccType_VECTABLE];
tbit = tmp<0>;
APSR = bits(32) UNKNOWN; /* flags UNPREDICTABLE from reset */
IPSR<8:0> = Zeros(9); /* Exception Number cleared */
EPSR.T = tbit; /* T bit set from vector */
EPSR.IT<7:0> = Zeros(8); /* IT/ICI bits cleared */
BranchTo(tmp AND 0xFFFFFFFE<31:0>); /* address of reset service routine */
  

ExceptionActive [*] es una matriz conceptual de bits de marca activa para todas las excepciones, lo que significa que tiene marcas activas para la   excepciones del sistema de prioridad fija, las excepciones del sistema de prioridad configurable y las interrupciones externas. los   los indicadores activos para las excepciones de prioridad fija son solo conceptuales y no se requiere que existan en un registro del sistema.

Los pasos que debe seguir para implementar el comportamiento de inicio en el software depende del compilador y el vinculador que utilice, por lo que probablemente no exista una solución general.

Por lo general, consiste en crear una estructura de tablas vectoriales y de reinicio y rellenarla con los valores correctos. El stackpointer es el primer valor, se debe inicializar con la dirección de RAM donde residirá la pila. Este valor generalmente se define en el archivo de control del enlazador como símbolo exportado. El siguiente valor es la dirección de tu código de inicio, así que solo pones el puntero a la función en .code 16 o lo que sea que esté allí.

Lo que sigue es una larga lista de punteros de función que apuntan a los controladores de servicio de interrupción individuales. Debe tener cuidado de no omitir posiciones en la tabla solo porque el ISR no está implementado. Inicialice esos valores con c_startup() (las cosas pueden salir mal) o con un "controlador no implementado" que consiste en un 0 (forma segura).

Después de eso tienes que llenar el while(true) con vida, que depende del compilador. Las cosas típicas a realizar son: inicializar la RAM con valores globales, inicializar la FPU, llamar a los constructores de objetos estáticos y finalmente saltar a c_startup() .

Como último paso, debe decirle al vinculador que coloca su superestructura recién creada al principio de la tabla de vectores (generalmente la primera dirección del flash, pero esto puede variar según la forma en que se obtenga el vector). en el dispositivo).

    
respondido por el Arsenal

Lea otras preguntas en las etiquetas