程宏玉 王宜懷 姚望舒 彭 濤 黃志賢
(蘇州大學計算機科學與技術學院 江蘇 蘇州 215000)
ARM架構處理器在性能、功耗等方面的優勢決定了其在芯片市場中不可取代的角色,其中Cortex-M系列微控制器(Microcontroller Unit,MCU)憑借出色的功耗和成本控制廣泛應用于嵌入式系統領域。引導加載程序(Bootloader)是嵌入式系統在引導操作系統內核或用戶程序之前運行的一段標準代碼,其主要功能是建立內存空間映射,設置系統堆棧和系統啟動參數區等,從而將系統的軟硬件環境帶到一個合適的狀態[1],其功能類似于PC端基本輸入輸出系統(BIOS)。
當前Bootloader的研究更多地關注嵌入式設備中操作系統的引導以及Bootloader在嵌入式終端中的移植:Sha等[2]設計了基于S3C2440開發板的Bootloader,完成Linux操作系統的啟動;Zhang等[3]成功將U-Boot移植到視頻監控硬件平臺DM6446上。而關于如何利用Bootloader為嵌入式開發提供支撐的論述相對較少。與之相對,面向MCU的嵌入式應用開發普遍存在難度大、周期長、移植困難等客觀問題,Bootloader程序的低復用率及其單一功能已難以滿足實際應用需求。
隨著嵌入式系統的發展,軟件構件化已成為嵌入式開發的發展趨勢[4],相對于普通代碼級編程,以構件為基本單位的嵌入式開發可有效提高編程顆粒度及應用可移植性。本文以ARM Cortex-M系列MCU為平臺,在Bootloader中實現構件實體和對應索引的獨立部署,優化Bootloader設計方案,在此基礎上提出嵌入式終端BIOS的概念及設計方法。
PC機BIOS作為預先安裝的固件,是上電啟動后運行的第一個程序,可為操作系統和應用程序的運行提供通用底層軟件接口。嵌入式終端BIOS以Bootloader為原型,遵循構件化軟件設計思想,以盡可能小的軟硬件資源為代價提高用戶程序開發效率。
嵌入式終端BIOS是固化于目標設備指定非易失性存儲區域(如Flash)的標準應用程序,與真正的用戶程序在物理空間分離。BIOS在完成傳統Bootloader啟動和初始化功能的基礎上,通過合理的存儲空間劃分實現底層驅動構件的駐留,為用戶程序提供合理的調用接口;優化BIOS到用戶程序的跳轉流程,并提供返回BIOS機制;實現中斷共享,擴展用戶程序權限。
針對不同的MCU和應用程序類型,其對應的BIOS可能存在特殊功能或應用需求[5]。從中抽取出共性需求,設計對應的功能模塊,同時考慮不同MCU間的移植以最大限度降低BIOS開發和移植的開銷。結合實際應用和理論分析,將BIOS需求概括為3類:程序跳轉,底層驅動構件駐留,共享中斷服務例程。
以BIOS共性需求為出發點,為之封裝易于移植復用的獨立函數或文件,并借助ARM Cortex-M系列軟件接口標準[6](Cortex Microcontroller Software Interface Standard,CMSIS),抽象出圖1所示嵌入式終端軟件架構。

圖1 嵌入式終端軟件架構
MCU上電后BIOS從指定Flash區域加載,完成系統初始化和存儲空間分配,在跳轉主函數之前建立供用戶程序調用的驅動函數接口表;隨后進行跳轉判斷決定是否執行中斷共享和運行狀態切換。BIOS執行流程如圖2所示。

圖2 BIOS執行流程
本文以NXP公司MKL36Z64VLH4[7](簡稱MKL36Z64)作為硬件平臺,闡述嵌入式終端BIOS的設計方法。MKL36Z64基于ARM Cortex-M0+內核,最大運行速率48 MHz,Flash大小為64 KB,RAM空間為8 KB。
MCU存儲器空間的合理劃分是保證系統穩定運行、充分發揮芯片性能的基礎,劃分原則是BIOS在實現必要功能的前提下占用盡可能少的存儲空間,為用戶保留更多的可用資源。MCU存儲空間的劃分可通過修改對應MCU工程中的鏈接文件實現。鏈接文件是一種規則性文件,它決定在鏈接步驟中MCU各存儲介質的起始地址和空間大小,并指示各段數據的存儲位置[8]。不同芯片廠商提供的鏈接文件格式及語法表示有所差異,但基本內涵一致。MKL36Z64中鏈接文件配置方式如圖3所示。

圖3 鏈接文件配置方式
通過鏈接文件的配置,將目標MCU的Flash區域一分為二,BIOS占據26 KB的空間,用戶程序使用剩余空間,實現物理空間的分離,最終Flash劃分情況如圖4所示。

圖4 MKL36Z64 Flash劃分圖
BIOS向用戶程序跳轉的判斷一般需借助特定標識信息,這個標識信息可以是某個特定GPIO引腳狀態[9]或是存儲在帶電可擦可編程只讀存儲器(Electrically Erasable Programmable Read-Only Memory,EEPROM)中某個標識位的值[10]。前者在實現上最為簡單,但依賴單個GPIO引腳狀態,可靠性相對較低;后者可靠性高但需要特定外設支持,通用性相對較差。本文選擇在RAM區域中開辟一段獨立的空間保存判斷程序跳轉的標識信息,該段區域處于data段和bss段之間,具有熱復位變量不清除的特性,即程序跳轉或正常軟件復位不會干預定義在該段區域中的變量值。以定義在該段區域中的標識信息作為程序跳轉依據擺脫硬件限制的同時具有更高的可靠性。
BIOS向用戶程序的跳轉參考MCU啟動流程,在Cortex-M系列MCU中主要涉及主棧指針(Main Stack Pointer,MSP)指針和程序計數器(Program Counter,PC)的設置。其中:MSP默認指向棧頂位置;PC存放下一條待運行指令的地址。當系統由BIOS跳轉至用戶程序時,軟件上主動從用戶程序Flash區域取出棧頂指針重新賦值MSP,然后利用函數指針的方式跳轉至用戶程序復位向量,完成BIOS到用戶程序的跳轉。
用戶程序一般由具體開發者編寫并燒入設備,系統由BIOS跳轉到用戶程序執行后,程序運行中發生的錯誤或異常可能對整個系統造成不可逆的影響,提供必要的防錯手段是嵌入式系統穩定運行的重要保障。本文利用非可屏蔽中斷(Non Maskable Interrupt,NMI)使系統由用戶程序強制跳轉回BIOS,并持續運行在BIOS中直至程序錯誤修復。NMI優先級僅次于復位中斷,可在系統停止響應或出現無法處理的異常時通過將芯片對應引腳拉低的方式觸發,從而保證系統整體的可維護性與可靠性。程序跳轉流程如圖5所示。

(a) BIOS跳轉用戶程序 (b) 用戶程序跳轉BIOS圖5 程序跳轉流程
中斷向量表是按照中斷源的中斷向量號以固定順序存放中斷服務例程入口地址的一段存儲區域,其默認存儲位置為Flash起始地址。當觸發中斷事件時MCU從中斷向量表中取得指定中斷服務例程的入口地址并轉而執行該中斷服務例程。BIOS和用戶程序是物理空間上獨立的應用程序,默認情況下擁有各自的中斷向量表及對應的中斷服務例程。但在實際場景中用戶程序需要使用BIOS中定義的某些系統服務,即兩段應用程序需共享中斷服務。
修改中斷向量表表項,使其指向BIOS中斷服務例程的物理地址,可實現中斷服務例程的共享。該方法存在兩個問題:(1) Flash以扇區為單位擦除的特點決定了修改某一表項內容的復雜性且過程難以向用戶開放;(2) 用戶必須提前獲知BIOS中斷服務例程的地址,無法實現動態修改。本文實現一種將中斷向量表拷貝至RAM,動態修改中斷向量表的方案。不同于Flash,RAM無須以扇區為最小操作單元。在跳轉用戶程序之前將BIOS的中斷向量表拷貝至特定RAM段,跳轉完成后用戶程序可選擇修改RAM區中斷向量表任一表項內容。
在ARM Cortex-M處理器中以向量表偏移寄存器(Vector Table Offset Register,VTOR)定位中斷向量表位置。在完成數據拷貝后,須將VTOR重新賦值為RAM區中斷向量表的起始位置,真正完成中斷向量表的重定向。
與Bootloader程序不同,BIOS內駐留了嵌入式常用驅動,如GPIO、UART、Flash、I2C、SPI、PWM等,并提供了函數原型級調用接口。用戶程序不需要從“零”編起,而是在相應框架基礎上,充分應用BIOS資源,實現快捷編程。
驅動函數的駐留一般采用在固定地址存放函數接口表的方式,但該方法需在BIOS特定存儲區域開辟固定大小的存儲空間,不利于程序移植和動態更新。本文利用請求管理調用(Supervisor Call,SVC)方式實現駐留驅動的調用,其思想借鑒DOS下的系統功能調用。在DOS操作系統中系統功能的調用是一個具有多種功能服務程序的軟中斷指令,DOS操作系統將特定功能編號,并向用戶提供編號列表[11]。用戶需要調用DOS系統功能時,將指定系統功能編號存入AH寄存器,并通過主動觸發INT 21H中斷實現系統服務調用。
SVC是ARM處理器中提供的一種能夠使任務觸發特定OS異常的軟件中斷機制,它既是一條指令,也是一種中斷[12]。SVC指令包含一個8位立即數,理論上可利用SVC指令實現255種不同的服務,其中0號服務由操作系統占用。本文使用SVC的1號服務獲取驅動函數接口表,并預留其他服務接口作為系統功能擴展。根據Cortex-M系列處理器的中斷機制,進入SVC中斷服務例程后,SVC指令編號可通過棧中PC寄存器值獲取,堆棧狀態如圖6所示。

圖6 SVC中斷發生時堆棧狀態
用戶程序需要使用BIOS駐留驅動函數時主動觸發SVC中斷,在其中斷服務例程中將函數接口表的地址作為返回參數傳遞至SVC中斷調用處,用戶根據驅動函數在表中的偏移量調用具體功能函數。獲取驅動函數接口表流程如圖7所示。

圖7 獲取驅動函數接口表流程
可移植性是軟件質量的重要屬性,本文結合BIOS終端程序框架,闡述ARM Cortex-M系列MCU之間移植方法。
結構清晰、組織合理的工程框架是軟件可移植、可復用的基礎[13]。針對嵌入式應用開發特點、兼顧BIOS在不同MCU間的移植,建立圖8所示BIOS工程框架。

圖8 BIOS工程框架
其中:Core存放內核文件,主要完成核內外設訪問控制、外設寄存器訪問權限設置、特設功能函數接口定義;MCU文件涉及芯片初始化、中斷向量表定義、啟動流程、外設驅動的具體實現等相關內容;UserBoard存放應用級硬件驅動;SoftComponent存放BIOS具體功能構件,projectJump實現程序跳轉,svc提供驅動函數調用列表,cpy_nvic實現中斷服務例程共享;NosPrg存放BIOS主程序文件、中斷服務例程文件。
3.1節所述程序框架已將相關文件按功能、軟硬件結構合理部署,結合BIOS功能模塊的構件化設計,可以較小的代價完成BIOS程序在不同芯片間移植。以MKL36Z64中BIOS為基準,表1給出BIOS在Cortex-M0+內核的MKW01Z128[14]以及Cortex-M4F內核的MSP432P401R[15]兩款MCU上的移植比較。

表1 BIOS在不同MCU間移植比較
可以看出,BIOS在相同內核MCU間的移植只需修改MCU文件,不同內核間的移植因架構差異改動量稍有增加。但得益于構件化結構設計,BIOS功能構件移植時無須改動,這在最大程度上保證了方案可移植性。
嵌入式應用程序與目標設備之間的高度耦合決定了其開發和移植的復雜性,降低硬件差異影響、抽象共性開發技術是提高應用開發效率的關鍵。本文以ARM Cortex-M系列MCU為平臺,提出嵌入式終端BIOS的概念和設計方法,結合構件化設計思想,提高終端程序可移植性;優化Bootloader實現關鍵技術,為用戶提供構件級開發支撐,有效降低用戶程序開發難度。