RO段精.docx
《RO段精.docx》由会员分享,可在线阅读,更多相关《RO段精.docx(17页珍藏版)》请在冰豆网上搜索。
![RO段精.docx](https://file1.bdocx.com/fileroot1/2023-1/6/62b82a80-2a9e-4045-91ae-470f5f3cea85/62b82a80-2a9e-4045-91ae-470f5f3cea851.gif)
RO段精
RO段、RW段和ZI段
一般而言,一个程序包括只读的代码段和可读写的数据段。
在ARM的集成开发环境中,只读的代码段和常量被称作RO段(ReadOnly);可读写的全局变量和静态变量被称作RW段(ReadWrite);RW段中要被初始化为零的变量被称为ZI段(ZeroInit)。
对于嵌入式系统而言,程序映象都是存储在Flash存储器等一些非易失性器件中的,而在运行时,程序中的RW段必须重新装载到可读写的RAM中。
这就涉及到程序的加载时域和运行时域。
简单来说,程序的加载时域就是指程序烧入Flash中的状态,运行时域是指程序执行时的状态。
对于比较简单的情况,可以在ADS集成开发环境的ARM LINKER选项中指定RO BASE和RW BASE,告知连接器RO和RW的连接基地址。
对于复杂情况,如RO段被分成几部分并映射到存储空间的多个地方时,需要创建一个称为“分布装载描述文件”的文本文件,通知连接器把程序的某一部分连接在存储器的某个地址空间。
需要指出的是,分布装载描述文件中的定义要按照系统重定向后的存储器分布情况进行。
在引导程序完成初始化的任务后,应该把主程序转移到RAM中去运行,以加快系统的运行速度。
关于44b0的RO,RW,ZI
程序固化在FLASH中,有程序和字符串什么的东西,把程序部分当作ro,字串什么的是zw,而RO,ZW,ZI是编译器知道的东西,如果RO定位于FLASH地址空间的话,那么程序只能在FLASH空间运行。
初始化程序只需把rw(ROLimit为起始地址)拷贝到SDRAM中指定的RW处,然后把ZI区域初始化为0;
如果RO定位于SDRAM空间的话,说明连接器生成代码位置无关,或者有些代码必须运行在SDRAM空间。
这样的话久需要初始化的时候把FLASH里面的程序拷贝到SDRAM的RO区域,拷贝完成的时候指向ROM的指针就应该到了zwbase了。
不知道这样理解对吗?
有没有哪位大哥有详细的资料介绍一下,最好有图示或则视频演示,
下面是我的程序,研究了一下好像不支持code从FLASH拷贝到SDRAM,
LDR r0,=|Image$$RO$$Limit| ;GetpointertoROMdata
LDR r1,=|Image$$RW$$Base| ;andRAMcopy
LDR r3,=|Image$$ZI$$Base|
;Zeroinitbase=>topofinitialiseddata
CMP r0,r1 ;Checkthattheyaredifferent
BEQ %F1
0
CMP r1,r3 ;Copyinitdata
LDRCC r2,[r0],#4 ;-->LDRCCr2,[r0]+ADDr0,r0,#4
STRCC r2,[r1],#4 ;-->STRCCr2,[r1]+ADDr1,r1,#4
BCC %B0
1
LDR r1,=|Image$$ZI$$Limit|;Topofzeroinitsegment
MOV r2,#0
2
CMP r3,r1 ;Zeroinit
STRCC r2,[r3],#4
BCC %B2
有没有哪位大哥有可以拷贝的也一起传一个来吧。
关于RO、RW、ZI的说明
本人的正在写的总结中的一段,希望对你有帮助
汇编:
AREAsectionname,CODE,READONLY Code
AREAsectionname,DATA,READWRITE RW数据区
AREAsectionname,DATA,READONLY RO数据区
C:
const常量带初值 RO数据区
全局变量带初值 ZW数据区
局部变量带初值 RO数据区
仅定义全局变量 ZI数据区
Release版本中,S3C44B0X一般RO区从0x00000000开始,RW区从0x0c000000开始,ZI区紧跟在RW区后。
有时没有RW区,ZI区就从0x0c000000开始。
这是简单的linktype。
2. |Image$$?
?
$$?
?
?
?
|符号的作用
S3C44B0X启动文件中有如下一段导入了这几个由ARM连接器产生的奇怪符号:
IMPORT |Image$$RO$$Limit| RO代码区末
IMPORT |Image$$RW$$Base| RW数据区头
IMPORT |Image$$ZI$$Base| ZI数据区头
IMPORT |Image$$ZI$$Limit| ZI数据区末
举例,某程序编译后Code区占2724(0x0aa4)字节,RW区40(0x28)字节,RO区9608字节,下面是将启动代码载入到FLASH中的结果(经处理后):
LDR r0,=|Image$$RO$$Limit| r0=#0x00000aa4
LDR r1,=|Image$$RW$$Base| r1=#0x0c000000
LDR r3,=|Image$$ZI$$Base| r3=#0x0c000028
CMP r0,r1
BEQ 0x44c
0x43c:
CMP r1,r3 搬FLASH中RW数据到RAM中
LDRCC r2,[r0],#4
STRCC r2,[r1],#4
BCC 0x43c
0x44c:
LDR r1,=|Image$$ZI$$Limit| r1=#0x0c0025b0
MOV r2,#0
0x454:
CMP r3,r1 ZI数据区清零
STRCC r2,[r3],#4
BCC 0x454
有所启发
经过一段时间的思考,已经初步了解了bootloader的机制:
RO如果是在SDRAM的话,(最好在RAMSTART那里方便计算)。
这样的话,所有的程序就最好在SDRAM空间运行了
因为所有的标号绝对定位的话,就在0x0c000000之后了。
下面说一下思路:
b ResetHandler; 因为是相对地址,所以也能跳到FLASH中的那个标号。
ResetHandler
防止有对PC绝对的赋值,最好一开始就开始拷贝
拷贝完毕之后,FLASH,和SDRAM里面的程序都有程序了,也不用再在FLASH里面混了,进行对pc绝对赋值:
copyend
ldrpc,=entersdram;(entersdram是绝对地址,这样赋值之后PC就指向
SDRAM 空间了。
上下衔接是比较连贯的。
移花接木,呵呵。
PC变了,但是对于CPU来说任务还是连贯的)
entersdram
然后进行其它的初始化、blmain什么的可以继续了。
一般而言,一个程序包括只读的代码段和可读写的数据段。
在ARM的集成开发环境中,只读的代码段和常量被称作RO段(ReadOnly);可读写的全局变量和静态变量被称作RW段(ReadWrite);RW段中要被初始化为零的变量被称为ZI段(ZeroInit)。
对于嵌入式系统而言,程序映象都是存储在Flash存储器等一些非易失性器件中的,而在运行时,程序中的RW段必须重新装载到可读写的RAM中。
这就涉及到程序的加载时域和运行时域。
简单来说,程序的加载时域就是指程序烧入Flash中的状态,运行时域是指程序执行时的状态。
对于比较简单的情况,可以在ADS集成开发环境的ARMLINKER选项中指定ROBASE和RWBASE,告知连接器RO和RW的连接基地址。
对于复杂情况,如RO段被分成几部分并映射到存储空间的多个地方时,需要创建一个称为“分布装载描述文件”的文本文件,通知连接器把程序的某一部分连接在存储器的某个地址空间。
需要指出的是,分布装载描述文件中的定义要按照系统重定向后的存储器分布情况进行。
在引导程序完成初始化的任务后,应该把主程序转移到RAM中去运行,以加快系统的运行速度。
什么是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模式中,robase对应的就是|Image$$RO$$Base|,rwbase对应的是|Image$$RW$$Base|,由于rw和zi相连,|Image$$ZI$$Base|就等于|Image$$ZI$$limit|.其它的值都是编译器自动计算出来的。
下面是2410启动代码的搬运部分,我给出注释
BaseOfROMDCD|Image$$RO$$Base|
TopOfROMDCD|Image$$RO$$Limit|
BaseOfBSSDCD|Image$$RW$$Base|
BaseOfZeroDCD|Image$$ZI$$Base|
EndOfBSSDCD|Image$$ZI$$Limit|
adrr0,ResetEntry; ResetEntry是复位运行时域的起始地址,在boot
nand中一般是0
ldrr2,BaseOfROM;
cmpr0,r2
ldreqr0,TopOfROM;TopOfROM=0x30001de0,代码段地址的结束
beqInitRam
ldrr3,TopOfROM
;part1,通过比较,将ro搬到sdram里,搬到的目的地址从|Image$$RO$$Base|开始,到|Image$$RO$$Limit|结束
0
ldmiar0!
{r4-r7}
stmiar2!
{r4-r7}
cmpr2,r3
bcc%B0;
;part2,搬rw段到sdram,目的地址从|Image$$RW$$Base|开始,到|Image$$ZI$$Base|结束
subr2,r2,r3;r2=0
subr0,r0,r2
InitRam;carryrwtobaseofBSS
ldrr2,BaseOfBSS;TopOfROM=0x30001de0,baseofrw
ldrr3,BaseOfZero;BaseOfZero=0x30001de0
0
cmpr2,r3
ldrccr1,[r0],#4
strccr1,[r2],#4
bcc%B0
;part3,将sdramzi初始化为0,地址从|Image$$ZI$$Base|到|Image$$ZI$$Limit|
movr0,#0;init0
ldrr3,EndOfBSS;EndOfBSS=30001e40
1
cmpr2,r3
strccr0,[r2],#4
bcc%B1
理解启动代码(ADS)
所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等.由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写.
具体到S64,启动代码分成两部分,一是与ARM7TDMI内核相关的部分,包括处理器各异常向量的配置,各处理器模式的堆栈设置,如有必要,复制向量到RAM,以便remap之后处理器正确处理异常,初始化数据(包括RW与ZI),最后跳转到Main.二是与处理器外部设备相关的部分,这和厂商的联系比较大.虽然都采用了ARM7TDMI的内核,但是不同的厂家整合了不同的片上外设,需要不同的初始化,其中比较重要的是初始化WDT,初始化各子系统时钟,有必要的话,进行remap.这一部分与一般控制器的初始化类似,因此,本文不作重点描述.
在进行分析之前,请确认如下相关概念:
S64片上FLASH起始于0x100000,共64kB,片上RAM起始于0x200000,共16kB.
S64复位之后,程序会从0开始执行,此时FLASH被映射到0地址,因此,S64可以取得指令并执行.显然,此时还是驻留在0x100000地址.如果使用remap命令,将会把RAM映射到0地址,同样的这时0地址的内容也只是RAM的镜像.
S64的FLASH可以保证在最差情况时以30MHz进行单周期访问,而RAM可以保证在最大速度时的单周期访问.
OK,以下开始分析启动代码.
一,处理器异常
S64将异常向量至于0地址开始的几个直接,这些是必需要处理的.由于复位向量位于0,也需要一条跳转指令.具体代码如下:
RESET
BSYSINIT;Reset
BUDFHANDLER;UNDEFINED
BSWIHANDLER;SWI
BPABTHANDLER;PREFETCHABORT
BDABTHANDLER;DATAABORT
B.;RESERVED
BVECTORED_IRQ_HANDLER
B.;ADDFIQCODEHERE
UDFHANDLER
B.
SWIHANDLER
B.
PABTHANDLER
B.
DABTHANDLER
B.
请注意,B指令经汇编后会替换为当前PC值加上一个修正值(+/-),所以这条指令是代码位置无关的,也就是不管这条指令是在0地址还是在0x100000执行,都能跳转到指定的位置,而LDRPC,=?
?
?
将向PC直接装载一个标号的值,请注意,标号在编译过后将被替换为一个与RO相对应的值,也就是说,这样的指令无论在哪里执行,都只会跳转到一个指定的位置.下面举一个具体的例子来说明两者的区别:
假定有如下程序:
RESET
BINIT或者LDRPC,=INIT
…
INIT
…
其中RESET为起始时的代码,也就是这条代码的偏移为0,设INIT的偏移量为offset.如果将这段程序按照RO=0x1000000编译,那么BINIT可理解为ADDPC,PC,#offset,而LDRPC,=INIT可被理解为MOVPC,#(RO+offset).显然当系统复位时,程序从0开始运行,而0地址有FLASH的副本,执行BINIT将把PC指向位于0地址处的镜像代码位置,也即INIT;如果执行LDRPC,=INIT将会将PC直接指向位于FLASH中的原始代码.因此以上两者都能正确运行.下面将RO设置为0x200000,编译后生成代码,还是得烧写到FLASH中,也就是还是0x100000,系统复位后从0地址执行,还是FLASH的副本,此时执行BINIT,将跳到副本中的INIT位置执行,此处有对应的代码;但是如果执行LDRPC,=INIT,将向PC加载0x200000+offset,这将使得PC跳到RAM中,而此时由于代码没有复制,RAM中的指定位置并没有代码,程序无法运行.
二,处理器模式
ARM的处理器可工作于多种模式,不同模式有不同的堆栈,以下设置各模式及其堆栈.
预定义一些参数:
MODUSREQU0x10
MODSYSEQU0x1F
MODSVCEQU0x13
MODABTEQU0x17
MODUDFEQU0x1B
MODIRQEQU0x12
MODFIQEQU0x11
IRQBITEQU0x80
FIQBITEQU0x40
RAMENDEQU0x00204000;S64:
16KBRAM
VECTSIZEEQU0x100;
UsrStkSzEQU8;sizeofUSRstack
SysStkSzEQU128;sizeofSYSstack
SvcStkSzEQU8;sizeofSVCstack
UdfStkSzEQU8;sizeofUDFstack
AbtStkSzEQU8;sizeofABTstack
IrqStkSzEQU128;sizeofIRQstack
FiqStkSzEQU16;sizeofFIQstack
修改这些值即可修改相应模式堆栈的尺寸.
以下为各模式代码:
SYSINIT
;
MRSR0,CPSR
BICR0,R0,#0x1F
MOVR2,#RAMEND
ORRR1,R0,#(MODSVC:
OR:
IRQBIT:
OR:
FIQBIT)
MSRcpsr_cxsf,R1;ENTERSVCMODE
MOVsp,R2
SUBR2,R2,#SvcStkSz
ORRR1,R0,#(MODFIQ:
OR:
IRQBIT:
OR:
FIQBIT)
MSRCPSR_cxsf,R1;ENTERFIQMODE
MOVsp,R2
SUBR2,R2,#FiqStkSz
ORRR1,R0,#(MODIRQ:
OR:
IRQBIT:
OR:
FIQBIT)
MSRCPSR_cxsf,R1;ENTERIRQMODE
MOVsp,R2
SUBR2,R2,#IrqStkSz
ORRR1,R0,#(MODUDF:
OR:
IRQBIT:
OR:
FIQBIT)
MSRCPSR_cxsf,R1;ENTERUDFMODE
MOVsp,R2
SUBR2,R2,#UdfStkSz
ORRR1,R0,#(MODABT:
OR:
IRQBIT:
OR:
FIQBIT)
MSRCPSR_cxsf,R1;ENTERABTMODE
MOVsp,R2
SUBR2,R2,#AbtStkSz
;ORRR1,R0,#(MODUSR:
OR:
IRQBIT:
OR:
FIQBIT)
;MSRCPSR_cxsf,R1;ENTERUSRMODE
;MOVsp,R2
;SUBR2,R2,#UsrStkSz
ORRR1,R0,#(MODSYS:
OR:
IRQBIT:
OR:
FIQBIT)
MSRCPSR_cxsf,R1;ENTERSYSMODE
MOVsp,R2;
三,初始化变量
编译完成之后,连接器会生成三个基本的段,分别是RO,RW,ZI,并会在image中顺序摆放.显然,RW,ZI在运行开始时并不位于指定的RW位置,因此必须初始化
LDRR0,=|Image$$RO$$Limit|
LDRR1,=|Image$$RW$$Base|
LDRR2,=|Image$$ZI$$Base|
1
CMPR1,R2
LDRLOR3,[R0],#4
STRLOR3,[R1],#4
BLO%B1
MOVR3,#0
LDRR1,=|Image$$ZI$$Limit|
2
CMPR2,R1
STRLOR3,[R2],#4
BLO%B2
四,复制异常向量
由于代码于RAM运行时,有明显的速度优势,而且变量可以动态配置,因此可以通过remap将RAM映射到0,使得出现异常时ARM从RAM中取得向量.
IMPORT|Image$$RO$$Base|
IMPORT|Image$$RO$$Limit|
IMPORT|Image$$RW$$Base|
IMPORT|Image$$RW$$Limit|
IMPORT|Image$$ZI$$Base|
IMPORT|Image$$ZI$$Limit|
COPY_VECT_TO_RAM
LDRR0,=|Image$$RO$$Base|
LDRR1,=SYSINIT
LDRR2,=0x200000;RAMSTART
0
CMPR0,R1
LDRLOR3,[R0],#4
STRLOR3,[R2],#4