I have a little "os" for an arm cortex m4. I implemented a wait function. But since then somehow, the context switch is corrupted. When stepping through the instructions i noticed, that for whatever reason the current_task
variable gets overriden at entering the PendSV interrupt.
These are global variables
volatile struct OS_task * current_task;
volatile struct OS_task * next_task;
of the following type:
struct OS_task{
volatile unsigned int *sp;
void (*handler)(void * params);
void * params;
volatile enum task_state state;
volatile unsigned char number;
volatile unsigned int delay;
};
This is the scheduler function. It is called from the Systick interrupt, too.
void OS_Scheduler(void)
{
current_task = &OS_tasktable.task_list[OS_tasktable.current_task];
current_task->state = OS_TASK_STATE_IDLE;
int next = OS_GetNextTask(OS_tasktable.current_task);
while (1)
{
if (OS_tasktable.task_list[next].delay == 0)
break;
OS_tasktable.task_list[next].delay--;
next = OS_GetNextTask(next);
}
OS_tasktable.current_task = next;
next_task = &OS_tasktable.task_list[OS_tasktable.current_task];
next_task->state = OS_TASK_STATE_ACTIVE;
S32_SCB->ICSR |= S32_SCB_ICSR_PENDSVSET_MASK;
}
This is the PendSV handler. Although i'm working on a Cortex-M4F i do not save the FPU registers simply because i don't need floating point arithmetics.
.syntax unified
.thumb
.global PendSV_Handler
.type PendSV_Handler, %function
PendSV_Handler:
/* Disable interrupts: */
cpsid i
/* Save registers R4-R11 (32 bytes) onto current PSP (process stack
pointer) and make the PSP point to the last stacked register (R8).*/
mrs r0, psp
subs r0, #16
stmia r0!,{r4-r7}
mov r4, r8
mov r5, r9
mov r6, r10
mov r7, r11
subs r0, #32
stmia r0!,{r4-r7}
subs r0, #16
/* Save current task's SP: */
ldr r2, =current_task
ldr r1, [r2]
str r0, [r1]
/* Load next task's SP: */
ldr r2, =next_task
ldr r1, [r2]
ldr r0, [r1]
/* Load registers R4-R11 (32 bytes) from the new PSP and make the PSP
point to the end of the exception stack frame. */
ldmia r0!,{r4-r7}
mov r8, r4
mov r9, r5
mov r10, r6
mov r11, r7
ldmia r0!,{r4-r7}
msr psp, r0
/* EXC_RETURN - Thread mode with PSP: */
ldr r0, =0xFFFFFFFD
/* Enable interrupts: */
cpsie i
bx r0
.size PendSV_Handler, .-PendSV_Handler
This is the wait function. It is called through a SVC interrupt. After this has executed, the current_task
and next_task
variables are set correctly. Only upon entering the following PendSV interrupt, somehow current_task
gets overriden. Which leads to both tasks getting set to the same stack -> not good.
void __os_wait_ms(unsigned int ms)
{
struct OS_task * current;
current = &OS_tasktable.task_list[OS_tasktable.current_task];
current->delay = ms * OS_tasktable.delay_factor;
OS_Scheduler();
return;
}
If it helps: I use the S32K146EVB from NXP to be precise.
EDIT: I disable interrupts during the wait function executes, to avoid the Systick calling the Scheduler and messing things up.