Building Bare-Metal ARM Systems with GNU: Part 8

Editor's note: In this series of ten articles Miro Samek ofQuantum Leaps details developing apps on the ARM processor using QNU,complete with source code in C and C++.

In this part in the series, I describe thelow-level interrupt wrapper functions ARM_irq() and ARM_fiq(). Thesefunctions have been introduced in Part6. In this series, and their purpose is to allow handling ofnested interrupts in the ARM architecture, which the GNU gcc__attribute__ ((interrupt (“IRQ”))) cannot do.

Perhaps the most interesting aspect of the implementation that Ipresent here is its close compatibility with the new ARM v7-Marchitecture (e.g., Cortex-M3) [4] .Specifically, the low-level wrapper functions deal with all theARM-magic internally, so that the interrupt service routines (ISRs)that you hook to the interrupt controller can be regular C-functions.

The C-level ISRs run in the same processor mode (SYSTEM) as the codecalled from main() (task-level). Also, the assembler wrapper functionsexpressly avoid using the IRQ/FIQ stacks and instead nest interrupts ofall types (IRQs and FIQs) on the SYSTEM/USER stack.

The interrupt context saved to the stack is optimized for high-levellanguages and just like in the ARM v7-M specification, the wrapperfunctions save only the 8 registers clobbered in the ARM ArchitectureProcedure Calling Standard (AAPCS) [4] .

In fact, the interrupt wrapper functions generate in software theexact same interrupt stack frame (SPSR, PC, LR, R12, R3, R2, R1, R0) asthe ARM v7-M processors generate in hardware [4] .

<>I should perhaps note right away that the ARM interrupthandlingimplementation described here goes off the beaten path established bythe traditional approaches [1,2,3,5] .Most published implementations recommend initializing the ARM vectortable to use the “auto-vectoring” feature of the interrupt controller(see Part 6 of this articleseries). <>

Consequently the ISRs that you hook to the interrupt controller requirevery special entry and exit sequences, so they cannot be regularC-functions. Also, the interrupt handlers execute in IRQ or FIQ modeand use the IRQ and FIQ stacks (at least for some portion of the savedcontext). Consequently the stack frames as well as stack usage in thetraditional implementations are quite different compared to ARM v7-M.

The IRQ Interrupt Wrapper ARM_irq
The interrupt “wrapper” function ARM_irq() for handling the IRQ-typeinterrupts is provided in the file arm_exc.s included in the codeaccompanying this article series. Listing1 below shows the entire function.

Listing1. The ARM_irq() assembly wrapper function defined in the filearm_exc.s.

The highlights of the implementation are as follows:

(1) The low-level IRQ/FIQhandlers must be written in the 32-bit instruction set (ARM), becausethe ARM core automatically switches to the ARM state when IRQ/FIQ isrecognized.

(2) The ARM_irq wrapperfunction is defined in the special section (.text.fastcode), which thelinker script locates in RAM (see part 3 of this article series) forfaster execution.

(3) The IRQ stack is notused, so the banked stack pointer register r13_IRQ (sp_IRQ) is used asa scratchpad register to temporarily hold r0 from the SYSTEM context.

NOTE: As part of the IRQ startupsequence, the ARM processor sets the I bit in the CPSR (CPSR[7] = 1),but leaves the F bit unchanged (typically cleared), meaning thatfurther IRQs are disabled, but FIQs are not. This implies that FIQ canbe recognized while the ARM core is in the IRQ mode. The FIQ handlerARM_fiq discussed in the next section can safely preempt ARM_irq in allplaces where FIQs are not explicitly disabled.

(4) Now r0 can be clobberedwith the return address from the interrupt that needs to be saved tothe SYSTEM stack.

(5) At this point the bankedlr_IRQ register can be reused to temporarily hold r1 from the SYSTEMcontext.

(6) Now r1 can be clobberedwith the value of spsr_IRQ register (Saved Program Status Register)that needs to be saved to the SYSTEM stack.

(7) Mode is changed toSYSTEM with IRQ interrupt disabled, but FIQ explicitly enabled. Thismode switch is performed to get access to the SYSTEM registers.

NOTE: The F bit in the CPSR isintentionally cleared at this step (meaning that the FIQ is explicitlyenabled). Among others, this represents the workaround for the Problem2 described in ARM Technical Note “What happens if an interrupt occursas it is being disabled?” (see the previous Part7 of this article series).

(8) The SPSR register andthe return address from the interrupt (PC after the interrupt) arepushed on the SYSTEM stack.

(9) All registers (exceptr0 and r1) clobbered by the AAPCS (ARM Architecture Procedure CallStandard) [4] are pushed on the SYSTEM stack.

(10) The SYSTEM stackpointer is placed in r0 to be visible in the IRQ mode.

(11) The SYSTEM stackpointer is adjusted to make room for two more registers of the savedIRQ context. By adjusting the SYSTEM stack pointer, the IRQ handler canstill keep FIQ enabled without the concern of corrupting the SYSTEMstack space reserved for the IRQ context.

(12) The mode is switchedback to IRQ with IRQ interrupt disabled, but FIQ still enabled. This isdone to get access to the rest of the context sitting in the IRQ-bankedregisters.

(13) The context is entirelysaved by pushing the original r0 and r1 (still sitting in the bankedIRQ Registers r14_IRQ and r13_IRQ, respectively) to the SYSTEM stack.At this point the saved SYSTEM stack frame contains 8 registers andlooks as follows (this is exactly the ARM v7-M interrupt stack frame[4]):

(14) The mode is switchedonce more to SYSTEM with IRQ disabled and FIQ enabled. Please note thatthe stack pointer sp_SYS points to the top of the stack frame, becauseit has been adjusted after the first switch to the SYSTEM mode at point(11).

(15-17) The board-specificfunction BSP_irq() is called to perform the interrupt processing at theapplication-level. Please note that BSP_irq() is now a regular Cfunction in ARM or Thumb. Typically, this function uses thesilicon-vendor specific interrupt controller (such as the Atmel AIC) tovector into the current interrupt, as was discussed in part 6 of thisarticle.

NOTE: The BSP_irq() function isentered with IRQ disabled (and FIQ enabled), but it can internallyunlock IRQs, if the MCU is equipped with an interrupt controller thatperforms prioritization of IRQs in hardware.

(18) All interrupts (IRQ andFIQ) are locked to execute the following instructions atomically.

(19) The sp_SYS register ismoved to r0 to make it visible in the IRQ mode.

(20) Before leaving theSYSTEM mode, the sp_SYS stack pointer is adjusted to un-stack the wholeinterrupt stack frame of 8 registers. This brings the SYSTEM stack toexactly the same state as before the interrupt occurred.

NOTE: Even though the SYSTEM stackpointer is moved up, the stack contents have not been restored yet. Atthis point it's critical that the interrupts are completely locked, sothat the stack contents above the adjusted stack pointer cannot becorrupted.

(21) The mode is changed toIRQ with IRQ and FIQ interrupts locked to perform the final return fromthe IRQ.

(22) The SYSTEM stackpointer is copied to the banked sp_IRQ, which thus is set to point tothe top of the SYSTEM stack

(23-24) The value of SPSR isloaded from the stack (please note that the SPSR is now 7 registersaway from the top of the stack) and placed in SPSR_irq.

(25) The 6 registers arepopped from the SYSTEM stack. Please note the special version of theLDM instruction (with the '^' at the end), which means that theregisters are popped from the SYSTEM/USER stack. Please also note thatthe special LDM(2) instruction does not allow the write-back, so thestack pointer is not adjusted. (For more information please refer toSection “LDM(2)” in the “ARM Architecture Reference Manual” [5]. )

Listing 4(26) It'simportant not to access any banked register after the special LDM(2)instruction.

Listing 4(27) The returnaddress is retrieved from the stack. Please note that the returnaddress is now 6 registers away from the top of the stack.

Listing 4(28) The interruptreturn involves loading the PC with the return address and the CPSRwith the SPSR, which is accomplished by the special version of the MOVSpc,lr instruction.

The FIQ Interrupt Wrapper ARM_fiq
The interrupt “wrapper” function ARM_fiq() for handling the FIQ-typeinterrupts is provided in the file arm_exc.s included in the codeaccompanying this article series. Listing2 below shows the entire function.

Listing2. The ARM_fiq() assembly wrapper function defined in the filearm_exc.s.

The ARM_fiq() “wrapper” function is very similar to the IRQ handler (Listing 1 ), except the FIQ mode isused instead of the IRQ mode. The following comments explain only theslight, but important differences in disabling interrupts and theresponsibilities of the C-level handler BSP_fiq().

(1) The FIQ handler isalways entered with both IRQ and FIQ disabled, so the FIQ mode is notvisible in any other modes. The ARM_fiq handler keeps the IRQ and FIQlocked at all times.

(2) The mode is switched toSYSTEM to get access to the SYSTEM stack pointer. Please note that bothIRQ and FIQ interrupts are kept disabled throughout the FIQ handler.

(3) The C-function BSP_fiq()is called to perform the interrupt processing at the application-level.Please note that BSP_fiq() is now a regular C function in ARM or THUMB.Unlike the IRQ, the FIQ interrupt is often not covered by the prioritycontroller, therefore the BSP_fiq() should NOT unlock interrupts.

NOTE: The BSP_fiq() function isentered with both IRQ and FIQ interrupts disabled and it should NEVERenable any interrupts. Typically, the FIQ line to the ARM core does nothave a priority controller, even though the FIQ line typically goesthrough a hardware interrupt controller .

Coming Up Next
In the next Part 9 in this series ,I'll wrap up the interrupt handling for ARM by presenting examples ofthe interrupt service routines (ISRs) in C, as well as theinitialization of the vector table and the interrupt controller. I'llalso discuss a rudimentary policy of handling other ARM Exceptions,such as Undefined Instruction or Data Abort.

To read Part 1, go to What'sneed to get started.
To read Part 2, go to Startup code and the low level initialization
To read Part 3, go to  TheLinker Script.
To read Part 4, go to  Cand C++ compiler options
To read Part 5, go to  Fine-tuningthe application
To read Part 6, go to  Generaldescription of interrupt handling
To read Part 7, go to  InterruptLocking and Unlocking

To download the C and C++ sourcecode associated with this article series, go to Embedded.com's Downloadable Code page , or go to Blinky for C and Blinky for C++ to download theZipfiles.

MiroSamek, Ph.D., is president of QuantumLeaps, LLC. He can becontacted at miro@quantum-leaps.com.

References
[1] ARM Technical Support Note”Writing InterruptHandlers” available online.
[2] Philips Application NoteAN10381 “Nestingof Interrupts in the LPC2000” available online.
[3] Atmel Application Note”InterruptManagement: Auto-vectoring and Prioritization” availableonline.
[4] ARM Limited, “ARMv7-MArchitecture Application Level Reference Manual“, available online. .
[5] Seal, David Editor, “ARMArchitecture Manual, 2nd Edition”, Addison Wesley 2000.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.