UCOS在LPC上的移植
一、一、OS_CPU_A.ASM文件的編寫
1、??? 1、?SoftwareInterrupt的編寫
? 當發生軟件中斷時,程序通過異常向量表跳轉到軟中斷的匯編與C接口程序SoftwareInterrupt處,下圖為SoftwareInterrupt的流程圖。
????????????????????????
程序:
SoftwareInterrupt
??????? LDR???? SP, StackSvc??????????? ; 重新設置堆棧指針,堆棧指向的是SVC模式下的堆棧
??????? STMFD SP!,{R0-R3,R12,LR}
???? ???STMFD?? SP!, {R0-R3, R12, LR}? ;此時R0-R3, R12,在svc和用戶模式下都是同一物理地址,lr為svc下的寄存器,保存著中斷返回地址
??????? MOV???? R1, SP??????????????? ; R1指向參數存儲位置(svc下的堆棧指針地址)
?這個不知道用在哪??
??????? MRS???? R3, SPSR??? ??;R3中放SWI前的CPSR(即被中斷的任務的cpsr),在切換函數中用到
??????? 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
??????? LDREQ?? PC, =__OSStartHighRdy ; SWI 0x01為第一次任務切換
??????? BL????? ?SWI_Exception
????????LDMFD SP!,{R0-R3,R12,PC}^;
??????? LDMFD?? SP!, {R0-R3, R12, PC}^????????????????????????????????????? R12處的上一堆棧指針(&R12+4)存放的為LR,所以就把LR裝入PC
?
分析:因為執行任務切換時(執行新任務)堆棧指針會指向用戶的堆棧,這樣下一次進入管理模式(在osintctxsw中就會用到管理模式)就會破壞用戶堆棧,從而導致程序執行不正確。所以程序在一開始設置堆棧指針。軟中斷指令使處理器進入管理模式,而用戶程序處于系統/用戶模式,其它異常也有自己的處理器模式,都有各自的堆棧指針,不會因為給堆棧指針賦值而破壞其它處理器模式的堆棧而影響其它程序的執行。返回的地址已經存儲在連接寄存器LR中而不是存儲在堆棧中。由于進人管理模式自動關中斷,所以這段程序不會被其它程序同時調用,設置的堆棧指針指向的位置肯定是空閑位置,后一次調用不會影響前一次調用。這樣就可以保證“LDR? SP, StackSvc”進行正確的堆棧指針設置。
??? 因為ARM處理器核具有兩個指令集,在執行Thumb指令的狀態時不是所有寄存器都可見(參考ARM的相關資料),而且任務又可能不在特權模式(不能改變CPSR)。為了兼容任意一種模式,本移植使用軟中斷指令SWI使處理器進入管理模式和ARM指令狀態,并使用功能0實現OS_TASK_SW()的功能,
?附:?(1)LR沒有入棧:
???????? 從SWI和Undef異常返回時使用:
movs pc, LR;
?????????????? ?從FIQ、IRQ和預取終止返回時使用:
SUBS PC, LR,#4;
?? ?????????????從數據異常返回時使用:
SUBS PC, LR,#8
??????? (2)LR有入棧:
在使用上述指令異常返回時,如果LR之前被壓棧的話使用LDM “∧”,達到同樣效果。 例如:
LDMFD SP!, {PC}∧
?
?
?
?
?
?
?
2?、OSIntCtxSw的編寫:
??? 在μC/OS-Ⅱ中,任務切換只是簡單的將處理器寄存器保存到將被掛起的任務的堆棧中,并且將更高優先級的任務從堆棧中恢復出來。處于就緒狀態的任務的堆棧結構看起來就像剛發生過中斷并將所有的寄存器保存到堆棧中的情形一樣。換句話說,μC/OS-Ⅱ要運行處于就緒狀態的任務必須要做的事就是將所有處理器寄存器從任務堆棧中恢復出來,并且執行中斷的返回。
在μC/OS-Ⅱ中,用戶級任務調度時會調用宏(或者函數)OS_TASK_SW(),它是在μC/OS-Ⅱ從低優先級任務切換到最高優先級任務時被調用的,μC/OS-Ⅱ建議OS_TASK_SW()通過某種途徑最終調用函數OSCtxSw()。函數OSCtxSw()是與系統相關的,μC/OS-Ⅱ提供的OSCtxSw()函數原型如下:
?
OSCtxSw()原型的程序清單
void OSCtxSw(void)
{
保存處理器寄存器;???????????
將當前任務的堆棧指針保存到當前任務的OS_TCB中;
OSTCBCur->OSTCBStkPtr = Stack pointer;
??調用用戶定義的OSTaskSwHook();
??OSTCBCur? = OSTCBHighRdy;
??OSPrioCur = OSPrioHighRdy;
??得到需要恢復的任務的堆棧指針;
??? Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
??將所有處理器寄存器從新任務的堆棧中恢復出來;
??執行中斷返回指令;
}
?
?
?
?
?
下面將各部分分解來分析:
在進入OSIntCtxSw?前,當前處理器堆棧模式(SVC)為:SP指向R0
???????????????????
(1)???????? (1)保存處理器寄存器:
?OSIntCtxSw? ???????????????????;下面為保存任務環境
??????? LDR???? R2, [SP, #20]?????LDR???? R2, [SP, #20]?;?????
??????????????????????????????? ;獲取PC?,此時SP指向的是SVC的任務堆棧;SP+20=LR,即把中斷返回地址給PC,?????? 這里的PC是當前任務的PC?, 跟下面需要恢復的新任務的PC(程序37)是不一樣的?……………………………………………………………………………………………(1)
獲取PC?,此時SP指向的是SVC
錢
?????? LDR???? R12, [SP, #16]???????????????? ;獲取R12? …………………………………………(2)
?????????????? ?
?MRS???? R0, CPSR????????????????????????;用來存儲模式切換?………………………… (3)
? ??????MSR???? CPSR_c, #(NoInt | SYS32Mode)?????;系統模式…………………………………(4)
***注***自己的理解:
1.? Ucos的任務運行在用戶模式或是系統模式下,也就意味著所有任務的堆棧位于為用戶模式開辟的堆棧區中(系統模式和用戶模式擁有相同的堆棧區)。
2.? 當進行任務切換時,ARM都會自動進入相應的處理器模式,如任務級調度進入管理模式,中斷級調度進入IRQ模式,而每個處理器模式擁有不同的堆棧區。
3. 任務切換需要保存被中斷中止任務的上下文,將寄存器內容壓入自己的任務堆棧,而任務堆棧位于用戶模式堆棧區,所以必須先將ARM處理器模式由管理模式或是IRQ模式切換到系統模式,才能正確完成上下文切換。
?
??????? MOV???? R1,LR???????????????????………………………………………………………?? (5)
??????? STMFD SP!,{R1,R2};
??????? STMFD?? SP!, {R1-R2}???? ;????????????????????????????? 保存LR,PC到任務堆棧下;ARM 7中模式R0-R12公用,
???????????????????????????????????? 所以轉到SYS,R1,R2還是不變 ?…………………………(6)
??????? STMFD?? SP!, {R4-R12}???????????????? ;保存R4-R12到任務堆棧下 …………………(7)
???????????????
??????? MSR???? CPSR_c, R0????????;??????????? ????……………………………………………… (8)
??????? LDMFD?? SP!, {R4-R7}??????????? ;獲取R0-R3,在SVC的堆棧中R3存放著SPSR值? ………… (9)
????????ADD???? SP, SP, #8?;
??????? ADD???? SP, SP, #8????;????????????????????????????? 出棧R12,PC,在堆棧中剩余的數據(R12、PC)已經沒有用處,所以
????????????????????????????????????? 要進行調整;清理軟中斷堆棧,復原到任務切換前的樣子…… (10)
?????????????????? ??? ?????????
??????? MSR???? CPSR_c, #(NoInt | SYS32Mode)????????????? ;系統模式……………………………(11)
??????? STMFD?? SP!, {R4-R7}?????????????????? ;把獲取的R0-R3參數保存到任務堆棧中………(12)
??????? LDR???? R1, =OsEnterSum???????? ;獲取OsEnterSum單元地址(CPU寄存器下的地址)… (13)
??????? LDR???? R2, [R1]?????????????????????????????????;……………………………………? (14)
??????? STMFD?? SP!, {R2, R3}???????? ;保存CPSR,OsEnterSum 到任務堆棧 ………………………(15)
???????
至此,處理器寄存器已保存完畢。
(2)將當前任務的堆棧指針保存到當前任務的OS_TCB中;
OSTCBCur->OSTCBStkPtr = Stack pointer
OSTCBCur:當前任務的OS_TCB任務塊指針
.OSTCBStkPtr:指向當前任務堆棧棧頂的指針
?棧頂:堆棧中剛存入有效數據的那一個單元地址
代碼如下:
??????;保存當前任務堆棧指針到當前任務的TCB
??????? LDR???? R1, =OSTCBCur??????????????????;取???& OSTCBCur?單元地址? …………………(16)
??????? LDR???? R1, [R1]???????????????????LDR???? R1, [R1]??;?
??????? STR???? SP, [R1]??????????????????????????????????? ?取 OSTCBCur指向的內容,OS_TCB本身就是一個數據結構 ,因此OSTCBCu指向的內容即為該任務塊的?第一個元素OSTCBStkPtr?? ?…………………………………………………………………………………(17)
?
STR???? SP, [R1]?????????????? ??;OSTCBCur->OSTCBStkPtr = Stack pointer…………………………(18)
?
(3)調用用戶定義的OSTaskSwHook():
(3)調用用戶定義的OSTaskSwHook():這樣可直接訪問OSTCBCur和OSTCBHighRdy這2個全局變量。OSTCBCur指向將被切換出去的任務控制塊;而OSTCBHighRdy指向新任務的任務控制塊
?OSTCBCur? = OSTCBHighRdy;任務控制塊更新
??OSPrioCur = OSPrioHighRdy;? 任務優先級更新
代碼如下:
?????? BL????? OSTaskSwHook???????????? ???;調用子函數??…………………………………………(19)
??????? LDR???? R4, =OSPrioCur????????????????;???????? ………………………………………(20)
??????? LDR???? R5, =OSPrioHighRdy????????;????????????? 優先級更新 ???………………… (21)
??????? LDRB??? R6, [R5]???????????????????????????????;……………………………………………………………… (22)
??????? STRB?? ?R6, [R4]???????????????????????????????;……………………………………………………………… (23)
??????????任務控制塊更新:
??????? LDR???? R6, =OSTCBHighRdy??????????????;取&???=OSTCBHighRdy單元地址……………………………………??(24)
????????????????????????????????????? OSTCBHighRdy??指向的是新任務控制塊的第一個元素
??????? LDR???? R6, [R6]?????????????????????? ??? ;取OSTCBHighRd的值????? ………… (25)
??????? LDR???? R4, =OSTCBCur???????????????? ????;??…………………………………………(26)
??????? STR???? R6, [R4]?????????;把OSTCBHighRdy指向的內容復制給 OSTCBCur?? …………(27)
此時,任務塊已更新,OSTCBHighRdy已復制到OSTCBCur,即OSTCBCur已指向新任務控制塊的第一個元素(棧頂指針)。
?
?
?
?
?
?
(4)得到需要恢復的任務的堆棧指針;
??? ??Stack pointer = OSTCBHighRdy->OSTCBStkPtr;
???? ?將所有處理器寄存器從新任務的堆棧中恢復出來;
???? 執行中斷返回指令;
代碼分析:從標號OSIntCtxSw_1處開始至程序的最后,是將所有處理器寄存器從新任務的堆棧中恢復出來。處理器執行這段代碼時處于ARM狀態,但用戶任務可能處于Thumb狀態。而由ARM的相關資料可知,程序不可以直接改變程序狀態寄存器的CPSR的T位來改變處理器的狀態。但“LDMFD?? SP!, {R0-R12, LR, PC }^ ” 異常返回指令是可以使用的(具體參見ARM的多寄存器加載指令),所以在恢復寄存器時,必須使處理器處于某種異常模式。由于FIQ模式和IRQ模式對應中斷,SP指針不能隨意改變,而未定義模式和中止模式以后可能會用到,所以本移植選擇為管理模式。
OSIntCtxSw_1?????????????????????????????????????
?????;獲取新任務堆棧指針,[R6]?的值為指針:指向的是新任務的棧頂指針,而新任務的棧頂指針指向哪?因為在堆棧初始化后(OSTaskStkInit()函數),堆棧指針已經指向任務堆棧的最低地址單元(OsEnterSum)
??????? LDR???? R4, [R6]??;[R6]?:新任務的棧頂指針;指針指向的內容為(&?OsEnterSum?)…(28)
??????? ADD???? SP, R4, #68?????ADD???? SP, R4, #68;????????????????????????????????????????????????????????????????????????????????????堆棧指向(PC+4??)處,這就是新任務CPU寄存器的SP值(R13),為入其他數據做堆棧指針調整 …………………………………………(29)
??????? LDR???? LR, [SP, #-8]???????????;把任務堆棧的LR給CPU寄存器R14 ……………………(30)
???????????????
??????? MSR???? CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式?? ……………………………………(31)
???? ???MOV???? SP, R4??????;設置堆棧指針,SP為R13_SVC下的,SP指向(&?OsEnterSum?)……(32)
??????? LDMFD SP!, {R4, R5}??????? ;取任務下的CPSR,OsEnterSum?到CPU寄存器R5,R4………?? (33)
??????? LDR???? R3, =OsEnterSum????????;取CPU寄存器下的地址單元 ………………………………(34)
??????? STR???? R4, [R3]???????? ??????;恢復新任務的OsEnterSum………………………………??(35)
??? ????MSR???? SPSR_cxsf, R5?????? ;恢復CPSR ,異常下的SPSR返回后給新任務的CPSR…………(36)
???????????????
??????? LDMFD?? SP!, {R0-R12, LR, PC???????LDMFD?? SP!, {R0-R12, LR, PC }^?? ;異常返回,這里的PC在堆棧初始化時已經設置好了,運行新? 任?務…………………………………………………………………………………………………………………(37)
?
轉載于:https://www.cnblogs.com/gmh915/archive/2009/09/30/1577029.html
總結
以上是生活随笔為你收集整理的UCOS在LPC上的移植的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PCB板查短路点的一种技巧 (转载)
- 下一篇: 金蝶BOS社区版Beta1 下载