Where could I find reentrant interrupt routine sample code that work with IRQ and/or FIQ. I am sure that working source code will speak by itself and will help other developper also.
I found some example from arm website documentation that switch to System mode but it faill after a shotrt time at exeution. I test it using a FIQ NHET interruption at 32Khz. . I seem that sometimes there it perform return att SRSR register instead of LR address.
http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf
If an interrupt handler enables interrupts before calling a subroutine and another interrupt occurs, the return address of the subroutine stored in the IRQ mode LR is corrupted when the second IRQ is taken. This is because the processor automatically saves the return address into the IRQ mode LR for the new interrupt overwriting the return address for the subroutine. This results in an infinite loop when the subroutine in the original interrupt tries to return.
A reentrant interrupt handler must save the IRQ state, switch processor modes, and save the state for the new processor mode before branching to a nested subroutine or C function. It must also ensure that the stack is eight-byte aligned for the new processor mode before calling AAPCS-compliant compiled C code that might use LDRD or STRD instructions or eight-byte aligned stack-allocated data.
Using the __irq keyword in C does not cause the SPSR to be saved and restored, as required by reentrant interrupt handlers, so you must write your top level interrupt handler in assembly language.
In ARMv4 or later you can switch to System mode if you require privileged access.
Note
This method works for both IRQ and FIQ interrupts. However, because FIQ interrupts are meant to be handled as quickly as possible there is normally only one interrupt source, so it might not be necessary to provide for reentrancy.
The steps required to enable interrupts safely in an IRQ handler are:
-
Construct the return address and save it on the IRQ stack.
-
Save the work registers, non callee-saved registers and IRQ mode SPSR.
-
Clear the source of the interrupt.
-
Switch to System mode, keeping IRQs disabled.
-
Check that the stack is eight-byte aligned and adjust if necessary.
-
Save the User mode LR and the adjustment, 0 or 4 for Architectures v4 or v5TE, used on the User mode SP.
-
Enable interrupts and call the C interrupt handler function.
-
When the C interrupt handler returns, disable interrupts.
-
Restore the User mode LR and the stack adjustment value.
-
Readjust the stack if necessary.
-
Switch to IRQ mode.
-
Restore other registers and IRQ mode SPSR.
-
Return from the IRQ.
The following examples show how this works for System mode.
Example 37. Reentrant interrupt handler for ARMv4/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 38. 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
