新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM 的分散加載

        ARM 的分散加載

        作者: 時間:2016-11-09 來源:網絡 收藏
        對于剛學習ARM的人來說,如果分析它的啟動代碼,往往不明白下面幾個變量的含義:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。

        首先申明我使用的調試軟件為ADS1.2,當我們把程序編寫好以后,就要進行編譯和鏈接了,在ADS1.2中選擇MAKE按鈕,會出現一個Errors and Warnings的對話框,在該欄中顯示編譯和鏈接的結果,如果沒有錯誤,在文件的最后應該能看到Image component sizes,后面緊跟的依次是Code,RO Data,RW Data,ZI Data,Debug各個項目的字節數,最后會有他們的一個統計數據:

        本文引用地址:http://www.104case.com/article/201611/317890.htm

        Code 163632,RO Data 20939,RW Data 53,ZI Data 17028

        Tatal RO size (Code+ RO Data) 184571 (180.25kB)

        Tatal RW size(RW Data+ ZI Data) 17081(16.68 kB)

        Tatal ROM size(Code+ RO Data+ RW Data) 184624(180.30 kB)

        后面的字節數是根據用戶不同的程序而來的,下面就以上面的數據為例來介紹那幾個變量的計算。

        在ADS的Debug Settings中有一欄是Linker/ARM Linker,在output選項中有一個RO base選項,下面應該有一個地址,我這里是0x0c100000,后面的RW base地址是0x0c200000,然后在Options選項中有Image entry point,是一個初始程序的入口地址,我這里是0x0c100000。

        有了上面這些信息我們就可以完全知道這幾個變量是怎么來的了:

        |Image$$RO$$Base| = Image entry point = 0x0c100000;表示程序代碼存放的起始地址

        |Image$$RO$$Limit|=程序代碼起始地址+代碼長度+1=0x0c100000+Tatal RO size+1

        = 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1

        = 0x0c12d0fc

        |Image$$RW$$Base| = 0x0c200000;由RW base地址指定

        |Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Data 53 = 0x0c200000+0x37(4的倍數,0到55,共56個單元)

        =0x0c200037

        |Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038

        |Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Data 17028

        =0x0c200038 + 0x4284

        =0x0c2042bc

        也可以由此計算:

        |Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData) 17081

        =0x0c200000+0x42b9+3(要滿足4的倍數)

        =0x0c2042bc

        原文地址http://blog.csdn.net/yyt7529/archive/2009/06/05/4245604.aspx

        簡單應用時可以不寫.scf文件。而在"Output"頁中選擇"Simple".然后填寫"RO Base"和"RW Base"的起始地址。在"Lay Out"頁中,填寫Object/Symble: Startup.o, Section: Start.編寫啟動文件:Startup.s.

        在"Option"頁里的"Image Entry Point"填入起始地址。

        --------------------------------------------------------------------------------

        Scatter-Load Description File的結構:

        ".scf"文件中的"+RW"對應".s"源文件中的"READWRITE".

        ".scf"文件中的"+ZI"對應".s"源文件中的"NOINIT".

        ".scf"文件中的"+RO"對應".s"源文件中的"READONLY".

        在".s"源文件中有:

        AREA area_name CODE/DATA,READONLY/NOINIT/READWRITE

        END

        ".scf"的例子

        內容 注解

        ROM_LOAD 0x80000000

        { ;Name of Load Region, Start Address for Load Region and Maximum size of Load Region(省略了)

         ROM_EXEC 0x80000000 0x20000

        {;片外存儲區,從0x80000000開始,最多0x20000字節。

         Startup.o(Vector,+First);Startup模塊的Vector段放在最前面。注1

         *(+RO);其他所有模塊中的所有代碼和只讀的數據放在這里。

         } 

         IRAM 0x40000000 0x00004000

        {;片內RAM區,從0x40000000開始,最多0x4000字節

         Startup.o(MyStacks,+first);指定Startup.o中MyStacks放在最前面。

         Startup.o(+RW,+ZI);Startup.o中的其他+RW/+ZI段。注1

         os_cpu_a.o(+RW,+ZI) 

         } 

         STACKS 0x40004000 UNINIT

        {;片內16K RAM的頂端,存放不需要被"C library"初始化的段。

         Stack.o(+ZI)注2

         } 

         ERAM 0x80040000

        { 

         *(+RW,+ZI) 

         } 

         HEAP +0 UNINIT

        {;"+0"表示接著上一段"ERAM"的結尾,繼續安排存儲區。

         Heap.o(+ZI)注3

         } 

        }  

        下面是在scf文件中引用過的源文件示意:"Startup.s"

        code 32

        area Vectors,CODE,READONLY

        entry

        ...

        end注1:在"Startup.o"里面會生成名為"Vectors"的段,段的屬性為"READONLY"

        "Stack.s"

        area Stacks, DATA, NOINIT

        export StackUsr

        StackUsr SPACE 1

        end注2:在"Stack.o"里面會生成名為"Stacks"的段,段的屬性為"NOINIT",該屬性對應scf文件中的"+ZI".該段不需要初始化或者可以被初始化為"0".

        "Heap.s"

        area Heap,DATA,NOINIT

        export bottom_of_heap

        bottom_of_heap SPACE 1

        end注3: "Heap.o"里面名為"Heap"的段。

        在Scatter文件中最好每一個Region都加一個Maximum參數,這樣當編譯時如果實際使用的空間大于Maximum Size,會有Error:16220E: Excution region xxx size (xxx bytes) exceeds limit (xx bytes)。如果地址有重復,會有Error: 16221E: Excution region xxx overlaps with excution region xxx。前一個Region的首地址+ Maximum >后一個Region的首地址時不一定有Error。只有當一分配的內存出現覆蓋時才會有Error。

        Region的"UNINIT"之類的參數要放在"Maximum size"參數之前。

        在一個Region中,RAM的分配不是按照羅列的順序來的。要想讓匯編中使用的變量有固定的位置,可以把所有匯編文件產生的".o"放在同一個Region中。如:

        IRAM1 0x40000000

        {

        startup.o(+RW,+ZI)

        ASMSourceCode1.o(+RW,+ZI)

        ASMSourceCode2.o(+RW,+ZI)

        }

        IRAM2 +0

        {

        CSourceCode1.o(+RW,+ZI)

        CSourceCode2.o(+RW,+ZI)

        }

        這樣,所有匯編中定義的變量地址就相對集中了。

        如果只有一個匯編文件如startup.s,也可以這樣:

        IRAM 0x40002000 0x1000

        {

        startup.o (Mystack,+first)

        *(+RW,+ZI)

        }

        用一個"+first"強行將startup.s中的Mystack放在0x40002000位置。

        在"Edit -> DebugRel Settings...->ARM Linker"中選中"Image map"。編譯后在Error & Warnings窗口會顯示出詳細的內存分配情況。如果在"List file name"中指定一個輸出文件名,該祥單會直接存在制定文件中以供多次研究。

        --------------------------------------------------------------------------------

        關于JTAG接口:

        P1.20/TRACESYNC應該加上拉電阻以禁止TRACE功能。PINSEL2一定要在程序開始時初始化一下。LPC2210

        JTAG

         1,2/VDD3.3V 

        P1.31/nTRST, input 3/nTRST, output EasyJTAG中有上拉電阻。

        P1.28/TDI, input 5/TDI, output EasyJTAG中有上拉電阻。

        P1.30/TMS, input 7/TMS,output EasyJTAG中有上拉電阻。

        P1.29/TCK, input/output 9/TCK, input/output EasyJTAG中有上拉電阻。

        P1.26/RTCK, input 11/RTCK, output P1.26外接下拉電阻。

        P1.26有內部上拉電阻,故測量時該引腳會呈現高電平。但是在復位時,它的上拉電阻不起作用,只有外部的下拉電阻起作用,P1.26 = 0V,所以上電后PINSEL2的D3~D0會是0x04(B0100),JTAG有效。

        若將P1.26接到3.3V再復位,此時PINSEL2的D3~D0將會是0x00,JTAG無效。

        P1.27/TDO, output 13/TDO, input EasyJTAG中有上拉電阻。

        nRESET, input 15/nRST, output EasyJTAG中有上拉電阻。

         4,6,8,10,12,14,16,18,20/GND 

         17,19/NC 

        G18控制板采用LPC2114,每次運行Axd都不會正確調入程序。原因如下:

        有一次是因為已經有一個Axd在運行了,打開第二個Axd,當然不會正確調入程序。

        還有一次是重新編譯了一下,就好了。

        以上兩次都不奇怪,奇怪的是下面幾次:

        在"Config Target -> Config -> Easy JTag Setup"隨便點兩下"Halt Mode"中的選項,然后一路點擊"OK",會出現"Reload the last Image?",點擊"Yes"。有時會有正確的程序被調入,但有時候不成功。要檢驗是不是已經成功調入了,只要按下"Ctrl-D"顯示Disassembly窗口,即可看到芯片中的程序是否正確。

        在"Option -> Config Interface -> Session File -> Session file Options"中選擇"Reload Images",之后每次啟動Axd都會提示"The processor ARM_1 already has image(s) loaded. Continue the operation will replace the currently loaded images(s).... Do you wish to continue?"選擇"Yes",有時候也可以成功調入程序。

        當然,在"Easy JTag Setup -> Aux Option"中要選中"Erase Flash when need".

        固化的程序中有禁止JTag調試端口的語句(操作PINSEL2的語句),連不上時用LPC2000 Flash Utility擦除了Flash。偶爾可行。

        注意使用LPC2000 Flash Utility時要先將電路復位,再點"OK".

        當然最根本的解決辦法是將計算機并口設置為"EPP"模式。其他地方都按照"Default"就可以了。

        --------------------------------------------------------------------------------

        有效用戶代碼:

        ARM把“向量表所有32位數據累加和為0”作為有效用戶代碼的條件,只適用于使用片內程序存儲器的時候,片外程序存儲器無此限制。

        C語言程序通常需要一段用于初始化的匯編代碼,通常存儲為"Startup.s",它實現的任務通常是:

        1、做好中斷向量表

        2、初始化外部總線控制器/堆棧/目標板基本模塊。

        3、給庫函數使用的"__user_inital_stackheap"。

        4、除數為零時的死循環"__rt_div0: B __rt_div0"。

        5、支持加密功能的語句。

        6、定義堆棧空間:AREA MyStacks, DATA,NOINIT,ALIGN = 2

        要定義棧的起始地址:IrqStackSpace SPACE ...

        和棧的頭部:StackIrq DCD IrqStackSpace + Length*4。因為棧是向下生長的。

        與目標板有關的初始化程序可以放在一個名為"Target.c"的文件里。

        1、定義中斷處理入口:void IRQ_Exception(void), void FIQ_Exception(void), void Timer0_Exception(void)。

        2、向量中斷控制器初始化。

        3、remap,系統時鐘,實時時鐘,存儲器加速。

        --------------------------------------------------------------------------------

        C語言中的延時:

        __asm{

        nop;

        nop;}

        即可。

        關注C編譯器:"=="的優先級確實比"&"的高,所以,凡牽扯到邏輯的東西,用"()"確認優先級,以避免出現低級錯誤。

        --------------------------------------------------------------------------------

        對定時器的操作:

        void Timer0Init(uint8 VICSlot, uint32 fdiv)

        {

        T0PR = 0; //Prescaling = 0

        T0PC = 0; //Prescalar Counter

        T0TC = 0; //T0 Counter

        T0MR0 = Fpclk / fdiv; //計數周期

        T0MCR = 0x03; //計數達到T0MR0則置位中斷,計數器復位并繼續運行。

        T0CCR = 0x00; //不用捕獲模式

        T0TR = 0xffffffff; //清中斷

        T0TCR = 0x01; //運行

        if(VICSlot <= 15){

        *((uint32*)(&VICVectAddr0 + VICSlot)) = (uint32)Timer0_Exception;

        *((uint32*)(&VICVectCntl0 + VICSlot)) = 0x20 | 0x04;

        VICIntEnable = 1 << 0x04;

        }

        }

        注意:

        1、"*((uint32*)(&VICVectAddr0 + VICSlot)) = ..."中,&VICVectAddr0作為基址,VICSlot作為偏移量。由于前面已經有(uint32*)聲明這是一個指向uint32的指針,故偏移量每變化一個數字代表地址變化了4個字節,在基址與偏移量相加的時候,系統自動將VICSlot乘以4。如果程序中寫成"... + 4 * VICSlot"就錯了。

        2、一定要用"Fpclk / fdiv"設置,以延時1/fdiv秒。該參數不可以以uS為單位。若"Fpclk * us / 1000000"在計算中會乘法溢出,不易避免,又無警告,故不可用。

        --------------------------------------------------------------------------------

        對I2C占空比的設置:

        I2SCLH = (Fpclk / fi2c + 1) / 2;

        I2SCLL = (Fpclk / fi2c) / 2;

        妙哉!無論"Fpclk / fi2c"是奇是偶,單方面的"Fpclk / fi2c + 1"使得I2C總周期"Fpclk / fi2c = I2SCLH + I2SCLL"在方法上沒有誤差。

        I2C必須工作在中斷模式。因為:"When the "SI" flag is reset, no serial interrupt is requested, and there is no stretching of the serial clock on the SCL line."

        I2C的資料在http://www.semiconductors.philips.com/acrobat/various/8xC552_562OVERVIEW_2.pdf.

        --------------------------------------------------------------------------------

        宏的應用:

        在片內外設如I2C,UART,T0,T1,SPI的設置過程中,都需要根據Fpclk計算出一些設定值。我討厭用ARM做除法,所以就用宏來實現,除法在編譯時就可以完成。

        首先,所有片內外設的初始化程序都名為:"void _xxxInit();"。之所以在正式函數名之前加一個"_",是為了與宏區別開,不至于誤寫函數。因為宏的名字與函數名相同,只是全部大寫,并且前面沒有"_"。如:

        #define TIMER0INIT(VICSlot,ms) _Timer0Init(VICSlot,Fpclk/100*ms/10);

        void _Timer0Init(uint8 VICSlot,uint32 ClockCycle);

        在函數中,直接"T0MR0 = ClockCycle"即可。

        注意宏里面的表達式,不可寫成"Fpclk*ms/1000",因為如果這樣寫,當mS太大時,比如mS=1000, Fpclk*mS=(11059200/4)*1000=0xA4CB8000,算到這一步,編譯器認為是溢出(它把計算結果看作是有符號數),只要有溢出的警告出現,設置就不正確。

        也不可以先做除法,以防止吃掉精度,使計算結果為"0"而令定時器死掉。

        總之,既要保證計算精度,又不可以出現溢出警告。

        --------------------------------------------------------------------------------

        關于C編譯器使用的堆棧設置:

        1、在Startup.s中有一句:

        MSR CPSR_C,#0x5f//系統模式

        LDR SP, =UsrStack //用戶棧

        2、在Scatter文件中,有

        STACKS 0x40004000 UNINT

        {

        UsrStack.o(+ZI)

        }

        3、在UsrStack.s中有

        AREA Stacks, DATA, NOINT

        EXPORT UsrStack

        UsrStack SPACE 1

        END

        定義一個UsrStack,大小都無所謂,把它放在可用物理內存的最頂端。C編譯器在編譯子程序調用時,會將要保護的寄存器壓棧,如:

        stmfd r13!,{r3-r7,r14}

        其中,r13的別名是SP。

        這是一個滿遞減堆棧。即SP指向的單元內的數據是有效的,入棧時先減SP再存數據。



        關鍵詞: ARM分散加

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 鸡西市| 德兴市| 海安县| 翁源县| 甘泉县| 信阳市| 教育| 惠东县| 吴旗县| 清徐县| 营口市| 信阳市| 江津市| 陇西县| 姚安县| 合阳县| 江华| 青川县| 衡东县| 萍乡市| 五大连池市| 涿鹿县| 斗六市| 西充县| 通辽市| 会宁县| 丹寨县| 兰溪市| 三原县| 乌兰察布市| 龙口市| 韩城市| 德清县| 色达县| 海林市| 广东省| 金溪县| 南丰县| 万盛区| 纳雍县| 澄迈县|