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