文獻標(biāo)識碼: A
文章編號: 0258-7998(2012)11-0022-04
基于實時操作系統(tǒng)RTOS和C語言的開發(fā),具有良好的可繼承性,在處理器升級以及更換處理器類型時,只需要在系統(tǒng)底層做相應(yīng)的移植,大部分應(yīng)用程序代碼可以不做修改就可以移植過來使用。μC/OS-II是一個完整的、可移植、可固化、可裁剪的占先式實時多任務(wù)內(nèi)核。μC/OS-II絕大部分代碼采用ANSI的C語言編寫,包含一部分匯編代碼,使之可供不同架構(gòu)的微處理器使用。μC/OS-II運行穩(wěn)定可靠,通過了美國航空管理局的認證,可以應(yīng)用到汽車、飛機等安全性要求嚴格的場合[1]。
1 MPC5604B硬件資源
1.1 MCU基本參數(shù)
MPC5604B是飛思卡爾公司基于PowerPC架構(gòu)針對車身控制領(lǐng)域而設(shè)計的新型處理器。該處理器采用e200z0核,時鐘頻率可達64 MHz,內(nèi)核集成有中斷向量控制器和存儲保護模塊。存儲模塊包括512 KB的程序Flash、64 KB的數(shù)據(jù)Flash、48 KB的SRAM,而且都具有錯誤檢查和糾正(ECC)功能。通信接口包括3路FlexCAN、4路LINFlex、3路SPI以及1路IIC。時鐘模塊包括2路eMIOS。此外,還有36通道的ADC模塊以及啟動輔助模塊[2-3](BAM)等。
1.2 e200z0指令模型
PowerPC架構(gòu)Book E中定義了固定32 bit長度的指令集,但是一些指令不需要32 bit長度,16 bit長度就可以執(zhí)行同樣的操作,所以e200z0內(nèi)核采用可變長度編碼(VLE)指令集。該指令集是32 bit和16 bit指令長度混合指令集,相比于Book E中固定長度指令集,VLE指令集代碼密度更高,節(jié)省了存儲空間。
在匯編代碼形式上,16 bit VLE指令集在匯編指令前加se前綴,32 bit VLE指令集在匯編指令前加e前綴。以stw指令為例,VLE指令和Book E 32 bit指令區(qū)別如表1所示。
軟件向量模式下,外圍設(shè)備的所有中斷最終都只響應(yīng)一個中斷,即偏移為0x0040的外圍設(shè)備中斷(IVOR4)。當(dāng)外圍設(shè)備發(fā)生中斷后,執(zhí)行IVOR4中斷處理函數(shù),該函數(shù)讀取INTC_IACKR寄存器確定具體該處理哪一個外圍設(shè)備的中斷函數(shù)。軟件向量模式的好處是所有的外設(shè)中斷只需要寫一個上下文保存和切換函數(shù),節(jié)省了用戶的代碼編寫量和存儲空間。
硬件向量模式就是在表2所示的中斷向量表后面距離中斷向量基址寄存器(IVPR)偏移0x800位置再添加了一個外圍設(shè)備中斷向量表。當(dāng)發(fā)生外設(shè)中斷時,寄存器直接跳轉(zhuǎn)到相應(yīng)的外設(shè)中斷處理函數(shù)。硬件向量模式其中斷響應(yīng)更迅速,但每一個中斷處理函數(shù)都需要有單獨的上下文保存和切換函數(shù),其代碼量很大。
2 μC/OS-II調(diào)度原理及軟件實現(xiàn)
2.1 原理及移植
μC/OS-II實時操作系統(tǒng)的工作原理是:最大程度地讓處于就緒狀態(tài)的最高優(yōu)先級任務(wù)處于運行狀態(tài)。在μC/OS-II中,主要通過如下三方面的機制來實現(xiàn)實時性[4]。
(1)用戶主動調(diào)用API函數(shù)。在用戶當(dāng)前任務(wù)執(zhí)行到一定步驟,比如已經(jīng)執(zhí)行結(jié)束或者需要暫停掛起當(dāng)前任務(wù)時,調(diào)用系統(tǒng)函數(shù)OSSemPost()、OSTimeDly()、OSSemPend()等API函數(shù)。在這些函數(shù)里,通過調(diào)用OS_Sched()函數(shù),如果檢測到當(dāng)前有更高優(yōu)先級別的任務(wù)處于就緒狀態(tài),則調(diào)用OS_TASK_SW()函數(shù)切換到更高優(yōu)先級的任務(wù)執(zhí)行。
(2)通過系統(tǒng)時鐘(Systick)進行調(diào)度。每隔一個系統(tǒng)時鐘間隔進行一次任務(wù)就緒狀態(tài)檢測,如果檢測到有更高優(yōu)先級的任務(wù)處于就緒狀態(tài),則執(zhí)行任務(wù)切換。系統(tǒng)時鐘間隔選擇要合適,太長會影響系統(tǒng)實時性,太短會造成系統(tǒng)花費過多的資源處理定時器中斷。
(3)通過外部中斷程序觸發(fā)任務(wù)切換。當(dāng)發(fā)生了外部中斷,造成系統(tǒng)任務(wù)就緒狀態(tài)的變化、退出中斷處理函數(shù)時,調(diào)用OSIntExit()函數(shù)。在這個函數(shù)里,如果檢測到更高優(yōu)先級任務(wù)處于就緒狀態(tài),則調(diào)用OSIntCtxSw()函數(shù)執(zhí)行任務(wù)的切換。
通過上面三種機制實現(xiàn)了系統(tǒng)最大程度的實時性調(diào)度。系統(tǒng)移植主要的任務(wù)是:結(jié)合具體的硬件平臺,實現(xiàn)任務(wù)堆棧的建立,任務(wù)調(diào)度過程中上下文的保存和切換以及系統(tǒng)時鐘的實現(xiàn)。主要包括OS_CPU.h、OS_CPU_C.c以及OS_CPU_A.s幾個移植文件。
2.2 實現(xiàn)OS_TASK_SW()函數(shù)
OS_TASK_SW()函數(shù)用于實現(xiàn)任務(wù)級別的任務(wù)切換。根據(jù)前面介紹的MPC5604B異常向量表,采用位于中斷向量表偏移地址為0x0080(IVOR8)的系統(tǒng)調(diào)用(system call)異常來實現(xiàn)該函數(shù),程序如下:
#define OS_TASK_SW() asm(“se_sc”);
2.3 實現(xiàn)OSTaskInit()函數(shù)
OSTaskInit()函數(shù)在任務(wù)創(chuàng)建時被調(diào)用,為新創(chuàng)建的任務(wù)在RAM中申請一塊棧區(qū),并把堆棧指針傳遞給該任務(wù)的控制模塊TCB中。在程序執(zhí)行中,需要保存的寄存器有:通用寄存器R0~R30、器件模式寄存器MSR、連接寄存器LR、計數(shù)寄存器CTR、存儲/恢復(fù)寄存器對SRR0/SRR1、整型異常寄存器EXR。PowerPC架構(gòu)中沒有專門的堆棧指針寄存器,采用通用寄存器R1作為堆棧指針寄存器。
2.4 任務(wù)上下文保存和恢復(fù)函數(shù)
μC/OS-II中,執(zhí)行OSStxSw、OSIntCtxSw、OSStartHighRdy等任務(wù)切換函數(shù)時,需要保存任務(wù)上下文prologue或者恢復(fù)任務(wù)上下文epilogue。執(zhí)行這些操作,要對CPU內(nèi)部寄存器進行直接訪問。C語言不能實現(xiàn)對這些寄存器的直接訪問,需要通過匯編代碼來執(zhí)行這些與處理器直接相關(guān)的操作。下面是保存任務(wù)上下文prologue的匯編實現(xiàn)代碼:
prologue: .macro
e_add2i.r1,-STACK_FRAME_SIZE
e_stwu r1,0(r1)
e_stw r0,4(r1)
……
e_stw r31,4*31(r1)
mfmsr r0
e_stw r0, XMSR(r1)
……
mfcr r0
e_stw r0, 152(r1)
mfmsr r0
epilogue函數(shù)執(zhí)行與prologue相反的操作,即把任務(wù)堆棧中存儲的數(shù)據(jù)恢復(fù)到寄存器中。
2.5 系統(tǒng)時鐘函數(shù)OSTickISR
OSTickISR()函數(shù)是系統(tǒng)時鐘處理函數(shù),主要目的是讓MCU每隔一定的時間去執(zhí)行OSTimeTick()函數(shù)。MPC5604B所采用的e200z0內(nèi)核沒有專門的系統(tǒng)時鐘(Systick)模塊,因此選擇外設(shè)中的周期時鐘通道0(PIT0)作為系統(tǒng)時鐘基準(zhǔn)。處理函數(shù)只需要完成如下兩項任務(wù):調(diào)用OSTimeTick()函數(shù)和清除通道中斷標(biāo)志即可。實現(xiàn)代碼如下:
void Pit1ISR(void) {
OSTimeTick ();
PIT.CH[1].TFLG.B.TIF = 1;
}
這里中斷控制器(INTC)采用軟件向量模式。下面介紹如何配置軟件向量工作模式的PIT0中斷。
(1)在鏈接命令文件(lcf)中申請一塊區(qū)域存儲中斷向量表,鏈接腳本如下:
MEMORY
{ ……
interrupts_flash: org = 0x00010000, len = 0x00010000
……}
GROUP :
{ ……
.ivor_branch_table(VLECODE)LOAD(ADDR(interrupts_flash)) : {}
…… } > interrupts_flash
(2)將OSExtIntISR函數(shù)添加到中斷向量表中,代碼如下:
.extern OSExtIntISR
……
.section .ivor_branch_table, text_vle
……
.align SIXTEEN_BYTES
IVOR4trap: e_b OSExtIntISR
……
OSExtIntISR函數(shù)中,通過讀取INTC_IACKR寄存器,查詢當(dāng)前具體是哪一個外設(shè)處理函數(shù)需要被執(zhí)行。而相應(yīng)的外設(shè)函數(shù)指針存放在一個數(shù)組中,初始化中斷時,要把該數(shù)組賦值給INTC_IACKR寄存器,代碼如下:
INTC.IACKR.R=(uint32_t)&IntcVectorTable[0];
uint32_t IntcVectorTable[] = {
(uint32_t)&dummy……
(uint32_t)&Pit1ISR,
……}
外設(shè)處理函數(shù)在數(shù)組中的位置根據(jù)其中斷編號決定。
以上介紹的是與MPC5604B處理器直接相關(guān)的代碼,其他移植部分代碼都有成熟的代碼可以參考,這里不作介紹。
3 任務(wù)調(diào)度算法硬件指令優(yōu)化
3.1 μC/OS-II最高優(yōu)先級就緒任務(wù)查詢原理
μC/OS-II操作系統(tǒng)最初是針對8 bit機寫的,采用了一個全局字節(jié)變量OSRdyGrp和全局字節(jié)數(shù)組OSRdyTbl[8]實現(xiàn)了對64個任務(wù)就緒狀態(tài)的管理。OSRdyTbl[8]數(shù)組共64 bit,表示64個優(yōu)先級的任務(wù)的就緒狀態(tài),如果為1,則表示該優(yōu)先級的任務(wù)已經(jīng)就緒。這64個任務(wù),從0~63分成8組,OSRdyGrp字節(jié)的每一位,就代表OSRdyTbl[8]哪一組中有任務(wù)處于就緒狀態(tài)。優(yōu)先級就緒表如圖1所示。
操作系統(tǒng)查詢最高優(yōu)先級就緒任務(wù)的步驟是:先查詢OSRdyGrp字節(jié)中為1的最低位。例如,如果OSRdyGrp字節(jié)為0x1C(00011100B),則處于就緒狀態(tài)的優(yōu)先級最高的組的索引值為Y=2;然后查詢OSRdyTbl[2]中字節(jié)為1的最低位,這里假設(shè)OSRdyTbl[2]為0x12(00010010B),則該組中優(yōu)先級最高的位索引值為X=1.從而可以計算出優(yōu)先級Prio=Y<<3+X=17。
根據(jù)前面的描述,處于就緒狀態(tài)的任務(wù)中最高優(yōu)先級查詢的軟件算法代碼如下:
INT8U y;
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy=(INT8U)((y << 3)+OSUnMapTbl
[OSRdyTbl[y]]); [1]
OSUnMapTbl是一個常數(shù)表格,該表格的作用是查詢索引OSRdyTbl[y]對應(yīng)的字節(jié)為1的所有位中的最低位所在位置。例如,如果OSRdyTbl[y]=12,則OSUnMapTbl[12]=2。這種查表法通過空間換時間,避免了軟件逐位判斷的時間開銷。
3.2 基于MPC5604B硬件指令的優(yōu)化
在PowerPC e200z0內(nèi)核指令中,有一條指令cntlzw(Count Leading Zeros Word):其前導(dǎo)0計數(shù)指令,該指令在32 bit e200z0內(nèi)核處理器中用于查詢一個32 bit數(shù)從高位開始的0的數(shù)量。比如內(nèi)核寄存r5中存儲了0x02000000的值,執(zhí)行指令(cntlzw r3,r5)后,r3中得到的數(shù)值為6,這與操作系統(tǒng)任務(wù)調(diào)度過程中查詢最高優(yōu)先級就緒任務(wù)的軟件算法要實現(xiàn)的目的是一致的。
以上介紹的軟件調(diào)度算法,其實就是查詢OSRdyTbl[8]數(shù)組共64 bit中為1的最低位的位置。在e200z0內(nèi)核運算中,可以把就緒表拆成兩個32 bit數(shù),通過執(zhí)行一次或者兩次cntlzw指令就能確定就緒的最高優(yōu)先級任務(wù)所在的位置。
為了保證操作系統(tǒng)的實時性,μC/OS-II操作系統(tǒng)會頻繁地進行查詢就緒任務(wù)狀態(tài)表。每一次硬件中斷,用戶調(diào)用的任務(wù)調(diào)度API函數(shù)以及系統(tǒng)時鐘Tick函數(shù)都會執(zhí)行就緒狀態(tài)表查詢函數(shù)。如果采用硬件指令替換軟件調(diào)度算法,將大大提升系統(tǒng)任務(wù)調(diào)度的性能。
3.3 硬件指令的優(yōu)化實現(xiàn)
所謂任務(wù)調(diào)度函數(shù)硬件優(yōu)化就是采用cntlzw硬件指令取代軟件算法的任務(wù)調(diào)度查詢函數(shù)。要實現(xiàn)該優(yōu)化過程,需要完成下面兩個步驟。
3.3.1 重寫函數(shù)OS_SchedNew
MPC5604B所采用的是e200z0內(nèi)核為32 bit指令長度,要查詢64 bit的長度,需要分成2次。先查詢低位32 bit,如果查詢到有就緒位置位,則把結(jié)果賦值給全局變量OSRdyTbl并退出函數(shù);如果低32 bit沒有查詢到就緒位,則用同樣方法查詢高32 bit長度,并把查詢結(jié)果加上32后賦給OSRdyTbl。具體的實現(xiàn)代碼如下:
static asm void OS_SchedNew(void)
{ e_lis r9,OSRdyTbl@ha
ori r9, r9,OSRdyTbl@l
e_lwz r8,0(r9)
cntlzw r8,r8
e_cmpi 0,0,r8,32
se_bne __HighWord
e_lwz r8,4(r9)
cntlzw r8,r8
e_addi r8,r8,32
__HighWord:
e_stb r8,OSPrioHighRdy
se_blr }
在操作系統(tǒng)中,函數(shù)OS_EventTaskRdy也用到了就緒最高優(yōu)先級任務(wù)查詢。按照上面的代碼,把prio= (INT8U)((y<<3)+x)語句用相應(yīng)的匯編指令替換掉即可,這里不再贅述。
相比于軟件調(diào)度算法代碼,經(jīng)過優(yōu)化的調(diào)度函數(shù)指令執(zhí)行時間縮短一半以上,主要省掉了查詢調(diào)度表OSUnMapTbl[256]。此外,對于處理能力比較強的32 bit機,如果需要擴充μC/OS-II最大任務(wù)數(shù)量(如1 024個任務(wù)數(shù)量),則采用硬件指令cntlzw處理任務(wù)調(diào)度將比較容易完成這個操作。而μC/OS-II最初的任務(wù)系統(tǒng)是針對8 bit機寫的,直接采用軟件算法擴充到支持1 024個任務(wù)數(shù)會比較麻煩,并要消耗更多的資源。
3.3.2 修改任務(wù)控制塊初始化OS_TCBInit
OS_TCBInit函數(shù)中有以下代碼:
ptcb->OSTCBBitY=(INT8U)(1<<ptcb->OSTCBY);
ptcb->OSTCBBitX=(INT8U)(1<<(ptcb->OSTCBX));
該段代碼用于創(chuàng)建任務(wù)時,需要計算該任務(wù)優(yōu)先級掩碼。但是,該段代碼計算出來的掩碼卻不適用于前面替換的硬件指令(cntlzw)調(diào)度函數(shù)。其原因分析如下:
設(shè)優(yōu)先級就緒表OSRdyTbl[0..3]當(dāng)前的值為{0x00,
0x02,0x00,0x22},則執(zhí)行了e_lwz r8,0(r9)指令后,r8寄存器中32 bit數(shù)值為0x00020022。采用cntlz r8,r8計算前導(dǎo)零值為14。而實際上此時OSRdyTbl代表的就緒最高優(yōu)先級值為9,差別在于對0x02這個值的解析。按照cntlzw對0x02(00000010B)的解析,其前導(dǎo)零數(shù)量為6,則所計算出的優(yōu)先級為8+6=14;而根據(jù)前面的掩碼計算公式可知,0x02(00000010B)表示第1組中第1位(均從0開始計算)所代表的優(yōu)先級為8×1+1=9。
因此,為了適應(yīng)硬件指令cntlzw,需要修改掩碼計算方法。采用了cntlzw指令后,狀態(tài)就緒組變量OSRdyGrp以及管理該變量的掩碼OSTCBBitY已經(jīng)沒有用處,因此,只需要將函數(shù)修改為:
ptcb->OSTCBBitX=(INT8U)(1<<((7-ptcb->OSTCBX))。
同理,涉及到修改x掩碼位的所有函數(shù)(OSMutexPend、OSMutex_RdyAtPrio、OSTaskChangePrio)都要做同樣的調(diào)整。調(diào)整過后,系統(tǒng)就可以正常運行了。
本文詳細介紹了基于MPC5604B的μC/OS-II操作系統(tǒng)的移植程序以及操作系統(tǒng)最高優(yōu)先級就緒任務(wù)查詢算法的硬件指令優(yōu)化。在移植過程中,首先要分析硬件系統(tǒng)資源,使用鏈接命令文件(lcf)分配代碼和數(shù)據(jù)段;然后要熟練掌握e200z0核匯編指令,實現(xiàn)操作系統(tǒng)堆棧維護、中斷上下文保存、時鐘tick、任務(wù)切換等底層函數(shù)的編寫;最后在系統(tǒng)移植過程中,要善于發(fā)現(xiàn)和利用處理器一些特殊的指令,實現(xiàn)軟件算法的硬件優(yōu)化,以提高程序的執(zhí)行效率。
參考文獻
[1] (美)LABROSSE J J,著.嵌入式實時操作系統(tǒng)μC/OS-II (第2版)[M].邵貝貝,譯.北京:北京航空航天大學(xué)出版社,2003.
[2] SOJA R,BANNOURA M,著.MPC5553/5554微處理器揭秘[M].龔光華,宮輝,安鵬,譯.北京:北京航空航天大學(xué)出版社,2010.
[3] Freescale Semiconductor,Inc.MPC5604B/C microcontroller reference manual[Z].2011.
[4] 孫旭祥.淺析實時操作系統(tǒng)的任務(wù)調(diào)度[J].信息對抗,2005(6):37-39.