ARM remap与重定位摘抄

ARM 76浏览
 

(一)关于ARM处理器Remap的理解
0.什么是Remap
    我的理解是:在ROM从0x0用几句指令引导系统之后,把RAM映射到0x0就是Remap。
1.Remap的作用
    当ARM处理器上电或者Reset之后,处理器从0x0取指。因此,必须保证系统上电时,0x0处有指令可以执行。所以,上电的时候,0x0地址处必定是ROM或者Flash(NOR)。
    但是,为了加快启动的速度,也方便可以更改异常向量表,加快中断响应速度,往往把异常向量表映射到更快、更宽(32bit/16bit)的RAM中。但是异常向量表的开始地址是由ARM架构决定的,必须位于0x0处,因此,必须把RAM映射到0x0。
2.Remap的配置
    Remap的实现和ARM处理器的实现相关。
    1)如果处理器有专门的寄存器可以完成Remap。那么Remap是通过Remap寄存器的相应bit置1完成的。如Atmel AT91xx
    2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank的起始地址,那么只要把RAM的起始地址编程为0x0,也可以完成remap。如samsung s3c4510
    3)如果上面两种机制都没有,那么Remap就不要做了。因为处理器实现决定了SDRAM对应的bank地址是不能改变的。如Samsung S3c2410.
3.Remap配置前后要做的工作
    Remap前后,不同之处就是RAM的位置变了。为了达到Remap的目的,就是加快启动的速度和异常处理速度,一定要初始化异常堆栈和建立异常向量表的。
4.如果象2410那样不能Remap的话怎么办?
    2410不是不能Remap吗?为了加快启动速度,可以这样做
    1)使用它的NAND boot模式。为什么NAND boot会比较快,那是因为2410里面有块小石头——"SteppingStone",一块 4KB SRAM,它是映射在0x0的。启动程序会自动被copy到这个石头里面。自然异常向量的入口放到这个地方,一样可以达到比NOR boot快的启动、异常响应速度。
    2)如果你对NOR Boot情有独衷,那么你只好把你的异常向量的入口copy到SDRAM里面,实现所谓的High Vector

对重定位我的理解:

关于重定位,基于NOR BOOT的情况下,需计算出代码搬移后的偏移量,需要将全局变量地址加上这个偏移量,全局变量又分初始化的和未初始化的全局变量,RO,RW,BSS

(二)如何正确使用ads(axd) 
1、ads是由arm公司提供的编译和调试环境,非常好,不容质疑!搞arm开发的人员,不能不用ads,示例要开发一个驱动,首先是在ads中编译、调试通过,才会加入操作系统中,这是开发过程。 2、Ads中codewarria是编译环境,在这个环境中,应正确设置程序工作或调试的地址,这两个地址不一定同址,每次只能设置一种,除非它们相同。设置是通过菜单项edit->target setting完成的。编译环境中的这个地址是由链结器标记在程序上的(暂且这样说吧,因为这部分实际涉及的是编译原理中的链接定位问题,它主要涉及的是程序中的跳转地址,这是与软件相关的)。这两个地址都必须与硬件地址配合,程序才能正常工作。因为mcu(arm)是硬件寻址的,比如4510在正常初始化后,flash被定位在0x0地址,这时如果mcu有一条指令是跳转到0x0地址,那么mcu一定会将PC指向flash的0x0地址,mcu却是由软件控制的。如果这时remap了,flash被定位在0x1000000地址,但写在flash中的程序仍然控制mcu跳转到0x0地址,那么PC就指向了sdram的0x0地址,那里写着什么是你的事情了。 
3、工作地址是指编译好的程序(一般是*.bin文件)写入flash的地址,如软件程序工作地址从0x0开始,flash也应该定位在0x0地址开始,软件与硬件地址要相同,才能正常工作。当然,如果做remap,把程序拷贝到新的0x0地址处,也就是sdram中,程序也可以正常工作的。 
4、如果用axd调试,则是另外一种情况:程序必须写在sdram中,而不是flash中。Sdram的地址在那里呢?4510上电后,一般设置在0x1000000,而把flash设置在0x0地址。如果codewarria编译时把程序设置在0x0地址,调试时就会把软件加载在一个“虚”位置,这个位置(地址)是flash的地址,mcu是无法把程序写入flash中,程序就成为空中飘荡的幽灵,这时如果用反汇编,可以看到有些内容已经变质了,不是你的程序了,明白是为什么吗?而执行的话,mcu读取flash中的内容,它们并非你的程序,结果是跑飞了。解决办法是remap(对4510。如是44b0,请在codewarria编译环境中把程序定位在 sdram段,如需中断,请在flash中0x0地址先写入相关中断向量,并跳转到sdram中),把sdram设置在0x0地址,再加载程序。 5、Remap时,请用axd中的命令行方式,程序是实现不了的。即:system view->command line interface。只有先把硬件地址设置的与程序相同了、程序段可以被装入sdram中时,才可加载程序。 
6、切记这两点:调试程序一定要放在sdram中、sdram地址与程序地址一定要相同。 
7、0x8000这个地址是怎么来的呢?原来:0x0地址是中断向量地址,这要占用几十个字节,有些操作系统利用中断向量之后、0x8000地址之前的部分做点事情,Linux就是其一。所以,编程人员默认的把0x8000这个地址作为一般程序的开始地址。调试程序时,可以把程序起始地址定在0x8000,这个地址一定要是sdram覆盖的地址(我再强调一次吧),mcu执行时,仍然是从0x0开始的,是谁把PC挪到0x8000的,是ads在编译的时候,连接器加进的代码实现的。如果不是调试程序,是工作程序,要写入flash的,程序应该定位在0x0地址,而不是0x8000。 
8、有一本书“ARM应用开发系统祥解--S3C4510B”写的非常好,我读过有7、8遍了。但有一点是错的。书中写到:“针对本书所介绍的目标板,就可以使用这个默认地址值(0x8000)。”实际上,只要是4510的板子,不做remap,0x8000地址就是flash,是不能加载程序的。 
9、在axd中,注意菜单options->configure processor选项,如果不会用,就把它们都关了。带中断的程序和有跳转到0x0地址的程序执行不了,多是由这项引起的。其实它们非常有用,不提了。 
10、所有我以上提到的,均是有程序重定位和跳转情况的,象简单的跑马灯实验,不需要中断、跳转的,把程序直接定位在高位sdram中,remap也不用做,就可以跑的不错了。 
11、顺便提一下banyan的接口包非常好,支持ads,不用怀疑它。

(三)基于S3C4510B系统的启动流程及REMAP

1    S3C4510B简介

S3C4510B,基于以太网,16/32位RISC微处理器。芯片部集成了8KB的Cache/SRAM和Ethernet控制器,片外可扩展ROM、Flash、SDRAM等存储芯片。

S3C4510B芯片内部没有程序存储器,所有程序都被存储在片外扩展的ROM和Flash中。开始启动时,存有启动代码的ROM或Flash将被映射为0x00地址,系统从此开始运行。但在实际应用中,为提高系统的实时性,加快代码的执行速度,系统启动后程序往往要被搬移到RAM中,因为RAM的存取速度要比ROM快得多,这样大大提升系统的性能。由于S3C4510B芯片中的异常中断入口地址被固定在0x00开始的8个字中,系统只能将地址空间重新分配,把RAM映射到0x00地址处,这正是Remap的原因所在。

S3C4510B内部有几个特殊寄存器,用于实现地址空间和芯片内外存储介质的映射。这几个寄存器的简介如下:

SYSCFG:设置特殊寄存器的起始地址和片内SRAM的起始地址。

EXTDBWTH:设置各Bank寄存器所映射芯片的数据线宽度。

ROMCON0~ROMCON5:设置系统内片扩展ROM和Flash的起始和终止地址。

DRAMCON0~DRAMCON3:设置系统内片外扩展RAM的起始和终止地址。

S3C4510B芯片内特殊寄存器段的物理地址为0x3ff0000,各特殊寄存器的偏移地址详见S3C4510B的技术手册。

2S3C4510B系统中Remap的实现

地址空间的重新分配,与处理器的硬件结构紧密相关。总体来说,32位系统中的地址重映射机制可以分为两种情况:一类是处理器内部专门的寄存器可以完成Remap,这样只需将Remap寄存器的相应位置1,由硬件逻辑来完成地址的重新映射,如AtmelAT91xx系列;另一类没有专门的Remap控制寄存器,需要重新改写处理器内部用于控制Memory起止地址的Bank寄存器,来实现Remap过程。S3C4510B属于第二种情况。

2.1硬件系统结构及地址分配

假设系统是以Samsung公司给出的测试板为参考建立的,其中ROM的容量为512KB,8位数据总线,Remap前的地址范围为0x0000000~0x0100000,Remap后的地址范围为0x1000000~0x1100000;RAM的容量为16MB,32位数据总线,Remap前的地址范围为0x0100000~0x100000,Remap后的地址范围为0x0000000~0x1000000;Flash的容量为2MB,16位数据总线,Remap前后地址不变,均为0x1100000~0x1300000。Remap前后的地址映射关系如图2所示。

2.2系统启动过程及Remap实现

系统的地址重映射应该在系统的启动中完成,以下是S3C4510B的Remap启动过程。

①系统特殊寄存器的设置。主要是配置如上所述的用于实现地址空间和芯片内外存储介质映射的寄存器,在本系统中配置如下:

SYSCFG=0x87ffff90

EXTDBWTH=0x3001

ROMCON0=0x01000060

ROMCON1=0x13044060

DRAMCON0=0x11004060

②初始化系统堆栈。在ARM7的体系结构中共有七种工作模式,不同的模式有不同堆栈指针,互不干扰。各模式对应于不同异常中断,至于哪些模式的堆栈需要初始化取决于用户使用了哪些中断,以及系统需要处理些异常类型。一般来说,管理者(SVC)堆栈必须设置,如果使用了IRQ中断,则IRQ堆栈也必须设置。有一点需要注意的是,为保证Remap后程序运行正常,所有堆栈应设置在RAM的高端地址中。

③初始化I/O口、UART、定时器、中断控制器以及系统中所用到的其它资源。在初始化异常向量表或修改异常向量表中的入口地址前,要关掉所有中断。

   ④异常向量表的初始化。将民常中煌怛处理程序的入口地址写入RAM中相应的异常向量。必须保证的是,异常向量表绝对不会被从ROM搬移到RAM中的代码和数据所覆盖,为此,异常向量表一般被定义在RAM中的高端地址中。

⑤程序代码及数据的搬移。Remap后,RAM被映射到0x0000的地址空间,ROM则被移到高端地址上。为保证Remap后程序能够瞠运行,ROM中的代码和数据必须地址不变地被移到RAM中。这是Remap成功的关键。两种途径可以实现搬移。

一种是不管实际的代码空间有多大,直接将ROM地址空间整个搬移到RAM中。当然,这种方法并不适合在真正的启动代码中使用,但在做初步的Remap测试时,可以用来检验堆栈及异常中断的设置是否合理。

另一种方法较复杂,它使用了SDT链接器ARMLink产生的定位信息,仅把RO风吹草动的有效代码和数据段到RAM中。ARMLink将编译后的程序链接成ELF文件。映像文件内部共有三种输出段:RO段、RW段和ZI段。这三种输出段分别包含了只读代码及包含在代码段中的少量数据、可读写的数据、初始化为0的数据,ARMLink同时还产生了这三种输出段的起始和终止定位信息:Image$$RO$$Base、Image$$RO$$Limit、Image$$RW$$Base、Image$$Limit、Image$$Linit和Image$$ZI$$Limit。可以在程序中使用这些定位信息。将ROM中的代码和数据搬移到RAM中,其实现代码如下:

数据定义:

BaseOfROMDCD|Image$$RO$$Base|

TopOfROMDCD|Image$$RO$$Limit|

BaseOfBSSDCD|Image$$RW$$Base|

BaseOfZeroDCD|Image$$ZI$$Base|

EndOfBSSDCD|Image$$ZI$$Limit|

源程序:

;将ROM中的程序搬移到RAM中,重映射后的地址不变

adrr0,ResetEntry;ROM中程序起始地址

movr3,#(RamBaseAddr<<16);RamBaseAddr=0x100

Idrr1,BaseOfROM

Idrr2,TopOfROM

Addr1,r1,r3

Addr2,r2,r3

0

Idmiar0!,{r4-r11}

Stmiar1!,{r4-r11}

Cmpr1,r2

Bcc%B0

;将RW段中预初始化的变量搬移到RAM中

subr1,r1,r2

subr0,r0,r1;将r0指向RO段的结束,即RW段的开始

ldrr1,BaseOfBSS

Idrr2,BaseOfZero

Addr1,r1,3

Addr2,r2,r3

1;基于局部标号的相对跳转,PC+偏移地址,产生与位置无关的代码

cmpr1,r2

ldrccr4,[r0],#4

strccr4,[r1],#4

bcc%B1

;接着把ZI段搬移到RAM中,并其将初始化为0

movr0,#0

Idrr2,EndOfBSS

Addr2,r2,r3

2

cmpr1,2

strccr0,[r1],#4

bcc%B2

⑥地址的重新映射。S3C4510B中的Remap过程其实很简单,只需重新设置ROMCON0~ROMCON5和DRAMCON0~DRAMCON3。在本系统中只需重新设置ROMCON0和DRAMCON0。

源代码:

;/*内存控制寄存器重新设置-存储空间重新映射地址空间*/

EXPORTRemapMemory

RemapMemory

movr12,r14

adrr0,RemapMem

ldmiar0,{r1-r11}

ldrr0,=ROMCON0;ROMCON0为Bank寄存器的起始地址

stmiar0,{r1-r11}

blExceptionTalbeInit;中断向量表重新初始化

movpc,r12

RemapMem

DCD&11040060;/*ROMCON00x1000000~0x1100000*/

DCD&10000398;/*DRACON00x0~0x1000000*/

⑦进入C代码空间,开始主程序的运行。此时代码应该运行于RAM中。

   上面的步骤可以根据实际需要进行适当的添加或删节。值得注意的是:汇编生成的代码应该是与位置无关的代码,即代码在运行期间可以被映射到不同的地址空间,其中的跳转指令都是基于PC寄存器的相对跳转指令。基于PC的标号是位于目标指令前或者程序中数据定义伪操作前的标号,这种符号在汇编时将被处理成PC值加上或减去一个数字常量。

3异常中断的处理

在Remap的启动代码中,需要特别注意的是异常中断的处理。在S3C4510B中,异常中断的入口地址是固定的,按表1次序排列。

表1 

异常类型 工作模式 正常地址 复位 管理 0x00000000 未定义指令 未定义 0x00000004 软件中断(SWI) 管理 0x00000008 预取中止 中止 0x0000000 数据中止 中止 0x00000010 预留 - 0x00000014 IRQ(中断) IRQ 0x00000018 FIQ(快速中断) FIQ 0x0000001 地址重新映射之后,入口地址被映射到RAM中,中断处理代码也被搬移到RAM地址空是。此时,中断响应和中断处理的速度都将大大加快,这将有利于提高整个系统的实时性。异常中断向量表的设计结构如图3所示。

下面是各部分的源代码(以IRQ异常中断为例)。

异常向量表的定义:(系统初始化时,将异常处理代码入口地址写入异常中的向量表)

_RAM_END_ADDREQU0x01000000;重映射后RAM的终止地址

MAP(_RAM_END_ADDR-0x100)

SYS_RST_VECTOR#4

UDF_INS_VECTOR#4

SWI_SVC_VECTOR#4

INS_ABT_VECTOR#4

DAT_ABT_VECTOR#4

RESERVED_VECTOR#4

IRQ_SVC_VECTOR#4

FIQ_SVC_VECTOR#4

异常初始化代码:

bIRQ_SVC_HANDLER;0x18

IRQ_SVC_HANDLER

SUBsp,sp,#4;满递减堆栈

STMFDsp!,{r0}

LDRr0,=IRQ_SVC_VECTOR;读取中断向量,

;IRQ_SVC_VECTOR=SystemrqHandle

LDRr0,[r0]

STRr0,[sp,#4]

LDMFDsp!,{r0,pc};跳转到异常中断处理代码入口

异常处理入口代码:

SystemIrqHandler

IMPORTISR_IrqHandler

STMFDsp!,{r0-r12,lr}

BLISR_IrqHandler;跳转到C代码中异常中断处理程序ISR_IrqHandler

LDMFDsp!,{r0-r12,lr}

SUBSpc,lr,#4

在如上的结构中,不管系统是否进行了地址的重映射,异常中断向量都可以在运行时动态改变,大大提高了中断处理中的灵活性。中断向量可以在运行时指向不同的异常处理代码入口。