BeRTOS
switch_ctx_cm3.c
Go to the documentation of this file.
00001 
00038 #include <cfg/compiler.h>
00039 #include <cfg/cfg_proc.h> /* CONFIG_KERN_PREEMPT */
00040 #include <cpu/irq.h> /* IRQ_PRIO_DISABLED */
00041 #include <cpu/types.h> /* cpu_stack_t */
00042 #include <kern/proc_p.h> /* asm_switch_context() prototype */
00043 #include <kern/proc.h> /* proc_preempt() */
00044 #include "switch_ctx_cm3.h"
00045 
00046 #if CONFIG_KERN_PREEMPT
00047 /*
00048  * Kernel preemption: implementation details.
00049  *
00050  * The kernel preemption is implemented using the PendSV IRQ. Inside the
00051  * SysTick handler when a process needs to be interrupted (expires its time
00052  * quantum or a high-priority process is awakend) a pending PendSV call is
00053  * triggered.
00054  *
00055  * The PendSV handler is called immediately after the SysTick handler, using
00056  * the architecture's tail-chaining functionality (an ISR call without the
00057  * overhead of state saving and restoration between different IRQs). Inside the
00058  * PendSV handler we perform the stack-switching between the old and new
00059  * processes.
00060  *
00061  * Voluntary context switch is implemented as a soft-interrupt call (SVCall),
00062  * so any process is always suspended and resumed from an interrupt context.
00063  *
00064  * NOTE: interrupts must be disabled or enabled when resuming a process context
00065  * depending of the type of the previous suspension. If a process was suspended
00066  * by a voluntary context switch IRQs must be disabled on resume (voluntary
00067  * context switch always happen with IRQs disabled). Instead, if a process was
00068  * suspended by the kernel preemption IRQs must be always re-enabled, because
00069  * the PendSV handler resumes directly the process context. To keep track of
00070  * this, we save the state of the IRQ priority in register r3 before performing
00071  * the context switch.
00072  *
00073  * If CONFIG_KERN_PREEMPT is not enabled the cooperative implementation
00074  * fallbacks to the default stack-switching mechanism, performed directly in
00075  * thread-mode and implemented as a normal function call.
00076  */
00077 
00078 /*
00079  * Voluntary context switch handler.
00080  */
00081 void NAKED svcall_handler(void)
00082 {
00083     asm volatile (
00084     /* Save context */
00085         "mrs r3, basepri\n\t"
00086         "mrs ip, psp\n\t"
00087         "stmdb ip!, {r3-r11, lr}\n\t"
00088     /* Stack switch */
00089         "str ip, [r1]\n\t"
00090         "ldr ip, [r0]\n\t"
00091     /* Restore context */
00092         "ldmia ip!, {r3-r11, lr}\n\t"
00093         "msr psp, ip\n\t"
00094         "msr basepri, r3\n\t"
00095         "bx lr" : : : "memory");
00096 }
00097 
00098 /*
00099  * Preemptible context switch handler.
00100  */
00101 void NAKED pendsv_handler(void)
00102 {
00103     register cpu_stack_t *stack asm("ip");
00104 
00105     asm volatile (
00106         "mrs r3, basepri\n\t"
00107         "mov %0, %2\n\t"
00108         "msr basepri, %0\n\t"
00109         "mrs %0, psp\n\t"
00110         "stmdb %0!, {r3-r11, lr}\n\t"
00111         : "=r"(stack)
00112         : "r"(stack), "i"(IRQ_PRIO_DISABLED)
00113         : "r3", "memory");
00114     proc_current()->stack = stack;
00115     proc_preempt();
00116     stack = proc_current()->stack;
00117     asm volatile (
00118         "ldmia %0!, {r3-r11, lr}\n\t"
00119         "msr psp, %0\n\t"
00120         "msr basepri, r3\n\t"
00121         "bx lr"
00122         : "=r"(stack) : "r"(stack)
00123         : "memory");
00124 }
00125 #else /* !CONFIG_KERN_PREEMPT */
00126 #ifdef __IAR_SYSTEMS_ICC__
00127 #else /* __IAR_SYSTEMS_ICC__ */
00128 void NAKED asm_switch_context(cpu_stack_t **new_sp, cpu_stack_t **old_sp)
00129 {
00130     register cpu_stack_t **_new_sp asm("r0") = new_sp;
00131     register cpu_stack_t **_old_sp asm("r1") = old_sp;
00132 
00133     asm volatile (
00134         "mrs ip, psp\n\t"
00135         /* Save registers */
00136         "stmdb ip!, {r4-r11, lr}\n\t"
00137         /* Save old stack pointer */
00138         "str ip, [%1]\n\t"
00139         /* Load new stack pointer */
00140         "ldr ip, [%0]\n\t"
00141         /* Load new registers */
00142         "ldmia ip!, {r4-r11, lr}\n\t"
00143         "msr psp, ip\n\t"
00144         "bx lr"
00145         : : "r"(_new_sp), "r"(_old_sp) : "ip", "memory");
00146 }
00147 #endif /* __IAR_SYSTEMS_ICC__ */
00148 #endif /* CONFIG_KERN_PREEMPT */