If your system doesn’t support a programmable interrupt controller and you want tasks to be rescheduled when interrupts occur, you must save the register state of the CPU on entry to an interrupt service routine and increment the global variable ctl_interrupt_count.

When you are executing an interrupt service routine you must not call the tasking library functions that may block (task_wait_events, task_wait_semaphore, task_post_message, task_receive_message, task_wait_timeout) — you can call other tasking library functions, but a task switch will only occur when the last interrupt handler has completed execution.

Whilst you are executing an interrupt service routine you can allow interrupts of a higher priority to occur by calling ctl_global_interrupts_re_enable_from_isr. You must also disable interrupts before exit from the interrupt service routine by calling ctl_global_interrupts_un_re_enable_from_isr.

In order to achieve a task switch from an interrupt service routine the ctl_exit_isr function must be jumped to as the last action of an interrupt service routine. This function must be passed a pointer to the saved registers.

Interrupt service routine (ARM example)

This example declares an ISR using the GCC syntax for declaring naked functions and accessing assembly code instructions.

void irq_handler(void) __attribute__((naked));

void
irq_handler(void)
{
  asm("stmfd sp!, {r0-r12, lr}");
  asm("mrs r0, spsr");
  asm("stmfd sp!, {r0}");
  ctl_interrupt_count++;
  ....
  // do interrupt handling stuff in here
  ....
  asm("mov r0, sp");
  asm("b ctl_exit_isr");
}

Note that the registers SPSR, R0 through R12 and R14 (user mode program counter) must be saved on the stack. The user mode R13 and R14 registers don’t need to be saved because they are held in banked registers.

Note that FIQ handlers are not supported on the ARM.