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

可以看出,以16位短指針替代ARM編譯器默認的32位長指針,能使Cortex—M0/M0+MCU對RAM資源的占用接近8/16位MCU。這一點對“全面替代”是十分重要的。
結語
以ARM Cortex-M0/M0+為內(nèi)核的32位MCU以其性能、功耗和價格的優(yōu)勢,“全面替代”以8051/52、68S08/12等為代表的8/16位MCU已是大勢所趨。而目前主流ARM IDE中的C編譯器僅支持長指針變量。若將原有的8/16位MCU應用程序移植到內(nèi)存資源相當?shù)腁RM MCU上,大量長指針變量的使用可能會導致RAM資源不足,而改用更大內(nèi)存的MCU無疑會增加產(chǎn)品成本。通過使用Cortex—M0/M0+內(nèi)核的高組寄存器操作指令,可以實現(xiàn)長短指針的轉換,極大地節(jié)約RAM占用量,為既有應用的順利移植提供幫助。
當然,長短指針的轉換操作會帶來額外的運行時間的開銷,轉換指令也帶來代碼存儲量的增加。在一定程度上,這種方法是通過增加程序存儲量和運行周期的代價來換取數(shù)據(jù)存儲量的減少。由于ARM精簡指令集的結構,其指令編碼長度和執(zhí)行速度上都有提升,可以部分抵銷程序存儲量和運行周期的開銷,而數(shù)據(jù)存儲量的矛盾則更加突出和棘手。本文介紹的方法對此作出了有益嘗試。
本文介紹的方法需要對已有代碼進行一定的改造,筆者希望ARM編譯器能盡快提供面向Cortex—M0/M0+內(nèi)核的短指針優(yōu)化編譯選項,為完成ARM對8/16位MCU內(nèi)核的“全面替代”提供良好的支持。
評論