Cortex M0下coos调度上下文切换原理

Coos上下文切换

Coos与上下文切换有关的文件主要是arch.c和port.c。
Coos使用PendSV_Handler中断进行上下文切换。
Coos使用SysTick_Handler中断作为trick计时,并引发调度,导致上下文切换。
Coos在等待sem, mutex,event,创建/删除/挂起/Delay task,修改task优先级会引发调度,导致上下文切换。

Schedule

Schedule函数判断是否要切换上下问,如果要找出要切换的下一个task的pcb放到TCBNext中。
执行SwitchContext引发上下文切换

SwitchContext

U32 NVIC_INT_CTRL  = 0xE000ED04;            // Interrupt control state register
U32 NVIC_PENDSVSET = 0x10000000;            // Value to trigger PendSV exception
void SwitchContext(void)
{
  __asm volatile
  (
      " LDR     R3,=NVIC_INT_CTRL  \n"
      " LDR     R3,[R3]            \n"
      " LDR     R2,=NVIC_PENDSVSET \n"
      " LDR     R1,[R2]            \n"
      " STR     R1, [R3]           \n"
      " BX      LR               \n"   
  ); 
}

SwitchContext通过向0xE000ED04写0x10000000触发PendSV中断,在PendSV中断中进行上下文切换。

上下文切换

void PendSV_Handler(void)
{
////////debug block /////////////////////////
  __asm volatile
 (
     " LDR     R3,=TCBRunning   \n"
     " LDR     R1,[R3]          \n"   // R1 == running tcb
     " LDR     R2,=TCBNext      \n"
     " LDR     R2,[R2]          \n"   // R2 == next tcb

     " CMP     R1,R2            \n"
     " BEQ     exitPendSV       \n"    //如果上下文是同一个task,不做切换直接退出   
     " MRS     R0, PSP          \n"    //将SP保存在R0中,Cortex-M0在中断处理中,使用的是MSP,而各个Task的SP是保存在PSP中,需要获得当前执行任务的SP,需要从PSP中读出

     //Cortex-M0在进中断时硬件会自动将R0~R3,R12,RL(R14),PC(R15),xPSR入栈,因此不用软件保护
     //R4~R11软件入栈,完成当前任务的上下文切换
     " SUB     R0,R0,#32        \n"    //SP指针移动32字节,准备保存R4~R11
     " STR     R0,[R1]          \n"    //将SP指针保存到R1中,因为之后的SIMIA会修改R0

     " STMIA   R0!,{R4-R7}      \n"    //将R4~R7入栈
     " MOV     R4,R8            \n"
     " MOV     R5,R9            \n"
     " MOV     R6,R10           \n"
     " MOV     R7,R11           \n"
     " STMIA   R0!,{R4-R7}      \n"    //将R8~R11入栈

     //切换到下一个task
     " popStk:                  \n" 
       " STR     R2, [R3]         \n"    // TCBRunning  = TCBNext;
       " LDR     R0, [R2]         \n"    // 把next task 的sp指针送到R0中.

       " ADD     R0,R0,#16        \n"    //SP指针移动堆栈内的R8保存处,准备恢复R8~R11
       " LDMIA   R0!,{R4-R7}      \n"    // Restore new Context (R8-R11)
       " MOV     R8,R4            \n"
       " MOV     R9,R5            \n"
       " MOV     R10,R6           \n"
       " MOV     R11,R7           \n"
       " SUB     R0,R0,#32        \n"    //SP移动到堆栈内的R4保存处,准备恢复R4~R7
       " LDMIA   R0!,{R4-R7}      \n"    // Restore new Context (R4-R7)
       " ADD     R0,R0,#16        \n"    //将SP指向堆栈内的R0保存处
       " MSR     PSP, R0          \n"    //将新的SP更新到PSP中

     " exitPendSV:              \n"      //将调度锁设置为0
       " LDR    R3,=OSSchedLock   \n"
       " MOV    R0, #0x0          \n"
       " STRB   R0, [R3]          \n"

       " LDR     R3,=0xFFFFFFED     \n"   //设置EXC_RETURN为返回到thread mode使用PSP作为SP
       " LDR     R0, [R3]         \n"
       " BX      R0               \n"    // Exit interrupt,完成上下文切换
  );
}

以上过程为中断处理过程,整个上下文切换包括硬件处理和软件处理可图示为:
Cortex M0下coos调度上下文切换原理_第1张图片

Cortex-M0堆栈与CoOS OS启动

Cortex-M0堆栈特性

Cortex-M0的R13为SP指针,根据CONTROL寄存器的bit1不同,分别操作MSP和PSP。
Cortex M0下coos调度上下文切换原理_第2张图片
进行POP和PUSH时根据CONTROL[1]的不同,选择操作MSP或是PSP.
当Cortex-M0上电启动时,默认是MSP

Coos OS启动

Coos OS在第一次调度前都处于使用MSP的状态。
调用CoInitOS初始化OS,此时会创建一个idle task。
创建task_init。
调用CoStartOS开始调度。将runing tcb设置为idle task,将task init设置为next tcb。
由于在CoStartOS的时候会引发第一次调度,此后就不会调度回CoStartOS的调用者。因此要在CoStartOS前再创建初始化任务,否则无法调度起来。
第一次调度发生时,会将CoStartOS调用者的R0~R3,R12,R14,R15入栈到MSP,当第一次调度完成后,返回到Thread mode使用PSP,之后除了在中断处理下使用MSP,再无机会使用MSP,因此CoStartOS的调用者入栈的内容无法再使用。

你可能感兴趣的:(Cortex M0下coos调度上下文切换原理)