打印

EasyARM2200开发板学习笔记:初级C程序运行环境的建立

[复制链接]
1936|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
olyaim|  楼主 | 2008-7-20 19:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    以下启动代码是从EasyARM2200开发板的例子程序中提取出来的简单启动代码,当然这种启动代码在实际开发中是不太可能用到的,这里只是为了启动过程的理解方便。但就这么简单的几行代码却还是有一些东西让人不太理解,比如|Image$$XX$$Limit|和|Image$$XX$$Base|,有意思,那就接着分析吧。

1 ; 启动文件,初始化C程序的运行环境,然后进入C程序代码。
3         IMPORT    |Image$$RO$$Limit|
4         IMPORT    |Image$$RW$$Base|
5         IMPORT    |Image$$ZI$$Base|
6         IMPORT    |Image$$ZI$$Limit|

8         IMPORT    Main      ; 声明C程序中的Main()函数
9
10         AREA      Start,CODE,READONLY   ; 声明代码段Start
11         ENTRY      ; 标识程序入口
12         CODE32     ; 声明32位ARM指令    
13            
14 Reset   LDR       SP,=0x40003F00     ; 设置堆栈指针
15 
16         ; 初始化C程序的运行环境
17         LDR       R0,=|Image$$RO$$Limit|
18         LDR       R1,=|Image$$RW$$Base|    
19         LDR       R3,=|Image$$ZI$$Base|    
20             
21         CMP       R0,R1
22         BEQ       LOOP1
23 LOOP0   CMP       R1,R3      
24         LDRCC     R2,[R0],#4     
25         STRCC     R2,[R1],#4 
26         BCC       LOOP0
27         
28 LOOP1   LDR       R1,=|Image$$ZI$$Limit| 
29         MOV       R2,#0
30 LOOP2   CMP       R3,R1
31         STRCC     R2,[R3],#4
32         BCC       LOOP2
33         
34         B         Main     ; 跳转到C程序代码Main()函数
35        
36         END

    用过ADS1.2的朋友都知道在编译连接一个ARM程序之前要根据开发板的实际存储配置设置连接器的选项,这里我就以EasyARM2200的开发板来配置,因为我使用的是LPC2210处理器,片内无FLASH,片内RAM地址范围初设置为0x40000000~0x40003FFF,大小只有16Kbytes,所以大一点的程序就不能在这里面调试了,当然此开发板片外也配有512Kbytes的RAM和2Mbytes的FLASH,它们的地址可根据跳线来配置,这里就先不讲解。我们这个程序只在片内RAM中调试就可以了,下面就是DebugDel生成目标的连接器配置:

 
https://bbs.21ic.com/upfiles/img/20079/200794194254526.gif
 
  配置完成后,点击Make后会生成一个编译连接报告,如下:
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtmlclip1/01/clip_image001.gif
 
  结合启动代码、连接器配置、编译连接报告和ADS1.2帮助文档我们隐约可以看出刚才那几个怪怪的 symbol代表什么了:
|Image$$RO$$Base| 
表示RO输出段运行时起始地址,也可以说是程序代码存放的起始地址,由-ro-base这个参数       指定,所以|Image$$RO$$Base|=0x40000000;
|Image$$RO$$Limit|
表示RO输出段运行时存储区域界限,其值可通过|Image$$RO$$Base|+Code sizes+RO Data  sizes计算得出,所以|Image$$RO$$Limit|=0x40000090;
|Image$$RW$$Base|
表示RW输出段运行时起始地址,记得是运行时的地址,而不一定是加载时的存放地址,因为RW输出段 在加载时可能是在ROM中并紧跟着RO输出段存放的,当程序运行时才复制到RAM起始地址为|Image $$RW$$Base|的区域,由-rw-base这个参数指定,所以|Image$$RW$$Base|=0x40003000;如 果未指定-rw-base,默认紧跟RO输出段,那么|Image$$RW$$Base|=|Image$$RO$$Limit|;
|Image$$RW$$Limit|
表示RW输出段运行时存储区域界限,其值可通过|Image$$RW$$Base|+RW Data sizes计算得 出,所以|Image$$RW$$Limit|=0x40003004;
|Image$$ZI$$Base|
表示ZI输出段运行时起始地址,它是运行时在RAM中生成的,紧跟着RW输出段存放,其值和|Image $$RW$$Limit|一样,所以|Image$$ZI$$Base|=0x400030004;
|Image$$ZI$$Limit|
表示ZI输出段运行时存储区域界限,其值可通过|Image$$ZI$$Base|+ZI Data sizes计算得 出,所以|Image$$ZI$$Limit|=0x40003008。
 
  看出什么没有,原来上面这些都是在运行时域的地址,那从加载时域到运行时域之间我们的处理器还对其烧进去的程序做了什么呢?这就又得回到启动代码中去看,认真分析可以看出LOOP0和LOOP1正是引导处理器对其上面这些输出段的操作。下图就是LOOP0和LOOP1所做的工作。
 
 
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtmlclip1/01/clip_image001.png
 
  不过这里的RW段完全可以不做复制,因为0x40003000也是在内部RAM中,那怎么让RW段不做复制呢?有两种方法:
第一种是不定义RW Base为0x40003000,这样加载时域和运行时域的RW段在同一个地址且紧接着RO段,生成的ZI段也紧接着RW段,如下图:
 
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtmlclip1/01/clip_image002.png
 
  第二种是使用两个加载时域,定义RO段所在的加载时域的起始地址为0x40000000,RW段所在的加载时域的起始地址直接设置为0x40003000,如下图:这样RW段也就可以和RO段分开加载了,运行时就不用重新复制RW段,提高运行效率。
 
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtmlclip1/01/clip_image003.png
 
  不过上面这几种方法都只适用于调试,也就是程序加载时都是放在RAM中才行。如果是最终加载到ROM的程序那就要用到一个加载时域和多个运行时域的方法,这里先不深入,下次再分析,吃饭去了。

相关帖子

沙发
olyaim|  楼主 | 2008-7-20 19:33 | 只看该作者

晕啊,不能显示

使用特权

评论回复
板凳
olyaim|  楼主 | 2008-7-20 19:47 | 只看该作者

EasyARM2200开发板学习笔记:初级C程序运行环境的建立


以下启动代码是从EasyARM2200开发板的例子程序中提取出来的简单启动代码,当然这种启动代码在实际开发中是不太可能用到的,这里只是为了启动过程的理解方便。但就这么简单的几行代码却还是有一些东西让人不太理解,比如|Image$$XX$$Limit|和|Image$$XX$$Base|,有意思,那就接着分析吧。

1 ; 启动文件,初始化C程序的运行环境,然后进入C程序代码。
3         IMPORT    |Image$$RO$$Limit|
4         IMPORT    |Image$$RW$$Base|
5         IMPORT    |Image$$ZI$$Base|
6         IMPORT    |Image$$ZI$$Limit|

8         IMPORT    Main      ; 声明C程序中的Main()函数
9
10         AREA      Start,CODE,READONLY   ; 声明代码段Start
11         ENTRY      ; 标识程序入口
12         CODE32     ; 声明32位ARM指令    
13            
14 Reset   LDR       SP,=0x40003F00     ; 设置堆栈指针
15 
16         ; 初始化C程序的运行环境
17         LDR       R0,=|Image$$RO$$Limit|
18         LDR       R1,=|Image$$RW$$Base|    
19         LDR       R3,=|Image$$ZI$$Base|    
20             
21         CMP       R0,R1
22         BEQ       LOOP1
23 LOOP0   CMP       R1,R3      
24         LDRCC     R2,[R0],#4     
25         STRCC     R2,[R1],#4 
26         BCC       LOOP0
27         
28 LOOP1   LDR       R1,=|Image$$ZI$$Limit| 
29         MOV       R2,#0
30 LOOP2   CMP       R3,R1
31         STRCC     R2,[R3],#4
32         BCC       LOOP2
33         
34         B         Main     ; 跳转到C程序代码Main()函数
35        
36         END

用过ADS1.2的朋友都知道在编译连接一个ARM程序之前要根据开发板的实际存储配置设置连接器的选项,这里我就以EasyARM2200的开发板来配置,因为我使用的是LPC2210处理器,片内无FLASH,片内RAM地址范围初设置为0x40000000~0x40003FFF,大小只有16Kbytes,所以大一点的程序就不能在这里面调试了,当然此开发板片外也配有512Kbytes的RAM和2Mbytes的FLASH,它们的地址可根据跳线来配置,这里就先不讲解。我们这个程序只在片内RAM中调试就可以了,下面就是DebugDel生成目标的连接器配置:

https://bbs.21ic.com/upfiles/img/20079/200794194254526.gif

配置完成后,点击Make后会生成一个编译连接报告,如下:

https://bbs.21ic.com/upfiles/img/20079/200794194244924.gif

结合启动代码、连接器配置、编译连接报告和ADS1.2帮助文档我们隐约可以看出刚才那几个怪怪的 symbol代表什么了:

|Image$$RO$$Base| 
表示RO输出段运行时起始地址,也可以说是程序代码存放的起始地址,由-ro-base这个参数指定,所以|Image$$RO$$Base|=0x40000000;
|Image$$RO$$Limit|
表示RO输出段运行时存储区域界限,其值可通过|Image$$RO$$Base|+Code sizes+RO Data  sizes计算得出,所以|Image$$RO$$Limit|=0x40000090;
|Image$$RW$$Base|
表示RW输出段运行时起始地址,记得是运行时的地址,而不一定是加载时的存放地址,因为RW输出段 在加载时可能是在ROM中并紧跟着RO输出段存放的,当程序运行时才复制到RAM起始地址为|Image $$RW$$Base|的区域,由-rw-base这个参数指定,所以|Image$$RW$$Base|=0x40003000;如 果未指定-rw-base,默认紧跟RO输出段,那么|Image$$RW$$Base|=|Image$$RO$$Limit|;
|Image$$RW$$Limit|
表示RW输出段运行时存储区域界限,其值可通过|Image$$RW$$Base|+RW Data sizes计算得 出,所以|Image$$RW$$Limit|=0x40003004;
|Image$$ZI$$Base|
表示ZI输出段运行时起始地址,它是运行时在RAM中生成的,紧跟着RW输出段存放,其值和|Image $$RW$$Limit|一样,所以|Image$$ZI$$Base|=0x400030004;
|Image$$ZI$$Limit|
表示ZI输出段运行时存储区域界限,其值可通过|Image$$ZI$$Base|+ZI Data sizes计算得 出,所以|Image$$ZI$$Limit|=0x40003008。

看出什么没有,原来上面这些都是在运行时域的地址,那从加载时域到运行时域之间我们的处理器还对其烧进去的程序做了什么呢?这就又得回到启动代码中去看,认真分析可以看出LOOP0和LOOP1正是引导处理器对其上面这些输出段的操作。下图就是LOOP0和LOOP1所做的工作。

https://bbs.21ic.com/upfiles/img/20079/20079420144102.gif

可以看出这里的RW段完全可以不做复制,因为0x40003000也是在内部RAM中,那怎么让RW段不做复制呢?有两种方法:
第一种是不定义RW Base为0x40003000,这样加载时域和运行时域的RW段在同一个地址且紧接着RO段,生成的ZI段也紧接着RW段,如下图:

https://bbs.21ic.com/upfiles/img/20079/20079420223448.gif

第二种是使用两个加载时域,定义RO段所在的加载时域的起始地址为0x40000000,RW段所在的加载时域的起始地址直接设置为0x40003000,如下图:这样RW段也就可以和RO段分开加载了,运行时就不用重新复制RW段,提高运行效率。

https://bbs.21ic.com/upfiles/img/20079/20079420246286.gif

不过上面这几种方法都只适用于调试,也就是程序加载时都是放在RAM中才行。如果是最终加载到ROM的程序那就要用到一个加载时域和多个运行时域的方法,这里先不深入,下次再分析。

使用特权

评论回复
地板
machunshui| | 2008-7-21 13:46 | 只看该作者

不如看基于gnu的启动代码

不如看基于gnu的启动代码,

对以后搞os有帮助.

使用特权

评论回复
5
olyaim|  楼主 | 2008-7-21 17:37 | 只看该作者

我会考虑的,谢谢!

使用特权

评论回复
6
桂电飞天鼠| | 2008-7-22 23:54 | 只看该作者

嗯,分析得不错,谢谢分享~

当初刚开始学习时也想记录下来的,后来没时间静下心来写

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

11

帖子

0

粉丝