Processor modes

The ARM implementation of CTL uses System and IRQ processor modes. Other processor modes are not used and, therefore, are available for use by the application. In normal execution, tasks run in System mode with IRQ interrupts enabled.

When CTL requires exclusive access to variables, for example when traversing the task list, IRQ interrupts are disabled. FIQ interrupts are always enabled by CTL. Co-operative context switching is done by changing to IRQ mode (with IRQ interrupts disabled) and, consequently, uses the IRQ mode stack. Preemptive context switching is done from an IRQ handler, which by definition is running in IRQ mode.

Register save order

When a task is not executing, the ARM register context is saved on the task's stack in the following order:

…with the stack_pointer member of the task structure pointing to the R4 entry which requires 16 words of memory.

For devices that have a VFP with 16 double precision registers the floating point registers are also saved as follows:

…with the stack_pointer member of the task structure pointing to the D8 entry (with 1 added to indicate that the floating point registers have been saved) which requires 49 words of memory.

For devices that have a VFP with 32 double precision registers the floating point registers are also saved as follows:

…with the stack_pointer member of the task structure pointing to the D8 entry (with 3 added to indicate that the 32 double precision floating point registers have been saved) which requires 81 words of memory.

IRQ handler

On entry to an IRQ handler, the ctl_interrupt_count variable should be incremented. On exit from an IRQ handler, the ctl_exit_isr routine should be called with a parameter in R0 specifying which registers have been saved by the IRQ handler.

If ctl_exit_isr(0) is called, the registers should be saved, as in the following example:

irq_handler:
  \vdots
  // store the APCS registers
  sub   lr, lr, #4
  stmfd sp!, {r0-r3, r12, lr}

  // ctl_interrupt_count++
  ldr   r2, =ctl_interrupt_count
  ldrb  r3, [r2]
  add   r3, r3, #1
  strb  r3, [r2]
  \vdots
  // handle interrupt, possibly re-enabling interrupts
  \vdots
  // ctl_exit_isr(0)
  mov   r0, #0
  ldr   r1, =ctl_exit_isr
  bx    r1

If ctl_exit_isr(!0) is called, the registers should be saved, as in the following example:

irq_handler:
  \vdots
  // store all the registers
  stmfd sp!, {r0-r12, lr}
  mrs   r0, spsr
  stmfd sp!, {r0}

  // ctl_interrupt_count++
  ldr   r2, =ctl_interrupt_count
  ldrb  r3, [r2]
  add   r3, r3, #1
  strb  r3, [r2]
  \vdots
  // handle interrupt, possibly re-enabling interrupts
  \vdots
  // ctl_exit_isr(!0)
  mov   r0, sp
  ldr   r1, =ctl_exit_isr
  bx    r1

The first form (ctl_exit_isr(0)) is recommended because it uses less stack space and takes fewer machine cycles. The second form (ctl_exit_isr(!0)) is provided for backwards compatibility with earlier releases of CTL.