Author Topic: AVR32 Generic Interrupt Handling  (Read 10317 times)

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3216
    • View Profile
    • uTasker
AVR32 Generic Interrupt Handling
« on: September 11, 2009, 09:00:36 PM »
Hi All

I wanted to post the AVR32 interrupt handling method in case any one working with the project was wondering how it is done. Here is the original idea and explanation...

"I found the interrupt controller a bit strange in its use of a 16k range for interrupt code. This is handled differently by whatever compiler is being used and seemed a bit messy. Therefore I decided to ‘invent’ another method which seems a bit more flexibly and compiler independent. I set the first 260 bytes of SRAM as an exception table and I pointed the EVBA to it (just set 0x00000000). At initialization I filled this table with 0xe08f0000 which is the machine code of a forever loop (branch to self). If any exceptions occur, they jump to their own loop and the debugger can easily see which exception it was and look at the saved context to find out where it came from. In between the exceptions there is enough space to define locations to handle peripheral interrupts (20 groups exist in the AT32UC3A) and I have defined each 2 long words. When an interrupt handler for the group is entered its interrupt priority register is loaded with the offset to its group space in the exception table and the address of the handler is written there, together with the long word 0x481fd703 (machine code for load PC with the value at the next long address + NOP). When any peripheral interrupt occurs it thus vectors to the entry in the exception table and them immediately is sent to the handler location anywhere in FLASH. This does take one extra jump instruction but removes restrictions about locating the interrupt routines together in a single 16k area, at a correct boundary, and also the restriction of the EVBA boundary (since 0x00000000 is a perfect case for it). Also there is no more restriction of having many possible interrupt handlers (which can be exchanged at will) since where they are is no longer important – they can have any address in the memory range. Below is the code which has worked well for first peripheral tests. It defines the peripheral group locations, initializes the table and enters interrupts:

// Event table which is put to SRAM to handle all exceptions and interrupts
typedef struct stEXCEPTION_TABLE
    unsigned long evUnrecoverableException;
    unsigned long evTLBmultipleHit;
    unsigned long evBusErrorDataFetch;
    unsigned long evBusErrorInstructionFetch;
    unsigned long evNonMaskableInterrupt;
    unsigned long evMissingAddress;
    unsigned long evITLBProtection;
    unsigned long evBreakPoint;
    unsigned long evIllegalOpcode;
    unsigned long evUnimplementedInstruction;
    unsigned long evPrivilegeViolation;
    unsigned long evFloatingPoint;
    unsigned long evCoprocessorAbsent;
    unsigned long evDataAddressRead;
    unsigned long evDataAddressWrite;
    unsigned long evDTLBProtectionRead;
    unsigned long evDTLBProtectionWrite;
    unsigned long evDTLBModified;
    unsigned long evGroup0;                                              // 0x48
    unsigned long evAdd0;                                                // 0x4c
    unsigned long evITLBMiss;
    unsigned long evGroup1;                                              // 0x54
    unsigned long evAdd1;                                                // 0x58
    unsigned long evRes1;                                                // 0x5c
    unsigned long evITLBMissRead;
    unsigned long evGroup2;                                              // 0x64
    unsigned long evAdd2;                                                // 0x68
    unsigned long evRes2;                                                // 0x6c
    unsigned long evITLBMissWrite;
    unsigned long evGroup3;                                              // 0x74
    unsigned long evAdd3;                                                // 0x78
    unsigned long evGroup4;                                              // 0x7c
    unsigned long evAdd4;                                                // 0x80
    unsigned long evGroup5;                                              // 0x84
    unsigned long evAdd5;                                                // 0x88
    unsigned long evGroup6;                                              // 0x8c
    unsigned long evAdd6;                                                // 0x90
    unsigned long evGroup7;                                              // 0x94
    unsigned long evAdd7;                                                // 0x98
    unsigned long evGroup8;                                              // 0x9c
    unsigned long evAdd8;                                                // 0xa0
    unsigned long evGroup9;                                              // 0xa4
    unsigned long evAdd9;                                                // 0xa8
    unsigned long evGroup10;                                             // 0xac
    unsigned long evAdd10;                                               // 0xb0
    unsigned long evGroup11;                                             // 0xb4
    unsigned long evAdd11;                                               // 0xb8
    unsigned long evGroup12;                                             // 0xbc
    unsigned long evAdd12;                                               // 0xc0
    unsigned long evGroup13;                                             // 0xc4
    unsigned long evAdd13;                                               // 0xc8
    unsigned long evGroup14;                                             // 0xcc
    unsigned long evAdd14;                                               // 0xd0
    unsigned long evGroup15;                                             // 0xd4
    unsigned long evAdd15;                                               // 0xd8
    unsigned long evGroup16;                                             // 0xdc
    unsigned long evAdd16;                                               // 0xe0
    unsigned long evGroup17;                                             // 0xe4
    unsigned long evAdd17;                                               // 0xe8
    unsigned long evGroup18;                                             // 0xec
    unsigned long evAdd18;                                               // 0xf0
    unsigned long evGroup19;                                             // 0xf4
    unsigned long evAdd19;                                               // 0xf8
    unsigned long evRes3;                                                // 0xfc
    unsigned long evSupervisorCall;                                      // 0x100

#define BRANCH_TO_SELF                   0xe08f0000                      // AVR32 machine code to create a forever loop
#define LOAD_PC_WITH_NEXT_VALUE          0x481fd703                      // LDDPC relative plus NOP


// Perform very low level AVR32 initialisation - called by the start up code
static void AVR32_LowLevelInit(void)
    EXCEPTION_TABLE *ptrEventTable = (EXCEPTION_TABLE *)EXCEPTION_VECTOR_BASE_ADDRESS; // place an event table at the start of RAM
    unsigned long *ulPtrEntries = &ptrEventTable->evUnrecoverableException;
    int i = 0;
    while (i++ < (sizeof(EXCEPTION_TABLE)/sizeof(unsigned long))) {
        *ulPtrEntries++ = BRANCH_TO_SELF;                                // fill the event table with forever loops to catch unexpected exceptions
    __set_EVBA(EXCEPTION_VECTOR_BASE_ADDRESS);                           // set EVBA to the start of SRAM

static const unsigned char ucGroupLocation[] = {18, 21, 25, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61};

// Function used to enter interrupts
static void fnEnterAVRInterrupt(int iIntGroup, unsigned long ulIntLevel, void (__interrupt *InterruptFunc)(void))
    unsigned long *ptrEventTable  = (unsigned long *)EXCEPTION_VECTOR_BASE_ADDRESS;
    unsigned long *ptrIntPriority = (unsigned long *)INTC_BLOCK;
    ptrIntPriority += iIntGroup;
    ptrEventTable += ucGroupLocation[iIntGroup];
    *ptrEventTable++ = LOAD_PC_WITH_NEXT_VALUE;
    *ptrEventTable = (unsigned long)InterruptFunc;
    *ptrIntPriority = (ulIntLevel | (((unsigned long)ucGroupLocation[iIntGroup]) << 2));

Here is how an interrupt handler is entered – eg of the TICK:
    fnEnterAVRInterrupt(IR_GROUP_SYSBLOCK, INT_LEVEL_1, _RealTimeInterrupt);

Hope it is basically understandable. I do think that it is a fairly elegant solution to keep this stuff generic, otherwise I have the feeling that the technique is rather dictated by the particular compiler manufacturer...