在OS_CPU_A.ASM中,定义了开、关中断的方法,在uC/OS-II系统中有三种方法可以实现中断开关,而ARM只适用于模式三,即使用一个局部变量,在中断进入之间保存CPU状态,退出时候再恢复状态。
OSStart()函数调用OSStartHighRdy()来使就绪态任务中优先级最高的任务开始运行,OSStartHighRdy()必须调用OSTaskSwHook(),调用完STaskSwHook()后,应该使OSRunning标志位为TRUE,最后再切换到最高优先级任务。此函数只是完成了高优先级任务寄存器的恢复,并没有保存当前任务的寄存器。
OSCtxSw(),任务级切换;
OSIntCtxSw(),中断级别的任务切换;
异常处理程序
********************************************************************************************************
; PUBLIC FUNCTIONS
;********************************************************************************************************
; External references
EXTERN OSRunning
EXTERN OSPrioCur
EXTERN OSPrioHighRdy
EXTERN OSTCBCur
EXTERN OSTCBHighRdy
EXTERN OSIntNesting
EXTERN OSIntExit
EXTERN OSTaskSwHook
PUBLIC OS_CPU_SR_Save
PUBLIC OS_CPU_SR_Restore
PUBLIC OSStartHighRdy
PUBLIC OSCtxSw
PUBLIC OSIntCtxSw
PUBLIC OS_CPU_ARM_ExceptResetHndlr
PUBLIC OS_CPU_ARM_ExceptUndefInstrHndlr
PUBLIC OS_CPU_ARM_ExceptSwiHndlr
PUBLIC OS_CPU_ARM_ExceptPrefetchAbortHndlr
PUBLIC OS_CPU_ARM_ExceptDataAbortHndlr
PUBLIC OS_CPU_ARM_ExceptAddrAbortHndlr
PUBLIC OS_CPU_ARM_ExceptIrqHndlr
PUBLIC OS_CPU_ARM_ExceptFiqHndlr
; EQUATES
;********************************************************************************************************
;根据CPSR的低8位设置I,F,T,及M[4:0]
OS_CPU_ARM_CONTROL_FIQ_DIS EQU 0x40 ; Disable FIQ
OS_CPU_ARM_CONTROL_IRQ_DIS EQU 0x80 ; Disable IRQ
OS_CPU_ARM_CONTROL_THUMB EQU 0x20 ; Set THUMB mode
OS_CPU_ARM_CONTROL_ARM EQU 0x00 ; Set ARM mode
OS_CPU_ARM_MODE_USR EQU 0x10 ;用户模式
OS_CPU_ARM_MODE_FIQ EQU 0x11 ;FIQ模式
OS_CPU_ARM_MODE_IRQ EQU 0x12 ;IRQ模式
OS_CPU_ARM_MODE_SVC EQU 0x13 ;管理模式
OS_CPU_ARM_MODE_ABT EQU 0x17 ;终止模式
OS_CPU_ARM_MODE_UND EQU 0x1B ;未定义模式
OS_CPU_ARM_MODE_SYS EQU 0x1F ;系统模式
OS_CPU_ARM_EXCEPT_UNDEF_INSTR EQU 0x01
OS_CPU_ARM_EXCEPT_SWI EQU 0x02
OS_CPU_ARM_EXCEPT_PREFETCH_ABORT EQU 0x03
OS_CPU_ARM_EXCEPT_DATA_ABORT EQU 0x04
OS_CPU_ARM_EXCEPT_ADDR_ABORT EQU 0x05
OS_CPU_ARM_EXCEPT_IRQ EQU 0x06
OS_CPU_ARM_EXCEPT_FIQ EQU 0x07
; CODE GENERATION DIRECTIVES
;********************************************************************************************************
CODE32
;*********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;采用ucos所提出的模式3来禁止中断和开中断,实现硬保护
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
;也就是说cpu_sr是一个局部变量,在进入中断之前使用它来保存状态,退出时再恢复
;
; Prototypes : OS_CPU_SR OS_CPU_SR_Save (void);
; void OS_CPU_SR_Restore (OS_CPU_SR os_cpu_sr);
;
;
; Note(s) : (1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; /* Allocate storage for CPU status register. */
; #if (OS_CRITICAL_METHOD == 3)
; OS_CPU_SR os_cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL(); /* os_cpu_sr = OS_CPU_SR_Save(); */
; :
; :
; OS_EXIT_CRITICAL(); /* OS_CPU_SR_Restore(cpu_sr); */
; :
; :
; }
;
; (2) OS_CPU_SR_Restore() is implemented as recommended by Atmel's application note:
;
; "Disabling Interrupts at Processor Level"
;*********************************************************************************************************
MRS R0, CPSR ;复制CPSR到R0
; Set IRQ and FIQ bits in CPSR to disable all interrupts
ORR R1, R0, #OS_CPU_ARM_CONTROL_INT_DIS
;RO中存放了CPSR,把#OS_CPU_ARM_CONTROL_INT_DIS和R0进行逻辑或运算,结果存放到R1中
MSR CPSR_c, R1
;传送R1的内容到CPSR,但是只修改CPSR中的控制位域[7:0]
MRS R1, CPSR ; Confirm that CPSR contains the proper interrupt disable flags
AND R1, R1, #OS_CPU_ARM_CONTROL_INT_DIS
CMP R1, #OS_CPU_ARM_CONTROL_INT_DIS
BNE OS_CPU_SR_Save_Loop ; Not properly disabled (try again)
BX LR ; Disabled, return the original CPSR contents in R0
MSR CPSR_c, R0
BX LR
; START MULTITASKING
; void OSStartHighRdy(void)
;
; Note(s) : 1) OSStartHighRdy() MUST:
; a) Call OSTaskSwHook() then,
; b) Set OSRunning to TRUE,
; c) Switch to the highest priority task.
;;*********************************************************************************************************
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SYS)
MOV R1, #1
STRB R1, [R0]
MOV LR, PC
BX R0
LDR R0, ?OS_TCBHighRdy ; Get highest priority task TCB address
LDR R0, [R0] ; get stack pointer
LDR SP, [R0] ; switch to the new stack
;有些代码在这里写成 LDMFD SP!,{R0}
MSR CPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC} ; pop new task's context
; PERFORM A CONTEXT SWITCH (From task level) - OSCtxSw()
; 任务级切换
; Note(s) : 1) OSCtxSw() is called in SYS mode with BOTH FIQ and IRQ interrupts DISABLED
;
; 2) The pseudo-code for OSCtxSw() is:
; a) Save the current task's context onto the current task's stack
; b) OSTCBCur->OSTCBStkPtr = SP;
; c) OSTaskSwHook();
; d) OSPrioCur = OSPrioHighRdy;
; e) OSTCBCur = OSTCBHighRdy;
; f) SP = OSTCBHighRdy->OSTCBStkPtr;
; g) Restore the new task's context from the new task's stack
; h) Return to new task's code
;
; 3) Upon entry:
; OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBCur指向即将挂起的任务,而OSTCBHighRdy指向即将恢复的任务
; OSTCBHighRdy points to the OS_TCB of the task to resume
;*********************************************************************************************************
; SAVE CURRENT TASK'S CONTEXT
STMFD SP!, {LR} ; Push return address
;LR中其实是任务切换时对应的PC值
STMFD SP!, {LR}
STMFD SP!, {R0-R12} ; Push registers
MRS R0, CPSR ; Push current CPSR
TST LR, #1 ; See if called from Thumb mode
ORRNE R0, R0, #OS_CPU_ARM_CONTROL_THUMB ; If yes, Set the T-bit
STMFD SP!, {R0}
;至此,在堆栈中保存环境的任务已经完成
LDR R0, ?OS_TCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R0]
STR SP, [R1]
MOV LR, PC
BX R0
LDR R1, ?OS_PrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R1, ?OS_TCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDMFD SP!, {R0} ; Pop new task's CPSR
MSR CPSR_cxsf, R0
; PERFORM A CONTEXT SWITCH (From interrupt level) - OSIntCtxSw()
; 中断级的任务切换
; Note(s) : 1) OSIntCtxSw() is called in SYS mode with BOTH FIQ and IRQ interrupts DISABLED
;
; 2) The pseudo-code for OSIntCtxSw() is:
; a) OSTaskSwHook();
; b) OSPrioCur = OSPrioHighRdy;
; c) OSTCBCur = OSTCBHighRdy;
; d) SP = OSTCBHighRdy->OSTCBStkPtr;
; e) Restore the new task's context from the new task's stack
; f) Return to new task's code
;
; 3) Upon entry:
; OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; OSIntCtxSw()和OSCtxSw()相似,区别是OSIntCtxSw()是在ISR中被调用的,ISR已经保存了CPU的寄存器,
; 所以不需要再此保存寄存器的值
;*********************************************************************************************************
LDR R0, ?OS_TaskSwHook ; OSTaskSwHook();
MOV LR, PC
BX R0
LDR R1, ?OS_PrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R1, ?OS_TCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDMFD SP!, {R0} ; Pop new task's CPSR
MSR CPSR_cxsf, R0
; RESET EXCEPTION HANDLER
; 进入异常后,LR会指向下一条指令的地址,所以在压栈的时候,需要减去相应的值。
; 对于reset,svc,swi减去0,对于预取终止,irq,fiq减去4,对于数据中止和地址中止减去8
;********************************************************************************************************
; LR offset to return from this exception: 0
; (there is no way to return from a RESET exception)
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_RESET ; Set exception ID to OS_CPU_ARM_EXCEPT_RESET
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; UNDEFINED INSTRUCTION EXCEPTION HANDLER
;********************************************************************************************************
; LR offset to return from this exception: 0
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_UNDEF_INSTR ; Set exception ID to OS_CPU_ARM_EXCEPT_UNDEF_INSTR
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; SOFTWARE INTERRUPT EXCEPTION HANDLER
;********************************************************************************************************
; LR offset to return from this exception: 0
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_SWI ; Set exception ID to OS_CPU_ARM_EXCEPT_SWI
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; PREFETCH ABORT EXCEPTION HANDLER
;********************************************************************************************************
SUB LR, LR, #4 ; LR offset to return from this exception: -4
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_PREFETCH_ABORT ; Set exception ID to OS_CPU_ARM_EXCEPT_PREFETCH_ABORT
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; DATA ABORT EXCEPTION HANDLER
;********************************************************************************************************
SUB LR, LR, #8 ; LR offset to return from this exception: -8
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_DATA_ABORT ; Set exception ID to OS_CPU_ARM_EXCEPT_DATA_ABORT
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; ADDRESS ABORT EXCEPTION HANDLER
;********************************************************************************************************
SUB LR, LR, #8 ; LR offset to return from this exception: -8
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_ADDR_ABORT ; Set exception ID to OS_CPU_ARM_EXCEPT_ADDR_ABORT
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; INTERRUPT REQUEST EXCEPTION HANDLER
;********************************************************************************************************
SUB LR, LR, #4 ; LR offset to return from this exception: -4
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_IRQ ; Set exception ID to OS_CPU_ARM_EXCEPT_IRQ
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; FAST INTERRUPT REQUEST EXCEPTION HANDLER
;********************************************************************************************************
SUB LR, LR, #4 ; LR offset to return from this exception: -4
STMFD SP!, {R0-R12, LR} ; Push working registers
MOV R3, LR ; Save link register
MOV R0, #OS_CPU_ARM_EXCEPT_FIQ ; Set exception ID to OS_CPU_ARM_EXCEPT_FIQ
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler
; GLOBAL EXCEPTION HANDLER
;********************************************************************************************************
MRS R1, SPSR ; Save CPSR (i.e. exception's SPSR)
; SPSR.Mode = FIQ, IRQ, SVC, ABT, UND : Other exception
; SPSR.Mode = SYS : Task
; SPSR.Mode = USR : *unsupported state*
AND R2, R1, #OS_CPU_ARM_MODE_MASK ; 保留R1的低8位控制位
CMP R2, #OS_CPU_ARM_MODE_SYS
BNE OS_CPU_ARM_ExceptHndlr_BreakExcept
; EXCEPTION HANDLER: TASK INTERRUPTED
;********************************************************************************************************
MRS R2, CPSR ; Save exception's CPSR
MOV R4, SP ; Save exception's stack pointer
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SYS)
STMFD SP!, {R3} ; Push task's PC
STMFD SP!, {LR} ; Push task's LR
STMFD SP!, {R5-R12} ; Push task's R12-R5
LDMFD R4!, {R5-R9} ; Move task's R4-R0 from exception stack to task's stack
STMFD SP!, {R5-R9}
ORRNE R1, R1, #OS_CPU_ARM_CONTROL_THUMB ; If yes, Set the T-bit
STMFD SP!, {R1} ; Push task's CPSR (i.e. exception SPSR)
LDR R1, ?OS_Running
LDRB R1, [R1]
CMP R1, #1
BNE OS_CPU_ARM_ExceptHndlr_BreakTask_1
LDR R3, ?OS_IntNesting ; OSIntNesting++;
LDRB R4, [R3]
ADD R4, R4, #1
STRB R4, [R3]
LDR R4, [R3]
STR SP, [R4]
MSR CPSR_cxsf, R2 ; RESTORE INTERRUPTED MODE
MOV LR, PC
BX R1
; exception stack is not used when restoring task context.
ADD SP, SP, #(14*4)
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SYS)
; if a ready task with higher priority than
; the interrupted one is found.
LDR R0, ?OS_IntExit
MOV LR, PC
BX R0
LDMFD SP!, {R0} ; Pop new task's CPSR
MSR CPSR_cxsf, R0
; EXCEPTION HANDLER: EXCEPTION INTERRUPTED
;********************************************************************************************************
MRS R2, CPSR ; Save exception's CPSR
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SYS)
LDR R3, ?OS_IntNesting ; OSIntNesting++;
LDRB R4, [R3]
ADD R4, R4, #1
STRB R4, [R3]
MOV LR, PC
BX R3
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SYS)
LDR R3, ?OS_IntNesting ; OSIntNesting--;
LDRB R4, [R3]
SUB R4, R4, #1
STRB R4, [R3]
; POINTERS TO VARIABLES
;*********************************************************************************************************
DC32 OSRunning
DC32 OSPrioCur
DC32 OSPrioHighRdy
DC32 OSTCBCur
DC32 OSTCBHighRdy
DC32 OSIntNesting
DC32 OSTaskSwHook
DC32 OSIntExit
DC32 OS_CPU_ExceptHndlr