本文為RISC-V嵌入式開發(fā)準(zhǔn)備篇2:嵌入式開發(fā)的特點(diǎn)介紹。
本文的目的是對(duì)嵌入式開發(fā)的特點(diǎn)進(jìn)行簡(jiǎn)單的科普與回顧,為后續(xù)詳細(xì)介紹“RISC-V GCC工具鏈”和“RISC-V匯編語言程序設(shè)計(jì)”打下基礎(chǔ)。注:本文力求通俗易懂,主要面向初學(xué)者,對(duì)嵌入式開發(fā)有所了解的讀者可以忽略此文。
在本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介》中介紹過,嵌入式系統(tǒng)的程序編譯過程和開發(fā)有其特殊性,譬如:
嵌入式系統(tǒng)需要使用交叉編譯與遠(yuǎn)程調(diào)試的方法進(jìn)行開發(fā)。
需要自己定義引導(dǎo)程序。
需要注意減少代碼體積(Code Size)。
需要移植printf從而使得嵌入式系統(tǒng)也能夠打印輸入。
使用Newlib作為C運(yùn)行庫(kù)。
每個(gè)特定的嵌入式系統(tǒng)都需要配套的板級(jí)支持包。
下文將分別予以介紹。
1 交叉編譯和遠(yuǎn)程調(diào)試
在本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介》中介紹了如何在Linux系統(tǒng)的PC電腦上開發(fā)一個(gè)Hello World程序,對(duì)其進(jìn)行編譯,然后運(yùn)行在此電腦上。在這種方式下,我們使用PC電腦上的編譯器編譯出該P(yáng)C電腦本身可執(zhí)行的程序,這種編譯方式稱之為本地編譯。
嵌入式平臺(tái)上往往資源有限,嵌入式系統(tǒng)(譬如常見ARM MCU或8051單片機(jī))的存儲(chǔ)器容量通常只在幾KB到幾MB之間,且只有閃存而沒有硬盤這種大容量存儲(chǔ)設(shè)備,在這種資源有限的環(huán)境中,不可能將編譯器等開發(fā)工具安裝在嵌入式設(shè)備中,所以無法直接在嵌入式設(shè)備中進(jìn)行軟件開發(fā)。因此,嵌入式平臺(tái)的軟件一般在主機(jī)PC上進(jìn)行開發(fā)和編譯,然后將編譯好的二進(jìn)制代碼下載至目標(biāo)嵌入式系統(tǒng)平臺(tái)上運(yùn)行,這種編譯方式屬于交叉編譯。
交叉編譯可以簡(jiǎn)單理解為,在當(dāng)前編譯平臺(tái)下,編譯出來的程序能運(yùn)行在體系結(jié)構(gòu)不同的另一種目標(biāo)平臺(tái)上,但是編譯平臺(tái)本身卻不能運(yùn)行該程序,譬如,在x86平臺(tái)的PC電腦上編寫程序并編譯成能運(yùn)行在ARM平臺(tái)的程序,編譯得到的程序在x86平臺(tái)上不能運(yùn)行,必須放到ARM平臺(tái)上才能運(yùn)行。
與交叉編譯同理,在嵌入式平臺(tái)上往往也無法運(yùn)行完整的調(diào)試器,因此當(dāng)運(yùn)行于嵌入式平臺(tái)上的程序出現(xiàn)問題時(shí),需要借助主機(jī)PC平臺(tái)上的調(diào)試器來對(duì)嵌入式平臺(tái)進(jìn)行調(diào)試。這種調(diào)試方式屬于遠(yuǎn)程調(diào)試。
常見的交叉編譯和遠(yuǎn)程調(diào)試工具是GCC和GDB。在本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介》中介紹了如何使用Linux自帶的GCC本地編譯一個(gè)Hello World程序并運(yùn)行。但是,GCC不僅能作為本地編譯器,還能作為交叉編譯器;同理GDB不僅可以作為本地調(diào)試器,還可以作為遠(yuǎn)程調(diào)試器。
當(dāng)作為交叉編譯器之時(shí),GCC通常有不同的命名,譬如:
arm-none-eabi-gcc和arm-none-eabi-gdb是面向裸機(jī)(Bare-Metal)ARM平臺(tái)的交叉編譯器和遠(yuǎn)程調(diào)試器。
所謂裸機(jī)(Bare-Metal)是嵌入式領(lǐng)域的一個(gè)常見形態(tài),表示不運(yùn)行操作系統(tǒng)的系統(tǒng)
而riscv-none-embed-gcc和riscv-none-embed-gdb是面向裸機(jī)RISC-V平臺(tái)的交叉編譯器和遠(yuǎn)程調(diào)試器。
本號(hào)后續(xù)發(fā)文《RISC-V GCC工具鏈的介紹》將介紹RISC-V GCC工具鏈的更多信息。
2 移植newlib或newlib-nano作為C運(yùn)行庫(kù)
newlib是一個(gè)面向嵌入式系統(tǒng)的C運(yùn)行庫(kù)。相對(duì)于本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介》中介紹的glibc,newlib實(shí)現(xiàn)了大部分的功能函數(shù),但體積卻小很多。newlib獨(dú)特的體系結(jié)構(gòu)將功能實(shí)現(xiàn)與具體的操作系統(tǒng)分層,使之能夠很好地進(jìn)行配置以滿足嵌入式系統(tǒng)的要求。由于專為嵌入式系統(tǒng)設(shè)計(jì),newlib具有可移植性強(qiáng)、輕量級(jí)、速度快、功能完備等特點(diǎn),已廣泛應(yīng)用于各種嵌入式系統(tǒng)中。
由于嵌入式操作系統(tǒng)和底層硬件的多樣性,為了能夠?qū)/C++語言所需要的庫(kù)函數(shù)實(shí)現(xiàn)與具體的操作系統(tǒng)和底層硬件進(jìn)行分層,newlib的所有庫(kù)函數(shù)都建立在20個(gè)樁函數(shù)的基礎(chǔ)上,這20個(gè)樁函數(shù)完成具體操作系統(tǒng)和底層硬件相關(guān)的功能:
I/O和文件系統(tǒng)訪問(open、close、read、write、lseek、stat、fstat、fcntl、link、unlink、rename);
擴(kuò)大內(nèi)存堆的需求(sbrk);
獲得當(dāng)前系統(tǒng)的日期和時(shí)間(gettimeofday、times);
各種類型的任務(wù)管理函數(shù)(execve、fork、getpid、kill、wait、_exit);
這20個(gè)樁函數(shù)在語義、語法上與POSIX(Portable Operating System Interface of UNIX)標(biāo)準(zhǔn)下對(duì)應(yīng)的20個(gè)同名系統(tǒng)調(diào)用完全兼容。
所以,如果需要移植newlib至某個(gè)目標(biāo)嵌入式平臺(tái),成功移植的關(guān)鍵是在目標(biāo)平臺(tái)下找到能夠與newlib樁函數(shù)銜接的功能函數(shù)或者實(shí)現(xiàn)這些樁函數(shù)。本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將介紹蜂鳥E200的HBird-E-SDK平臺(tái)如何實(shí)現(xiàn)移植實(shí)現(xiàn)newlib的樁函數(shù)。
注意:newlib的一個(gè)特殊版本newlib-nano版本進(jìn)一步為嵌入式平臺(tái)減少了代碼體積(Code Size),因?yàn)閚ewlib-nano提供了更加精簡(jiǎn)版本的malloc和printf函數(shù)的實(shí)現(xiàn),并且對(duì)庫(kù)函數(shù)使用GCC的-Os(側(cè)重代碼體積的優(yōu)化)選項(xiàng)進(jìn)行編譯優(yōu)化。
3 嵌入式引導(dǎo)程序和中斷異常處理
在本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介》中介紹了如何在Linux系統(tǒng)的PC電腦上開發(fā)一個(gè)Hello World程序,對(duì)其進(jìn)行編譯,然后運(yùn)行在此電腦上。在這種方式下,程序員僅僅只需要關(guān)注Hello World程序本身,程序的主體由main函數(shù)組織而成,程序員可以無需關(guān)注Linux操作系統(tǒng)在運(yùn)行該程序的main函數(shù)之前和之后需要做什么。事實(shí)上,在Linux操作系統(tǒng)中運(yùn)行應(yīng)用程序(譬如簡(jiǎn)單的Hello World)時(shí),操作系統(tǒng)需要?jiǎng)討B(tài)地創(chuàng)建一個(gè)進(jìn)程、為其分配內(nèi)存空間、創(chuàng)建并運(yùn)行該進(jìn)程的引導(dǎo)程序,然后才會(huì)開始執(zhí)行該程序的main函數(shù),待其運(yùn)行結(jié)束之后,操作系統(tǒng)還要清除并釋放其內(nèi)存空間、注銷該進(jìn)程等。
從上述過程中可以看出,程序的引導(dǎo)和清除這些“臟活累活”都是由Linux這樣的操作系統(tǒng)來負(fù)責(zé)進(jìn)行。但是在嵌入式系統(tǒng)中,程序員除了開發(fā)以main函數(shù)為主體的功能程序之外,還需要關(guān)注如下兩個(gè)方面:
引導(dǎo)程序:
嵌入式系統(tǒng)上電后需要對(duì)系統(tǒng)硬件和軟件運(yùn)行環(huán)境進(jìn)行初始化,這些工作往往由用匯編語言編寫的引導(dǎo)程序完成。
引導(dǎo)程序是嵌入式系統(tǒng)上電后運(yùn)行的第一段軟件代碼。引導(dǎo)程序?qū)τ谇度胧较到y(tǒng)非常關(guān)鍵,引導(dǎo)程序所執(zhí)行的操作依賴于所開發(fā)的嵌入式系統(tǒng)的軟硬件特性,一般流程包括:初始化硬件、設(shè)置異常和中斷向量表、把程序拷貝到片上SRAM中、完成代碼的重映射等,最后跳轉(zhuǎn)到main函數(shù)入口。
本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將結(jié)合HBird-E-SDK平臺(tái)的引導(dǎo)程序?qū)嵗私庖龑?dǎo)程序的更多細(xì)節(jié)。
中斷異常處理
中斷和異常是嵌入式系統(tǒng)非常重要的一個(gè)環(huán)節(jié),因此,嵌入式系統(tǒng)軟件還必須正確地配置中斷和異常處理函數(shù)。有關(guān)RISC-V架構(gòu)的中斷和異常的詳細(xì)信息,請(qǐng)參見RISC-V中文書籍《手把手教你設(shè)計(jì)CPU——RISC-V處理器篇》 中第13章內(nèi)容《不得不說的故事——中斷和異常》。
本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將結(jié)合HBird-E-SDK程序?qū)嵗私馊绾闻渲弥袛嗪彤惓L幚砗瘮?shù)。
4 嵌入式系統(tǒng)鏈接腳本
在本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介》中介紹了如何在Linux系統(tǒng)的PC電腦上開發(fā)一個(gè)Hello World程序,對(duì)其進(jìn)行編譯,然后運(yùn)行在此電腦上。在這種方式下,程序員也無需關(guān)心編譯過程中的“鏈接”這一步驟所使用的鏈接腳本,無需為程序分配具體的內(nèi)存空間。
但是在嵌入式系統(tǒng)中,程序員除了開發(fā)以main函數(shù)為主體的功能程序之外,還需要關(guān)注“鏈接腳本”為程序分配合適的存儲(chǔ)器空間,譬如程序段放在什么區(qū)間、數(shù)據(jù)段放在什么區(qū)間等等。
本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將結(jié)合HBird-E-SDK的“鏈接腳本”實(shí)例了解更多細(xì)節(jié)。
5 減少代碼體積
嵌入式平臺(tái)上往往存儲(chǔ)器資源有限,嵌入式系統(tǒng)(譬如常見的ARM MCU或8051單片機(jī))的存儲(chǔ)器容量通常只在幾KB到幾MB之間,且只有閃存而沒有硬盤這種大容量存儲(chǔ)設(shè)備,在這種資源有限的環(huán)境中,程序的代碼體積(Code Size)顯得尤其重要,因此,有效地降低降低代碼體積(Code Size)是嵌入式軟件開發(fā)必須要考慮的問題,常見的方法如:
使用newlib-nano作為C運(yùn)行庫(kù)以取得較小代碼體積(Code Size)的C庫(kù)函數(shù)。
盡量少使用C語言的大型庫(kù)函數(shù),譬如在正式發(fā)行版本的程序中避免使用printf和scanf等函數(shù)。
如果在開發(fā)的過程中一定需要使用printf函數(shù),可以使用某些自己實(shí)現(xiàn)的閹割版printf函數(shù)(而不是C運(yùn)行庫(kù)中提供的printf函數(shù))以生成較小的代碼體積。
除此之外,在C/C++語言的語法和程序開發(fā)方面也有眾多技巧以取得更小的代碼體積(Code Size)。
本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將結(jié)合HBird-E-SDK平臺(tái)實(shí)例了解更多“減少代碼體積”的實(shí)現(xiàn)細(xì)節(jié)。減小代碼體積(Code Size)的方法很多,本文在此不做一一贅述,請(qǐng)初學(xué)的讀者自行查閱相關(guān)資料進(jìn)行學(xué)習(xí)。
6 支持printf函數(shù)
在本號(hào)上次發(fā)表的文章《編譯過程簡(jiǎn)介 》中介紹了如何在Linux系統(tǒng)的PC電腦上開發(fā)一個(gè)Hello World程序,程序中使用C語言的標(biāo)準(zhǔn)庫(kù)函數(shù)printf打印了一個(gè)“Hello World”字符串。該程序在Linux系統(tǒng)里面運(yùn)行的時(shí)候字符串被成功的輸出到了Linux的終端界面上。在這個(gè)過程中,程序員無需關(guān)心Linux系統(tǒng)到底是如何將printf函數(shù)的字符串輸出到Linux終端上的。事實(shí)上,如《編譯過程簡(jiǎn)介》中所述,在Linux本地編譯的程序會(huì)鏈接使用Linux系統(tǒng)的C運(yùn)行庫(kù)glibc,而glibc充當(dāng)了應(yīng)用程序和Linux操作系統(tǒng)之間的接口,glibc提供的 printf 函數(shù)就會(huì)調(diào)用如sys_write等操作系統(tǒng)的底層系統(tǒng)調(diào)用函數(shù),從而能夠?qū)ⅰ白址陛敵龅絃inux終端上。
從上述過程中可以看出,由于有g(shù)libc的支持,所以printf函數(shù)能夠在Linux系統(tǒng)中正確的進(jìn)行輸出。但是在嵌入式系統(tǒng)中,printf的輸出卻不那么容易了,基于如下幾個(gè)原因:
嵌入式系統(tǒng)使用newlib作為C運(yùn)行庫(kù),而newlib的C運(yùn)行庫(kù)所提供的printf函數(shù)最終依賴于如本文中所介紹的newlib樁函數(shù)write,因此必須實(shí)現(xiàn)此write函數(shù)才能夠正確的執(zhí)行printf函數(shù)。
嵌入式系統(tǒng)往往沒有“顯示終端”存在,譬如常見的單片機(jī)其作為一個(gè)黑盒子一般的芯片,根本沒有顯示終端。因此,為了能夠支持顯示輸出,通常需要借助單片機(jī)芯片的UART接口將printf函數(shù)的輸出重新定向到主機(jī)PC的COM口上,然后借助主機(jī)PC的串口調(diào)試助手顯示出輸出信息。同理,對(duì)于scanf輸入函數(shù),也需要通過主機(jī)PC的串口調(diào)試助手獲取輸入然后通過主機(jī)PC的COM口發(fā)送給單片機(jī)芯片的UART接口。
從以上兩點(diǎn)可以看出,嵌入式平臺(tái)的UART接口非常重要,往往扮演了輸出管道的角色,為了能夠?qū)rintf函數(shù)的輸出定向到UART接口,需要實(shí)現(xiàn)newlib的樁函數(shù)write,使其通過編程UART的相關(guān)寄存器將字符通過UART接口輸出。本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將結(jié)合HBird-E-SDK平臺(tái)移植printf函數(shù)的實(shí)例了解更多細(xì)節(jié)。
7 提供板級(jí)支持包
對(duì)于特定的嵌入式硬件平臺(tái),為了方便用戶在硬件平臺(tái)上開發(fā)嵌入式程序,硬件平臺(tái)一般會(huì)提供板級(jí)支持包(Board Support Package,BSP)。板級(jí)支持包所包含的內(nèi)容沒有絕對(duì)的標(biāo)準(zhǔn),通常說來,其必須包含如下內(nèi)容:
底層硬件設(shè)備的地址分配信息
底層硬件設(shè)備的驅(qū)動(dòng)函數(shù)
系統(tǒng)的引導(dǎo)程序
中斷和異常處理服務(wù)程序
系統(tǒng)的鏈接腳本
如果使用newlib作為C運(yùn)行庫(kù),一般還提供newlib樁函數(shù)的實(shí)現(xiàn)。
由于板級(jí)支持包往往會(huì)將很多底層的基礎(chǔ)設(shè)施和移植工作搭建好,因此應(yīng)用程序開發(fā)人員通常都無需關(guān)心本文第1.2節(jié)至第1.6節(jié)中描述的內(nèi)容,能夠從底層細(xì)節(jié)中被解放出來避免重復(fù)建設(shè)而出錯(cuò)。本號(hào)后續(xù)發(fā)文《基于HBird-E-SDK平臺(tái)的軟件開發(fā)與運(yùn)行》將結(jié)合HBird-E-SDK平臺(tái)的BSP實(shí)例了解更多細(xì)節(jié)。
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3620瀏覽量
129712 -
RISC-V
+關(guān)注
關(guān)注
45文章
2322瀏覽量
46526
原文標(biāo)題:RISC-V嵌入式開發(fā)準(zhǔn)備篇2:嵌入式開發(fā)的特點(diǎn)介紹
文章出處:【微信號(hào):real_farmer,微信公眾號(hào):硅農(nóng)亞歷山大】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論