Interrupts And Exceptions
The Cortex-M3 controller features a Nested Vector Interrupt Controller, or NVIC. The NVIC supports a numer of exceptions, including a fixed number of system exceptions and a number of external interrupts, commonly known as IRQs.
Depending on the implementation used by the silicon manufacturer, the NVIC can support up to 240 external interrupts with up to 256 different priority levels that can be dynamically reprioritized.
The Cortex-M3 vector table contains the address of the exception handlers and ISR. The initial stack pointer and the address of the reset handler must be located at 0x0 and 0x4 respectively.
These values are then loaded into the appropriate CPU registers at reset.
Exception types
On Cortex-M3, the interrupts/exceptions are divided into two categories, based on their priority: configurable and unconfigurable.
The unconfigurable exceptions have fixed priority, and consist of following :
-
Reset - Reset is invoked on power up or a warm reset. The exception model treats reset as a special form of exception. When reset is asserted, the operation of the processor stops, potentially at any point in an instruction.
When reset is deasserted, execution restarts from the address provided by the reset entry in the vector table. Execution restarts as privileged execution in Thread mode. -
NMI - A NonMaskable Interrupt (NMI) can be signalled by a peripheral or triggered by software. This is the highest priority exception other than reset. It is permanently enabled and has a fixed priority of -2.
NMIs cannot be:- masked or prevented from activation by any other exception,
- preempted by any exception other than Reset.
- HardFault - A HardFault is an exception that occurs because of an error during exception processing, or because an exception cannot be managed by any other exception mechanism.
HardFaults have a fixed priority of -1, meaning they have higher priority than any exception with configurable priority. - MemManage - A MemManage fault is an exception that occurs because of a memory protection related fault. The MPU or the fixed memory protection constraints determines this fault, for both instruction and data memory transactions.
This fault is always used to abort instruction accesses to Execute Never (XN) memory regions. - BusFault - A BusFault is an exception that occurs because of a memory related fault for an instruction or data memory transaction. This might be from an error detected on a bus in the memory system.
- UsageFault - A UsageFault is an exception that occurs because of a fault related to instruction execution. This includes:
- an undefined instruction,
- an illegal unaligned access,
- invalid state on instruction execution,
- an error on exception return.
- an unaligned address on word and halfword memory access,
- division by zero
- SVCall - A supervisor call (SVC) is an exception that is triggered by the SVC instruction. In an OS environment, applications can use SVC instructions to access OS kernel functions and device drivers.
- PendSV - PendSV is an interrupt-driven request for system-level service. In an OS environment, use PendSV for context switching when no other exception is active.
- SysTick - A SysTick exception is an exception the system timer generates when it reaches zero. Software can also generate a SysTick exception. In an OS environment, the processor can use this exception as system tick.
- Interrupt (IRQ) - A interrupt, or IRQ, is an exception signalled by a peripheral, or generated by a software request. All interrupts are asynchronous to instruction execution. In the system, peripherals use interrupts to communicate with the processor.
Priority Levels
The NVIC supports software-configurable priority levels. You can assign a priority level from 0 to 255 to an interrupt. Hardware priority decreases with increasing interrupt number.
Priority level 0 is the highest priority level, and priority level 255 is the lowest. The priority level overrides the hardware priority.
For example, if you assign priority level 1 to IRQ[0] and priority level 0 to IRQ[31], then IRQ[31] has higher priority than IRQ[0].
When multiple interrupts have the same priority number, the pending interrupt with the lowest interrupt number takes precedence.
For example, if both IRQ[0] and IRQ[1] are priority level 1, then IRQ[0] has higher priority than IRQ[1].
Software prioritization does not affect Reset, Non-Maskable Interrupt (NMI), and hard fault. They always have higher priority than the external interrupts.
Interrupt Vector Table
The Cortex-M3 processor uses a re-locatable vector table that contains the address of the function to be executed for a particular interrupt handler.
On accepting an interrupt, the processor fetches the address from the vector table through the instruction bus interface.
Following picutre shows the order of the exception vectors in the vector table :
On system reset, the vector table is fixed at address 0x0000.0000. Privileged software can relocate the vector table start address to a different memory location.
Interrupt Service Routine
Interrupt service routine is defined in this way :
procedure interrupt(); iv IVT_INT_TIMER0A; ics ICS_OFF; begin // Interrupt service routine code end;
where :
iv
- reserved word that inform the compiler that it is an interrupt service routine.IVT_INT_TIMER0A
- appropriate Interrupt Vector.ics
Interrupt Context Saving; Interrupt Context Saving can be performed in several ways :ICS_OFF
- No context savingICS_AUTO
- Compiler chooses whether the context saving will be perfomed or not.
If interrupt context saving is not explicitly declared, compiler will set ICS_AUTO
by default.
User can explicitly declare starting interrupt routine address using org
directive :
procedure interrupt(); org 0xC00; iv IVT_INT_TIMER0A; ics ICS_OFF; begin // Interrupt service routine code end;
Function Calls from Interrupt
Calling functions from within the interrupt routine is possible. The compiler takes care about the registers being used, both in "interrupt" and in "main" thread, and performs "smart" context-switching between two of them, saving only the registers that have been used in both threads. It is not recommended to use a function call from interrupt. In case of doing that take care of stack depth.
Disable Context Saving
Use the DisableContextSaving to instruct the compiler not to automatically perform context-switching.
This means that no register will be saved/restored by the compiler on entrance/exit from interrupt service routine.
This enables the user to manually write code for saving registers upon entrance and to restore them before exit from interrupt.
Interrupt Example
Here is a simple example of handling the interrupts from Timer0A
(if no other interrupts are allowed):
program Timer0_interrupt; procedure Timer0A_interrupt(); iv IVT_INT_TIMER0A ics ICS_OFF; begin TIMER_ICR_TATOCINT_bit := 1; // Clear time-out timer A interrupt GPIO_PORTJ_DATA := not GPIO_PORTJ_DATA; // Toggle PORTJ led's end; begin GPIO_Digital_Output(@GPIO_PORTJ, _GPIO_PINMASK_ALL); // Enable digital output on PORTJ GPIO_PORTJ_DATA := 0; SYSCTL_RCGC1_TIMER0_bit := 1; // Enable clock gating for timer module 0 EnableInterrupts(); // Enables the processor interrupt. TIMER_CTL_TAEN_bit := 0; // Disable timer TIMER0_CFG := 0; // Set 32-bit timer configuration TIMER0_TAMR := TIMER0_TAMR or 2; // Set periodic mode TIMER0_TAILR := Get_Fosc_kHz()*1000; // Set interval load NVIC_IntEnable(IVT_INT_TIMER0A); // Enable timer interrupt TIMER_IMR_TATOIM_bit := 1; // Enable time-out timer A interrupt TIMER_CTL_TAEN_bit := 1; // Enable timer A while true do ; end.