基于S3c4510b芯片的系统中的地址重映射的实现
地址重映射,说白了就是存储空间的重新分配,又被称为Remap。地址空间的重新分配,与处理器的硬件结构紧密相关。总体来说,32位系统中的地址重映射机制可以分为两类情况,一类是处理器内部有专门的寄存器可以完成Remap,这种只需将Remap寄存器的相应位置1,由硬件逻辑来完成地址的重新映射,如Atmel AT91xx系列,另一类则没有专门的Remap控制寄存器,需要重新改写处理器内部用于控制Memory起止地址的Bank寄存器来实现Remap过程。S3c4510b属于第二种情况。
S3c4510b内部有几个特殊寄存器,用于实现地址空间和芯片内外存储介质的映射。这几个寄存器的简介如下:
SYSCFG: 用于设置特殊寄存器的起始地址和片内SRAM的起始地址
EXTDBWTH: 用于设置各Bank寄存器所映射芯片的数据线宽度
ROMCON0--ROMCON5: 用于设置系统内片外扩展ROM和Flash的起始和截止地址
DRAMCON0--DRAMCON3: 用于设置系统内片外扩展RAM的起始和截止地址
S3c4510b芯片内特殊寄存器段的物理地址为0x3ff0000,各特殊寄存器的偏移地址详见S3c4510b的技术手册。
为便于对地址重映射的过程进行分析,下面给出了本人用于测试的基于S3c4510b的系统的硬件结构,本文中给出的所有流程及代码都经SDT 2.51下编译连接,并在此系统上通过测试。
此系统是以SAMSUNG公司给出的测试板为参考建立的,其中ROM的容量为512K,8位数据总线,Remap前的地址范围为:0x000000--0x100000,Remap后的地址范围为:0x1000000--0x1100000;RAM的容量为16M,32位数据总线,Remap前的地址范围为:0x100000--0x1100000,Remap后的地址范围为:0x0--0x1000000;Flash的容量为2M,16位数据总线,Remap前后地址不变:0x1100000—0x1300000。Remap前后的地址映射关系如下图所示:
(抱歉,图贴不上来)
系统的地址重映射必须在系统的启动中完成,以下是S3c4510b的Remap启动过程:
(1)系统特殊寄存器的设置:主要是配置如上所述的用于实现地址空间和芯片内外存储介质映射的寄存器,在本系统中配置如下:
SYSCFG=0x87ffff90
EXTDBWTH=0x3001
ROMCON0=0x01000060
ROMCON1=0x13044060
DRAMCON0=0x11004060
(2)初始化系统堆栈:在ARM7的体系结构中共有五种工作模式,不同的模式有不同的堆栈指针,互不干扰。各模式对应于不同的异常中断,至于哪些模式的堆栈需要初始化取决于用户使用了哪些中断,以及系统需要处理哪些异常类型。一般来说管理者(SVC)堆栈必须设置,如果使用了IRQ中断,则IRQ堆栈也必须设置。有一点需要注意的是,为保证Remap后程序运行正常,所有堆栈应设置在RAM的高端地址中。
(3)初始化IO口、UART、定时器、中断控制器以及系统中所用到的其它资源。在初始化异常向量表或修改异常向量表中的入口地址前,要关掉所有中断。
(4)异常向量表的初始化:将异常中断处理程序的入口地址写入RAM中相应的异常向量中。必须保证的是,异常向量表绝对不会被从ROM搬移到RAM中的代码和数据所覆盖,为此,异常向量表一般被定义在RAM中的高端地址中。
(5)程序代码及数据的搬移:Remap后,RAM被映射到0x0000的地址空间,ROM则被移到高端地址上,为保证Remap后程序能够正常运行,ROM中的代码和数据必须地址不变的被移到RAM中,这是Remap成功的关键。有两种途径可以实现搬移:
一种是直接将ROM地址空间整个的搬移到RAM中,不管实际的代码空间有多大。当然这种方法并不适合在真正的启动代码中使用,但在做初步的Remap测试时,可以用来检验堆栈及异常中断的设置是否合理。
另一种方法较复杂,它使用了SDT连接器ARMLink产生的定位信息,仅把ROM中的有效代码和数据段搬移到RAM中。ARMLink将编译后的程序连接成ELF文件,映像文件内部共有三种输出段:RO段、RW段和ZI段,这三种输出段分别包含了只读代码及包含在代码段中的少量数据,可读写的数据,初始化为0的数据。ARMLink同时还产生了这三种输出段的起始和截止定位信息:Image$$RO$$Base,Image$$RO$$Limit,Image$$RW$$Base,Image$$RW$$Limit,Image$$ZI$$Base,Image$$ZI$$Limit。可以在程序中使用这些定位信息,将ROM中的代码和数据搬移到RAM中,其实现代码如下:
数据定义:
BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|
源程序:
;将RW段中预初始化的变量搬移到RAM中
sub r1, r1, r2
sub r0, r0, r1 ;将r0指向RO段的结束--即RW段的开始
ldr r1, BaseOfBSS
ldr r2, BaseOfZero
add r1, r1, r3
add r2, r2, r3
1 ;基于局部标号的相对跳转,PC+偏移地址,产生与位置无关的代码
cmp r1, r2
ldrclearcase/" target="_blank" >cc r4, [r0], #4
strcc r4, [r1], #4
bcc %B1
;接着把ZI段搬移到RAM中,并将其初始化为0
mov r0, #0
ldr r2, EndOfBSS
add r2, r2, r3
2
cmp r1, r2
strcc r0, [r1], #4
bcc %B2
(6)地址的重新映射:S3c4510b中的Remap过程其实很简单,只需重新设置ROMCON0--ROMCON5和DRAMCON0--DRAMCON3,在本系统中只需重新设置ROMCON0和DRAMCON0。
源代码:
;====================================================
;/*内存控制寄存器重新设置--存储空间重新映射地址空间*/
;====================================================
EXPORT RemapMemory
RemapMemory
mov r12,r14
adr r0, RemapMem
ldmia r0,
ldr r0, =ROMCON0 ;ROMCON0为Bank寄存器的起始地址
stmia r0,
bl ExceptionTalbeInit ;中断向量表重新初始化
mov pc, r12
RemapMem
DCD &11040060 ;/*ROMCON0 0x1000000--0x1100000*/
...
DCD &10000398 ;/*DRAMCON0 0x0--0x1000000*/
...
(7)进入C代码空间,开始主程序的运行。此时代码应该运行于RAM中。
上面的步骤可以根据实际的需要进行适当的添加或删节。值得注意的是,汇编生成的代码应该是与位置无关的代码,即代码在运行期间可以被映射到不同的地址空间,其中的跳转指令都是基于PC寄存器的相对跳转指令。(基于PC的标号是位于目标指令前或者程序中数据定义伪操作前的标号,这种符号在汇编时将被处理成PC值加上或减去一个数字常量。)
在Remap的启动代码中,需要特别注意的是异常中断的处理。在S3c4510b中,异常中断的入口地址是固定的,按如下次序排列:
异常类型 工作模式 正常地址
复位 管理 0x00000000
未定义指令 未定义 0x00000004
软件中断(SWI) 管理 0x00000008
预取中止 中止 0x0000000C
数据中止 中止 0x00000010
预留 0x00000014
IRQ(中断) IRQ 0x00000018
FIQ(快速中断) FIQ 0x0000001C
地址重映射之后,入口地址被映射到RAM中,中断处理代码也被搬移到RAM地址空间,此时中断响应和中断处理的速度都将大大加快,这将有利于提高整个系统的实时性。异常中断向量表的设计结构如下:
(抱歉,图贴不上来)
各部分的源代码:(以IRQ异常中断为例)
异常向量表的定义:
_RAM_END_ADDR EQU 0x01000000 ;重映射后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
异常初始化代码:
...
b IRQ_SVC_HANDLER ;0x18
...
IRQ_SVC_HANDLER
SUB sp, sp, #4 ;满递减堆栈
STMFD sp!,
LDR r0, =IRQ_SVC_VECTOR ;读取中断向量,IRQ_SVC_VECTOR=SystemIrqHandler
LDR r0, [r0]
STR r0, [sp, #4]
LDMFD sp!, {r0, pc} ;跳转到异常中断处理代码入口
...
异常处理入口代码:
...
SystemIrqHandler
IMPORT ISR_IrqHandler
STMFD sp!, {r0-r12, lr}
BL ISR_IrqHandler ;跳转到C代码中异常中断处理程序ISR_IrqHandler
LDMFD sp!, {r0-r12, lr}
SUBS pc, lr, #4
...
在如上的结构中,不管系统是否进行了地址的重映射,异常中断向量都可以在运行时动态改变,这大大提高了中断处理中的灵活性,中断向量可以在运行时指向不同的异常处理代码入口。按照上面的异常处理结构,异常中断的响应流程如下:(以IRQ异常中断为例)
(1)接收到IRQ中断请求,执行完当前指令后,程序自动跳转到0x18处
(2)程序跳转到异常初始化代码中,完成满递减堆栈初始化及所用寄存器压栈(允许异常中断嵌套),读取中断向量IRQ_SVC_VECTOR,跳转到中断向量所指向的异常处理入口
(3)将通用寄存器和LR入栈,跳入异常中断处理程序ISR_IrqHandler
C语言中中断的安装:
S3c4510b中有21个中断源,各中断源可以选择异常响应的模式:快速中断模式FIQ或普通中断模式IRQ,以IRQ为例,中断处理程序的安装代码如下:
#define MAXHNDLRS 21;
...
void (*InterruptHandlers[MAXHNDLRS])(void);
//中断源向量表,保存21个中断的中断处理程序入口
static void DummyIsr(void) {}; //空操作函数,用于初始化中断源向量表
...
void SysSetInterrupt(REG32 vector, void (*handler)()) //中断安装函数
{
InterruptHandlers[vector] = handler;
}
void InitIntHandlerTable(void) //中断源初始化
{
REG32 i;
for (i = 0; i >2])(); // Call interrupt service routine
}