Detectant errors greus a ARM

 

Sovint treballant amb sistemes encastats ens trobem amb errors d’origen desconegut que es poden provocar per múltiples causes. Així, per exemple, una divisió per zero, accés incorrecte a una zona de memòria o accés a la memòria fora de rang.

Aquests casos poden ser molt difícils de trobar si son casos esporàdics, però l’arquitectura ARM te unes característiques que ajuden a detectar-los i trobar-los. En síntesi, el cortex-M llença una interrupció molt prioritària anomenada HardFault_Handler() quan succeeix un problema greu del que el processador no pot recupera-se, com una divisió per zero, un accés il·legal a memòria, etc. Abans de cridar a l’excepció, la CPU guarda tot de valors claus a diferents registres, i així per exemple en el registre PC s’hi emmagatzema l’adreça de la instrucció executada, així que, en principi, només cal anar a aquella posició de memòria per veure quin ha estat el codi que ha causat el problema. També s’emmagatzema el valor de retorn (la instrucció següent a l’executada que ha causat l’error) al registre LR.

Així doncs, es pot reescriure la ISR per obtenir les dades que ens informi sobre què ha passat per ajudar-nos a obtenir pistes de quin codi està fallant.

Detectant errors

A l’exemple del repositori hi ha un codi que genera diferents errors segons la funció que es cridi i una implementació de HardFault_Handler(). Aquesta funció està escrita en assemblador, però el que cal veure és que es crida a la funció my_HardFault_Handler() que és qui en realitat fa tota la feina i és la que cal entendre.

void my_HardFault_Handler(uint32_t *stack) {
 printf("Error Handler\r\n");
 printf("SCB->HFSR = 0x%08lx\r\n", (uint32_t) SCB->HFSR);

if ((SCB->HFSR & (1 << 30)) != 0) {
 printf("Forced Hard Fault\r\n");
 printf("SCB->CFSR = 0x%08lx\r\n", SCB->CFSR);

if ((SCB->CFSR & 0x02000000) != 0) {
 printf("Divide by zero\r\n");
 }
 if ((SCB->CFSR & 0x01000000) != 0) {
 printf("Unaligned\r\n");
 }
 if ((SCB->CFSR & 0x00010000) != 0) {
 printf("Undefined\r\n");
 }
 ...
}

A la primera part de la ISR es treu per la consola de debug la causa de l’excepció (bus fault, memory access, divide by zero, etc.).

Tot seguit es treu per la mateixa consola els valors dels registres que hi ha a l’stack per tenir dades que ens permetin localitzar l’error:

void my_HardFault_Handler(uint32_t *stack) {
 ...
 printf("sp = 0x%08lX\r\n", (uint32_t) stack);
 printf("r0 = 0x%08lX\r\n", stack[0]);
 printf("r1 = 0x%08lX\r\n", stack[1]);
 printf("r2 = 0x%08lX\r\n", stack[2]);
 printf("r3 = 0x%08lX\r\n", stack[3]);
 printf("r12 = 0x%08lX\r\n", stack[4]);
 printf("lr = 0x%08lX\r\n", stack[5]);
 printf("pc = 0x%08lX\r\n", stack[6]);
 printf("psr = 0x%08lX\r\n" stack[7]);
 ...
}

Per últim, es crida la macro DEBUG_BREAK, que està definida com una instrucció en assemblador (BKPT #01) que posa el core en mode Debug i atura l’execució en aquest punt. Així, si tenim un debugger connectat, veurem com l’execució s’atura en aquest punt i torna el control a la nostra eina.

HardFault_Console

Si anem a la finestra Disassembly i anem a la posició de memòria que indica el registre PC (0x6AE a l’exemple), veurem que apunta a una instrucció assemblador sdiv, que es corresponent amb una divisió. Si mirem el codi anterior, podem deduir que a la posició de memòria R7+0x8 (corresponent a la variable b) s’hi ha emmagatzemat un 0 (instruccions a 0x6A6 i 0x6A8) i aquesta variable es fa servir a la divisió com a divisor, causant l’error.

HardFault_Dissassembly

També cal comentar que les diferents funcions que generen errors son les següents:

  • WrongfunctionDiv0() causa una divisió per zero.
  • WrongfunctionAlign() causa un error d’accés a memòria fora d’alineament.
  • WrongfunctionWrongMemory() causa un error per accés fora dels límits de la memòria.
  • fp() causa un intent d’executar a la posició 0x0000\_0000 de memòria..

Per saber-ne més

 

 

 

 

Anuncis

Deixa un comentari

Fill in your details below or click an icon to log in:

WordPress.com Logo

Esteu comentant fent servir el compte WordPress.com. Log Out /  Canvia )

Google photo

Esteu comentant fent servir el compte Google. Log Out /  Canvia )

Twitter picture

Esteu comentant fent servir el compte Twitter. Log Out /  Canvia )

Facebook photo

Esteu comentant fent servir el compte Facebook. Log Out /  Canvia )

S'està connectant a %s

Aquest lloc utilitza Akismet per reduir els comentaris brossa. Apreneu com es processen les dades dels comentaris.