1、
对进入临界区和跳出临界区的函数理解
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SRSave();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
OS_CPU_SR_Save
MRS R0, PRIMASK
;把状态寄存器的值保存到R0中
CPSID I
;关中断
BX LR
OS_CPU_SR_Restore
MSR PRIMASK, R0
把R0的值赋值到PRIMASK中
BX LR
临界段的代码需要关中断,处理完毕后开中断。为了保存当前系统的中断转态。
一直没理解cpu_sr变量与R0是如何传值的,其实这个属于一个《AAPCS对ARM结构的一些标准做了定义》,也就是说默认是用R0自动做传递的,参考AAPTCS,可以了解到,这里有一个变量,故使用R0做一个传递值的寄存器。
2、
对OS_TSAK_SW()函数的理解
在UCOS-II中对任务进行切换的时候,调用了OS_TSAK_SW()函数,这个函数也是用汇编写成的。这个函数人为的模仿了一次中断。调用PendSV异常,对任务实现了切换。
NVIC_INT_CTRL EQU 0xE000ED04;
;中断控制寄存器地址
NVIC_PENDSVSET EQU 0x10000000;
;该值能够触发PendSV异常
OSCtxSw
LDR R0, =NVIC_INT_CTRL
;Trigger the PendSV exception (causes context switch)把NVIC_INT_CTRL的值赋值给R0
LDR R1, =NVIC_PENDSVSET
;把NVIC_PENDSVSET赋值给R1
STR R1, [R0]
;把R1的值赋值到R0指向的地址中,即R1=*R0
BX LR
上面的函数运行结束后,已经触发了一个PendSV异常。运行到PendSV函数中。
OS_CPU_PendSVHandler
;xPSR, PC, LR, R12, R0-R3 已自动保存
CPSID I
; Prevent interruption during context switch
MRS R0, PSP
; PSP is process stack pointer
CBZ R0, OS_CPU_PendSVHandler_nosave
; Skip register save the first time
;如果是第一次任务切换,
;直接跳转,不进行下面的这部分
SUBS R0, R0, #0x20
; Save remaining regs r4-11 on process stack,
;因为CM3是向下满栈的,也就是向下增长的,每入栈一字节之前,
;SP每次减4;这里需要入栈,R4-R11为32位的寄存器,8*32/8=32
STM R0, {R4-R11}
;一次把R4-R11的值放到R0指向的堆栈
LDR R1, =OSTCBCur
; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
;R1=*R1
STR R0, [R1]
;*R1=R0
; R0 is SP of process being switched out
; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
PUSH {R14}
; Save LR exc_return value
; OSTaskSwHook();
LDR R0, =OSTaskSwHook
BLX R0
POP {R14}
; OSPrioCur = OSPrioHighRdy;
LDR R0, =OSPrioCur
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
; OSTCBCur = OSTCBHighRdy;
LDR R0, =OSTCBCur
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2]
; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11}
; Restore r4-11 from new process stack
ADDS R0, R0, #0x20
MSR PSP, R0
; Load PSP with new process SP
ORR LR, LR, #0x04
; Ensure exception return uses process stack
CPSIE I
BX LR
; Exception return will restore remaining context
END
在上面的代码中主要讲述了使用PendSV中断来对两个任务之间进行切换。主要做的工作是对两个任务的各个寄存器进行PUSH及POP。同时第一个任务切换的时候不需要保存各个寄存器的值。
3、
在OSStart()函数中调用OSStartHighRdy()函数。这部分也是一段汇编代码。
NVIC_INT_CTRL EQU 0xE000ED04
NVIC_SYSPRI14 EQU 0xE000ED22
NVIC_PENDSV_PRI EQU 0xFF
NVIC_PENDSVSET EQU 0x10000000
OSStartHighRdy
; Set the PendSV exception priority,设置PendSV的优先级为0XFF,为最低优先级
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
MOVS R0, #0
; Set the PSP to 0 for initial context switch call
MSR PSP, R0;置PSP为0,与上文2的跳转相呼应了
; OSRunning = TRUE
MOVS R1, #1
STRB R1, [R0]
LDR R0, =NVIC_INT_CTRL
; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
; Enable interrupts at processor level
这部分的代码是任务在第一次运行的时候,设置PendSV中断以及中断优先级,同时开启PendSV中断,进行第一个任务切换。