地址重映射,说白了就是存储空间的重新分配,又被称为 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 ldrcc 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, {r1-r11} ldr r0, =ROMCON0 ;ROMCON0 为 Bank 寄存器的起始地址 stmia r0, {r1-r11} 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!, {r0} 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 }
|