在嵌入式系统中实现从RAM快速引导技术
扫描二维码
随时随地手机看文章
前言
嵌入式系统是以各种嵌入式微处理器为内核,运行RTOS的面向应用的计算机控制系统,也是SOC技术的一个重要分支。ARM 是一个IP(知识产权)公司,以arm体系结构为基础的各种RISC 微处理器针对不同应用领域提供了不同的指令集(ARM、THUMB、DSP、XSCALE)可以为各种实时应用提供灵活的选择。
在研制开发基于ARM处理器的嵌入式系统过程中,如何让系统正常快速的启动是一个关键环节,本文主要分析讨论嵌入式系统启动的问题。相关代码以ArmStd2.51IDE环境为参考。
几个相关的概念
Arm/Thumb状态
Arm,Thumb分别是ARM处理器的32/16bits的指令集,对应处理器的两种执行状态。
异常(Exception)
由内/外部源引起的需要处理器干预的一个事件,每种异常模式有自己的特殊功能寄存器,堆栈。处理异常需要保护处理器的当前状态,以便在异常处理后可以恢复执行。当异常发生时,系统强制从固定的地址执行程序, 如表1 所示。
无论在Arm/Thumb状态进入异常,处理程序都是在Arm状态下执行,PC->R14和CPSR->SPSR保存PC和处理器状态,返回时CPSR->SPSR,R14->PC。
中断处理
ARM提供了两种中断源IRQ,FIQ,发生中断时,会进入相应的IRQ,FIQ异常模式,然后异常处理程序会识别不同的中断,调用相应的中断服务程序。所以中断只是异常的一个子集。未用中断通常指向一个哑函数。
在嵌入式系统设计中应正确的辨析异常和中断。
系统启动(start-up)
嵌入式系统的应用程序通常都是固化在ROM中运行。通常用汇编语言编写启动程序完成系统硬件和软件运行环境的初始化。启动程序与应用程序一起固化在ROM中。系统在上电和复位会跳到复位异常向量入口地址处。
在目标文件中,代码、数据放在不同的段中。源文件编译链接生成含.data、.text段的目标文件,且链接器生成的.data段是以系统RAM为参考地址,故在系统启动时需要拷贝ROM中的.data段到RAM,以完成对RAM的初始化。拷贝从.text结束位置开始,一般以2kbytes对齐取到下一个2kbytes,确定data的初始位置。这样,就定位.data 、.text段在链接文件中所确定的链接位置。
CPU对ROM或Flash ROM访问速度慢,在一定程度上降低了系统的性能。当ROM在地址0x0时,ARM内核使用ROM 0x0 到0x1c作为异常向量区,那么当异常发生的时候,CPU访问ROM区的入口。我们可以在RAM建立异常向量表镜像,这样可以提高系统的性能(镜像建立)。最优的方法,就是让系统RAM配置在0x0,把初始化程序放在RAM中运行(RAM启动),建立异常向量表的自己对应关系。
为了实现异常的快速处理:
1、 在图2,虚线框中表示的是当RAM在0x0时的情况,这是一种直接对应的关系。直接在向量入口处放置sys_**_handler处理程序。
2、 当ROM在0x0时需要建立了一种镜像的关系。地址指针表示对应的数据存储单元的物理地址,**_hander表示对应的处理程序在ROM的入口。 Handle**是物理的存储单元地址,里面放置了处理程序的入口指针。异常发生时经过**_handler---handler**--- sys_**_handler的过程。Handler**定义在RAM中。
启动过程分析
设置异常向量
ARM7要求中断向量表必须设置在从0地址开始,连续8×4字节的空间,具体分配如表1。如果ROM定位于0地址,向量表包含一系列指令跳转到中断服务程序,否则应使用一串位置无关代码(PIC)处理,使用直接加载PC指针的指令。可以在启动程序中添加一段代码,使其在运行时将这段PIC指令拷贝到对应地址开始的存储器空间。
这段代码建立了ROM中的异常入口地址和RAM中的处理代码的镜像关系。这种处理需要在RAM中手动的建立异常向量表,从RAM启动时不需要。(以FIQ的处理为例)
FIQ_Handler /* 从这里开始进入FIQ异常模式,*/
SUB sp, sp, #4
STMFD sp!, {r0}/*装入并减一个字,使用r0,应该首先压入堆栈*/
LDR r0, =HandleFiq /*从这里开始进入在RAM中的异常处理程序*/
LDR r0, [r0]
STR r0, [sp, #4]
LDMFD sp!, {r0, pc} /*返回退出FIQ异常处理模式*/
异常处理程序
Sys_Fiq_Handler
IMPORT ISR_FiqHandler /*进入异常处理,保存寄存器,每种模式有自己的分组寄存器(banked registers)*/
STMFD sp!, {r0-r7, lr} /*发生*/
BL ISR_FiqHandler /*进入异常处理程序*/
LDMFD sp!, {r0-r7, lr}
SUBS pc, lr, #4 /*恢复寄存器,退出异常处理模式*/
对比具体的讨论从ROM/RAM启动的实现代码
(说明【1】从RAM启动 【2】从ROM启动)
AREA Init, CODE, READONLY //初始化代码
ENTRY /*设置入口指针*/
/*启动程序首先必须定义入口指针,而且整个应用程序只有一个入口指针
*/
IF :DEF: ROM_AT_ADDRESS_ZERO
B Reset_Handler B Reset_Handler
B **_Handler B sys_**_Handler
建立镜像关系【2】 直接跳转【1】。
ELSE
/*如果不是从ROM在0x0启动,那么必须把直接加载指令拷贝到0x0位置,这是必须使用ldr完成*/
MOV R8, #0
/*ADR伪指令把PC相关的地址装入寄存器*/
ADR R9, Vector_Init_Block
/* 块加载存储指令,IA = 加载后继增*/
LDMIA R9!, {R0-R7}
STMIA R8!, {R0-R7}
LDMIA R9!, {R0-R7}
STMIA R8!, {R0-R7}
把这些指令放在0x0的位置,实现跳转。直接加载相应的处理程序的地址到PC指针。
Vector_Init_Block //如果不是从rom在0x0启动,这里是一组直接加载PC的指令
LDR PC, Reset_Addr /*在执行拷贝过程建立了异常处理,继续执行Reset_Handler*/
。。。。。。。。。。。。。。。。。。
LDR PC, **_Addr
/*定义 地址指针Reset_Addr 其值为reset_Handler*/
Reset_Addr DCD Reset_Handler
。。。。。。。。。。。。。。。。。。。。。。。。
**_addr DCD **_handler
ENDIF
AREA Main, CODE, READONLY //配置存储器,为运行程序作准备。[!--empirenews.page--]
从这里进入reset异常处理模式
EXPORT Reset_Handler
Reset_Handler ;/* 复位入口点,关闭所有中断 */
LDR r1, =IntMask
LDR r0, =0xFFFFFFFF
STR r0, [r1]
INITIALIZE_STACK /*初始化堆栈*/
。。。。。。。。。。。。。。。。。。。。。。。。。。。
LDR sp, =SUP_STACK ; 改变CPSR,进入SVC模式
SYNC_DRAM_CONFIGURATION 配置RAM空间
LDR r0, =0x3FF0000
LDR r1, =0x83FFFF90 ; 赋值 = 0x83FFFF91
STR r1, [r0] ; 特殊功能寄存器Start_addr = 0x3FF00000
;ROM 和 RAM空间配置
;ADRL r0, SysInitDataSDRAM【1】
LDR r0, =SysInitDataSDRAM【2】
LDMIA r0, {r1-r12}
LDR r0, =0x3FF0000 + 0x3010 ; ROMCntr Offset : 0x3010
STMIA r0, {r1-r12}
在RAM中建立异常向量表的镜像入口。【2】
EXCEPTION_VECTOR_TABLE_SETUP
LDR r0, =HandleReset ; 分配的异常向量表在存储区的位置.
LDR r1, =ExceptionHandlerTable ; 异常向量表
MOV r2, #8; 向量数
ExceptLoop /*建立过程*/
LDR r3, [r1], #4
STR r3, [r0], #4
SUBS r2, r2, #1;
BNE ExceptLoop
把代码从ROM拷贝到RAM【1】
ROM2SDRAM_COPY_START
LDR r0, =|Image$$RO$$Base| ;
指向 ROM 数据的指针
LDR r1, =|Image$$RO$$Limit| ;
LDR r2, =DRAM_BASE ;
RAM区的基地址
SUB r1, r1, r0 ; [r1] 循环计数
ADD r1, r1, #4 ; [r1]
ROM2SDRAM_COPY_LOOP
LDR r3, [r0], #4
STR r3, [r2], #4
SUBS r1, r1, #4 ; 减计数
BNE ROM2SDRAM_COPY_LOOP
改变ROM ,RAM的基地址
ADRL r0, SysInitDataSDRAM_S
/*装载新的地址表,重新配置ROM和RAM*/
LDMIA r0, {r1-r12}
LDR r0, =0x3FF0000 + 0x3010 ;
ROMCntr 偏移地址值 : 0x3010
STMIA r0, {r1-r12}
异常模式下堆栈的初始化
系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。一般来说管理者堆栈必须设置,如果使用了IRQ中断,则IRQ堆栈也必须设置。
初始化C语言所需的存储器空间:拷贝初始化数据
改变到用户模式并设置用户堆栈
MRS r0, cpsr
BIC r0, r0, #LOCKOUT | MODE_MASK
ORR r1, r0, #USR_MODE
MSR cpsr_cf, r0
LDR sp, =USR_STACK
呼叫C程序
; 进入C程序 IMPORT C_Entry
BL C_Entry
AREA ROMDATA, DATA, READONLY 在ROM中定义的常量
SysInitDataSDRAM 特殊功寄存器常量的定义的入口地址
SysInitDataSDRAM_S
/* 用于在ROM启动时建立异常向量表镜像的地址定义,存放的是异常发生时跳转的地址,是异常处理程序的入口,这个表的位置可以自己分配。
异常向量表【2】
^ DRAM_BASE
HandleReset # 4
HandleUndef # 4
HandleSwi # 4
HandlePrefetch # 4
HandleAbort # 4
HandleReserv # 4
HandleIrq # 4
HandleFiq # 4
结语
在嵌入式系统设计开发的过程中,对基本原理的深刻理解有利于设计优化。本文详细辨析了嵌入式设计在系统启动时一些概念,最后在上述分析的基础上给出了实现从RAM快速启动的具体步骤。