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