Cortex M7 va a HardFault inmediatamente después de reiniciar

2

Estoy usando el procesador ATSAME70Q21 con una CPU Cortex-M7.

Mi entorno de desarrollo es Atmel Studio 7. Estoy usando la placa de demostración SAME70 XPlained.

Estoy creando algo de código en lenguaje ensamblador.

Como prueba, configuro una tabla de vectores al inicio de flash (0x00400000) que contiene el valor del puntero de pila inicial y la ubicación del Reset_Handler en los primeros 8 bytes.

Cuando presiono restablecer en el depurador, el contador del programa se configura en 0x0040001C (que es correcto). La instrucción en la ubicación 0x0040001C es 0xC046, que es un NOP. Las siguientes instrucciones en la memoria también son NOP.

Si paso un paso el depurador (ejecuta el NOP), el contador del programa salta inmediatamente al HardFault_Handler.

No veo cómo la ejecución de un NOP puede generar un HardFault.

¿Cuáles son los motivos probables para que se produzca un HardFault inmediatamente después del restablecimiento?

No creo que sea un problema de hardware ya que estoy usando una placa de demostración disponible y el problema no se produce si utilizo uno de los proyectos de ejemplo C en Atmel studio.

    
pregunta user4574

2 respuestas

2

Tenga en cuenta que el lenguaje ensamblador está definido por la herramienta, no por el objetivo. Para gnu assembler, hasta la fecha para el pulgar, particularmente con estos córtex-ms donde necesita / desea obtener la tabla de vectores correcta que comenzó con este problema:

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop
reset:
    b reset
loop:
    b loop

ensamble / desarme y puede ver el problema

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000010    andeq   r0, r0, r0, lsl r0
   8:   00000014    andeq   r0, r0, r4, lsl r0
   c:   00000014    andeq   r0, r0, r4, lsl r0

00000010 <reset>:
  10:   eafffffe    b   10 <reset>

00000014 <loop>:
  14:   eafffffe    b   14 <loop>

(una vez que sepa que las direcciones vectoriales deben ser la dirección orred con 1)

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop
.thumb_func
reset:
    b reset
loop:
    b loop

la solución mínima pero limpia es agregar .thumb_func para indicar que la próxima etiqueta que encuentre es una función de pulgar, no una dirección genérica.

(debe vincular no solo ensamblar / desensamblar)

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000011    andeq   r0, r0, r1, lsl r0
   8:   00000012    andeq   r0, r0, r2, lsl r0
   c:   00000012    andeq   r0, r0, r2, lsl r0

00000010 <reset>:
  10:   e7fe        b.n 10 <reset>

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

y que fija un vector.

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop
.thumb_func
reset:
    b reset
.thumb_func
loop:
    b loop

y que arregló el otro.

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000011    andeq   r0, r0, r1, lsl r0
   8:   00000013    andeq   r0, r0, r3, lsl r0
   c:   00000013    andeq   r0, r0, r3, lsl r0

00000010 <reset>:
  10:   e7fe        b.n 10 <reset>

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

agrega algo de C

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word fun
.thumb_func
reset:
    b reset
.thumb_func
loop:
    b loop

void fun ( void )
{
}

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000011    andeq   r0, r0, r1, lsl r0
   8:   00000013    andeq   r0, r0, r3, lsl r0
   c:   00000015    andeq   r0, r0, r5, lsl r0

00000010 <reset>:
  10:   e7fe        b.n 10 <reset>

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

00000014 <fun>:
  14:   4770        bx  lr
  16:   46c0        nop         ; (mov r8, r8)

Luego mire la salida del compilador de C

    .cpu arm7tdmi
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file   "fun.c"
    .text
    .align  1
    .p2align 2,,3
    .global fun
    .arch armv4t
    .syntax unified
    .code   16
    .thumb_func
    .fpu softvfp
    .type   fun, %function
fun:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    @ sp needed
    bx  lr
    .size   fun, .-fun
    .ident  "GCC: (GNU) 8.2.0"

(no importa que haya predeterminado el arm7, pero hay .thumb_func)

algunos lenguajes de ensamblaje tendrán directivas como proc o function u otras cosas similares que usas para declarar una etiqueta como una función en lugar de solo una dirección genérica para algo, por lo que cuando pruebes otros ensambladores para armar puedes encontrar eso.

si esto usa el ensamblador gnu, entonces .thumb_func debería solucionarlo. En su mente, pensaría que si esto es una orden con una que no sea una adición con una en caso de que la dirección sea correcta, podría terminar rompiéndola con la noción de agregar una a todo.

Lo mismo ocurre con la función de puntero de función en C, debes tener cuidado y puedes encontrarlo.

void loop ( void );
void hop ( unsigned int );
void fun ( void )
{
    unsigned int x;
    x = (unsigned int) loop;
    hop(x);
}
void more_fun ( void )
{
    unsigned int x;
    unsigned short z[4];
    z[0]=0x46c0;
    z[1]=0x4770;
    z[2]=0x46c0;
    z[3]=0x46c0;
    x = (unsigned int) z;
    hop(x);
}

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

00000014 <hop>:
  14:   4700        bx  r0

00000018 <fun>:
  18:   b510        push    {r4, lr}
  1a:   4803        ldr r0, [pc, #12]   ; (28 <fun+0x10>)
  1c:   f7ff fffa   bl  14 <hop>
  20:   bc10        pop {r4}
  22:   bc01        pop {r0}
  24:   4700        bx  r0
  26:   46c0        nop         ; (mov r8, r8)
  28:   00000013    andeq   r0, r0, r3, lsl r0

0000002c <more_fun>:
  2c:   b500        push    {lr}
  2e:   4b05        ldr r3, [pc, #20]   ; (44 <more_fun+0x18>)
  30:   b083        sub sp, #12
  32:   9300        str r3, [sp, #0]
  34:   4b04        ldr r3, [pc, #16]   ; (48 <more_fun+0x1c>)
  36:   4668        mov r0, sp
  38:   9301        str r3, [sp, #4]
  3a:   f7ff ffeb   bl  14 <hop>
  3e:   b003        add sp, #12
  40:   bc01        pop {r0}
  42:   4700        bx  r0
  44:   477046c0    
  48:   46c046c0    

no es que vayas a ejecutar un código así, pero puedes ver el problema.

y una solución

void more_fun ( void )
{
    unsigned int x;
    unsigned short z[4];
    z[0]=0x46c0;
    z[1]=0x4770;
    z[2]=0x46c0;
    z[3]=0x46c0;
    x = (unsigned int) z;
    hop(x|1);
}

0000002c <more_fun>:
  2c:   b500        push    {lr}
  2e:   4b06        ldr r3, [pc, #24]   ; (48 <more_fun+0x1c>)
  30:   b083        sub sp, #12
  32:   9300        str r3, [sp, #0]
  34:   4b05        ldr r3, [pc, #20]   ; (4c <more_fun+0x20>)
  36:   4668        mov r0, sp
  38:   9301        str r3, [sp, #4]
  3a:   2301        movs    r3, #1
  3c:   4318        orrs    r0, r3
  3e:   f7ff ffe9   bl  14 <hop>
  42:   b003        add sp, #12
  44:   bc01        pop {r0}
  46:   4700        bx  r0
  48:   477046c0 
  4c:   46c046c0 

lo que puedes hacer muy bien es escribir un gestor de arranque y para obtener el salto al código cargado correctamente, necesitas resolverlo, hay una función de puntero que puedes probar y tal vez no escribir un poco de asm, pero te recomiendo el asm y orr con uno, es más probable que funcione.

    
respondido por el old_timer
1

En la arquitectura ARM, las instrucciones de la máquina nunca pueden residir en una dirección impar. Por lo tanto, el bit de dirección menos significativo de todas las instrucciones de salto o tablas vectoriales sería 0. En lugar de tener un bit de 0 constante, los diseñadores optaron por reutilizar el bit de dirección 0 para indicar la codificación del conjunto de instrucciones para un objetivo de salto / rama. Un 0 indica que el objetivo es un código ARM de 32 bits y un 1 indica que el objetivo es un código Thumb.

Parece que el problema fue que los valores en la tabla de vectores eran pares (lo que indica al procesador que los vectores apuntaban al código ARM de 32 bits). Pero el núcleo ARM en el ATSAME70Q21 es compatible con el ISA Armv7-M (que es un conjunto de instrucciones Thumb). El ensamblador estaba generando instrucciones para el pulgar pero saltando a ese código con el bit T desactivado causó un HardFault (incluso al ejecutar un NOP).

Noté el problema mientras miraba el documento ARM ID120114 página B1-573 que muestra el registro de registro de estado de programa de ejecución (EPSR). El bit T (bit-24) indica si el procesador está en el modo Pulgar (1) o en el modo ARM de 32 bits (0).

Mi código de ensamblaje originalmente parecía ...

 .section .vectors
 vec_table:
    .word _estack
    .word Reset_Handler
...
.section .text
Reset_Handler:
    NOP
    NOP

Para garantizar que el bit 0 de la dirección se configuró en la tabla vectorial, lo cambié a ...

 .section .vectors
 vec_table:
    .word _estack
    .word Reset_Handler + 0x00000001
...
.section .text
Reset_Handler:
    NOP
    NOP

Ahora todo funciona.

Aparentemente los programas en C funcionaron bien porque el compilador ya estaba haciendo esto. Pero en mis programas de ensamblaje necesito hacer esto manualmente.

    
respondido por el user4574

Lea otras preguntas en las etiquetas