9.5.1. Simple interrupt handlers in C

You can write simple C interrupt handlers by using the __irq function declaration keyword. You can use the __irq keyword both for simple one-level interrupt handlers, and interrupt handlers that call subroutines. However, you cannot use the __irq keyword for reentrant interrupt handlers, because it does not store all the required state. In this context, reentrant means that the handler re-enables interrupts, and may itself be interrupted. Refer to Reentrant interrupt handlers for more information.

The __irq keyword:

If the function calls a subroutine, __irq preserves the link register for the interrupt mode in addition to preserving the other corruptible registers. See Calling subroutines from interrupt handlers for more information.

Note

C interrupt handlers cannot be produced in this way using tcc. The __irq keyword is faulted by tcc because tcc can only produce Thumb code, and the processor is always switched to ARM state when an interrupt, or any other exception, occurs.

However, the subroutine called by an __irq function can be compiled for Thumb, with interworking enabled. Refer to Chapter 7 Interworking ARM and Thumb for more information on interworking.

Example 9.11 shows a simple handler that does not call any subroutines. The handler reads a byte from location 0x80000000 and clears the interrupt by writing it to location 0x80000004.

The __irq keyword ensures that r0-r3 and r12 are preserved, and that the function exits with SUBS pc,lr,#4.

Example 9.11. 

__irq void IRQHandler(void)
{
	volatile char *base = (char *) 0x80000000;											// read a byte
	*(base + 4) = *base;									// clear the interrupt
}

Compiled with armcc Example 9.11 gives the following code:


 EXPORT IRQHandler
IRQHandler
0x000000:  e92d100f  ..-. : STMFD    sp!,{r0-r3,r12}
0x000004:  e3a00102  .... : MOV      r0,#0x80000000
0x000008:  e5d01000  .... : LDRB     r1,[r0,#0]
0x00000c:  e5c01004  .... : STRB     r1,[r0,#4]
0x000010:  e8bd100f  .... : LDMFD    sp!,{r0-r3,r12}
0x000014:  e25ef004  ..^. : SUBS     pc,lr,#4

Calling subroutines from interrupt handlers

If you call subroutines from your top level interrupt handler, the __irq keyword also restores the value of lr_IRQ from the stack so that it can be used by a SUBS instruction to return to the correct address after the interrupt has been handled.

Example 9.12 shows how this works. The top level interrupt handler reads the value of a memory mapped interrupt controller base address at 0x80000000. If the value of the address is 1, the top level handler branches to a handler written in C.

Example 9.12. 

__irq void IRQHandler (void)
{	
	volatile unsigned int *base = (unsigned int *) 0x80000000;
	if (*base == 1)						// which interrupt was it?
	{
		C_int_handler();					// process the interrupt
	}
	*(base+1) = *base;						// clear the interrupt
}

Compiled with armcc, Example 9.12 produces the following code:


  EXPORT IRQHandler
IRQHandler
  0x000000:  e92d501f  .P-. :STMFD    sp!,{r0-r4,r12,lr}
  0x000004:  e3a04102  .A.. :MOV      r4,#0x80000000
  0x000008:  e5940000  .... :LDR      r0,[r4,#0]
  0x00000c:  e3500001  ..P. :CMP      r0,#1
  0x000010:  0bfffffa  .... :BLEQ     C_int_handler
  0x000014:  e5940000  .... :LDR      r0,[r4,#0]
  0x000018:  e5840004  .... :STR      r0,[r4,#4]
  0x00001c:  e8bd501f  .P.. :LDMFD    sp!,{r0-r4,r12,lr}
  0x000020:  e25ef004  ..^. :SUBS     pc,lr,#4

Compare this to the result of not using the __irq keyword:


  EXPORT IRQHandler
IRQHandler
  0x000000:  e92d4010  .@-. :STMFD    sp!,{r4,lr}
  0x000004:  e3a04102  .A.. :MOV      r4,#0x80000000
  0x000008:  e5940000  .... :LDR      r0,[r4,#0]
  0x00000c:  e3500001  ..P. :CMP      r0,#1
  0x000010:  0bfffffa  .... :BLEQ     C_int_handler
  0x000014:  e5940000  .... :LDR      r0,[r4,#0]
  0x000018:  e5840004  .... :STR      r0,[r4,#4]
  0x00001c:  e8bd8010  .... :LDMFD    sp!,{r4,pc}

Copyright © 1997, 1998 ARM Limited. All rights reserved.ARM DUI 0040D
Non-Confidential