您的位置 首页 IC

ARM 的涣散加载

对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义:

关于刚学习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各个项目的字节数,最终会有他们的一个统计数据:

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再存数据。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/bandaoti/277112.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部