1 /*-----------------------------------------------------------*/ 2 // 当进入PendSVC Handler时, 3 // 上一个任务运行的环境即xPSR、PC(任务入口地址)、r14/r12/r3/r2/r1/r0寄存器的值 4 // 会自动存储到任务的栈中,剩下的r4~r11需要手动保存 5 __asm void xPortPendSVHandler( void ) 6 { 7 extern uxCriticalNesting; 8 extern pxCurrentTCB; 9 extern vTaskSwitchContext; 10 11 /* *INDENT-OFF* */ 12 PRESERVE8 13 /* PendSV用于上下文的切换,进入PendSV时,处于上文环境 */ 14 /* 进入xPortPendSVHandler时,r0~r3已经自动入栈了,此时可以任意使用r0~r3的值而不会破坏上下文环境 */ 15 /* 此时psp仍然是"上文"任务的堆栈指针 */ 16 /* 保存psp到r0的目的是为了后面通过stmdb指令保存"上文"任务的r4~r11 */ 17 /* 把xPSR、PC、LR、r12、r3、r2、r1、r0、r11、r10、r9、r8、r7、r6、r5、r4都入栈了,才算是完整保存"上文"环境 */ 18 mrs r0, psp /* 将PSP的值存储到r0中 */ 19 isb 20 /* Get the location of the current TCB. */ 21 /* 此时pxCurrentTCB还是指向被切换的任务,可以认为是"上文"任务 */ 22 /* 加载pxCurrentTCB地址到r3 */ 23 ldr r3, =pxCurrentTCB 24 /* 加载r3指向的内容到r2,即r2等于pxCurrentTCB */ 25 /* 用于最后保存"上文"环境时,更新栈顶指针到"上文"任务TCB的pxTopOfStack中 */ 26 ldr r2, [ r3 ] 27 28 /* Is the task using the FPU context? If so, push high vfp registers. */ 29 tst r14, #0x10 30 it eq 31 vstmdbeq r0!, {s16-s31} 32 33 /* Save the core registers. */ 34 /* 以r0作为基地址(即前面保存的psp),将CPU寄存器r4~r11的值存储到任务堆栈中,同时更新r0的值 */ 35 /* 最后r0指向"上文"任务的栈顶 */ 36 stmdb r0!, {r4-r11, r14} 37 38 /* Save the new top of stack into the first member of the TCB. */ 39 /* 经过stmdb指令的操作,"上文"任务的栈顶指针已经更新了 */ 40 /* 因此需要更新到"上文"任务TCB的pxTopOfStack中 */ 41 str r0, [ r2 ] 42 /* 至此,上下文切换的"上文"环境保存完成 */ 43 44 45 /* 临时保存r0和r3,此时进栈是MSP的栈 */ 46 stmdb sp!, {r0, r3} 47 /* 关中断 */ 48 mov r0, # configMAX_SYSCALL_INTERRUPT_PRIORITY 49 msr basepri, r0 50 dsb 51 isb 52 /* 选择优先级最高的"下文"任务,然后更新pxCurrentTCB */ 53 bl vTaskSwitchContext 54 /* 开中断 */ 55 mov r0, # 0 56 msr basepri, r0 57 /* 临时恢复r0和r3,此时出栈是MSP的栈 */ 58 ldmia sp!, {r0, r3} 59 60 /* 以下为上下文切换的"下文"环境切换 */ 61 62 /* 经过vTaskSwitchContext,pxCurrentTCB的值已经更新,pxCurrentTCB已经指向了"下文"任务 */ 63 /* r3仍然保存着pxCurrentTCB的地址值 */ 64 /* 加载r3指向的内容到r1,r3存放的是pxCurrentTCB的地址值,即r1等于pxCurrentTCB */ 65 /* r1指向了下一个将要运行的任务的TCB */ 66 /* The first item in pxCurrentTCB is the task top of stack. */ 67 ldr r1, [ r3 ] 68 /* 加载r1指向的内容到r0,即下一个要运行的任务的栈顶指针 */ 69 ldr r0, [ r1 ] 70 71 /* Pop the core registers. */ 72 /* 以r0作为基地址,将下一个要运行的任务的任务堆栈内容加载到CPU寄存器r4~r11(手动加载) */ 73 ldmia r0!, {r4-r11, r14} 74 75 /* Is the task using the FPU context? If so, pop the high vfp registers 76 * too. */ 77 tst r14, # 0x10 78 it eq 79 vldmiaeq r0!, {s16-s31} 80 81 /* 更新psp的值,等PendSV退出时,会以psp作为基地址,将任务栈中剩下的内容自动加载到CPU寄存器 */ 82 /* 剩下的内容包括: xPSR、PC、LR、r12、r3、r2、r1、r0 */ 83 msr psp, r0 84 85 isb 86 #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */ 87 #if WORKAROUND_PMU_CM001 == 1 88 push { r14 } 89 pop { pc } 90 nop 91 #endif 92 #endif 93 /* 异常发生时,r14中保存异常返回标志,包括返回后进入任务模式还是处理器模式,使用psp栈指针还是msp栈指针 */ 94 bx r14 95 /* *INDENT-ON* */ 96 }