• No results found

Interrupt Handling

12.1 External interrupt requests

Types of exception on page 11-3, described how all ARM cores have two external interrupt requests, FIQ and IRQ. Both of these are level-sensitive active-LOW inputs. Individual implementations have interrupt controllers that accept interrupt requests from a wide variety of external sources and map them onto FIQ or IRQ, causing the core to take an exception. In general, an interrupt exception can be taken only when the appropriate CPSR disable bit (the F and I bits respectively) is clear and if the corresponding input is asserted.

The CPS instruction provides a simple mechanism to enable or disable the exceptions controlled by CPSR A, I and F bits (asynchronous abort, IRQ and FIQ respectively).

CPSIE or CPSID will enable or disable exceptions respectively. The exceptions to be enabled or disabled are specified using one or more of the letters A, I and F. Exceptions whose

corresponding letters are omitted will not be modified.

In Cortex-A series processors, it is possible to configure the core so that FIQs cannot be masked by software. This is known as Non-Maskable FIQ and is controlled by a hardware configuration input signal that is sampled when the core is reset. They will still be masked automatically on taking an FIQ exception.

12.1.1 Assigning interrupts

A system will always have an interrupt controller that accepts and arbitrates interrupts from multiple sources. This typically contains a number of registers enabling software running on the core to mask individual interrupt sources, to acknowledge interrupts from external devices, to assign priorities to individual interrupt sources and to determine which interrupt sources are currently requesting attention or require servicing.

This interrupt controller can be a design specific to the system, or it can be an implementation of the ARM Generic Interrupt Controller (GIC) architecture, described in The Generic Interrupt Controller on page 12-7.

12.1.2 Simplistic interrupt handling

This represents the simplest kind of interrupt handler. On taking an interrupt, additional interrupts of the same kind are disabled until explicitly enabled later. We can only handle additional interrupts at the completion of the first interrupt request and there is no scope for a higher priority or more urgent interrupt to be handled during this time. This is not generally suitable for complex embedded systems, but it is useful to examine before proceeding to a more realistic example, in this case of a non re-entrant interrupt handler.

The steps taken to handle an interrupt are as follows:

1. An IRQ exception is raised by external hardware. The core performs several steps automatically. The contents of the PC in the current execution mode are stored in LR_IRQ. The CPSR register is copied to SPSR_IRQ. The CPSR content is updated so that the mode bits reflects the IRQ mode, and the I bit is set to mask additional IRQs. The PC is set to the IRQ entry in the vector table.

Interrupt Handling

Figure 12-1 Save the context of the program

2. The instruction at the IRQ entry in the vector table (a branch to the interrupt handler) is executed.

3. The interrupt handler saves the context of the interrupted program, that is, it pushes onto the stack any registers that will be corrupted by the handler. These registers will be popped from stack when the handler finishes execution.

Figure 12-2

4. The interrupt handler determines which interrupt source must be processed and calls the appropriate device driver.

Program flow PC -> LR_irq CPSR -> SPRR_irq ASM IRQ Handler Program flow PC -> LR_irq CPSR -> SPRR_irqASM IRQ Handler Stack LR_irq

Interrupt Handling

5. Prepare the core to switch to previous execution state by copying the SPSR_IRQ to CPSR, and restoring the context saved earlier, and finally the PC is restored from LR_IRQ.

Figure 12-3

The same sequence is also applicable to an FIQ interrupt. A very simple interrupt handler is shown in Example 12-1.

Example 12-1 Simple interrupt handler

IRQ_Handler

PUSH {r0-r3, r12, lr} @ Store AAPCS registers and LR onto the IRQ mode stack

BL @ identify_and_clear_source

BL @ C-irq_handler

POP {r0-r3, r12, lr} @ Restore registers and

SUBS pc, lr, #4 @ return from exception using modified LR

12.1.3 Nested interrupt handling

Nested interrupt handling is where the software is prepared to accept another interrupt, even before it finishes handling the current interrupt. This enables you to prioritize interrupts and make significant improvements to the latency of high priority events at the cost of additional complexity. It is worth noting that nested interrupt handling is a choice made by the software, by virtue of interrupt priority configuration and interrupt control, rather than imposed by hardware.

A reentrant interrupt handler must save the IRQ state and then switch core modes, and save the state for the new core mode, before it branches to a nested subroutine or C function with interrupts enabled. This is because a fresh interrupt could occur at any time, which would cause the core to store the return address of the new interrupt and overwrite the original interrupt.

Program flow PC -> LR_irq CPSR -> SPRR_irq ASM IRQ Handler Stack LR_irq SPSR_irq -> CPSRLR_irq -> PC

Interrupt Handling

Figure 12-4 Nested interrupts

Note

A computer program is reentrant if it can be interrupted in the middle of its execution and then be called again before the previous version has completed.

In Figure 12-4 the value of SPSR must be preserved before interrupts are re-enabled. If it is not, any new interrupt will overwrite the value of SPSR_irq. The solution to this is to stack the SPSR before re-enabling the interrupts by using the following:

SRSFD sp!, #0x12

Additionally, using the BL instruction within the interrupt handler code will cause LR_IRQ corruption. The solution is to switch to Supervisor mode before using the BL instruction. A reentrant interrupt handler must therefore take the following steps after an IRQ exception is raised and control is transferred to the interrupt handler in the way previously described. 1. The interrupt handler saves the context of the interrupted program (that is, it pushes onto

the alternative kernel mode stack any registers that will be corrupted by the handler, including the return address and SPSR_IRQ).

2. It determines which interrupt source must be processed and clears the source in the external hardware (preventing it from immediately triggering another interrupt).

3. The interrupt handler changes to the core SVC mode, leaving the CPSR I bit set (interrupts are still disabled).

4. The interrupt handler saves the exception return address on the stack (a stack for the new mode, located in kernel memory) and re-enables interrupts.

5. It calls the appropriate handler code.

6. On completion, the interrupt handler disables IRQ and pops the exception return address from the stack.

Program flow CPSR -> SPSR_irq ASM IRQ Handler CPSR -> SPSR_irq SPSR_irq is corrupted

Interrupt Handling

7. It restores the context of the interrupted program directly from the alternative kernel mode stack. This includes restoring the PC, and the CPSR which switches back to the previous execution mode. If the SPSR does not have the I bit set then the operation also re-enables interrupts.

Sample code for a nested interrupt handler (for non-vectored interrupts) is given in

Example 12-2.

Example 12-2 Nested interrupt handler

IRQ_Handler

SUB lr, lr, #4

SRSFD sp!, #0x1f @ use SRS to save LR_irq and SPSR_irq in one step onto the @ System mode stack

CPS #0x1f @ Use CPS to switch to system mode

PUSH {r0-r3, r12} @S tore remaining AAPCS registers on the System mode stack AND r1, sp, #4 @ Ensure stack is 8-byte aligned. Store adjustment and

@ LR_sys to stack SUB sp, sp, r1

PUSH {r1, lr}

BL @ identify_and_clear_source

CPSIE i @ Enable IRQ with CPS

BL C_irq_handler

CPSID i @ Disable IRQ with CPS

POP {r1, lr} @ Restore LR_sys ADD sp, sp, r1 @ Unadjust stack

POP {r0-r3, r12} @ Restore AAPCS registers

Interrupt Handling