Encontrar el origen de una falla dura utilizando HardFault_Handler extendido

2

He estado golpeando algunos errores en el firmware que he creado con FreeRTOS en una SAMD21 (ARM Cortex-M0) MCU.

Así que tomé una acción adicional para descubrir la causa y finalmente me topé con este artículo en Code_Red señalando el fragmento mencionado abajo. Sin embargo, en esta etapa no me queda claro cómo usar los números que he extraído después de que este método es alcanzado.

Obviamente tengo muchas ubicaciones de memoria, sin embargo, ¿cómo puedo sacar conclusiones sobre qué línea de código causó el problema de acuerdo con estas ubicaciones?

Por cierto, la pila de llamadas no ha sido útil y solo tiene una sola que apunta a los puntos de interrupción actuales en el HardFault_handlerC()

Gracias de antemano por tu ayuda,

/**
 * HardFault_HandlerAsm:
 * Alternative Hard Fault handler to help debug the reason for a fault.
 * To use, edit the vector table to reference this function in the HardFault vector
 * This code is suitable for Cortex-M3 and Cortex-M0 cores
 */

// Use the 'naked' attribute so that C stacking is not used.
__attribute__((naked))
void HardFault_HandlerAsm(void){
        /*
         * Get the appropriate stack pointer, depending on our mode,
         * and use it as the parameter to the C handler. This function
         * will never return
         */

        __asm(  ".syntax unified\n"
                        "MOVS   R0, #4  \n"
                        "MOV    R1, LR  \n"
                        "TST    R0, R1  \n"
                        "BEQ    _MSP    \n"
                        "MRS    R0, PSP \n"
                        "B      HardFault_HandlerC      \n"
                "_MSP:  \n"
                        "MRS    R0, MSP \n"
                        "B      HardFault_HandlerC      \n"
                ".syntax divided\n") ;
}

/**
 * HardFaultHandler_C:
 * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
 * as the parameter. We can then read the values from the stack and place them
 * into local variables for ease of reading.
 * We then read the various Fault Status and Address Registers to help decode
 * cause of the fault.
 * The function ends with a BKPT instruction to force control back into the debugger
 */
void HardFault_HandlerC(unsigned long *hardfault_args){
        volatile unsigned long stacked_r0 ;
        volatile unsigned long stacked_r1 ;
        volatile unsigned long stacked_r2 ;
        volatile unsigned long stacked_r3 ;
        volatile unsigned long stacked_r12 ;
        volatile unsigned long stacked_lr ;
        volatile unsigned long stacked_pc ;
        volatile unsigned long stacked_psr ;
        volatile unsigned long _CFSR ;
        volatile unsigned long _HFSR ;
        volatile unsigned long _DFSR ;
        volatile unsigned long _AFSR ;
        volatile unsigned long _BFAR ;
        volatile unsigned long _MMAR ;

        stacked_r0 = ((unsigned long)hardfault_args[0]) ;
        stacked_r1 = ((unsigned long)hardfault_args[1]) ;
        stacked_r2 = ((unsigned long)hardfault_args[2]) ;
        stacked_r3 = ((unsigned long)hardfault_args[3]) ;
        stacked_r12 = ((unsigned long)hardfault_args[4]) ;
        stacked_lr = ((unsigned long)hardfault_args[5]) ;
        stacked_pc = ((unsigned long)hardfault_args[6]) ;
        stacked_psr = ((unsigned long)hardfault_args[7]) ;

        // Configurable Fault Status Register
        // Consists of MMSR, BFSR and UFSR
        _CFSR = (*((volatile unsigned long *)(0xE000ED28))) ;   

        // Hard Fault Status Register
        _HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ;

        // Debug Fault Status Register
        _DFSR = (*((volatile unsigned long *)(0xE000ED30))) ;

        // Auxiliary Fault Status Register
        _AFSR = (*((volatile unsigned long *)(0xE000ED3C))) ;

        // Read the Fault Address Registers. These may not contain valid values.
        // Check BFARVALID/MMARVALID to see if they are valid values
        // MemManage Fault Address Register
        _MMAR = (*((volatile unsigned long *)(0xE000ED34))) ;
        // Bus Fault Address Register
        _BFAR = (*((volatile unsigned long *)(0xE000ED38))) ;

        __asm("BKPT #0\n") ; // Break into the debugger

}
    
pregunta Mehrad

3 respuestas

4

Entonces, aquí está la parte divertida: puede ser imposible citar exactamente qué línea está lanzando la falla. La razón es que un error en su código puede estar causando que aparezca la falla en otro lugar donde, o bien, el error podría estar destruyendo toda la información de estado en el sistema, lo cual es muy bueno. Sin embargo, lo que realmente ayudaría es ver su base de código completa: incluidos los scripts del vinculador y el código de inicio.

Sin embargo, en general, si está terminando en un territorio de falla dura, estas son las primeras cosas que verificaría:

  • Fallos causados al tratar de asignar dinámicamente la memoria cuando no hay un montón definido por su enlazador. Lo que sucede aquí es que alguna función llama a malloc (o uno de sus primos) y la biblioteca está fallando porque no hay suficiente espacio en el montón para asignar memoria, por lo que bloquea el programa. Esta es una posibilidad real para usted, está utilizando un RTOS & la mayoría de los scripts del enlazador de vainilla no tienen espacio de almacenamiento asignado. Vea esto: enlace

  • Las fallas causadas por hacer algo tonto, como escribir datos, pasan al final de una matriz. Esto puede ser realmente fácil de hacer si está usando matemática para generar índices de matriz o usando punteros a elementos directamente. Lo que (puede) suceder aquí es si sus comprobaciones de límites tienen errores, cuando escribe datos en su matriz, de hecho, ¡puede que solo esté sobrescribiendo todo! Si esto no causa un error directamente (por ejemplo, escribir en una ubicación protegida o de solo lectura), puede que solo se rompa la pila. Luego salta a una ubicación de basura, y probablemente ejecuta una instrucción no válida y luego falla.

También echaría un vistazo a este documento, que está relacionado con su publicación de Code Red. Aunque las instrucciones son para ARM Cortex-M3 y ARM Cortex-M4 , el método para interpretar los resultados es el mismo.
Debugging Hard Fault & Otras excepciones

  

Usando los valores de registro

     

El primer registro de interés es el contador del programa. En el codigo   arriba, la variable pc contiene el valor del contador del programa. Cuando el   la falla es una falla precisa, el PC tiene la dirección de la instrucción   que se estaba ejecutando cuando ocurrió la falla dura (u otra falla). Cuando   la falla es una falla imprecisa, entonces se requieren pasos adicionales para   Encuentra la dirección de la instrucción que causó la falla.

     

Para encontrar la instrucción en la dirección contenida en la variable de PC,   o bien ...

     
  1. Abra una ventana de código de ensamblaje en el depurador e ingrese manualmente la dirección para ver las instrucciones de ensamblaje en esa dirección, o

  2.   
  3. Abra la ventana de punto de interrupción en el depurador y defina manualmente un punto de interrupción de ejecución o acceso en esa dirección. Con el descanso   conjunto de puntos, reinicie la aplicación para ver qué línea de código   la instrucción se relaciona con.

  4.   

Conocer la instrucción que se estaba ejecutando cuando el fallo   ocurrido le permite saber qué otros valores de registro son también de   interesar. Por ejemplo, si la instrucción estaba usando el valor de R7 como   una dirección, entonces el valor de R7 debe ser conocido. Además, examinando   el código de ensamblaje y el código C que generó el código de ensamblaje,   mostrará lo que realmente tiene R7 (podría ser el valor de una variable,   por ejemplo).

Esas son solo mis dos razones principales. Si publica toda su base de código, probablemente podamos brindarle más ayuda directa. Buena suerte!

    
respondido por el pgvoorhees
1

El libro de Joseph Yiu sobre MCU ARM, La guía definitiva para la ARM Cortex-M0 , proporciona una forma estándar de grabar el contexto justo antes de la falla.

Muchas veces se deben a que operan en un periférico sin haberle sincronizado el reloj.

    
respondido por el dannyf
1

Si estás siguiendo Depurando Hard Fault & Otras excepciones publicadas por FreeRTOS y usando Atmel Studio , para poder realizar el siguiente paso,

  
  1. Abra una ventana de código de ensamblaje en el depurador e ingrese manualmente la dirección para ver las instrucciones de ensamblaje en esa dirección.
  2.   

puede utilizar Vista de desmontaje para saltar a la línea que el Contador de programas (PC), o de hecho cualquiera de las otras direcciones, señalaba, en el momento en que ocurrió la falla.

En Atmel Studio 7 se puede acceder desde

  

Debug → Windows → Disassembly o Ctrl + Alt + D durante una sesión de depuración.

    
respondido por el Mehrad

Lea otras preguntas en las etiquetas

Comentarios Recientes

Función 50b6257e para compilar.26-45-2015 - Corrección de errores.26-41-2015 - Se agregaron herramientas de Windows 10: Agregue el SDK [DllBuildingRequest] avanzado, BuildKeyArrows [FeatureInfo], etc.26 -31-2015 - Arreglos diversos.26-29-2015 - Se corrigieron errores de verificación de 32 bits contra 64 bits en el proceso de certificados simples26-23-2015 - Versión experimental actualizada de GALISS.26-13-2015 - DragN Folder API Cambios Agrega el módulo Carpeta DragN a su objeto de PowerShell Ahora puede incluir... Lees verder