国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

使用ADS1.2進行嵌入式軟件開發

2019-11-17 04:57:54
字體:
來源:轉載
供稿:網友
概述
      嵌入式應用程序通常都是在樣機環境下調試與開發的,這種環境與最終產品之間并不完全相同。因此,在系統調試階段就考慮應用程序在最終目標硬件中的運行情況是非常重要的。
     本文旨在討論如何將一個開發/調試環境下的嵌入式應用程序轉移到最終獨立運行的目標系統中去,并提到了ARM ADS1.2開發工具包的一些功能特性及其在這個過程中所起到的作用。
    使用ADS開發嵌入式程序時,需要著重考慮以下幾個問題:
1.與硬件相關的C語言庫函數的使用;
2.某些C語言庫函數使用了調試環境中的資源,要把這些使用的資源重定向到目標系統中的硬件上來;
3.可執行映象文件的存儲器映射必須根據目標硬件的存儲器分布進行裁剪;
4.在主程序執行前,嵌入式應用程序必須先完成系統的初始化。一個完整的初始化包括用戶的啟動執行代碼和ADS中C庫函數的初始化過程。 使用ADS1.2進行嵌入式軟件開發(圖一)
圖1 Semihosting的實現舉例使用ADS1.2進行嵌入式軟件開發(圖二)
圖2 C語言庫函數結構使用ADS1.2進行嵌入式軟件開發(圖三)
圖3 缺省的存儲器映射使用ADS1.2進行嵌入式軟件開發(圖四)
圖4 連接器布局規則缺省的工程項目設置
剛開始一個嵌入式應用軟件開發時,ADS用戶可能并不完全清楚目標硬件的一些參數指標。比如有關外設、存儲器地址分布,甚至處理器類型等一些細節,可能還沒有最終確定。為了在所有這些細節全部就緒前就能進行軟件開發,ADS工具有一套程序構建和調試的缺省設置。了解這套缺省的工程項目設置方法,對于把握最終的移植步驟非常有好處。
ADS1.2C語言函數庫
Semihosting
在ADS的C語言函數庫中,某些ANSIC的功能是由主機的調試環境來提供的,這套機制有一個專門術語叫Semihosting。Semihosting通過一組軟件中斷(SWI)指令來實現。如圖1所示,當一個Semihosting軟中斷被執行時,調試系統先識別這個SWI請求,然后掛起正在運行的程序,調用Semihosting的服務,完成后再恢復原來的程序執行。因此,主機執行的任務對于程序來說是透明的。
C語言庫函數結構
從概念上來講,C語言庫函數可以被分成兩部分,一是ANSIC語言規范本身的一部分,一是只受某一特定ANSIC層次支持的函數,如圖2所示。
其中一些ANSIC的功能是由主機調試環境調用驅動程序級的函數完成的。例如,ADS的庫函數PRintf()把輸出信息輸出到調試器的控制臺窗口,這個功能通過調用__sys_write()實現,__sys_write()執行了一個把字符串輸出到主機控制臺的Semihosting軟中斷服務程序。
缺省的存儲器映射
假如用戶在程序編譯時沒有指定映象的存儲器映射分布,ADS將為生成的目標代碼和數據分配一個缺省的存儲器映射圖,如圖3所示。
目標印象被連接至地址0x8000,存儲和執行區域都位于該地址開始的空間。RO(只讀)部分放在前面,接著是RW(讀寫)部分,最后是ZI(零初始化)部分。
在ZI部分之上緊跟著HEAP,所以HEAP的確切地址要在連接時才能確定。
STACK的基地址是在應用程序啟動時由一個Semihosting操作提供。這項Semihosting操作返回的地址值視不同調試環境而定:
ARMulator返回配置文件peripherals.ami中的設置值;缺省為0x08000000。
Multi-ICE返回的是調試器內部變量$top_of_memory的值;缺省為0x00080000。
連接器布局規則
連接器對代碼和數據在存儲器系統中的分配,遵循一套規則,如圖4所示。
映象首先按照屬性以RO-RW-ZI的次序進行排列,在同一種屬性里面代碼先于數據。然后連接器將輸入段根據名字的字母順序進行排列,輸入段的名字與匯編代碼里面的塊名字指示一致(在匯編程序中用AREA要害字)。在輸入段中,來自不同對象的代碼和數據放置次序與在連接器命令行中指定的對象文件次序一致。
在需要靈活分配代碼和數據放置位置的情況下,建議用戶不要簡單地依靠這些規則。后面會介紹一種如何控制代碼和數據布局的機制Scatterloading。使用ADS1.2進行嵌入式軟件開發(圖五)
圖5 缺省的ADS初始化過程
使用ADS1.2進行嵌入式軟件開發(圖六)
圖6 C庫函數重定向使用ADS1.2進行嵌入式軟件開發(圖七)
圖7 scatter文件語法使用ADS1.2進行嵌入式軟件開發(圖八)
圖8 分散加載的簡單樣例啟動應用程序
大多數嵌入式系統在進入應用主程序之前有一個初始化的過程,該過程完成系統的啟動和初始化功能。缺省的ADS初始化過程如圖5所示。
總體上,初始化過程可以分成兩部分來看:
_main負責設置運行映像存儲器映射;
_rt_entry負責庫函數的初始化。
_main完成代碼和數據的復制,并把ZI數據區清零。這一步只有當代碼和數據區在存儲和運行時處于不同的存儲器位置時才有意義。接著_main跳進_rt_entry,進行STACK和HEAP等的初始化。最后_rt_entry跳進應用程序的入口main()。當應用程序執行完時,_rt_entry又將控制權交還給調試器。
函數main()在ADS中有非凡的意義。當一個程序工程項目中存在main()時,連接器會把_main和_rt_entry中的初始化代碼連接進來;假如沒有main()函數,初始化過程就不會被連接,結果就會導致一些標準的C庫函數無效。
根據目標環境裁減C庫函數
缺省狀態下C庫函數利用Semihotsting機制來實現設備驅動的功能。但一個真正的嵌入式系統,要使用到具體的外設或硬件獨立于主機環境運行。
C庫函數重定向
用戶可以定義自己的C語言庫函數,連接器在連接時自動使用這些新的功能函數。這個過程叫做重定向C語言庫函數,如圖6所示。
舉例來說,用戶有一個I/O設備(如UART)。本來庫函數fputc()是把字符輸出到調試器控制窗口中去的,但用戶把輸出設備改成了UART端口,這樣一來,所有基于fputc()函數的printf()系列函數輸出都被重定向到UART端口上去了。
下面是實現fputc()重定向的一個例子:
externvoidsendchar(char*ch);
intfputc(intch,FILE*f)
{/*e.g.writeacharactertoanUART*/
chartempch=ch;
sendchar(&tempch);
returnch;

這個例子簡單地將輸入字符重新定向到另一個函數sendchar(),sendchar()假定是一個另外定義的串口輸出函數。在這里,fputc()就似乎目標硬件和標準C庫函數之間的一個抽象層。
在C語言庫函數中禁用Semihosting
在一個獨立的嵌入式應用程序中,應該不存在SemihostingSWI操作。因此,用戶必須確定在所有調用到的庫函數中沒有使用Semihosting。為了保證這一點,在程序中可以引進一個符號要害字_use_no_semihosting:
在C代碼中,使用#prgrama #pragmaimport〈_use_no_semihosting_swi〉
在匯編程序中,使用IMPORT
IMPORT_use_no_semihosting_swi
這樣,當有使用SWI機制的庫函數被連接時,連接器會進行報錯:
Error:Symbol_semihosting_swi_guardmultiplydefined
為了確定具體是哪一個函數,連接時打開-verbose選項。這樣在結果信息輸出時,該庫函數上將有一個_I_use_semihosting_swi的標記。
Loadingmembersys_wxit.ofromc_a_un.1.
Definition:_sys_exit
Reference:_I_use_semihosting_swi
用戶必須要把這些函數定義成自己的執行內容。
有一點需要注重,連接器只能報告庫函數中被調用的Semihosting,對用戶自定義函數中使用的Semihosting則不會報錯。根據目標硬件定制存儲器映射
分散裝載(Scatlerloading)
在實際的嵌入式系統中,ADS提供的缺省存儲器映射是不能滿足要求的。用戶的目標硬件通常有多個存儲器設備位于不同的位置,并且這些存儲器設備在程序裝載和運行時可能還有不同的配置。
Scattertoading可以通過一個文本文件來指定一段代碼或數據在加載和運行時在存儲器中的不同位置。這個文本文件scatterfile在命令行中由-scatter開關指定,例如:
armlink_scatterscat.scffilel.ofile2.0
在scatterfile中可以為每一個代碼或數據區在裝載和執行時指定不同的存儲區域地址,Scatlertoading的存儲區塊可以分成二種類型:
裝載區:當系統啟動或加載時應用程序的存放區。
執行區:系統啟動后,應用程序進行執行和數據訪問的存儲器區域,系統在實時運行時可以有一個或多個執行塊。
映像中所有的代碼和數據都有一個裝載地址和運行地址(二者可能相同也可能不同,視具體情況而定)。在系統啟動時,C函數庫中的__main初始化代碼會執行必要的復制及清零操作,使應用程序的相應代碼和數據段從裝載狀態轉入執行狀態。
1.scatter文件語法
scatter文件是一個簡單的文本文件,包含一些簡單的語法。
My_Region0x00000x1000
{
thecontextofregion
}
每個塊由一個頭標題開始定義,頭中至少包含塊的名字和起始地址,另外還有最大長度和其他一些屬性選項。塊定義的內容包括在緊接的一對花括號內,依靠于具體的系統情況。
一個加載塊必須至少含有一個執行塊;實踐中通常有多個執行塊。
一個執行塊必須至少含有一個代碼或數據段;這些通常來自源文件或庫函數等的目標文件;通配符號*可以匹配指定屬性項中所有沒有在文件中定義的余下部分。
2.簡單分散加載樣例
圖8所示樣例中,只有一個加載塊,包含了所有的代碼和數據,起始地址為0。這個加載塊一共對應兩個執行塊。一個包含所有的RO代碼和數據,執行地址與裝載地址相同;同時另一個起始地址為0x10000的執行塊,包含所有的RW和ZI數據。這樣當系統開始啟動時,從第一個執行塊開始運行(執行地址等于裝載地址),在執行過程中,有一段初始化代碼會把裝載塊中的一部分代碼轉移到另外的執行塊中。
下面是這個scatter描述文件,該文件描述了上述存儲器映射方式。
LOAD_ROM0x4000

EXE_ROM0x00000x4000;Rootregion

*〈+RO〉;Allcodeandconstantdata

RAM0x100000x8000

*〈+RW,+ZI〉;Allnon-constantdata


3.在分散文件中放置對象
在大多數應用中,并不是像前例那樣,簡單地把所有屬性都放在一起,用戶需要控制特定代碼和數據段的放置位置。這可以通過在scatter文件中對單個目標文件進行定義實現,而不是只簡單地依靠通配符。
為了覆蓋標準的連接器布局規則,我們可以使用+FIRST和+LAST分散加載指令。典型的例子是在執行塊的開始處放置中斷向量表格:
LOAD_ROM0x00000x4000

EXEC_ROM0x00000x4000

vectors.o〈Vect,+FIRST〉
*〈+RO〉

;moreexecregions...

在這個scatter文件中,保證了vextors.o中的Vect域被放置于地址0x0000。
4.RootRegion(根區)
根區是一個執行塊,它的加載地址與執行地址是一致的。每個scatter文件至少有一個根區。分散加載有一個限制:創建執行塊的代碼和數據(即完成復制和清零的代碼和數據)無法自行復制到另一個位置。因此,在根區中必須含有下面的部分:
_main.o,包含復制代碼/數據的代碼;
連接器輸出變量$$Table和ZISection$$Table,包含被復制代碼/數據的地址。
由于上面兩個部分的屬性是只讀的,因此他們被*〈+RO〉通配符語法匹配。假如*〈+RO〉被用在了非根區中,則在根區中必須顯式地指明另一個RO區域。
下面是一個例子:
LOAD_ROM0x00000x4000

EXE_ROM0x00000x4000;rootregion

_main.o〈+RO〉;copyingcode
*〈Region$$Tabl0e〉;RO/RWaddressestocopy
*〈ZISection$$Table〉;ZIaddressestozero

RAM0x100000x8000

*〈+RO〉;allotherROsections
*〈+RW,+ZI〉;allRWandZIsections


放置堆棧和heap
Scatterloading機制提供了一種指定代碼和靜態數據布局的方法。下面介紹如何放置應用程序的堆棧和heap。
* _user_initial_stackheap重定向
應用程序的堆棧和heap是在C庫函數初始化過程中建立起來的。可以通過重定向對應的子程序來改變堆棧和heap的位置,在ADS的庫函數中,即_user_initial_stackheap()函數。
_user_initial_stackheap()可以用C或匯編來實現,它必須返回如下參數:
r0:heap基地址;
r1:堆棧基地址;
r2:heap長度限制值(需要的話);
r3:堆棧長度限制值。
當用戶使用分散裝載功能的時候,必須重調用_user_initial_stackheap(),否則連接器會報錯:
Error: L6218E: Undefined symbol Image$$ZI$$Limit (referred from sys_stackheap.o)
*存儲器模型
ADS提供了兩種實時存儲器模型。缺省時為one-region,應用程序的堆棧和heap位于同一個存儲器區塊,使用的時候相向生長,當在heap區分配一塊存儲器空間時需要檢查堆棧指針。另一種情況是堆棧和heap使用兩塊獨立的存儲器區域。對于速度非凡快的RAM,可選擇只用來作堆棧使用。為了使用這種two-region模型,用戶需要導入符號use_two_region_memory,heap使用需要檢查heap的長度限制值。
對這兩種模型來說,缺省情況下對堆棧的生長都不進行檢查。用戶可以在程序編譯時使用 -apcs/swst 編譯器選項來進行軟件堆棧檢查。假如使用two-region模型,必須得在執行_user_initial_stackheap時指定一個堆棧限制值。 使用ADS1.2進行嵌入式軟件開發(圖九)
圖9 重定向_user_initial_stackheap()使用ADS1.2進行嵌入式軟件開發(圖十)
圖10 基本初始化過程使用ADS1.2進行嵌入式軟件開發(圖十)
圖11 ROM/RAM重定向和映射使用ADS1.2進行嵌入式軟件開發(圖十二)
表1系統復位和初始化
目前情況,一般假設程序從C庫函數的初始化入口_main開始執行。實際上,所有的嵌入式程序在啟動時都要執行一些系統級的初始化操作。在此討論這方面的內容。
初始化過程
圖10中顯示了一個基于ARM的嵌入式系統的基本初始化過程。可以看到,在_main之前加入了一個復位處理模塊reset handler,它在系統上電復位時立即啟動。標識為$sub$$main的新代碼塊在進入主程序之前執行。
復位處理模塊reset handler通常是一小段匯編代碼,在系統復位時執行。它至少完成應用程序中使用到的所有處理器模式的堆棧初始化工作。對于含有本地存儲器系統的內核(比如含cache的ARM內核),配置工作也必須在這一段初始化過程中完成。當完成系統初始化之后,通常程序會跳向_main,開始C庫函數的初始化過程。
系統初始化過程一般還包括另外一些內容,中斷使能等,這些大多安排在C庫函數的初始化完成之后執行。$sub$$main()完成這部分功能。
向量表(vector table)
所有的ARM系統都有一張中斷向量表當出現異常需要處理時,必須調用向量表。向量表一般要位于0地址處。使用ADS1.2進行嵌入式軟件開發(圖十三)
表2使用ADS1.2進行嵌入式軟件開發(圖十四)
表3使用ADS1.2進行嵌入式軟件開發(圖十五)
表4使用ADS1.2進行嵌入式軟件開發(圖十六)
表5使用ADS1.2進行嵌入式軟件開發(圖十七)
表6使用ADS1.2進行嵌入式軟件開發(圖十八)
表7使用ADS1.2進行嵌入式軟件開發(圖十九)
表8使用ADS1.2進行嵌入式軟件開發(圖二十)
表9使用ADS1.2進行嵌入式軟件開發(圖二十)
表10
存儲器配置
*ROM/RAM重定向
當系統啟動的時候,為了保證0地址處有正確的啟動代碼存在,需要非易失性的存儲器。
一種簡單的方法,就是把系統0x0000開始的一塊地址分配給ROM。其缺點是,由于ROM的訪問速度比RAM慢很多,當執行中斷響應需要從中斷向量表跳轉時,會給系統性能帶來損失;同時,在ROM中的向量表內容也不能被用戶程序動態修改。
另外一種可行的方案如圖11所示。ROM位于地址0x1000開始的地方,但是在系統復位時又被存儲器控制器映射到0x0000地址處。這樣當系統啟動之后,在地址0x0000看到的是ROM,系統執行這塊ROM中的啟動代碼,啟動代碼跳轉到真正的ROM的地址,并讓存儲器控制器移除對ROM的地址映射。這時0x0000地址處的存儲器又恢復回了RAM。__main中的代碼把向量表copy到0x0000處的RAM中去,使得異常時能被正確響應。
表1為ARM匯編中執行ROM/RAM重定向和映射的一個例子。它以ARM公司的Integrator平臺為基礎的,該方法適用于類似ROM/RAM重定向方法的所有平臺。第一條指令完成從ROM的映射地址(0x00000)到真實地址的跳轉。地址標號instrUCt_2是ROM的真實地址(0x180004)。然后通過設置Integrator平臺上的相應控制寄存器,移除ROM的地址映射。代碼在系統一啟動就被執行。所有關于地址重定向/映射的操作必須在C庫函數初始化之前完成。
*本地存儲器配置
許多ARM處理器都有片上存儲器系統,如cache和緊密耦合存儲器(TCM)、存儲器治理單元(MMU)或存儲器保護單元(MPU)。這些設備都要在系統初始化過程中正確配置,并且有一些非凡的要求需要考慮。
由前文可知,_main中的C庫函數初始化代碼負責程序運行時的存儲器系統設置。因此,整個存儲器系統本身必須得在__main之前完成初始化工作,如MMU或MPU必須在reset handler里面完成配置。
緊密耦合存儲器(TCM)的初始化同樣須在_main之前完成(通常在MMU/MPU之前),因為一般程序都需要把代碼和數據分散裝入TCM。需要注重的是當TCM被使能后,不再訪問被TCM屏蔽的存儲器。
關于cache的一致性問題,假如cache在_main之前使能的話,那么當_main里面進行從裝載區到執行區的代碼和數據拷貝時(因為在拷貝過程中指令和數據在本質上都是被當作數據處理),指令會出現在數據緩沖區。避免此問題的方法是在C庫函數初始化完成后再使能cache。
*Scatter loading與存儲器配置
無論是通過ROM/RAM重定向還是MMU配置的方法,假如系統在啟動和運行時存儲器分布不一致,scatterloading文件中的定義就要按照系統重定向后的存儲器分布情況進行。
以上文ROM/RAM重定向為例:
LOAD_ROM 0x10000 0x8000
{
EXE_ROM 0x10000 0x8000
{
reset_handler.o (+RO, +FIRST)
...
}
RAM 0x0000 0x4000
{
vectors.o (+RO, +FIRST)
...
}
}
裝載區LOAD_ROM被放置在0x10000處,代表了重定向之后代碼和數據的裝載地址。堆棧的初始化
程序中可能用到的處理器模式,都需要定義一個堆棧指針。
在表2中,堆棧位于stack_base標識的地址中。這個符號可以是存儲器系統中的一個直接地址,也可以在另外的匯編文件中定義,由scatter文件來定義分配地址。表2代碼為FIQ和IRQ模式各分配了一個256字節的堆棧,用戶可以用同樣的方法為其他模式也分配堆棧。最簡單的方法就是進入相應的模式,然后為SP寄存器指定相應的值。假如想使用軟件堆棧檢查,還必須指定一個堆棧長度限制值。
堆棧指針和堆棧限制的數值會作為參數自動傳遞到C庫函數的初始化代碼__user_initial_stackheap中,在__user_initial_stackheap中不應該修改這些值。
硬件初始化 $sub$$main()
一般來說,應該把所有的系統初始化代碼與主應用程序分離開來,但是有幾個例外,比如cache和中斷的使能,需要在C庫函數初始化之后執行。
表3代碼顯示了如何使用 $sub和 $supper 。連接器把呼叫main()的函數替換成呼叫$sub$$main(),完成cache和中斷的使能,并最終跳向main()。
執行模式考慮
為主應用程序選擇一個處理器執行模式非常重要,這取決于系統的初始化代碼。
許多在啟動過程中使用到的功能,如MMU/MPU的配置、中斷的使能等,只能在特權級模式下進行。假如需要在特權極模式下運行自己的應用程序,只要在退出初始化過程之前改變到相應的模式就行了,沒有其他任何問題。
假如使用user模式,必須保證所有只能在特權模式下執行的功能完成之后,才能進入user模式。因為system模式和user模式使用相同的寄存器組,reset handler應該從system模式退出,_user_initial_stackheap在system模式下完成應用程序堆棧的初始化。這樣在處理器進入user模式后,所有的堆棧空間都已經被正確設置好了。對存儲器布局的進一步考慮
在scatter文件中分配硬件地址
雖然可以在一個scatter文件中描述代碼和數據的分散布局,但是目標硬件中的外設寄存器,堆棧和heap配置仍然直接采用硬件地址在程序源代碼中進行設置。假如把所有存儲器地址相關的信息都在scatter文件中進行定義,避免在源文件中引用絕對硬件地址,對程序的工程化治理是有大好處的。
*在scatter文件中定義目標外設地址
通常外設寄存器的地址在程序文件或頭文件中定義,也可以聲明一個結構類型指向外設寄存器,結構的地址定位在scatter文件中完成。
舉例來說,目標定時器上有2個32位的寄存器,可以用表4來映射這些寄存器。為了把結構放置在指定的存儲器地址上面,創建一個新的執行區(見表5)。scatter文件便把timer_regs結構定位在了地址0x40000000。

注重,在啟動過程當中這些寄存器的內容不需要清零,改變寄存器的內容可能影響系統狀態。在執行區上加UNINIT屬性可以防止ZI數據在初始化過程中被清零。
在scatter文件中分配堆棧和heap
在許多情況下,用scatter文件來定義堆棧和heap的地址會帶來一些好處,主要有:所有的存儲器分配信息集中在一個文件里;改變堆棧和heap的地址只要重新連接就行了,不需要重新編譯。
*顯式地放置符號
在ADS1.2環境下,這是最簡單的方法。在前文中引用過2個符號stack_base和heap_base,這2個符號在匯編模塊中創建,在scatter文件中各自的執行區里定位(見表6)。
表7文件中,heap基地址定位在0x20000上,堆棧基地址位于0x40000。現在heap和堆棧的位置就可以非常方便地進行編輯了。
*使用連接器產生的符號
這種方法需要在目標文件中指定好heap和堆棧的長度。這在一定程度上減弱了本節開頭描述的兩個優點。
首先在匯編源程序中定義heap和堆棧的長度。要害詞SPACE用來保留一塊存儲器空間,NOINT則可以阻止清零操作(見表8)。注重在這里的源文件中并不需要地址標號。
然后這些部分就可以在scatter文件中對應的執行區里定位了(見表9)。連接器產生的符號指向每一個執行區的基地址和長度限制,這些符號可以被_user_initial_stackheap調用的重定向代碼使用。在代碼中使用DCD來給這些值定義更有意義的名字,可以增強代碼的可讀性(見表10)。
文件把heap基地址定位在0x15000,堆棧地址定位在0x4000。Heap和堆棧的位置可以通過編輯對應執行區的地址方便地改變。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 马边| 寿光市| 东阿县| 天津市| 甘德县| 巴楚县| 元朗区| 合作市| 海林市| 墨江| 浦东新区| 盐池县| 温宿县| 武鸣县| 邹平县| 陵水| 祥云县| 凤庆县| 于都县| 镇沅| 江源县| 丘北县| 景洪市| 定日县| 广平县| 衢州市| 剑阁县| 德钦县| 定安县| 蒙自县| 潢川县| 明溪县| 濮阳县| 临颍县| 武乡县| 大埔区| 固镇县| 临沂市| 阳西县| 中阳县| 饶平县|