这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » VXWORKS,NUCLEUS,keil,for,arm,01a,ccd,135

共2条 1/1 1 跳转至

,VXWORKS,NUCLEUS,keil,for,arm,01a,ccd,13581980230, 编译器到底做了什么?

院士
2006-09-17 18:14:16     打赏
,VXWORKS,NUCLEUS,keil,for,arm,01a,ccd,13581980230, 编译器到底做了什么?



关键词: VXWORKS     NUCLEUS     135819802    

院士
2006-12-22 22:43:00     打赏
2楼
问 大家都知道remap的过程,经常有人问RO,RW怎么设置,我也做ARM不少时间了,问题是,我映射的地址是我自己决定的(设置寄存器)那么编译器在这其中到底做了什么,为什么要知道RO,RW的位置,或者说编译器怎么告诉CPU我的RO在哪里RW在哪里的。谢谢 1: RE在ARM的程序里面, 要理解加载时域和运行时域。 所谓加载时域就是你把编译过的目标文件烧录的位置,当然这个位置是能够永久保存的存储介质。运行时域就是当你的程序真正运行的时候所在的位置。
如:程序加载到地址0处,在0地址开始的一段代码将加载到地址0处的代码全部拷贝到运行时域所指定的位置上去,拷贝完之后,将PC指针指到运行时域的起始地址上去。

RO 的意义为代码段,我的理解是  当你的RO设置为0X40000000时,这时,代码的运行时域为0X40000000,而程序的加载时域为0,这样代码就必须进行拷贝,因为编译器给你生成的目标地址都是以0X40000000为基准进行跳转的,你把代码拷贝完后,将PC指到0X40000000处,这样程序就可以正常运行了。如果你的代码不拷贝到0X40000000处,那么由于PC指针都是以0X40000000为基准的,程序在FLASH中执行过程中,跳转到0X40000000之后处,发现那里没有可执行代码。。。。就乱了!  RO,RW,ZI,编译器会为你生成一些符号。。。如下,

    |Image$$RO$$Base|    ; ROM code start    
    |Image$$RO$$Limit|    ; RAM data starts after ROM program
    |Image$$RW$$Base|    ; Pre-initialised variables
    |Image$$ZI$$Base|    ; uninitialised variables
    |Image$$ZI$$Limit|    ; End of variable RAM space

其中部分理解可能有错误,。。。如有疑问,共同探讨。。
2: 你好luhuaren!正如你说的,“因为编译器给你生成的目标地址都是以0X40000000为基准进行跳转的”这个基准cpu是怎么知道的,cpu只知道一开始在0地址取指,remap对于cpu来说也只是操作而已,完全是依据程序来的,按照你的理解就是编译器能知道你何时何地remap,并在程序里适当修改,请做出解释 谢谢 3: 编译器是不知道的需要你自己控制,记住,很多指令的执行是位置无关的 4: RE我的理解如下:
设置的RO  就是0X40000000  
即 |Image$$RO$$Base| = 0X40000000
具体我没有验证过。。不一定对啊。。。你只做个参考。。  
5: 很多指令的执行是位置无关的这个怎么理解,在汇编的时候应该是位置相关的吧
请大家理解我的意思
我想知道既然编译器不能帮你remap 那么设置RO和RW有什么意义
生成镜像的文件总是RO在前后面紧接着RW
然后加载的时候是由程序控制的,那我们在编译器里面(ads1.2)设置RO,RW的值有什么意义

谢谢 6: RO是程序段起始地址,而RW则是数据段起始地址例如你设置RO为0x10000,RO段大小为0x10000,那么编译器就会将生成的代码从RO即0x10000处开始存放。
因为只有0x10000到0x20000之间才是可以存放代码的,这个地址一定要正确。烧写器就是按照这个地址烧写的。

ARM复位时,是从地址0开始运行的。这就导致复位后不能正确运行ROM里的代码。所以在硬件上增加
remap功能,将地址0 remap到地址0x10000,那么去地址0取指令,就会取到地址0x10000的内容,
而0x10000里面放的是一条跳转指令,它跳转到0x10000后面不远处(中间是中断向量表)的启动代码去运行,
这样程序就运行在ROM区了。接下来的函数调用,用的地址都是ROM的实际地址,如果这时
再修改remap,并不会影响程序的正常运行,但是中断入口地址就变了,所以要事先将中断向量表
复制到remap的位置去,中断发生时,才能正常处理。

因此如果要复位后就开始运行本程序,RO必须设置为硬件复位后,地址0 remap到的位置。

所生成的代码,如果用的是绝对地址跳转,则是跟位置有关的。如果用的相对地址跳转,即根据PC来跳转,
则生成的代码是跟地址无关的。 7: 还是有写疑问我的程序是可以跑的,但是我在汇编里面做了打印的函数,打印标号和PC的值我发现所有的标号和PC值都有偏移地址,我用的是分散加载,在下面列出了,发现第一个标号 RESET_GO 就是OX8000,但是在ROM中这个地址是错误的,我烧结的文件明显是从0开始的,所以这就有问题了,但是PC的值是对的,和相应的标号差0x8000,这是为什么呢?
谢谢
RAM_LOAD 0x8000
{
    RAM +0
    {
        init.o(Init, +First)
        * (+RO)
        * (+RW,+ZI)
    }

    HEAP +0 UNINIT
    {
        heap.o (+ZI)
    }

    STACK 0x400000 UNINIT
    {
        stack.o (+ZI)
    }

    VECTOR 0x0
    {
        vectors.o (Vect, +First)
    }

}
8: 不太清楚你要说什么。有些处理器,在remap时,是将整段都remap的。

例如将地址0 remap到地址0x8000,那么你去读0和读0x8000是一样的。标号是0x300和0x8300是一样的,所以运行起来也不会错。 9: re我是说一开始 没有remap的时候
我打印的RESET_GO的值是从0x8000
而PC是0
在FLASH中镜像是从0开始的,那么为什么b RESET_GO时候为什么不会出错呢?
10: re我是说一开始 没有remap的时候
我打印的RESET_GO的值是从0x8000
而PC是0
在FLASH中镜像是从0开始的,那么为什么b 时候为什么不会出错呢?


编译器为你生成的代码 的运行时域是从0X8000开始的,所以 生成的标号RESET_GO  应该是0X8000,  你0X8000处是FLASH 还是RAM?

11: 转载的一篇文章。。。什么是ARM的映像文件,ARM映像文件其实就是可执行文件,包括bin或hex两种格式,可以直接烧到rom里执行。在axd调试过程中,我们调试的是axf文件,其实这也是一种映像文件,它只是在bin文件中加了一个文件头和一些调试信息。映像文件一般由域组成,域最多由三个输出段组成(RO,RW,ZI)组成,输出段又由输入段组成。所谓域,指的就是整个bin映像文件所处在的区域,它又分为加载域和运行域。加载域就是映像文件被静态存放的工作区域,一般来说flash里的 整个bin文件所在的地址空间就是加载域,当然在程序一般都不会放在 flash里执行,一般都会搬到sdram里运行工作,它们在被搬到sdram里工作所处的地址空间就是运行域。我们输入的代码,一般有代码部分和数据部分,这就是所谓的输入段,经过编译后就变成了bin文件中ro段和rw段,还有所谓的zi段,这就是输出段。对于加载域中的输出段,一般来说ro段后面紧跟着rw段,rw段后面紧跟着zi段。在运行域中这些输出段并不连续,但rw和zi一定是连着的。zi段和rw段中的数据其实可以是rw属性。

   | Image$$RO$$Base| |Image$$RO$$Limit| |Image$$RW$$Base| |Image$$ZI$$Base| |Image$$ZI$$Limit|这几个变量是编译器通知的,我们在 makefile文件中可以看到它们的值。它们指示了在运行域中各个输出段所处的地址空间| Image$$RO$$Base| 就是ro段在运行域中的起始地址,|Image$$RO$$Limit| 是ro段在运行域中的截止地址。其它依次类推。我们可以在linker的output中指定,在 simple模式中,ro base对应的就是| Image$$RO$$Base|,rw base 对应的是|Image$$RW$$Base|,由于rw和zi相连,|Image$$ZI$$Base| 就等于|Image$$ZI$$limit| .其它的值都是编译器自动计算出来的。我们还可以通过scatter文件更详细得指定各个输出段的工作地址。

    下面是2410启动代码的搬运部分,我给出注释

BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|



adr r0, ResetEntry;ResetEntry是复位运行时域的起始地址,在boot nand中一般是0
ldr r2, BaseOfROM;
cmp r0, r2
ldreq r0, TopOfROM;TopOfROM=0x30001de0,代码段地址的结束
beq InitRam
ldr r3, TopOfROM

;part 1,通过比较,将ro搬到sdram里,搬到的目的地址从 | Image$$RO$$Base| 开始,到|Image$$RO$$Limit|结束

0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0;


;part 2,搬rw段到sdram,目的地址从|Image$$RW$$Base| 开始,到|Image$$ZI$$Base|结束
sub r2, r2, r3;r2=0
sub r0, r0, r2    
InitRam ;carry rw to baseofBSS
ldr r2, BaseOfBSS ;TopOfROM=0x30001de0,baseofrw
ldr r3, BaseOfZero ;BaseOfZero=0x30001de0
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc %B0



;part 3,将sdram zi初始化为0,地址从|Image$$ZI$$Base|到|Image$$ZI$$Limit|
mov r0, #0;init 0
ldr r3, EndOfBSS;EndOfBSS=30001e40
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1
转载自:
http://fancyy.bokee.com/

12: 不是基础概念的解释remap的过程和方法都很好理解,我现在是碰到一个问题,先说一下前提,ARM7的板子,512KB的bootFLASH专门放bootloader文件的,还有sdram,一开始bootFLASH挂在0地址下,所以最先执行它里面的东西,这都没有问题,我编译的程序也没有问题,在启动的时候初始化,然后remap,bootFLASH到高地址,初始化sdram到低地址,不过并没有copy程序到bootFLASH,所以程序还是一直在bootFLASH中跑的,其他一切正常,然后我优化代码的时候发现,在我的程序里面remap之前,所有的标号比如RESET_GO 以及自己定义跳转的标号都是从一个基数开始的(比如0X8000),但是我烧入bootFLASH的.bin文件是从0开始的,而些在0X8000已经是全F了,所以我就纳闷了,按照你们说的,运行时域我map bootFLASH是在0X0,所以应该是从0开始的,那为什么程序还可以运行呢?
请回答,谢谢
真的想弄得一清二白啊 呵呵 13: 好像有点明白了是否生成的映象文件顺序的安排是通过RO,RW设置的,然后组合成一个整的文件,我们在执行的时候,比如可以是vectors.o 然后是init.o 等等
在一开始的时候肯定是从0x0开始执行,程序必须要跑下去所以在vectors.o里面,完成必要的操作然后跳转到init.s中去,所以采用scat文件的目的就是安排各个 .o在镜像文件中的排列顺序,那么在执行的时候就可以按照作者的意愿去执行了,我忽略了vector文件,所以就被郁闷了,谢谢大家啊
还有最后一个问题,在分散加载文件中设置的0x8000为什么在.bin文件中找不到,.bin文件不到0x8000就没有了,是不是这两个地址的算法不一样?
再次谢谢大家啊 14: RERAM_LOAD 0x8000//////////在这里************************
{
    RAM +0
    {
        init.o(Init, +First)
        * (+RO)
        * (+RW,+ZI)
    }

    HEAP +0 UNINIT
    {
        heap.o (+ZI)
    }

    STACK 0x400000 UNINIT
    {
        stack.o (+ZI)
    }

    VECTOR 0x0
    {
        vectors.o (Vect, +First)
    }

}
仔细看以下内容

   | Image$$RO$$Base| |Image$$RO$$Limit| |Image$$RW$$Base| |Image$$ZI$$Base| |Image$$ZI$$Limit|这几个变量是编译器通知的,我们在 makefile文件中可以看到它们的值。它们指示了在运行域中各个输出段所处的地址空间| Image$$RO$$Base| 就是ro段在运行域中的起始地址,|Image$$RO$$Limit| 是ro段在运行域中的截止地址。其它依次类推。我们可以在linker的output中指定,在 simple模式中,ro base对应的就是| Image$$RO$$Base|,rw base 对应的是|Image$$RW$$Base|,由于rw和zi相连,|Image$$ZI$$Base| 就等于|Image$$ZI$$limit| .其它的值都是编译器自动计算出来的。我们还可以通过scatter文件更详细得指定各个输出段的工作地址。

也就是说  你所说的那个0X8000是运行域的起始地址,而这个地址 可以通过 分散加载来实现,如果不用分散加载,那么可以到ADS编译器里面设置RO的值,RW的值可以不设,如果不设置,RW直接跟在RO的后面。     15: RE加载时域和运行时域可能会不一样,程序一上电,加载时域与运行时域应该是相同的,但启动代码结束后,程序就在运行时域中运行,此是,加载时域与运行时域可能会不同,如果加载时域与运行时域不同,那么就需要把加载时域内加载的代码拷贝到运行时域中去,然后跳转到运行时域中去运行。
即运行时域一上电,可能没有可执行的代码,在实际运行过程中,运行时域中的代码是由启动代码来拷贝完成的。感觉这和硬盘和内存的关系差不多!
如果我理解的有不对的地方,请继续探讨。。。 16: re还是有点不清楚的地方,烧结的文件(bootloader)是按什么顺序烧结的(比如各种.o文件的排列顺序是按照scat文件来的么?),但是那里面的地址对于FLASH来说超过了最大范围,并且程序一开始运行的时候没有remap之前(还应该是你们所说的加载时域的时候,对吧?)已经在0x8000的地址了,而这个地址上的FLASH是没有东西的,所以这样的说法应该有问题 17: 表达比较困难呵呵或者这样说
RAM_LOAD 0x8000//////////在这里************************
{
    RAM +0
    {
        init.o(Init, +First)
        * (+RO)
        * (+RW,+ZI)
    }

    HEAP +0 UNINIT
    {
        heap.o (+ZI)
    }

    STACK 0x400000 UNINIT
    {
        stack.o (+ZI)
    }

    VECTOR 0x0
    {
        vectors.o (Vect, +First)
    }

}

这个文件,对于生成的映象文件有什么影响。

18: 有很多错误有很多错误,找主要的说吧:
1,关于RO,RW,地址0X8000或其它,包括Scatter文件,都和编译器无关,   只   和连接器有关,一定区别编译器和连接器LIKE。
2,为什么2个FIRST,这是不可以的。 init.o(Init, +First)应该指定+RO为好。
3,如果 init.o(Init, +First)第一部分的数据存储不是整字(出现字节或半
        * (+RO)
        * (+RW,+ZI
字,是十分不例的。
4,以上调试可以,对于实际程序有潜在的问题,比如* (+RW,+ZI)是只读的。
5,建议读《ARM开发工具ADS原理与应用》 19: 谢谢发现 对于编译和连接的概念还是不清楚了,网上也有很多讲得不清楚得地方,准备十一的时候恶补一下

共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]