要移植一個(gè)操作系統(tǒng)到一個(gè)特定的cpu體系結(jié)構(gòu)上并不是一件很容易的事情,它對移植者有以下要求:
1. 對目標(biāo)體系結(jié)構(gòu)要有很深了解;
2. 對os原理要有較深入的了解;
3. 對所使用的編譯器要有較深入的了解;
4. 對需要移植的操作系統(tǒng)要有相當(dāng)?shù)牧私猓?br> 5. 對具體使用的芯片也要一定的了解。
——移植需要編寫的文件
根據(jù)μc/os-ii的要求,移植μc/os-ii到一個(gè)新的體系結(jié)構(gòu)上需要提供2個(gè)或3個(gè)文件:
os_cpu.h(c語言頭文件)
os_cpu_c.c(c程序源文件)
os_cpu_a.asm(匯編程序源文件)
其中os_cpu_a.asm在某些情況下不需要,但極其罕見。不需要os_cpu_a.asm的必須滿足以下苛刻條件,而同時(shí)滿足這些條件的微控制器幾乎沒有:
1.可以直接使用c語言開關(guān)中斷;
2.可以直接使用c語言編寫中斷服務(wù)程序;
3.可以直接使用c語言操作堆棧指針;
4.可以直接使用c語言保存cpu的所有寄存器。
——移植代碼包括的內(nèi)容
實(shí)際上,還有一個(gè)文件很重要,它就是irq.inc,它定義了一個(gè)匯編宏,它是μc/os-iiforarm7通用的中斷服務(wù)程序的匯編與c函數(shù)接口代碼。時(shí)鐘節(jié)拍中斷服務(wù)程序也沒有移植,因?yàn)槠渑c芯片和應(yīng)用都強(qiáng)烈相關(guān),需要用戶自己編寫,不過可以通過irq.inc簡化用戶代碼的編寫。
關(guān)于頭文件includes.h和config.h
μc/os-ii要求所有.c文件的都要包含都文件includes.h,這樣使得用戶項(xiàng)目中的每個(gè).c文件不用分別去考慮它實(shí)際上需要哪些頭文件。使用includes.h的缺點(diǎn)是它可能會(huì)包含一些實(shí)際不相關(guān)的頭文件,這意味著每個(gè)文件的編譯時(shí)間可能會(huì)增加,但卻增強(qiáng)了代碼的可移植性。
在本移植中另外增加了一個(gè)頭文件config.h,我們要求所有用戶程序必須包含config.h,在config.h中包含includes.h和特定的頭文件和配置項(xiàng)。而μc/os-ii的系統(tǒng)文件依然只是包含includes.h,即μc/os-ii的系統(tǒng)文件完全不必改動(dòng)。所有的配置改變包括頭文件的增減均在config.h中進(jìn)行,而includes.h定下來后不必改動(dòng)(μc/os-ii的系統(tǒng)文件需要包含的東西是固定的)。這樣,μc/os-ii的系統(tǒng)文件需要編譯的次數(shù)大大減少,編譯時(shí)間隨之減少。
編寫os_cpu.h
——不依賴于編譯的數(shù)據(jù)類型
μcos-ii不使用c語言中的short、int、long等數(shù)據(jù)類型的定義,因?yàn)樗鼈兣c處理器類型有關(guān),隱含著不可移植性。代之以移植性強(qiáng)的整數(shù)數(shù)據(jù)類型,這樣,既直觀又可移植,不過這就成了必須移植的代碼。根據(jù)ads編譯器的特性,這些代碼如程序清單7.1所示。
typedef unsigned char boolean;
typedef unsigned char int8u;
typedef signed char int8s;
typedef unsigned short int16u;
typedef signed short int16s;
typedef unsigned int int32u;
typedef signed int int32s;
typedef float fp32;
typedef double fp64;
typedef int32u os_stk;
編寫os_cpu.h
——使用軟中斷swi作底層接口
μcos-ii運(yùn)行時(shí),處理器可能處于的狀態(tài)如下圖所示:
為了使底層接口函數(shù)與處理器狀態(tài)無關(guān),同時(shí)在任務(wù)調(diào)用相應(yīng)的函數(shù)不需要知道函數(shù)位置,本移植使用軟中斷指令swi作為底層接口,使用不同的功能號區(qū)分不同的函數(shù)。軟中斷功能號分配如下表所示,未列出的為保留功能。
用軟中斷作為操作系統(tǒng)的底層接口就需要在c語言中使用swi指令。在ads中,有一個(gè)關(guān)鍵字__swi,用它聲明一個(gè)不存在的函數(shù),則調(diào)用這個(gè)函數(shù)就在調(diào)用這個(gè)函數(shù)的地方插入一條swi指令,并且可以指定功能號。同時(shí),這個(gè)函數(shù)也可以有參數(shù)和返回值,其傳遞規(guī)則與一般函數(shù)一樣。
編寫os_cpu.h
——堆棧生長方式
μcos-ii使用結(jié)構(gòu)常量os_stk_growth中指定堆棧的生長方式:
置os_stk_growth為0表示堆棧從下往上長。
置os_stk_growth為1表示堆棧從上往下長。
雖然arm處理器核對于兩種方式均支持,但ads的c語言編譯器僅支持一種方式,即從上往下長,并且必須是滿遞減堆棧,所以os_stk_growth的值為1。
#define os_stk_growth 1
編寫os_cpu_c.c
——ostaskstkinit( )
該函數(shù)用于初始化任務(wù)堆棧,使任務(wù)的堆棧看起來就像剛發(fā)生中斷一樣。即任務(wù)被執(zhí)行時(shí),就像從中斷返回一樣。
在編寫此函數(shù)之前,必須先確定任務(wù)的堆棧結(jié)構(gòu)。而任務(wù)的堆棧結(jié)構(gòu)是與cpu的體系結(jié)構(gòu)、編譯器有密切的關(guān)聯(lián)。本移植的堆棧結(jié)構(gòu)如下圖所示。
編寫os_cpu_c.c
——ostaskstkinit( )
編寫os_cpu_c.c
——軟件中斷異常服務(wù)程序
前面介紹過,操作系統(tǒng)與硬件相關(guān)的底層函數(shù)使用軟件中斷作為接口,如下表所示。移植代碼中一個(gè)重要的工作就是為這些軟件中斷編寫服務(wù)程序。
編寫os_cpu_c.c
——軟件中斷異常服務(wù)程序
編寫os_cpu_c.c
——…h(huán)ook( )函數(shù)
在os_cpu_c.c文件中還有許多鉤子函數(shù),它們在某個(gè)特定的系統(tǒng)動(dòng)作時(shí)被調(diào)用,允許執(zhí)行函數(shù)中的用戶代碼。這些函數(shù)默認(rèn)是空函數(shù),用戶根據(jù)實(shí)際情況添加相關(guān)代碼。它們分別如下表所示。
編寫os_cpu_a.s
在os_cpu_a.s文件中有軟件中斷的匯編接口程序、任務(wù)切換程序、os啟動(dòng)時(shí)運(yùn)行就緒最高優(yōu)先級任務(wù)的程序。
編寫os_cpu_a.s
——軟件中斷匯編接口
在調(diào)用軟中斷之后,處理器切換到arm指令和管理模式下工作。在執(zhí)行軟件中斷服務(wù)函數(shù)之前,要提取中斷號和其它入口參數(shù),這些通過軟件中斷接口程序完成。
編寫os_cpu_a.s
——任務(wù)切換代碼
μcos-ii是搶占式實(shí)時(shí)操作系統(tǒng),得到運(yùn)行的始終是就緒條件下最高優(yōu)先級的任務(wù)。當(dāng)處于運(yùn)行狀態(tài)的任務(wù)因?yàn)槟撤N脫離就緒態(tài),或者有其它更高優(yōu)先級的任務(wù)進(jìn)入就緒態(tài),那么操作系統(tǒng)內(nèi)核就要運(yùn)行別的就緒任務(wù),這時(shí)需要進(jìn)行任務(wù)切換。
任務(wù)切換可能發(fā)生的情況有兩種:
1.當(dāng)前運(yùn)行的任務(wù)主動(dòng)交出cpu控制權(quán),通常發(fā)生在等待某個(gè)事件或是調(diào)用系統(tǒng)延時(shí)。調(diào)用函數(shù)os_task_sw( );
2.發(fā)生中斷,使更高優(yōu)先級的任務(wù)進(jìn)入就緒狀態(tài),內(nèi)核剝奪當(dāng)前任務(wù)的運(yùn)行資格。即發(fā)生在中斷退出時(shí)。調(diào)用函數(shù)osintctxsw( )。
編寫os_cpu_a.s
——任務(wù)切換代碼
雖然os_task_sw( )和osintctxsw( )的執(zhí)行條件不同,但是它們的功能相同,只要稍作處理就可以它們共用一段任務(wù)切換代碼。這些處理就是保證在執(zhí)行任務(wù)切換前兩者的任務(wù)現(xiàn)場是一致的 。共同執(zhí)行的任務(wù)切換代碼是“osintctxsw”
其中os_task_sw( )是通過軟件中斷0完成的,通過前面的分析,可以知道執(zhí)行任務(wù)切換時(shí)的現(xiàn)場環(huán)境如下所示,同時(shí)r3中保存著spsr,它是任務(wù)中斷前cpsr的備份。
編寫os_cpu_a.s
——osintctxsw
編寫os_cpu_a.s
——osstarthighrdy
μc/os-ii的多任務(wù)環(huán)境由函數(shù)osstart( ) 啟動(dòng)。用戶在調(diào)用該函數(shù)之前,必須已經(jīng)建立了一個(gè)或更多任務(wù)。osstart()最終調(diào)用函數(shù)osstarthighrdy( )運(yùn)行多任務(wù)啟動(dòng)前優(yōu)先級最高的任務(wù),而它最終是調(diào)用__osstarthighrdy實(shí)現(xiàn)的,其代碼如下所示:
編寫os_cpu_a.s
通過前面的分析,我們可以畫出下面這張結(jié)構(gòu)圖:
關(guān)于中斷及時(shí)鐘節(jié)拍
在本移植中,irq是受μc/os-ii管理的中斷,而對于fiq不做處理,這是為了提高fiq的響應(yīng)速度。由于各種arm芯片的中斷系統(tǒng)不一樣,各個(gè)用戶的目標(biāo)板也不一樣,對于中斷和時(shí)鐘節(jié)拍是需要進(jìn)一步移植的代碼。為此編寫了一個(gè)匯編宏,它是μc/os-ii for arm7通用的中斷服務(wù)程序的匯編與c函數(shù)接口代碼。
注:在不受管理的中斷服務(wù)程序中不能調(diào)用任何系統(tǒng)函數(shù)。
關(guān)于中斷及時(shí)鐘節(jié)拍
關(guān)于中斷及時(shí)鐘節(jié)拍
關(guān)于中斷及時(shí)鐘節(jié)拍
中斷服務(wù)程序的編寫
因?yàn)橹袛喟l(fā)生時(shí)肯定是允許中斷的,所以如果用戶在清除中斷源之前調(diào)用μc/os-ii的系統(tǒng)服務(wù)函數(shù)就很可能會(huì)造成芯片的中斷系統(tǒng)工作異常而使程序工作異常。因此在函數(shù)開始處關(guān)閉中斷,或者直接給變量osentersum賦1。如果用戶程序沒有這種情況,則不需要這個(gè)操作。在執(zhí)行os_exit_critical( )后,中斷重新打開,如果在接下來的用戶處理程序中發(fā)生中斷,就可以實(shí)現(xiàn)中斷嵌套。