色婷婷AⅤ一区二区三区|亚洲精品第一国产综合亚AV|久久精品官方网视频|日本28视频香蕉

          新聞中心

          EEPW首頁(yè) > 設(shè)計(jì)應(yīng)用 > 使用ADS1.2進(jìn)行嵌入式軟件開(kāi)發(fā)(上)

          使用ADS1.2進(jìn)行嵌入式軟件開(kāi)發(fā)(上)

          作者:美國(guó)ARM公司 時(shí)間:2004-06-18 來(lái)源:電子設(shè)計(jì)應(yīng)用 收藏
          概述
          嵌入式應(yīng)用程序通常都是在樣機(jī)環(huán)境下調(diào)試與開(kāi)發(fā)的,這種環(huán)境與最終產(chǎn)品之間并不完全相同。因此,在系統(tǒng)調(diào)試階段就考慮應(yīng)用程序在最終目標(biāo)硬件中的運(yùn)行情況是非常重要的。
          本文旨在討論如何將一個(gè)開(kāi)發(fā)/調(diào)試環(huán)境下的嵌入式應(yīng)用程序轉(zhuǎn)移到最終獨(dú)立運(yùn)行的目標(biāo)系統(tǒng)中去,并提到了 ADS1.2開(kāi)發(fā)工具包的一些功能特性及其在這個(gè)過(guò)程中所起到的作用。
          使用ADS開(kāi)發(fā)嵌入式程序時(shí),需要著重考慮以下幾個(gè)問(wèn)題:
          與硬件相關(guān)的C語(yǔ)言庫(kù)函數(shù)的使用;
          某些C語(yǔ)言庫(kù)函數(shù)使用了調(diào)試環(huán)境中的資源,要把這些使用的資源重定向到目標(biāo)系統(tǒng)中的硬件上來(lái);
          可執(zhí)行映象文件的存儲(chǔ)器映射必須根據(jù)目標(biāo)硬件的存儲(chǔ)器分布進(jìn)行裁剪;
          在主程序執(zhí)行前,嵌入式應(yīng)用程序必須先完成系統(tǒng)的初始化。一個(gè)完整的初始化包括用戶的啟動(dòng)執(zhí)行代碼和ADS中C庫(kù)函數(shù)的初始化過(guò)程。


          圖1 Semihosting的實(shí)現(xiàn)舉例


          圖2 C語(yǔ)言庫(kù)函數(shù)結(jié)構(gòu)


          圖3 缺省的存儲(chǔ)器映射


          圖4 連接器布局規(guī)則

          缺省的工程項(xiàng)目設(shè)置
          剛開(kāi)始一個(gè)嵌入式應(yīng)用軟件開(kāi)發(fā)時(shí),ADS用戶可能并不完全清楚目標(biāo)硬件的一些參數(shù)指標(biāo)。比如有關(guān)外設(shè)、存儲(chǔ)器地址分布,甚至處理器類型等一些細(xì)節(jié),可能還沒(méi)有最終確定。為了在所有這些細(xì)節(jié)全部就緒前就能進(jìn)行軟件開(kāi)發(fā),ADS工具有一套程序構(gòu)建和調(diào)試的缺省設(shè)置。了解這套缺省的工程項(xiàng)目設(shè)置方法,對(duì)于掌握最終的移植步驟非常有好處。
          ADS1.2C語(yǔ)言函數(shù)庫(kù)
          Semihosting
          在ADS的C語(yǔ)言函數(shù)庫(kù)中,某些ANSIC的功能是由主機(jī)的調(diào)試環(huán)境來(lái)提供的,這套機(jī)制有一個(gè)專門(mén)術(shù)語(yǔ)叫Semihosting。Semihosting通過(guò)一組軟件中斷(SWI)指令來(lái)實(shí)現(xiàn)。如圖1所示,當(dāng)一個(gè)Semihosting軟中斷被執(zhí)行時(shí),調(diào)試系統(tǒng)先識(shí)別這個(gè)SWI請(qǐng)求,然后掛起正在運(yùn)行的程序,調(diào)用Semihosting的服務(wù),完成后再恢復(fù)原來(lái)的程序執(zhí)行。因此,主機(jī)執(zhí)行的任務(wù)對(duì)于程序來(lái)說(shuō)是透明的。
          C語(yǔ)言庫(kù)函數(shù)結(jié)構(gòu)
          從概念上來(lái)講,C語(yǔ)言庫(kù)函數(shù)可以被分成兩部分,一是ANSIC語(yǔ)言規(guī)范本身的一部分,一是只受某一特定ANSIC層次支持的函數(shù),如圖2所示。
          其中一些ANSIC的功能是由主機(jī)調(diào)試環(huán)境調(diào)用驅(qū)動(dòng)程序級(jí)的函數(shù)完成的。例如,ADS的庫(kù)函數(shù)printf()把輸出信息輸出到調(diào)試器的控制臺(tái)窗口,這個(gè)功能通過(guò)調(diào)用__sys_write()實(shí)現(xiàn),__sys_write()執(zhí)行了一個(gè)把字符串輸出到主機(jī)控制臺(tái)的Semihosting軟中斷服務(wù)程序。
          缺省的存儲(chǔ)器映射
          如果用戶在程序編譯時(shí)沒(méi)有指定映象的存儲(chǔ)器映射分布,ADS將為生成的目標(biāo)代碼和數(shù)據(jù)分配一個(gè)缺省的存儲(chǔ)器映射圖,如圖3所示。
          目標(biāo)印象被連接至地址0x8000,存儲(chǔ)和執(zhí)行區(qū)域都位于該地址開(kāi)始的空間。RO(只讀)部分放在前面,接著是RW(讀寫(xiě))部分,最后是ZI(零初始化)部分。
          在ZI部分之上緊跟著HEAP,所以HEAP的確切地址要在連接時(shí)才能確定。
          STACK的基地址是在應(yīng)用程序啟動(dòng)時(shí)由一個(gè)Semihosting操作提供。這項(xiàng)Semihosting操作返回的地址值視不同調(diào)試環(huán)境而定:
          ulator返回配置文件peripherals.ami中的設(shè)置值;缺省為0x08000000。
          Multi-ICE返回的是調(diào)試器內(nèi)部變量$top_of_memory的值;缺省為0x00080000。
          連接器布局規(guī)則
          連接器對(duì)代碼和數(shù)據(jù)在存儲(chǔ)器系統(tǒng)中的分配,遵循一套規(guī)則,如圖4所示。
          映象首先按照屬性以RO-RW-ZI的次序進(jìn)行排列,在同一種屬性里面代碼先于數(shù)據(jù)。然后連接器將輸入段根據(jù)名字的字母順序進(jìn)行排列,輸入段的名字與匯編代碼里面的塊名字指示一致(在匯編程序中用AREA關(guān)鍵字)。在輸入段中,來(lái)自不同對(duì)象的代碼和數(shù)據(jù)放置次序與在連接器命令行中指定的對(duì)象文件次序一致。
          在需要靈活分配代碼和數(shù)據(jù)放置位置的情況下,建議用戶不要簡(jiǎn)單地依靠這些規(guī)則。后面會(huì)介紹一種如何控制代碼和數(shù)據(jù)布局的機(jī)制Scatterloading。


          圖5 缺省的ADS初始化過(guò)程


          圖6 C庫(kù)函數(shù)重定向


          圖7 scatter文件語(yǔ)法


          圖8 分散加載的簡(jiǎn)單樣例

          啟動(dòng)應(yīng)用程序
          大多數(shù)嵌入式系統(tǒng)在進(jìn)入應(yīng)用主程序之前有一個(gè)初始化的過(guò)程,該過(guò)程完成系統(tǒng)的啟動(dòng)和初始化功能。缺省的ADS初始化過(guò)程如圖5所示。
          總體上,初始化過(guò)程可以分成兩部分來(lái)看:
          _main負(fù)責(zé)設(shè)置運(yùn)行映像存儲(chǔ)器映射;
          _rt_entry負(fù)責(zé)庫(kù)函數(shù)的初始化。
          _main完成代碼和數(shù)據(jù)的復(fù)制,并把ZI數(shù)據(jù)區(qū)清零。這一步只有當(dāng)代碼和數(shù)據(jù)區(qū)在存儲(chǔ)和運(yùn)行時(shí)處于不同的存儲(chǔ)器位置時(shí)才有意義。接著_main跳進(jìn)_rt_entry,進(jìn)行STACK和HEAP等的初始化。最后_rt_entry跳進(jìn)應(yīng)用程序的入口main()。當(dāng)應(yīng)用程序執(zhí)行完時(shí),_rt_entry又將控制權(quán)交還給調(diào)試器。
          函數(shù)main()在ADS中有特殊的意義。當(dāng)一個(gè)程序工程項(xiàng)目中存在main()時(shí),連接器會(huì)把_main和_rt_entry中的初始化代碼連接進(jìn)來(lái);如果沒(méi)有main()函數(shù),初始化過(guò)程就不會(huì)被連接,結(jié)果就會(huì)導(dǎo)致一些標(biāo)準(zhǔn)的C庫(kù)函數(shù)無(wú)效。
          根據(jù)目標(biāo)環(huán)境裁減C庫(kù)函數(shù)
          缺省狀態(tài)下C庫(kù)函數(shù)利用Semihotsting機(jī)制來(lái)實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)的功能。但一個(gè)真正的嵌入式系統(tǒng),要使用到具體的外設(shè)或硬件獨(dú)立于主機(jī)環(huán)境運(yùn)行。
          C庫(kù)函數(shù)重定向
          用戶可以定義自己的C語(yǔ)言庫(kù)函數(shù),連接器在連接時(shí)自動(dòng)使用這些新的功能函數(shù)。這個(gè)過(guò)程叫做重定向C語(yǔ)言庫(kù)函數(shù),如圖6所示。
          舉例來(lái)說(shuō),用戶有一個(gè)I/O設(shè)備(如UART)。本來(lái)庫(kù)函數(shù)fputc()是把字符輸出到調(diào)試器控制窗口中去的,但用戶把輸出設(shè)備改成了UART端口,這樣一來(lái),所有基于fputc()函數(shù)的printf()系列函數(shù)輸出都被重定向到UART端口上去了。
          下面是實(shí)現(xiàn)fputc()重定向的一個(gè)例子:
          externvoidsendchar(char*ch);
          intfputc(intch,FILE*f)
          {/*e.g.writeacharactertoanUART*/
          chartempch=ch;
          sendchar(&tempch);
          returnch;

          這個(gè)例子簡(jiǎn)單地將輸入字符重新定向到另一個(gè)函數(shù)sendchar(),sendchar()假定是一個(gè)另外定義的串口輸出函數(shù)。在這里,fputc()就好像目標(biāo)硬件和標(biāo)準(zhǔn)C庫(kù)函數(shù)之間的一個(gè)抽象層。
          在C語(yǔ)言庫(kù)函數(shù)中禁用Semihosting
          在一個(gè)獨(dú)立的嵌入式應(yīng)用程序中,應(yīng)該不存在SemihostingSWI操作。因此,用戶必須確定在所有調(diào)用到的庫(kù)函數(shù)中沒(méi)有使用Semihosting。為了保證這一點(diǎn),在程序中可以引進(jìn)一個(gè)符號(hào)關(guān)鍵字_use_no_semihosting:
          在C代碼中,使用#prgrama #pragmaimport〈_use_no_semihosting_swi〉
          在匯編程序中,使用IMPORT
          IMPORT_use_no_semihosting_swi
          這樣,當(dāng)有使用SWI機(jī)制的庫(kù)函數(shù)被連接時(shí),連接器會(huì)進(jìn)行報(bào)錯(cuò):
          Error:Symbol_semihosting_swi_guardmultiplydefined
          為了確定具體是哪一個(gè)函數(shù),連接時(shí)打開(kāi)-verbose選項(xiàng)。這樣在結(jié)果信息輸出時(shí),該庫(kù)函數(shù)上將有一個(gè)_I_use_semihosting_swi的標(biāo)記。
          Loadingmembersys_wxit.ofromc_a_un.1.
          Definition:_sys_exit
          Reference:_I_use_semihosting_swi
          用戶必須要把這些函數(shù)定義成自己的執(zhí)行內(nèi)容。
          有一點(diǎn)需要注意,連接器只能報(bào)告庫(kù)函數(shù)中被調(diào)用的Semihosting,對(duì)用戶自定義函數(shù)中使用的Semihosting則不會(huì)報(bào)錯(cuò)。

          根據(jù)目標(biāo)硬件定制存儲(chǔ)器映射
          分散裝載(Scatlerloading)
          在實(shí)際的嵌入式系統(tǒng)中,ADS提供的缺省存儲(chǔ)器映射是不能滿足要求的。用戶的目標(biāo)硬件通常有多個(gè)存儲(chǔ)器設(shè)備位于不同的位置,并且這些存儲(chǔ)器設(shè)備在程序裝載和運(yùn)行時(shí)可能還有不同的配置。
          Scattertoading可以通過(guò)一個(gè)文本文件來(lái)指定一段代碼或數(shù)據(jù)在加載和運(yùn)行時(shí)在存儲(chǔ)器中的不同位置。這個(gè)文本文件scatterfile在命令行中由-scatter開(kāi)關(guān)指定,例如:
          armlink_scatterscat.scffilel.ofile2.0
          在scatterfile中可以為每一個(gè)代碼或數(shù)據(jù)區(qū)在裝載和執(zhí)行時(shí)指定不同的存儲(chǔ)區(qū)域地址,Scatlertoading的存儲(chǔ)區(qū)塊可以分成二種類型:
          裝載區(qū):當(dāng)系統(tǒng)啟動(dòng)或加載時(shí)應(yīng)用程序的存放區(qū)。
          執(zhí)行區(qū):系統(tǒng)啟動(dòng)后,應(yīng)用程序進(jìn)行執(zhí)行和數(shù)據(jù)訪問(wèn)的存儲(chǔ)器區(qū)域,系統(tǒng)在實(shí)時(shí)運(yùn)行時(shí)可以有一個(gè)或多個(gè)執(zhí)行塊。
          映像中所有的代碼和數(shù)據(jù)都有一個(gè)裝載地址和運(yùn)行地址(二者可能相同也可能不同,視具體情況而定)。在系統(tǒng)啟動(dòng)時(shí),C函數(shù)庫(kù)中的__main初始化代碼會(huì)執(zhí)行必要的復(fù)制及清零操作,使應(yīng)用程序的相應(yīng)代碼和數(shù)據(jù)段從裝載狀態(tài)轉(zhuǎn)入執(zhí)行狀態(tài)。
          1.scatter文件語(yǔ)法
          scatter文件是一個(gè)簡(jiǎn)單的文本文件,包含一些簡(jiǎn)單的語(yǔ)法。
          My_Region0x00000x1000
          {
          thecontextofregion
          }
          每個(gè)塊由一個(gè)頭標(biāo)題開(kāi)始定義,頭中至少包含塊的名字和起始地址,另外還有最大長(zhǎng)度和其他一些屬性選項(xiàng)。塊定義的內(nèi)容包括在緊接的一對(duì)花括號(hào)內(nèi),依賴于具體的系統(tǒng)情況。
          一個(gè)加載塊必須至少含有一個(gè)執(zhí)行塊;實(shí)踐中通常有多個(gè)執(zhí)行塊。
          一個(gè)執(zhí)行塊必須至少含有一個(gè)代碼或數(shù)據(jù)段;這些通常來(lái)自源文件或庫(kù)函數(shù)等的目標(biāo)文件;通配符號(hào)*可以匹配指定屬性項(xiàng)中所有沒(méi)有在文件中定義的余下部分。
          2.簡(jiǎn)單分散加載樣例
          圖8所示樣例中,只有一個(gè)加載塊,包含了所有的代碼和數(shù)據(jù),起始地址為0。這個(gè)加載塊一共對(duì)應(yīng)兩個(gè)執(zhí)行塊。一個(gè)包含所有的RO代碼和數(shù)據(jù),執(zhí)行地址與裝載地址相同;同時(shí)另一個(gè)起始地址為0x10000的執(zhí)行塊,包含所有的RW和ZI數(shù)據(jù)。這樣當(dāng)系統(tǒng)開(kāi)始啟動(dòng)時(shí),從第一個(gè)執(zhí)行塊開(kāi)始運(yùn)行(執(zhí)行地址等于裝載地址),在執(zhí)行過(guò)程中,有一段初始化代碼會(huì)把裝載塊中的一部分代碼轉(zhuǎn)移到另外的執(zhí)行塊中。
          下面是這個(gè)scatter描述文件,該文件描述了上述存儲(chǔ)器映射方式。
          LOAD_ROM0x4000

          EXE_ROM0x00000x4000;Rootregion

          *〈+RO〉;Allcodeandconstantdata

          RAM0x100000x8000

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


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

          EXEC_ROM0x00000x4000

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

          ;moreexecregions...

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

          EXE_ROM0x00000x4000;rootregion

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

          RAM0x100000x8000

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

          }■(待續(xù))

          在下期中將更詳細(xì)介紹利用scatter文件進(jìn)行存儲(chǔ)器配置的方法,以及系統(tǒng)啟動(dòng)和初始化的過(guò)程和存儲(chǔ)器映射變化關(guān)系。




          關(guān)鍵詞: ARM

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉