This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Exception Handling on ARM

Hi,

Im working on a data-abort handler for the LPC2194 and I've found the Roger Lynx article's here:
www.eetimes.com/.../How-to-use-ARM-s-data-abort-exception

I've found the article good and I want to adapt the example he give in a C code (with inlines directives).
The problem is that I don't understand how to get all registers of the processor in a C table?

Here is the code I've already wrote (working with the line commented in the Handler):

#include <LPC21xx.h>
#include <stdio.h>
#include <stdlib.h>


#define varerror   (*((volatile unsigned long *) 0xEF05006C))
// Définition d'une fonction en dehors de la zone programme
#define Fonction_err() asm volatile ( " B 0x00050000")

#define ENTRER_ISR_Dabt()       asm volatile (" sub   lr, lr,#4\n" \ 
                                                                                  " STMFD sp!,{r0-r12,lr}")

#define SORTIR_ISR_Dabt()       asm volatile (" LDMFD sp!, {r0-r12,pc}^")



void C_Abort_handler() __attribute__((interrupt("Abort")));

// Initialisation de l'UART0 sans interruptions void initUART0(void)

void initUART0(void)
{

        PINSEL0 = 0x0005;       // P0.0 -> transmission/Reception UART0
        U0LCR = 0x83;           // Config + DLAB 1
        U0DLM = 0x00;           // Baud Rate
        U0DLL = 16;             // PLL x1 - 9600 bauds - 10Mhz
        U0LCR = 0x03;           // DLAB 0

}

void send_RS(unsigned char caractere)
{

        while ((U0LSR & 0x20) == 0x00) ;    // Tant que le buffer d'émission contient des données

        U0THR = caractere;
}

// Fonction d'envoi chaine de caractere sur UART0

void send_uart(unsigned char *tabcarac)
{

        int i = -1;

        do {
                i++;
                send_RS(*(tabcarac + i));

        } while (*(tabcarac + i) != 0x00);

}

int main(void)
{

        //Init P1.16
        IODIR1=0X00010000;      //p1.16  en sortie

        initUART0();

        send_uart("                C_DATA_ABORT HANDLER\r\n\0");
        send_uart("                  BOUHARA Kamel\r\n\0");
        // Ecriture interdite
        varerror = 0x1;

        // Erreur de fonction
        Fonction_err();
        while(1);
}

void C_Abort_handler()
{

        long dabort_dump[256];          //tableau des 15 regs + cpsr (= 72 octets)
        volatile unsigned long *lnk_ptr;
        volatile unsigned long cpsr, spsr;
        char adr[20], cur_mode[20], exc_mode[20],stack[75];

        ENTRER_ISR_Dabt();

//      asm volatile ("stmfd sp!, {r0-r12,lr}");
//      asm volatile ("ldr sp, =(dabort_dump+5*4)");
//      asm volatile ("stmia sp, {r0-r15}");  //sauv. des regs
        asm volatile ("sub lr, lr, #8");      //récupère l'adresse de l'instruction d'exception
        asm volatile ("mov %0, lr":"=r" (lnk_ptr)); //adresse récupérer dans variable en C
        asm volatile ("mrs %0, cpsr":"=r" (cpsr));  //cpsr = r5
        asm volatile ("mrs %0, spsr":"=r" (spsr));  //spsr= r6
        //asm volatile ("mov r2, r6");        //r2 = cpsr (de l'exception)

        send_uart("Handling data abort exception...\r\n\0");
        sprintf(adr,"Data abort exception at %p value 0x%lX\r\n", lnk_ptr,*(lnk_ptr));
        send_uart(adr);

        sprintf(cur_mode,"Current CPU MODE (cpsr): 0x%lX\r\n",&cpsr);
        send_uart(cur_mode);

        sprintf(exc_mode,"Exception CPU MODE (spsr): 0x%lX\r\n",&spsr);
        send_uart(exc_mode);

        sprintf(stack,"sp : 0x%lX\r\n",&dabort_dump);
        send_uart(stack);

        for(;;);

}</prev>


Regards.


Parents Reply Children
  • As Andy notes, debug output on a serial port is just debug output. It doesn't bring safety.

    And with ROM you probably mean flash memory which often does allow in-application programming (IAP). But the non-easy thing here is to know that your platform is in a good enough state that you dare to use the IAP functionality without potentially erasing your program. That would not help with the safety issues.

    A more traditional way is to reserve a couple of bytes of RAM for a boot loader. Or at least make sure that they are not erased by the zero-init code when the unit boots.

    Then an error handler that detects a potentially catastropic problem may store some error state information and some form of checksum in this memory. Then sit tight in an idle loop while waiting for a watchdog reset (or manually force a reset to speed up the restart if the hw supports that).

    After the platform have been restarted, the boot loader or application can check this reserved RAM region to see if the checksum is valid. It can then perform one of many actions.
    - maybe log the error information to EEPROM or flash.
    - maybe decide that the reboot is enough to clear the problem.
    - maybe use SMS, Ethernet or similar to report the error.
    - maybe check if this was a once event or if the system have had multiple failures recently.
    - maybe decide that the system is compromised and may not be trusted anymore. So it may make its best to power off all external equipment and wildly flash some indication light telling the world: "Please replace me if you want your heavy machinery to be useable again."

    But the important thing when you get to your exception handler is that you don't really know how bad troubles you are in. Maybe the flash doesn't contain valid code anymore. Maybe some RAM cells are broken. Maybe a detector or some I/O pins are burned. Maybe the chip is overheated, performing random operations. Maybe the crystal oscillator is failing and is jittering instead of producing a stable frequency.

    In the end, it's very hard to figure out any safe decisions to make.

    That is why it's great to have an exception handler print debug info to the UART to help you find (normally) software errors in the test bench. But when you go live, you are not expected to have any software errors you can debug - or any equipment that can pick up the debug output. So there is a significant probability that you reached the exception handler because of failing hardware or unsupported environment (too much magnetic fields, radioactive radiation, ambient temperatures outside bounds, voltages out-of-range, massive power spikes, ...)

  • Hi,

    Thanks for you explanations, Im following the scheme you give me and I've modified my Startup.s to implement the assembler dabort handler.
    My problem is that it's doesn't seem to branch to my C handler(to disassemble), here is a part of my Startup with the exception stack definition and my dabort handler :

    ...
    
    Vectors:        LDR     PC, Reset_Addr
                    LDR     PC, Undef_Addr
                    LDR     PC, SWI_Addr
                    LDR     PC, PAbt_Addr
                    LDR     PC, DAbt_Addr
                    NOP                            /* Reserved Vector */
    #               LDR     PC, IRQ_Addr
                    LDR     PC, [PC, #-0x0FF0]     /* Vector from VicVectAddr */
                    LDR     PC, FIQ_Addr
    
    Reset_Addr:     .word   Reset_Handler
    Undef_Addr:     .word   Undef_Handler
    SWI_Addr:       .word   SWI_Handler
    PAbt_Addr:      .word   PAbt_Handler
    DAbt_Addr:      .word   DAbt_Handler
                    .word   0                      /* Reserved Address */
    IRQ_Addr:       .word   IRQ_Handler
    FIQ_Addr:       .word   FIQ_Handler
    
    Undef_Handler:  B       Undef_Handler
    SWI_Handler:    B       SWI_Handler
    PAbt_Handler:   B       PAbt_Handler
    DAbt_Handler:   B       C_Abort_handler
    IRQ_Handler:    B       IRQ_Handler
    FIQ_Handler:    B       FIQ_Handler
    
    ...
    
    __dabt:
                                    STMFD   SP!,{R0-R12, LR}
                                    LDR     SP, =(data_program_abort_context + 5*4)   /*on recupere le sp dans une tableau avec un offset */
                                    STMIA   SP!, {R0-R12} /*sauvegarde des registres dans le tableau*/
                                    SUB     R0, LR, #8 /*PC= adresse de l instruction coupable*/
                                    MRS     R5, CPSR   /*sauvegarde du mode cpu actuel pour changement de mode*/
                                    MRS     R6, SPSR   /*sauvegarde du mode cpu coupable de l exception */
    
                                    MOV     R2, R6          /*r2=CPSR coupable*/
                                    TST     R6, #0xf        /*test du mode coupable*/
                                    ORREQ   R6, R6, #0xf                                       /*si mode USR alors on change de mode pour mode sys(privilegie)*/
                                    BIC     R7, R6, #T_BIT            /*passe en mode thumb*/
                                    MSR     CPSR_c, R7                        /*on sort du mode ABT pour le mode coupable*/
                                    MOV     R3, LR                            /*on recupere le lr*/
                                    MOV     R4, SP                            /*on recup la sp*/
                                    MSR     CPSR_c, R5                        /*on revient au mode ABT*/
                                    TST             R6, #T_BIT                        /* */
                                    LDRNEH  R1, [R0]
                                    LDREQ   R1, [R0]
                                    LDR     SP, =data_program_abort_context
    
                                    STMIA   SP, {R0-R4}
    
                                    LDR     SP, =(Top_Stack - UND_Stack_Size)
                                    LDMDB   SP, {R0-R12, LR}
    
                                    LDR     R0, =SwTh
                                    BX              R0
                            SwTh:
                                    BL              C_Abort_handler
                            inf_loop:
                                    MOV             R0, R0
                                    B               inf_loop
    
    # Enter the C code
                    B       main
    

    And there is my C handler code :

    
    //---------------------- data_abort_handler.c --------------------
        struct
     {
            unsigned long pc;       // r15
            unsigned long inst;
            unsigned long cpsr;     // original cpsr
            unsigned long lr;       // r14
            unsigned long sp;       // r13
            unsigned long r0;
            unsigned long r1;
            unsigned long r2;
            unsigned long r3;
            unsigned long r4;
            unsigned long r5;
            unsigned long r6;
            unsigned long r7;       // fp for Thumb
            unsigned long r8;
            unsigned long r9;
            unsigned long r10;      // sl
            unsigned long r11;      // fp for ARM
            unsigned long r12;      // ip, scratch register
            unsigned long dabort;
    } data_program_abort_context;
    
    
    #define ctx data_program_abort_context
    
    // xxxx000xxxxxxxxxxxxxxxxx1xx1xxxx Extra Load/Store A3-5
    // xxxx000PUIWLnnnnddddmmmm1SH1mmmm Load/Store half and double word, byte sign extend, A5-33
    // xxxx010PUBWLnnnnddddiiiiiiiiiiii Load/Store Immediate offset, A5-18
    // xxxx011PUBWLnnnnddddaaaaass0mmmm Load/Store reg offset
    // xxxx100PUSWLnnnnllllllllllllllll Load/Store multiple, A5-41
    
    static void abt_hex(unsigned long x)
     {
    
     static const char hc[] = {
            "0123456789ABCDEF"};
    
    static char hb[12];
    
    int i;
    
    hb[0] = '0';
    
    hb[1] = 'x';
    
    for (i = 9; i > 1; i--, x >>= 4)
    
     {
    
    hb[i] = hc[x & 0x0f];
    
    }
    
    hb[10] = 0;
    
    send_uart(hb);
    }
    
    void C_Abort_handler(int pc, int inst)
     {
    
     send_uart("\r\n");
    
     if (ctx.dabort)
    
    send_uart("DATA");
    
            else
    
    send_uart("PROGRAM");
    
    send_uart(" ABORT\r\npc=");
    
    abt_hex(ctx.pc);
    
    if (ctx.dabort) {
    
    send_uart(" [pc]=");
    
    abt_hex(ctx.inst);
    
    }
    
    send_uart("\r\n");
    
    send_uart
                ("Dumping Registers [r10=sl; r11=fp; r12=ip; r13=sp; r14=lr; r15=pc; r7=fp(Thumb)]\r\n");
    
    send_uart("r0 =");
    
    abt_hex(ctx.r0);
    
    send_uart(" r1 =");
    
    abt_hex(ctx.r1);
    
    send_uart(" r2 =");
    
    abt_hex(ctx.r2);
    
    send_uart(" r3 =");
    
    abt_hex(ctx.r3);
    
    send_uart("\r\n");
    
    send_uart("r4 =");
    
    abt_hex(ctx.r4);
    
    send_uart(" r5 =");
    
    abt_hex(ctx.r5);
    
    send_uart(" r6 =");
    
    abt_hex(ctx.r6);
    
    send_uart(" r7 =");
    
    abt_hex(ctx.r7);
    
    send_uart("\r\n");
    
    send_uart("r8 =");
    
    abt_hex(ctx.r8);
    
    send_uart(" r9 =");
    
    abt_hex(ctx.r9);
    
    send_uart(" r10=");
    
    abt_hex(ctx.r10);
    
    send_uart(" r11=");
    
    abt_hex(ctx.r11);
    
    send_uart("\r\n");
    
    send_uart("r12=");
    
    abt_hex(ctx.r12);
    
    send_uart(" sp =");
    
    abt_hex(ctx.sp);
    
    send_uart(" lr =");
    
    abt_hex(ctx.lr);
    
    send_uart(" cpsr=");
    
    abt_hex(ctx.cpsr);
    
    send_uart("\r\n");
    
    send_uart
                ("Legend: r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15\r\n");
    
    send_uart(" sb sl fp ip sp lr pc\r\n");
    
    send_uart(" a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8\r\n");
    
    send_uart("Program halted\r\n");
    }
    

    Can you please help me with my startup.s, I think the problem comes form it.

    Best Regards.

  • No, you're not!

    It was strongly recommended to you that you do not do anything in 'C' in your Exception handler!

  • Maybe but Im not doing anything in my C handler, just sending data to the UART so it's not really a "handler"??? Its the same think that Roger Lynx(look the link in my first post) is doing , no?

  • You are calling a number of C functions.
    You are relying on the processor having a valid stack for the processor mode it currently is in.
    You are assuming that the UART is in a good state.
    You are assuming that the baudrate generator is in a good state.
    You may (we haven't seen all code) be relying on the C RTL being in a good state.
    ...

    In the end, it's debug output not in any way related to safety.

  • Hi,

    And thanks again for replying !

    The code is working, at all it's just a debug output ^^, I understand !
    But I need this C handler, just in the case I'm running an OS (witch don't handle data abort exceptions) on my LPC2194.
    My main idea is to save those registers informations in RAM or to send them by radio so as you said later Per Westermark.

    For safety mechanisms on a ARM architecture I don't really have any ideas but the MPU can be a way to increase it??

    If you have any suggestion at all I open to them :)!

    Many Thanks!

    Regards.

  • No, you don't need it to be 'C'.

    You might want it to be 'C' - but you certainly do not need it to be 'C'.

    There are potential dangers with using 'C' (or any HLL) in a situation like this, as explained in this thread. You (or your supervisors) will have to decide whether those risks are acceptable in your particular case.

    "For safety mechanisms ... I don't really have any ideas"

    Where safety is concerned, don't you think that sounds really dangerous?!

    If you're working on safety-critical stuff, you really need to be under the very close supervision of someone competent - an internet forum cannot provide that type or level of supervision!

  • Hi,

    You are right, but the problem is'nt really the need of the C but the C stack needed...
    If I want to use C and ensure safety on my system I have to ensure that my memory can be used to my C stack.
    So I need first in my boot program to test the memory in assembler, you are right !! ^^
    Sorry for my mistakes, Im learning slowly but Im learning !

    Thanks again !
    Regards

  • NNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOO

    "You are calling a number of C functions.
    You are relying on the processor having a valid stack for the processor mode it currently is in.
    You are assuming that the UART is in a good state.
    You are assuming that the baudrate generator is in a good state.
    You may (we haven't seen all code) be relying on the C RTL being in a good state."

    etc. etc. etc. !!!!

  • Note that the assembler code can always force itself to some stack space somewhere (as long as the hardware is working) if it is allowed to potentially overwrite the evidence of an error somewhere.

    Some people may have a debug build where the abort handler stays in a loop, while kicking the watchdog (at least for a limited amount of time), to allow a debugger to be connected and retrieve whatever evidence there may be about the problem.

    For a live system (especially a safety-critical system), you may possibly stay in the abort handler, after having tried everything to cut external power etc to whatever machinery that is controlled. Probably not a good alternative for an ABS system, but not all safety-critical systems have identical requirements or ways to reduce the risk.