2.1. Exception handling

Writing the exception table

The easiest way to populate the vector table is to use a scatter file to place a C array of function pointers at memory address 0x0. The C array can be used to configure the initial stack pointer, image entry point and the addresses of the exception handlers.

Example 1. Example C structure for exception handlers

/* Filename: exceptions.c */
typedef void(* const ExecFuncPtr)(void) __irq;

/* Place table in separate section */
#pragma arm section rodata="exceptions_area"

ExecFuncPtr exception_table[] = {
	(ExecFuncPtr)&Image$$ARM_LIB_STACKHEAP$$ZI$$Limit,
	(ExecFuncPtr)__main, /* Initial PC, set to entry point */
	NMIException,
	HardFaultException,
	MemManageException,
	BusFaultException,
	UsageFaultException,
	0, 0, 0, 0, /* Reserved */
	SVCHandler,
	DebugMonitor,
	0, /* Reserved */
	PendSVC,SysTickHandler,

	/* Configurable interrupts start here...*/
	InterruptHandler0,
	InterruptHandler1,
	InterruptHandler2

	/*
	…
	*/
};

#pragma arm section

Note

Note that the first two items in this structure are the initial stack pointer and the image entry point. The initial stack pointer is generated using a linker defined symbol. See Stack and heap configuration for details. Example 1 uses the C library entry point (__main) as the entry point for the image.

The exception table has also been placed in its own section. This has been done using #pragma arm section rodata="exceptions_area". This directive instructs the compiler to place all the RO (read-only) data between #pragma arm section rodata="exceptions_area"and #pragma arm sectioninto its own section called exceptions_area. This section can then be referred to in the scatter file. The exception table is then placed at the correct location in the memory map, at address 0x0.

Writing the exception handlers

The core saves the system state when an exception occurs and restores it on return. The exception handlers do not therefore need to save or restore the system state and can be written as an ordinary ABI-compliant C function. However, we recommend that you use the __irq qualifier to aid clarity of code. The keyword is also used by the compiler to maintain eight-byte alignment of the stack where necessary. See Eight byte stack alignment for further details.

Example 2. Simple C exception handler

__irq void SysTickHandler(void)
{	printf("----- SysTick Interrupt -----");}

Note

Clearing of an interrupt source must be handled by the ISR.

On the Cortex-M3, exception prioritization, nesting of exceptions, and saving of corruptible registers is handled entirely by the core to permit efficient handling. This means that interrupts remain enabled by the core on entry to every exception handler.

Placing the exception table

Because the exception table has been placed in its own section in the object it can be easily placed at 0x0 using a scatter file as shown in Example 3.

Example 3.  Placing exception table in scatterfile

LOAD_REGION 0x00000000 0x00200000
{	;; Maximum of 256 exceptions (256*4 bytes == 0x400)
	VECTORS 0x0 0x400	{		exceptions.o (exceptions_area, +FIRST)	}}

Note

+FIRST is used to ensure that exceptions_area is placed at the very beginning of the region. It also prevents the vector table from being removed by the unused section elimination mechanism of the linker.

Configuring the System Control Space (SCS) registers

The SCS registers are located at 0xE000E000. As there are a large number of individual registers, it is best to use a structure to represent them. This can then be positioned in the correct memory location by adding this structure to the scatter file, using a similar method to the exception table. Example 4 shows an example structure for the SCS registers.

Example 4. SCS Register Structure

typedef volatile struct {
    int MasterCtrl;
    int IntCtrlType;    int zReserved008_00c[2];
         /* Reserved space */    struct {        int Ctrl;        int Reload;        int Value;        int Calibration;
    } SysTick;    int zReserved020_0fc[(0x100-0x20)/4];
            /* Reserved space */

    /* Offset 0x0100 */    struct {        int Enable[32];        int Disable[32];        int Set[32];        int Clear[32];        int Active[64];        int Priority[64];    } NVIC;    int zReserved0x500_0xcfc[(0xd00-0x500)/4];
            /* Reserved space */

    /* Offset 0x0d00 */    int CPUID;    int IRQcontrolState;    int ExceptionTableOffset;    int AIRC;    int SysCtrl;    int ConfigCtrl;    int SystemPriority[3];    int SystemHandlerCtrlAndState;    int ConfigurableFaultStatus;    int HardFaultStatus;    int DebugFaultStatus;    int MemManageAddress;    int BusFaultAddress;    int AuxFaultStatus;    int zReserved0xd40_0xd90[(0xd90-0xd40)/4];        
            /* Reserved space */

    /* Offset 0x0d90 */    struct {        int Type;        int Ctrl;        int RegionNumber;        int RegionBaseAddr;        int RegionAttrSize;
    } MPU;
} SCS_t;

Note

This register structure might not contain all of the SCS registers in your device. See the reference manual provided by the silicon manufacturer of your device.

Configuring individual IRQs

Each IRQ has an individual enable bit in the Interrupt Set Enable Registers, part of theNVIC registers. To enable an interrupt you need to set the corresponding bit in the Interrupt Set Enable Register. See the reference manual provided by the silicon manufacturer of the device you are using for specific details on the Interrupt Set Enable Register.

Example 5 shows interrupt enable code for the SCS structure shown in Example 4.

Example 5. IRQ enable function

void NVIC_enableISR(unsigned isr){	/* The isr argument is the number of the interrupt to enable. */	SCS.NVIC.Enable[ (isr/32) ] = 1<<(isr % 32);}

Note

Some registers in the SCS region can only be accessed from Privileged mode. Individual IRQs can be disabled by setting the appropriate bit in the Interrupt Clear Enable Registers.

Interrupt priorities

Each individual interrupt can be assigned a priority level via the Interrupt Priority Registers. Depending on the implementation, up to 256 different priority levels can be assigned to each individual interrupt. The priority levels are represented using up to 8 bits. Groups of four interrupt priorities are stored in each word of memory.

The lower the assigned priority number, the higher the priority of the interrupt. Therefore 0 is the highest priority and 255 the lowest.

Copyright © 2007. All rights reserved.ARM DAI0179B
Non-Confidential