回复【2楼】zuozhongkai:
---------------------------------
对于初始化阶段OSStartHighRdy和任务级调度器OSCtxSw、中断级调度器OSIntCtxSw来说都是通过挂起PendSV异常实现的任务切换。因此重点在PendSV异常的编写。
由于OSStartHighRdy之前已经使用C语言编写的OSTaskStkInit()函数(位于cpu_core.c)将现场初始化完毕,因此进入PendSV后执行执行PendSVHandler_nosave 之后的代码。而任务级调度器OSCtxSw、中断级调度器OSIntCtxSw将会执行PendSV异常所有的代码。
我的疑问如下:
除PendSV异常之外的其他异常的模型和初始化阶段OSStartHighRdy和任务级调度器OSCtxSw、中断级调度器OSIntCtxSw稍稍有些不同,不难发现PendSV一开始就赶紧保存R4~R11,但为什么其他异常刚进入后却不需要先保存R4~R11,而仅在最后才使用OSIntExit()在确定会发生调度时才保存R4~R11。
Void xxxxx_xxxHandler(void) //除endSV异常之外的其他异常的“C代码”模型
{ OSIntEnter();
中断服务代码;
OSIntExit();
}
在进入异常时切换为handler mode,这种模式下是“privileged LEVEL特权模式”,所有的通用寄存器R0~R7(Low registers)+R8~R12(high registers)都可以使用。对于使用C编写的中断服务函数(如上模型),编译为机器码后难道只使用R0~R3、R12而不会对R4~R11造成破坏?
版主大人,怎么看?
;//////////////////////////////////os_cpu_a.s///////////////////////////////////////////
OSStartHighRdy
LDR R0, =NVIC_SYSPRI4 ; Set the endSV exception priority
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
;NVIC_SYSPRI4是配置PendSV优先级的寄存器的物理地址。NVIC_PENDSV_PRI是将要配置的PendSV的优先级别,通过宏定义可知,
;要将PendSV的优先级设置为255.注:这是cortex-M3的最低优先级。
;Cortex-M3内核提供了两个堆栈指针MSP和PSP。在《Cortex-M3 Devices Generic User Guide.pdf》的2.13节的介绍如下
;////////////////////////////
;Stack ointer
;The Stack ointer (SP) is register R13. In Thread mode, bit[1] of the CONTROL register
;indicates the stack pointer to use:
; 0 = Main Stack ointer (MSP). This is the reset value.
; 1 = rocess Stack ointer (PSP).
;On reset, the processor loads the MSP with the value from address 0x00000000.
;///////////////////////////////////
;下面5句话的作用:将PSR清零+将“异常的堆栈基地址赋”给MSP。
MOVS R0, #0 ; Set the SP to 0 for initial context switch call
MSR  SP, R0
LDR R0, =OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase
LDR R1, [R0]
MSR MSP, R1
;uC/OS-III专门为异常提供了一个堆栈空间,其基地址为OS_CPU_ExceptStkBase
;如上3句话的作用:将异常的堆栈的基地址给堆栈指针MSP.
LDR R0, =NVIC_INT_CTRL ; Trigger the endSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
;上边的三句汇编完成了对PendSV的挂起(触发)。挂起PendSV的唯一方法就是置位ICSR的bit[28]位。
;NVIC_INT_CTRL就是ICSR寄存器的地址。NVIC_PENDSVSET=0x10000000。都是宏定义。
;第1句: 将ICSR寄存器的地址NVIC_INT_CTRL放在R0中。
;第2句: 将NVIC_PENDSVSET放在R1中。
;第3句: 将R1放进R0所指向的存储器地址。也就是说向存储器的NVIC_INT_CTRL处(即ICSR寄存器)写入NVIC_PENDSVSET。
;说白了,这3句的作用就是将0x10000000写入到ICSR寄存器。这样一来,ICSR的bit[28]位被置位,即将PendSV挂起了。
CPSIE I ; Enable interrupts at processor level
;///////////
OSStartHang
B OSStartHang ; Should never get here
;/////////
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the endSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
;//////////////
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
;/////////////////////
PendSV_Handler
CPSID I ; Prevent interruption during context switch;关中断
MRS R0, PSP ; PSP is process stack pointer;将PSP的值写入到R0中。
CBZ R0, PendSVHandler_nosave ; Skip register save the first time
;如果R0=0则跳转到PendSVHandler_nosave处执行。PendSVHandler_nosave是个汇编编写的子函数。
;c代码的等价转化: if(R0==0)PendSVHandler_nosave();
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
STM R0, {R4-R11}
LDR R1, =OSTCBCurPtr ; OSTCBCurPtr->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out
; At this point, entire context of process has been saved
PendSVHandler_nosave
PUSH {R14} ; Save LR exc_return value
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCurPtr ; OSTCBCurPtr = OSTCBHighRdyPtr;
LDR R1, =OSTCBHighRdyPtr
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
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
|