arm Interrupt handlers

6.2.7. Interrupt handlers

This section describes how to write interrupt handlers.

Levels of external interrupt

The ARM processor has two levels of external interrupt, FIQand IRQ, both of which are level-sensitive active LOW signals intothe processor. For an interrupt to be taken, the appropriate disablebit in the CPSR must be clear.

FIQs have higher priority than IRQs in the following ways:

  • FIQs arehandled first when multiple interrupts occur.

  • Handling an FIQ causes IRQs and subsequent FIQsto be disabled, preventing them from being handled until after theFIQ handler enables them. This is usually done by restoring theCPSR from the SPSR at the end of the handler.

The FIQ vector is the last entry in the vector table so thatthe FIQ handler can be placed directly at the vector location andrun sequentially from that address. This removes the requirementfor a branch and its associated delay, and also means that if thesystem has a cache, the vector table and FIQ handler might all belocked down in one block within it. This is important because FIQsare designed to handle interrupts as quickly as possible. The fiveextra FIQ mode banked registers enable status to be held between callsto the handler, again increasing execution speed.

Note

An interrupt handler must contain code to clear the sourceof the interrupt.

Reentrant interrupt handlers

If an interrupt handler enables interrupts before callinga subroutine and another interrupt occurs, the return address ofthe subroutine stored in the IRQ mode LR is corrupted when the secondIRQ is taken. This is because the processor automatically savesthe return address into the IRQ mode LR for thenew interrupt overwriting the return address for the subroutine.This results in an infinite loop when the subroutine in the originalinterrupt tries to return.

A reentrant interrupt handler must save the IRQ state, switchprocessor modes, and save the state for the new processor mode beforebranching to a nested subroutine or C function. It must also ensurethat the stack is eight-byte aligned for the new processor modebefore calling AAPCS-compliant compiled C code that might use LDRD or STRD instructionsor eight-byte aligned stack-allocated data. There is more informationabout stack alignment issues in the ABI for the ARM ArchitectureAdvisory Note 1- SP must be 8-byte aligned on entry to AAPCS-conformingfunctions (ARM IHI 0046A).

Using the __irq keyword in C does not causethe SPSR to be saved and restored, as required by reentrant interrupthandlers, so you must write your top level interrupt handler inassembly language.

In ARMv4 or later you can switch to System mode if you require privilegedaccess. See System mode formore information.

Note

This method works for both IRQ and FIQ interrupts. However,because FIQ interrupts are meant to be handled as quickly as possiblethere is normally only one interrupt source, so it might not benecessary to provide for reentrancy.

The steps required to enable interrupts safely in an IRQ handlerare:

  1. Construct thereturn address and save it on the IRQ stack.

  2. Save the work registers, non callee-saved registersand IRQ mode SPSR.

  3. Clear the source of the interrupt.

  4. Switch to System mode, keeping IRQs disabled.

  5. Check that the stack is eight-byte aligned and adjustif necessary.

  6. Save the User mode LR and the adjustment, 0 or 4for Architectures v4 or v5TE, used on the User mode SP.

  7. Enable interrupts and call the C interrupt handlerfunction.

  8. When the C interrupt handler returns, disable interrupts.

  9. Restore the User mode LR and the stack adjustmentvalue.

  10. Readjust the stack if necessary.

  11. Switch to IRQ mode.

  12. Restore other registers and IRQ mode SPSR.

  13. Return from the IRQ.

Example 6.2 and Example 6.3 shows howthis works for System mode.

Example 6.2. Reentrant interrupt handler forARMv4/v5TE

    PRESERVE8
    AREA INTERRUPT, CODE, READONLY
    IMPORT C_irq_handler
    IMPORT identify_and_clear_source

IRQ_Handler
    SUB     lr, lr, #4             ; construct the return address
    PUSH    {lr}                   ; and push the adjusted lr_IRQ
    MRS     lr, SPSR               ; copy spsr_IRQ to lr
    PUSH    {R0-R4,R12,lr}         ; save AAPCS regs and spsr_IRQ
    BL      identify_and_clear_source
    MSR     CPSR_c, #0x9F          ; switch to SYS mode, IRQ is
                                   ; still disabled. USR mode
                                   ; registers are now current.
    AND     R1, sp, #4             ; test alignment of the stack
    SUB     sp, sp, R1             ; remove any misalignment (0 or 4)
    PUSH    {R1,lr}                ; store the adjustment and lr_USR
    MSR     CPSR_c, #0x1F          ; enable IRQ
    BL      C_irq_handler
    MSR     CPSR_c, #0x9F          ; disable IRQ, remain in SYS mode
    POP     {R1,lr}                ; restore stack adjustment and lr_USR
    ADD     sp, sp, R1             ; add the stack adjustment (0 or 4)
    MSR     CPSR_c, #0x92          ; switch to IRQ mode and keep IRQ
                                   ; disabled. FIQ is still enabled.
    POP     {R0-R4,R12,lr}         ; restore registers and
    MSR     SPSR_cxsf, lr          ; spsr_IRQ
    LDM     sp!, {pc}^             ; return from IRQ.
    END

Example 6.3. Reentrant Interrupt for ARMv6 (non vectored interrupts)

    PRESERVE8
    AREA INTERRUPT, CODE, READONLY
    IMPORT C_irq_handler
    IMPORT identify_and_clear_source

IRQ_Handler
    SUB         lr, lr, #4
    SRSDB       sp!,#31          ; Save LR_irq and SPSR_irq to System mode stack
    CPS #031                     ; Switch to System mode
    PUSH        {R0-R3,R12}      ; Store other AAPCS registers
    AND         R1, sp, #4
    SUB         sp, sp, R1
    PUSH        {R1, lr}
    BL          identify_and_clear_source
    CPSIE       i                ; Enable IRQ
    BL          C_irq_handler
    CPSID i                      ; Disable IRQ
    POP         {R1,lr}
    ADD         sp, sp, R1
    POP         {R0-R3, R12}     ; Restore registers
    RFEIA       sp!              ; Return using RFE from System mode stack
    END

These examples assume that FIQ remains permanently enabled.

Example interrupt handlers in assemblylanguage

Interrupt handlers are often written in assembly languageto ensure that they execute quickly. The following sections givesome examples:

Single-channel DMA transfer

Example 6.4 shows aninterrupt handler that performs interrupt driven I/O to memory transfers,soft DMA. The code is an FIQ handler. It uses the banked FIQ registersto maintain state between interrupts. This code is best situatedat location 0x1C.

In the example code:

R8

Pointsto the base address of the I/O device that data is read from.

IOData

Is the offset fromthe base address to the 32-bit data register that is read. Readingthis register clears the interrupt.

R9

Points to the memorylocation to where that data is being transferred.

R10

Points to the lastaddress to transfer to.

The entire sequence for handling a normal transfer is fourinstructions. Code situated after the conditional return is usedto signal that the transfer is complete.

Example 6.4. FIQ handler

    LDR     R11, [R8, #IOData]     ; Load port data from the IO device.
    STR     R11, [R9], #4          ; Store it to memory: update the pointer.
    CMP     R9, R10                ; Reached the end ?
    SUBLSS  pc, lr, #4             ; No, so return.
                                   ; Insert transfer complete
                                   ; code here.

Byte transfers can be made by replacing the load instructionswith load byte instructions. Transfers from memory to an I/O deviceare made by swapping the addressing modes between the load instructionand the store instruction.

Dual-channel DMA transfer

Example 6.5 issimilar to Example 6.4, exceptthat there are two channels being handled. The code is an FIQ handler.It uses the banked FIQ registers to maintain state between interrupts.It is best situated at location 0x1C.

In the example code:

R8

Points to the base address of the I/O device fromwhich data is read.

IOStat

Is the offset fromthe base address to a register indicating which of two ports causedthe interrupt.

IOPort1Active

Is a bitmask indicating if the first port caused the interrupt. Otherwiseit is assumed that the second port caused the interrupt.

IOPort1, IOPort2

Areoffsets to the two data registers to be read. Reading a data registerclears the interrupt for the corresponding port.

R9

Points to the memorylocation to which data from the first port is being transferred.

R10

Points to the memorylocation to which data from the second port is being transferred.

R11, R12

Pointto the last address to transfer to. This is R11 forthe first port, R12 for the second.

The entire sequence to handle a normal transfer takes nineinstructions. Code situated after the conditional return is usedto signal that the transfer is complete.

Example 6.5. FIQ handler

    LDR     sp, [R8, #IOStat]      ; Load status register to find which port
                                   ; caused the interrupt.
    TST     sp, #IOPort1Active
    LDREQ   sp, [R8, #IOPort1]     ; Load port 1 data.
    LDRNE   sp, [R8, #IOPort2]     ; Load port 2 data.
    STREQ   sp, [R9], #4           ; Store to buffer 1.
    STRNE   sp, [R10], #4          ; Store to buffer 2.
    CMP     R9, R11                ; Reached the end?
    CMPLE   R10, R12               ; On either channel?
    SUBSNE  pc, lr, #4             ; Return
                                   ; Insert transfer complete code here.

Byte transfers can be made by replacing the load instructionswith load byte instructions. Transfers from memory to an I/O deviceare made by swapping the addressing modes between the conditionalload instructions and the conditional store instructions.

Interrupt prioritization

Example 6.6 dispatchesup to 32 interrupt sources to their appropriate handlers. Becauseit is designed for use with the normal interrupt vector, IRQ, itis branched to from location 0x18.

External Vectored Interrupt Controller (VIC) hardwareis used to prioritize the interrupt and present the high-priorityactive interrupt in an I/O register.

In the example code:

IntBase

Holdsthe base address of the interrupt controller.

IntLevel

Holds the offsetof the register containing the highest-priority active interrupt.

R13

Is assumed to pointto a small full descending stack.

Interrupts are enabled after ten instructions, including thebranch to this code.

The specific handler for each interrupt is entered, with allregisters preserved on the stack, after two more instructions.

In addition, the last three instructions of each handler areexecuted with interrupts turned off again, so that the SPSR canbe safely recovered from the stack.

Note

Application Note 30: Software Prioritization ofInterrupts describes multiple-source prioritizationof interrupts using software, as opposed to using the VIC hardwareas described here.

Example 6.6. Dispatching interruptsto handlers

    ; first save the critical state
    SUB     lr, lr, #4              ; Adjust the return address
                                    ; before we save it.
    STMDB   sp!, {lr}               ; Stack return address
    MRS     lr, SPSR                ; get the SPSR ...
    PUSH    {R12,lr}                ; ... and stack that plus a
                                    ; working register too.
                                    ; Now get the priority level of the
                                    ; highest priority active interrupt.
    MOV     R12, #IntBase           ; Get the interrupt controller's
                                    ; base address.
    LDR     R12, [R12, #IntLevel]   ; Get the interrupt level (0 to 31).
                                    ; Now read-modify-write the CPSR
                                    ; to enable interrupts.
    MRS     lr, APSR                ; Read the status register.
    BIC     lr, lr, #0x80           ; Clear the I bit
                                    ; (use 0x40 for the F bit).
    MSR     CPSR_c, lr              ; Write it back to re-enable
                                    ; interrupts and
    LDR     pc, [pc, R12, LSL #2]   ; jump to the correct handler.
                                    ; PC base address points to this
                                    ; instruction + 8
    NOP                             ; pad so the PC indexes this table.
                                    ; Table of handler start addresses
    DCD     Priority0Handler
    DCD     Priority1Handler
    DCD     Priority2Handler
; ...
    Priority0Handler
    PUSH    {R0-R11}                ; Save other working registers.
                                    ; Insert handler code here.
; ...
    POP     {R0-R11}                ; Restore working registers (not R12).
                                    ; Now read-modify-write the CPSR
                                    ; to disable interrupts.
    MRS     R12, APSR               ; Read the status register.
    ORR     R12, R12, #0x80         ; Set the I bit
                                    ; (use 0x40 for the F bit).
    MSR     CPSR_c, R12             ; Write it back to disable interrupts.
                                    ; Now that interrupt disabled, can safely
                                    ; restore SPSR then return.
    POP     {r12,lr}                ; Restore R12 and get SPSR.
    MSR     SPSR_cxsf, lr           ; Restore status register from R14.
    LDM     sp!, {pc}^              ; Return from handler.
Priority1Handler
; ...

Context switch

Example 6.7 performsa context switch on the User mode process. The code is based arounda list of pointers to Process Control Blocks (PCBs)of processes that are ready to run.

Figure 6.2 showsthe layout of the PCBs that the example expects.

Figure 6.2. PCB layout


The pointer to the PCB of the next process to run is pointedto by R12, and the end of the list has a zeropointer. Register R13 is a pointer to the PCB,and is preserved between time slices, so that on entry it pointsto the PCB of the currently running process.

Example 6.7. Context switchon the User mode process

    STM     sp,{R0-lr}^             ; Dump user registers above R13.
    MRS     R0, SPSR                ; Pick up the user status
    STMDB   sp, {R0, lr}            ; and dump with return address below.
    LDR     sp, [R12], #4           ; Load next process info pointer.
    CMP     sp, #0                  ; If it is zero, it is invalid
    LDMDBNE sp, {R0, lr}            ; Pick up status and return address.
    MSRNE   SPSR_cxsf, R0           ; Restore the status.
    LDMNE   sp, {R0 - lr}^          ; Get the rest of the registers
    NOP
    SUBSNE pc, lr, #4               ; and return and restore CPSR.
                                    ; Insert "no next process code" here.

Copyright © 2002-2010 ARM. All rights reserved.ARM DUI 0203J
Non-Confidential
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值