OS_Sched()分析
?????????在uc/os中總是運行優先級最高的就緒任務,確定哪個任務優先級最高,該由哪個優先級人物運行了,這一工作是由任務調度器完成的,(而具體的任務切換,是任務調度器在調用其他函數來完成)。其中任務級的調度由函數OS_Sched()來完成,中斷級的調度由OSIntExt()來完成的。
OS_Sched()函數分析
ucoslll和ucos2,?
?
void OSSched (void)
{
ucos用的多嗎,?
INT8U y;
OS_ENTER_CRITICAL();
?
正點原子ucos。 if ((OSLockNesting | OSIntNesting) == 0)
{ //判斷是否滿足調度條件,在uc/os中任務級調度的調用不允許來自中斷服務子程序(OSIntNesting) == 0),此外當調度器上鎖時,任務調度函數將直接退出,不做任務調度
?
y = OSUnMapTbl[OSRdyGrp];
freertos 任務調度。? OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);//這兩行代碼是獲得進入就緒態且優先級最高的任務
? if (OSPrioHighRdy != OSPrioCur)?
{?//檢驗優先級最高的任務是否是當前正在運行的任務。以避免不必要的的任務調度,畢竟任務調度是需要時間滴。
crop函數?? OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//通過當前最高優先級OSPrioHighRdy,從任務控制塊優先級表中OSTCBPrioTbl[]獲得當前最高優優先級任務控制塊
? OSCtxSwCtr++;//該全局變量(32位)用于記錄任務切換的次數,
? OS_TASK_SW();最后調用OS_TASK_SW宏來完成實際上的任務切換,該宏是一個軟中斷
ucoslll,? }
?
}
?
OS_EXIT_CRITICAL();
}
?
?
OS_TASK_SW()函數分析
任務切換的內容其實是很簡單的:將被掛起任務的寄存器壓入堆棧,然后將高優先級的寄存器從棧中恢復到CPU的寄存器中。該過程使用軟中斷來實現。
SoftwareInterrupt
LDR SP, StackSvc ; 重新設置堆棧指針,在LPC2200開發板中軟中斷觸發后,開發板處于管理模式
STMFD SP!, {R0-R3, R12, LR} //壓棧,在下面過程中使用到了以下以下的寄存器,故需要將這些寄存器壓入管理模式堆棧,來實現原始寄存器的保存。
MOV R1, SP //R1指向參數存儲位置
MRS R3, SPSR /在ARM處理器中,只有MRS指令可以讀取狀態寄存器
TST R3, #T_bit //中斷前是否是Thumb狀態
LDRNEH R0, [LR,#-2] //是: 取得Thumb狀態SWI號
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] //否: 取得arm狀態SWI號
BICEQ R0, R0, #0xFF000000
// r0 = SWI號,R1指向參數存儲位置
CMP R0, #1//比較軟中斷號,確立軟中斷的服務程序
LDRLO PC, =OSIntCtxSw//0號軟中斷用于任務級切換
LDREQ PC, =__OSStartHighRdy ; SWI 0x01為第一次任務切換
BL SWI_Exception//跳轉到軟中斷服務子程序
LDMFD SP!, {R0-R3, R12, PC}^//SWI異常中斷返回
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
對于SWI中斷來說,其格式是SWI{cond} immed_24;后面的24位數字就是軟中斷號,故在SWI異常中斷出路程序中,取出SWI中斷號的方法是首先確定軟中斷SWI指令是ARM指令還是THUMB指令(通過訪問SPSR獲得),然后取得該指令的地址,可通過LR獲得,接著讀出指令,分解出立即數,該立即數就是軟中斷號。
在軟中斷觸發后,處理器應進入管理模式,故需要從新設置堆棧指針。
在軟中斷觸發后,SoftwareInterrupt相當于是一個判斷程序,只是簡單的取出中斷號,具體的實現是調用其他函數完成的。
在SoftwareInterrupt中,進行了一次壓棧,是因為在獲得軟中斷號的過程中使用了R0,R1,R2,R3故需要將這些寄存器壓棧,而使用的SP是管理模式下的寄存器,其于用戶模式下的SP寄存器不是一個寄存器,故不需要進行壓棧。
?
?該函數調用所需要的時間是常量,于實際的任務數無關?
?
?
任務級調度 OSIntCtxsw
OSIntCtxSw
//下面為保存任務環境
LDR R2, [SP, #20] //此時處理器仍處于管理模式,從管理模式堆棧中獲得PC保存到R2中
LDR R12, [SP, #16] //從堆棧中獲取R12,更新CPU寄存器
MRS R0, CPSR //保存當前狀態寄存器,用于模式之間的切換
MSR CPSR_c, #(NoInt | SYS32Mode)//進入系統模式,且關中斷
MOV R1, LR //獲得系統模式下的LR保存到R1中,該LR就是原始任務的LR
STMFD SP!, {R1-R2} //此時處理器處于系統模式,故SP是原始任務的堆棧指針,故原始任務的LR,PC壓入原始任務堆棧
STMFD SP!, {R4-R12} //由于R4-R11寄存器未改變,故其內容是原始任務的內容,直接壓入原始任務堆棧即可。
MSR CPSR_c, R0 //此時切換回管理模式
LDMFD SP!, {R4-R7} //從管理模式堆棧中獲得前面保存的原是任務的RO-R3,保存到CPU的R4-R7
ADD SP, SP, #8 //忽略掉R12,PC,使堆棧指針回到初始位置,使得下次繼續使用
MSR CPSR_c, #(NoInt | SYS32Mode)//進入系統模式
STMFD SP!, {R4-R7} //原始任務的R0-R3入棧保存
LDR R1, =OsEnterSum //獲取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} //從上面知道R3的內容為原始任務的CPSR,故該指令保存CPSR,OsEnterSum
LDR R1, =OSTCBCur //獲得原始任務的TCB地址
LDR R1, [R1]//該地址的內容是原始任務的SP值
STR SP, [R1] //保存原始任務堆棧指針到原始任務的TCB
BL OSTaskSwHook //調用鉤子函數
;OSPrioCur <= OSPrioHighRdy將當前優先級切換為最高優先級(優先級的切換)
LDR R4, =OSPrioCur//當前TCB結構體首地址
LDR R5, =OSPrioHighRdy//最高優先級TCB結構體首地址
LDRB R6, [R5]
STRB R6, [R4]//更新當前TCB指向最高優先級TCB
;OSTCBCur <= OSTCBHighRdy將當前TCB切換為最高優先級的TCB(TCB的切換)
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;獲取新任務堆棧指針
LDR R4, [R6]//R6為新任務的TCB地址,其內容是新任務的堆棧指針
ADD SP, R4, #68 //;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP,移動新任務堆棧的SP,指向棧底
LDR LR, [SP, #-8]//新任務LR進入CPU寄存器
MSR CPSR_c, #(NoInt | SVC32Mode) //進入管理模式
MOV SP, R4 //設置堆棧指針
LDMFD SP!, {R4, R5} //CPSR,OsEnterSum
//恢復新任務的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 //恢復CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;新任務RO-R12,LR,PC出棧,運行新任務
該函數執行前的數據結構:OSTCBCur指向原始任務(低優先級任務)、CPU的SP指向原始任務的棧頂、OSTCBHighRdy指向新任務的TCB
【轉】msr cpsr_cxsf,r1 ;這里的cxsf表示從低到高分別占用的4個8bit的數據域
指令中有時還有出現cpsr_cf, cpsr_all, cpsr_c等,這里:
?
c 指 CPSR中的control field ( PSR[7:0])
f 指 flag field (PSR[31:24])
x 指 extend field (PSR[15:8])
s 指 status field ( PSR[23:16])
其中cpsr的位表示為:
31 30 29 28 --- 7 6 - 4 3 2 1 0
N Z C V I F M4 M3 M2 M1 M0
?
0 0 0 0 0 User26 模式
0 0 0 0 1 FIQ26 模式
0 0 0 1 0 IRQ26 模式
0 0 0 1 1 SVC26 模式
1 0 0 0 0 User 模式
1 0 0 0 1 FIQ 模式
1 0 0 1 0 IRQ 模式
1 0 0 1 1 SVC 模式
1 0 1 1 1 ABT 模式
1 1 0 1 1 UND 模式
深入分析:
對于MSR(寄存器到狀態寄存器)的指令,
MSR CPSR, r0
MSR CPSR_all, r0
MSR CPSR_flg, r0
都是已經過時的表示方法。
對于MRS(狀態寄存器到寄存器)的指令,
MRS R0, CPSR 等同于MRS R0, CPSR_cxsf
MRS R0, CPSR_all 會有waring
MRS R0, CPSR_flg 會有錯誤
在ADS中使用c,f,x,s表示cpsr的各個部分是推薦的。從指令來說:
MSR CPSR_f, r0機器碼為0xe128f000
MSR CPSR_c, r0機器碼為0xe121f000
MSR CPSR_x, r0機器碼為0xe122f000
MSR CPSR_s, r0機器碼為0xe124f000
可見機器碼中用bit[29:16]4bit表示是f,c,x,s的。所以能夠在機器執行的時候,
給予不同的執行結果。為了代碼向后兼容性,建議使用f,c,x,s尾綴
?