分类: 嵌入式
2015-09-15 13:19:16
原文地址:转载:Remap地址重映射 作者:arthur.hawkings
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
存储器地址重映射是当前很多先进控制器所具有的功能。在上一节中已经提
到了0 地址处存储器重映射的例子,简而言之,地址重映射就是可以通过软件配
置来改变一块存储器物理地址的一种机制或方法。
当一段程序对运行自己的存储器进行重映射的时候,需要特别注意保证程序
执行流程在重映射前后的承接关系。下面是一种典型的存储器地址重映射情况:
系统上电后的缺省状态是0 地址上放有ROM,这块ROM 有两个地址:从0
起始和从0x10000 起始,里面存储了初始化代码。当进行地址remap 以后,从0
起始的地址被定向到了RAM 上,ROM 则只保留有唯一的从0x10000 起始的地
址了。
如果存储在ROM 里的Reset_Handler 一直在0 – 0x4000 的地址上运行,则
当执行完remap 以后,下面的指令将从RAM 里预取,必然会导致程序执行流程
的中断。根据系统特点,可以用下面的办法来解决这个问题:
(1) 上电后系统从0 地址开始自动执行,设计跳转指令在remap 发生前使PC指针指向0x10000 开
始的ROM 地址中去,因为不同地址指向的是同一块ROM,所以程序能够顺利执行。
(2) 这时候0 - 0x4000 的地址空间空闲,不被程序引用,执行remap 后把RAM
引进。因为程序一直在0x10000 起始的ROM 空间里运行,remap 对运行流程没有任何影响。
(3) 通过在ROM 里运行的程序,对RAM 进行相应的代码和数据拷贝,完成应用程序运行的初始化。
下面是一段实现上述步骤的例程:
ENTRY
;启动时,从0 开始,设法跳转到“真”的ROM 地址(0x10000 开始的空间里)
LDR pc, =start
;insert vector table here
…
Start ;Begin of Reset_Handler
; 进行remap 设置
LDR r1, =Ctrl_reg ;假定控制remap 的寄存器
LDR r0, [r1]
ORR r0, r0, #Remap_bit ;假定对控制寄存器进行remap 设置
STR r0, [r1]
;接下去可以进行从ROM 到RAM 的代码和数据拷贝
除此之外,还有另外一种常见的remap 方式,如下图
原来RAM 和ROM 各有自己的地址,进行重映射以后RAM 和ROM 的地址
都发生了变化,这种情况下,可以采用以下的方案:
(1) 上电后,从0 地址的ROM 开始往下执行。
(2) 根据映射前的地址,对RAM 进行必要的代码和数据拷贝。
(3) 拷贝完成后,进行remap 操作。
(4) 因为RAM 在remap 前准备好了内容,使得PC 指针能继续在RAM 里取
到正确的指令。
不同的系统可能会有多种灵活的remap 方案,根据上面提到的两个例子,可以总结出最根本的考虑是:
要使程序指针在remap 以后能继续往下得到正确的指令。
实例分析:基于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
…
在如上的结构中,不管系统是否进行了地址的重映射,异常中断向量都可以在运行时动态改变,大大提高
了中断处理中的灵活性。中断向量可以在运行时指向不同的异常处理代码入口。