ARM Cortex―M0/M0+單片機(jī)的指針變量替換方法
實(shí)際上,應(yīng)用程序中可通過(guò)MOV指令將R8~R11初始化成“寄存器常數(shù)”,而以后不再改變它們的值。例如可以令:
R8,=0 用于低寄存器的快速清零
R9,=RAM基地址 用于拼接長(zhǎng)指針
R10,=I/O模塊基地址
R11,=庫(kù)函數(shù)基地址
當(dāng)FLASH存儲(chǔ)器空間不大于64 KB時(shí),函數(shù)指針無(wú)需設(shè)定基地址,可以直接使用低16位作為16位指針。對(duì)于超過(guò)64 KB的FLASH,可以使用庫(kù)函數(shù)基地址,采用類似分頁(yè)的方法實(shí)現(xiàn)16位指針替換。
最后一個(gè)高組寄存器R12可在響應(yīng)中斷時(shí)和R0~R3,PC、SP一同自動(dòng)入棧,是用戶可以使用的寄存器變量。
2.1節(jié)中提出的宏定義方案形式上簡(jiǎn)單清楚,但展開(kāi)后需要多條指令才能完成。將Address_base作為寄存器變量,存放在R8~R12中的某個(gè)高組寄存器中,而不是使用宏定義常量或全局變量。由于C語(yǔ)言不能直接對(duì)通用寄存器進(jìn)行操作,需通過(guò)將匯編嵌入到C語(yǔ)言中實(shí)現(xiàn)長(zhǎng)指針的替換。在程序初始化時(shí),將R8~R12中的一個(gè)寄存器初始化為Address_base的值,例如下面給出的語(yǔ)句:
asm(“LDR r1,=0x1ffff000”);//R1=基地址
asm(“MOV R9,R1”);//R9=R1,即基地址
R9寄存器初始化后無(wú)需再修改,是一個(gè)“寄存器常數(shù)”。對(duì)于已經(jīng)存儲(chǔ)在R0中的長(zhǎng)指針,則使用如下匯編代碼,很容易將其轉(zhuǎn)化為16位地址:
asm(“MOV R1,R9”);//R1=基地址
asm(“SUB R0,R0,R1”);//R0=R0-R1,R0低16位即16位
//短指針值
代碼首先將R9寄存器存儲(chǔ)的基地址轉(zhuǎn)移到R1寄存器,隨后利用單條指令完成從R0寄存器所存長(zhǎng)指針值減去R1中存儲(chǔ)的基地址,并將所得結(jié)果保存在R0中。執(zhí)行完成后,R0低16位便是轉(zhuǎn)化后的16位地址。16位地址轉(zhuǎn)化為長(zhǎng)指針是類似的轉(zhuǎn)化形式(SUB指令換為ADD指令),在此不再贅述。這種方法充分利用了內(nèi)核提供的高組寄存器,并且簡(jiǎn)化了指針轉(zhuǎn)化的算法,減少了所需指令的數(shù)目,提高了運(yùn)行效率,縮短了轉(zhuǎn)換時(shí)間,降低MCU因指針替換而產(chǎn)生的時(shí)間損失。轉(zhuǎn)換所需指令數(shù)目也壓縮到兩條,減少轉(zhuǎn)換過(guò)程所帶來(lái)的額外指令代碼的存儲(chǔ)空間開(kāi)銷。
3 指針替換結(jié)果
μC/OS(含μC/OS-II、μC/OS—III)是適用于低成本MCU的多任務(wù)實(shí)時(shí)內(nèi)核。以μC/OS為例,當(dāng)最大任務(wù)數(shù)為10時(shí),整個(gè)內(nèi)核需使用12個(gè)全局指針型變量,而非指針型變量?jī)H需占用8字節(jié)RAM空間。若使用默認(rèn)的長(zhǎng)指針模式,共需12×4+8=56字節(jié);若改用短指針,則需使用12 ×2+8=32字節(jié)。任務(wù)數(shù)目、任務(wù)間通信機(jī)制增多時(shí),指針變量的使用將更頻繁,本文介紹的方法所節(jié)約的RAM空間也更加顯著。在Cortex-M0/M0+處理器替代8/16位MCU的應(yīng)用中,非常有必要使用短指針。
最新版本的μC/OS—III針對(duì)帶有計(jì)算前導(dǎo)零硬件指令(CLZ)的Cortex—M3/M4處理器進(jìn)行了重大改進(jìn),提高了其優(yōu)先級(jí)任務(wù)搜索的效率。但Cortex—M0/M0+的ARMv6指令集簡(jiǎn)化掉了CLZ指令,故不適宜使用μC/OS—III。這里以運(yùn)行μC/OS—II v2.92(最多256個(gè)任務(wù))為例,說(shuō)明指針替換效果。實(shí)際上對(duì)于內(nèi)存緊張的MCU,μC/OS—II v2.82及以下的版本(最多64個(gè)任務(wù))就足夠用了。
μC/OS—II每個(gè)任務(wù)都需要使用任務(wù)控制塊TCB(Task Control Block)的數(shù)據(jù)結(jié)構(gòu),來(lái)維護(hù)任務(wù)相關(guān)的信息。在μC/OS—II v2.92中,每個(gè)任務(wù)的TCB數(shù)據(jù)結(jié)構(gòu)包含9個(gè)指針變量,采用本文描述的16位指針替換方法后,每個(gè)任務(wù)控制塊均可以節(jié)省18字節(jié)的RAM空間。在μC/OS—II中還存在很多數(shù)據(jù)結(jié)構(gòu),均包含著大量的指針變量。這些數(shù)據(jù)結(jié)構(gòu)采用本文描述的方法所節(jié)約的RAM空間如表1所列。
可以看出,以16位短指針替代ARM編譯器默認(rèn)的32位長(zhǎng)指針,能使Cortex—M0/M0+MCU對(duì)RAM資源的占用接近8/16位MCU。這一點(diǎn)對(duì)“全面替代”是十分重要的。
結(jié)語(yǔ)
以ARM Cortex-M0/M0+為內(nèi)核的32位MCU以其性能、功耗和價(jià)格的優(yōu)勢(shì),“全面替代”以8051/52、68S08/12等為代表的8/16位MCU已是大勢(shì)所趨。而目前主流ARM IDE中的C編譯器僅支持長(zhǎng)指針變量。若將原有的8/16位MCU應(yīng)用程序移植到內(nèi)存資源相當(dāng)?shù)腁RM MCU上,大量長(zhǎng)指針變量的使用可能會(huì)導(dǎo)致RAM資源不足,而改用更大內(nèi)存的MCU無(wú)疑會(huì)增加產(chǎn)品成本。通過(guò)使用Cortex—M0/M0+內(nèi)核的高組寄存器操作指令,可以實(shí)現(xiàn)長(zhǎng)短指針的轉(zhuǎn)換,極大地節(jié)約RAM占用量,為既有應(yīng)用的順利移植提供幫助。
當(dāng)然,長(zhǎng)短指針的轉(zhuǎn)換操作會(huì)帶來(lái)額外的運(yùn)行時(shí)間的開(kāi)銷,轉(zhuǎn)換指令也帶來(lái)代碼存儲(chǔ)量的增加。在一定程度上,這種方法是通過(guò)增加程序存儲(chǔ)量和運(yùn)行周期的代價(jià)來(lái)?yè)Q取數(shù)據(jù)存儲(chǔ)量的減少。由于ARM精簡(jiǎn)指令集的結(jié)構(gòu),其指令編碼長(zhǎng)度和執(zhí)行速度上都有提升,可以部分抵銷程序存儲(chǔ)量和運(yùn)行周期的開(kāi)銷,而數(shù)據(jù)存儲(chǔ)量的矛盾則更加突出和棘手。本文介紹的方法對(duì)此作出了有益嘗試。
本文介紹的方法需要對(duì)已有代碼進(jìn)行一定的改造,筆者希望ARM編譯器能盡快提供面向Cortex—M0/M0+內(nèi)核的短指針優(yōu)化編譯選項(xiàng),為完成ARM對(duì)8/16位MCU內(nèi)核的“全面替代”提供良好的支持。
評(píng)論