ARM 学习
报告
软件系统测试报告下载sgs报告如何下载关于路面塌陷情况报告535n,sgs报告怎么下载竣工报告下载
001 杜云海 duyunhai@hotmail.com
- 1 - www.seajia.com
ARM映象文件及执行机理
ARM学习报告001 2004-5-7
这几天为了弄清楚ARM系统是如何运行相应的可执行程序可谓费尽心机,整个五一假期
都没有休息,其中由于烧写flash的软件出了些问题,使得理解 ARM映象文件及执行机理更
加曲折,不过还好在自己的努力和论坛上兄弟的帮助下,终于还是成功了。作为进入 ARM
系统设计的重要一步,我觉得这个过程是应该写下来的,既是为了自己的总结,也为了后来
进入ARM的人可以少走些弯路。
我的开发板配置如下:
l CPU: S3C4510B芯片(ARM7TDMI核)
l FLASH:1片 16×1M位数据宽度的 FLASH,共 2M 字节 Flash(MX29LV160BTC),
速度 70/90ns; 16位模式。
l 内存SDRAM:2片 4M ×16位数据宽度的 SDRAM(HY57V651620B TC-10S)构成,
共 16M 字节 SDRAM。2片 16位拼做 32位使用,共用一个片选。
l 简易JTAG调试,两个串口,一个以太网口
从我个人的学习经过认为,初学者最难突破的应该是以下三个方面。所以,本文基本上
从这三个方面来阐述。
l ARM映象文件(包括axf文件和bin文件)的生成和组成
l 映象文件下在flash中的状态和运行时的状态(加载域和运行时域)
l 地址重映射remap
本文基本围绕附录给出的那个串口发送程序MySComm4510b002.mcp而展开的。这个程序
的 初 始 化 和 地 址 重 映 射 部 分 参 考 了 twentyone的 程 序
(http://bbs.edw.com.cn/dispbbs.asp?boardID=20&ID=27980&page=1),串口发送部分参
考了sofa的程序(那我自己写了什么呢?J)。
首先我们应该建立这样一种概念,对于一个裸机(Flash里什么都没有)来说,所有的
细节都得自己设置,不要指望芯片或开发工具可以为你做些什么!所以从第一条指令开始,
你就必须负责所有的工作。对于S3C4510B,上电或复位后从0x00000000开始执行指令,而
硬件上我们把Flash接在了CPU的ROMCON0处,所以CPU就是从 Flash的0x00000000处开
始取指令,那么我们就必须保证CPU一开始可以取到正确的指令。
小插曲:
我前几天就是被我的flash下载程序害死了,由于下载程序的时序问题,开始的40多
个字节里不能正常烧写,结果老是为 0xff,而我刚刚开始也不懂得 ARM映象文件的内涵,
结果以为 axf文件就是可执行文件,将其烧入 flash,所以程序真正的第一条指令在 0x34
开始,不能正确烧写的部分正好是axf文件的头,没有影响到真正的指令。程序有时也可以
执行(因为0xff相当于空指令一样,程序也可以执行到真正的第一条指令),所以串口输出
的都是不对的乱码或字母。
一 ARM的映象文件
1.1 初步认识 axf和 bin文件
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 2 - www.seajia.com
这里我先谈谈ARM的映象文件(即可执行文件)的概念。我们生成的 ARM的映象文件有
axf
格式
pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载
和bin格式两种,有时容易被二者混淆。其实bin文件是真正的可执行文件,而 axf
文件是ARM特有的调试文件,里面除了包含 bin文件的内容之外,还附加了许多其他调试信
息。首先让我们来看看axf和bin的区别,图1~图4是用ultraedit打开的axf文件和bin
文件的头部和尾部。这两个文件都是MyScomm4510b002.mcp用ADS1.2生成的(选中target
setting中的post-linker:fromelf和ARM Fromelf中的output format:plain binary这
两个选项,就可以同时生成相应的axf和bin文件)。
在生成这两个映象文件时,请设置ARMlinker 的选项:ro_base设为0x0000,rw_base
设为0xa00000,作用将在本文后面解释。
图1是axf文件,其中反显处才是真正的第一条指令,开始的52个字节都是 axf文件
头,而图2的bin文件从 00000000h就是真正的第一条指令(所以为什么说 bin文件才是真
正的可执行文件)。关键是从第一条指令开始的二个文件的比较了,哦,竟然是完全相同的,
一直到bin文件的尾部,见图3和图4。即bin文件就是axf文件的00000034h~00000323h。
不过,二者的相应指令或数据的地址就不一样了,先记住这一点伏笔。Bin文件结束(图4
反显处),但axf文件还没有结束(图 3中反显指示 bin文件相应结束部分),其后还有很多
相关的调试信息,这些调试信息可以用在ADW或AXF的load image的调试中。
由于我们的烧写工具是原封不动地将映象文件烧入flash中,所以,axf文件是不可以
烧入的,否则flash的0x00000000处就不是真正的第一条指令,而是 axf头部分。我们应
该将bin文件烧入,保证flash的0x00000000处是第一条指令。
我个人猜测,axf文件应该可以通过“ADW中的下载功能”下载到ROM中运行,在下载
的过程中可能经过ADW相应的内部处理而导致真正烧入ROM中的还是bin文件。
到现在为止,我们只能感性认识一下映象文件:axf文件和bin文件的不同,也就是说,
bin文件是axf文件的一部分,最精华的一部分,那么到底哪部分是最精华的呢?bin文件
中包含了哪些内容呢?指令和数据到底放在映象文件的什么地方呢?下面接着分析。
图1 ultraedit中打开的axf文件二进制形式(头部部分)
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 3 - www.seajia.com
图2 ultraedit中打开的bin文件二进制形式(头部部分)
图3 ultraedit中打开的axf文件二进制形式(中间部分)
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 4 - www.seajia.com
图4 ultraedit中打开的bin文件二进制形式(尾部部分)
1.2 ARM映象文件的内容
讨论到映象文件的内容,就应该打开源程序并理解源程序的内容了,这个程序比较简单,
我们这里讲解的都是初级知识,给大家一个入门的启示,复杂的以后可以再慢慢学。所以大
家看到这篇文章不要老是想到复杂的情况,那样本文有些阐述可能就不太对头了:)
首先我们可以先不看程序具体做了什么,我们总应该可以看出这个简单程序分为两个部
分,一个是CODE部分,即指令代码部分;另一部分是DATA部分,即数据部分。
AREA Init, CODE, READONLY (代码部分RO)
CODE32
GET snds.s
ENTRY
Start
⋯⋯⋯⋯⋯
AREA PRINTLINEOUT,DATA,READWRITE (数据部分RW)
LINE1 DCB &A,&D,"***************************************",0
⋯⋯⋯⋯⋯⋯
END
书上说,映象文件一般由域组成,域由最多三个输出段(RO,RW,ZI)组成,输出段又由
输入段组成。那么我们来看看生成的映象文件MySComm4510b002.bin到底怎么符合书上的概
念,又怎么和上面源程序的代码和数据部分对应起来的。
先来看看图5吧,我觉得实物最可以给人以说服力。
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 5 - www.seajia.com
图5 ultraedit中打开的bin格式映象文件二进制形式(大部分)
大家看到了吧,蓝色反显的前部其实就是源程序对应的指令代码部分,反显后对应的就
是源程序的数据部分。反显处就是指令部分RO和数据部分RW的分界处。大家仔细看看,反
显开始的地方往后其实就是要从串口输出的字符数据(由于屏幕关系,没有完全显示出,大
家可以自己用ultraedit打开看看);而从00000000h开始至 00000227h处是程序中的指令
编码(编译好的二进制编码)。
所以,可以这么说,
l 域:整个bin映象文件,也就是说这个简单程序的映象文件只有一个域(加载域),
其实大部分程序都是只有一个加载域
l 输出段:有两个输出段,RO和RW,没有ZI。这个我们从源程序和图 5也可以看出。
l 输入段:两个输入段,即源程序的CODE部分和 DATA部分。CODE部分是READONLY,
属于RO输出段,DATA部分是READWRITE,属于RW输出段。
所以,域、输出段和输入段的关系如下图:
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 6 - www.seajia.com
图6 例子程序的域、输出段和输入段的关系
在加载域中,RW直接跟在 RO后面,那我们刚刚设置的连接选项:ro_base=0x0000,
rw_base=0xa00000有什么用呢?RO的确是从 0x00000000开始,可是 RW却从 0x00000228
开始,和rw_base不同阿?到底这是怎么回事呢?
二 ARM的映象文件的加载域和运行时域
所有这些,都是“ARM连接”和“系统存储器多样化”惹的祸!ARM连接器一个很重要
的工作就是要解析目标文件中各种符号,也就是得到各符号的值。
还是先来看看图7吧
图7 例子程序ADS编译连接的符号映射图(一部分)
这张图是例子程序在 ADS1.2中编译连接后显示出的符号映射 MAP的一部分(选中
整个
bin 映
象文
件
RW
RO
DATA部
分
CODE部
分
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 7 - www.seajia.com
ARMlinker中listing选项卡的 image map和symbols即可),其中设置了ro_base=0x0000,
rw_base=0xa00000,可以看到象COPY,LOOP,STOP,PrintLine等符号的值都是相对于ro_base
而定位的(在RO段中),而LINE1、LINE2、LINE3、LINE4和LINE5却是相对于 rw_base而
定位的(在RW段中)。
这些符号值是在映象文件开始运行时才起作用。如果仅仅是放在flash中或仅仅运行前
面几条初始化指令(加载域状态),那么这些值还暂时不起作用,否则问题就麻烦了。所以
真正运行时,就必须保证这些值是对的!!于是引出了“数据移动”。
2.1 概念
什么是加载域和运行时域呢?我个人认为用“域”这个词实在很容易混淆。其实简单明
了的说,加载域就是最原始的bin文件,就是映象文件烧入flash时的状态,如图5所示,
RW跟在RO后面;而运行时域就是经过了改头换面的映象文件,即由于运行了相应的初始化
程序而把 RW或 ZI拷贝到相应的地方,这是映象文件已经四分五裂成至多三个部分,图 8
示意例子程序映象文件的两个不同状态。
当然运行时域也可以和加载域相同,就是不设置 rw_base,那么运行时不需要移动,依
然跟在RO后面。
RAM
ROM
加载域 运行时域
图8 例子程序加载域和运行时域比较示意图
在加载域状态,RO地址是正确的,意味着指令可以正确执行;但 RW是不正确的(从图7
可知),但是暂时的错误不影响程序初始化,源程序的 part1~part6都暂时没有用到 RW,
所以程序还是可以正确运行,即使RW section在0x00000228处。程序终究是要用到 RW的,
那么怎么办?其实只要来个“乾坤大挪移”就好了,在用到RW之前将 RW移动到正确的位置
即可。Part7完成了这个“挪移”过程。当然移动前进行了地址重映射,不过,地址重映射
和RW的数据移动其实是两回事!
在twentyone的bootloader中(即例子程序的part5~part6),在映射前,将 RO的备
份拷到RAM中,然后将RAM重映射为0x00000000,非常巧,指令运行不受任何影响!重映
射后flash的地址为0x01200000,此时主动权已经到RAM中的RO copy手上了!然后再将
RW移动到相应的位置。哪里才是RW相应的正确位置呢?
2.2 RW的数据移动
有一个概念很重要:“加载域状态”的映象文件中RO、RW和ZI的所在地址都是临时的,
他们所在的真正位置(即连接时设置的地址值)都必须在程序初始化时由相应程序,将他们
RO section
RW section
RO copy
ZI section
RO section
RW section
RW copy 0x00200000
0x00000228
0x00000000
0x00a00000
0x00000000
0x01200000
ROM
RAM
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 8 - www.seajia.com
移动到相应的地方。这个数据移动过程由Bootloader完成!!!
源程序由8个部分组成:part1~part7主要是执行CPU的初始化和内存重映射,相关
资料可以参考文章(http://bbs.edw.com.cn/dispbbs.asp?boardID=20&ID=27980&page=1)。
Part8主要是通过串口发送几行字符,这里主要解释一下part3和part5。
;Part 3
;***************************************************************
; Import some important variables for later use
IMPORT |Image$$RO$$Base|
IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$RW$$Limit|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|
这六个全局变量分别是RO输出段运行时起始地址,RO运行时存储区域界限,RW输出段
运行时起始地址,RW运行时存储区域界限,ZI输出段运行时起始地址,ZI运行时存储区域
界限。引入的这六个变量可说是在映象文件编译连接中起到至关重要的作用,这里我们就不
说得非常复杂了,就简单用例子程序来举例说明。
现在再让我们回头看看前面编译连接时ADS1.2中的map及symbols图的另外一部分。
如图9。
图9 ADS1.2编译连接后的映射表一部分
其中:
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 9 - www.seajia.com
l Image$$RO$$Base =0x00000000
l Image$$RO$$Limit=0x00000228
l Image$$RW$$Base =0x00a00000
l Image$$RW$$Limit=0x00a000c8
l Image$$ZI$$Base =0x00a000c8 (其实ZI是不存在的)
l Image$$ZI$$Limit=0x00a000c8
大家把这些地址再和图5的bin文件(映象文件)对照看看,有哪些相似之处吗?
RO的base和limit和在ultraedit中打开的映象文件一样,也就是说和烧在 flash中
的一样了! Image$$RW$$Base和Image$$RW$$Limit明显不对,不过,仔细看看,除了地址
不对外,其实相对值还是一样的,也就是说,只要把Flash中的RW移到Image$$RW$$Base
所指示的位置即可。
Image$$RO$$Limit是RO的存储地址界限,其实是flash中RW的开始地址(加载域状
态时),因为加载域状态时RW就是紧跟在RO后面!而Image$$RW$$Base 是RW运行时的开
始地址,二者是不同的。
Part7中正是通过判断Image$$RO$$Limit和Image$$RW$$Base是否相等来决定程序运
行时RW是否需要“搬迁”,这个例子中,二者是不等的,就是说,“程序运行时”RW不应该
在flash中的这个位置——RO的后面,那么初始化程序就必须在未使用RW前将RW移到正
确的位置,例子程序中则将RW移到了0xa00000处。从而使得编译连接时和 RW相关的标号
值都正确。
LDR r0, =|Image$$RO$$Limit|
LDR r1, =|Image$$RW$$Base|
LDR r3, =|Image$$ZI$$Base|
CMP r0, r1
BEQ %1
0 CMP r1, r3 ; Copy init data
LDRCC r2, [r0], #4
STRCC r2, [r1], #4
一旦RW被移动到0xa00000处,那么图7中的LINE1、LINE2、LINE3、LINE4和LINE5
等RW段的符号值都是正确的了!
ADS在生成加载域——也就是映象文件(bin文件)时,是把RW紧接在 RO后面,这并
不是RW的实际地址,而仅仅是加载时,也就是烧写到flash时的代码和数据的临时地址。
而程序真正开始运行起来时——也就是运行时域,一切情况又都发生了变化,该到什么地方
的,就应该去什么地方,所以RW应该拷到0xa00000处,以保证连接时的符号值都是正确的。
三 ARM系统的地址重映射
其实如果真正懂了上面两个原理,那么理解地址重映射就很容易了,虽然他们之间没有
什么联系,但是,很多人不能理解地址重映射就是因为和映象文件的编译连接地址搞得一团
糊!
地址重映射就是通过系统的存储管理部件改变系统中各个存储器的映射地址,在
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 10 - www.seajia.com
S3C4510B中,通过改变系统控制寄存器组来改变存储器的映射地址。如果你对芯片的存储
管理部件还不是很熟的话,应该先把芯片资料再看看。
一般的S3C4510B开发板都是将flash连接到ROMCON0处,这样上电复位时CPU读的第
一条指令是flash的0x00000000处的值。例子程序中part1~part5程序在运行时都没有映
射,在part4为了part5的bootloader的复制,特意初始化了ROM和SDRAM,具体设置如
图8的加载域所示,flash为2M,从0x00000000~0x0001fffff;SDRAM为16M,从0x00200000~
0x01200000,其他系统控制寄存器则设置为0x03ff0000~0x03ffffff(这个由part2设置)。
;Part 4
;****************************************************************
;Initalize the memory as followa:
; FLASH @ 0 ~ 2 M
; SDRAM @ 2 ~ 18M
LDR r1, =rEXTDBWTH ;EXTDBWTH
LDR r2, =rROMCON0 ;ROMCON0 @ 0M ~ 2M
LDR r3, =rROMCON1 ;ROMCON1 @ DISABLED
LDR r4, =rROMCON2 ;ROMCON1 @ DISABLED
LDR r5, =rROMCON3 ;ROMCON1 @ DISABLED
LDR r6, =rROMCON4 ;ROMCON1 @ DISABLED
LDR r7, =rROMCON5 ;ROMCON1 @ DISABLED
LDR r8, =rSDRAMCON0 ;SDRAMCON0 @ 2M ~ 18M
LDR r9, =rSDRAMCON1 ;SDRAMCON1 @ DISABLED
LDR r10,=rSDRAMCON2 ;SDRAMCON2 @ DISABLED
LDR r11,=rSDRAMCON3 ;SDRAMCON3 @ DISABLED
LDR r12,=rSREFEXTCON
LDR r0, =ARM7_EXTDBWTH
STMIA r0, {r1-r12}
3.1 地址重映射
先来看看例子程序part6:
;Part 6
****************************************************************
;Remap the memory
; FLASH @ 16 ~ 18M
; SDRAM @ 0 ~ 16M
LDR r1, =rEXTDBWTH_R ;EXTDBWTH
LDR r2, =rROMCON0_R ;ROMCON0 @ 16M ~ 18M
LDR r3, =rROMCON1_R ;ROMCON1 @ DISABLED
LDR r4, =rROMCON2_R ;ROMCON2 @ DISABLED
LDR r5, =rROMCON3_R ;ROMCON3 @ DISABLED
ARM 学习报告 001 杜云海 duyunhai@hotmail.com
- 11 - www.seajia.com
LDR r6, =rROMCON4_R ;ROMCON4 @ DISABLED
LDR r7, =rROMCON5_R ;ROMCON4 @ DISABLED
LDR r8, =rSDRAMCON0_R ;SDRAMCON0 @ 0M ~ 16M
LDR r9, =rSDRAMCON1_R ;SDRAMCON1 @ DISABLED
LDR r10,=rSDRAMCON2_R ;SDRAMCON2 @ DISABLED
LDR r11,=rSDRAMCON3_R ;SDRAMCON3 @ DISABLED
LDR r12,=rSREFEXTCON_R
LDR r0, =ARM7_EXTDBWTH
STMIA r0, {r1-r12}
在前面的5个部分的准备后,特别是将FLASH中的内容复制到原先的0x00200000处,
系统已经进入了可以重映射的状态了。重映射后,系统开始在SDRAM中运行了,而不在flash
中,由于第五部分的拷贝,导致映射后的指令切换的无缝连接!!大家可以琢磨一下!
至于映射后,映象文件是否可以正常运行,以及下一步要做什么,我看我就不多说了⋯⋯
让我们在ARM学习报告002再见!!
感谢我的小猫,在五一节陪伴着我!!
文章版权属于 杜云海(duyunhai@hotmail.com),转载请注明作者及网站
(wwww.seajia.com)
很多东西,如果你懂,它就很简单;如果你不懂,仿佛简单的它
也会很难