Mini2440启动代码的编写Word文件下载.docx
- 文档编号:19244115
- 上传时间:2023-01-04
- 格式:DOCX
- 页数:21
- 大小:31.72KB
Mini2440启动代码的编写Word文件下载.docx
《Mini2440启动代码的编写Word文件下载.docx》由会员分享,可在线阅读,更多相关《Mini2440启动代码的编写Word文件下载.docx(21页珍藏版)》请在冰豆网上搜索。
启动方式可通过拨键开关S2来选择。
通过上述讲解我们简单了解了S3C2440的启动过程,现在就来讲述一下启动代码的编写过程。
启动代码是面向ARM处理器内核和硬件控制器的,所以它的绝大部分都是通过汇编语言实现的。
一.启动代码的功能与实现
启动代码主要是在主程序运行之前初始化系统硬件及软件的运行环境,它的主要功能包括以下几个方面:
■建立异常向量表
■初始化系统堆栈
■初始化硬件
■应用程序执行环境初始化
■跳转至主函数
下面我们来分别讲解一下:
1.建立异常向量表
异常向量表一般位于启动代码的开始部分,它是用户程序与启动代码之间以及启动代码的各部分之间联系的纽带。
它由一个一个的跳转函数组成,它就像一个普通的散转函数,只不过散转的过程中有硬件机制参与,当系统发生异常时,ARM处理器会通过硬件机制强制将PC指针指向异常向量表中对应的异常跳转函数存储的地址,然后程序会跳转到相应的异常中断服务程序去执行。
下面我们来逐步了解一下异常向量表。
ARM有7种异常,它们分别是:
复位异常,当开发板上电或复位时进入;
未定义指令异常,当遇到无法识别的指令时进入;
软中断异常,当发生软中断时进入;
预取中止异常,发生指令预取错误时进入;
数据中止异常,对数据访问不能完成时进入;
IRQ异常,当发生IRQ中断时进入;
FIQ异常,当发生FIQ中断时进入;
除了这7种异常之外,异常向量表中还有一个保留位置,所以建立异常向量表需要开辟一块大小为8×
4字节的空间,每个异常占据一个字(4个字节)的空间,这一个字的空间包含的是一个跳转指令,通过这条指令使PC指向相应异常处理函数的入口,具体的异常处理函数在别处实现。
异常向量表中指令、存储地址等对照表如表1所示:
表1
地址指令异常名称进入时处理器模式
0x00BReset复位异常管理模式
0x04BHandlerUndef未定义指令异常未定义模式
0x08BHandlerSWI软中断异常管理模式
0x0cBHandlerPabort指令预取异常中止模式
0x10BHandlerDabort数据中止异常中止模式
0x14B.保留——
0x18BHandlerIRQIRQ中断异常中断模式
0x1cBHandlerFIQFIQ中断异常快中断模式
异常向量表有关的代码如下:
CODE32//表明是ARM指令
AREAstartup,CODE,READONLY//AREA表明段,startup是段名,CODE表明是代码段,//READONLY是性质-只读
ENTRY//函数入口
BResetInit//复位异常
BHandlerUndef//未定义异常
BHandlerSWI//软中断异常
BHandlerPabort//取指中止异常
BHandlerDabort//数据中止异常
B.//保留
BHandlerIRQ//中断异常
BHandlerFIQ//快中断异常
ARM要求异常向量表必须存储在0地址处,这样当开发板上电或复位时,PC会指向0地址处,进而跳转到复位异常处理函数函数ResetInit,这个函数负责完成系统的初始化工作,即初始化堆栈、初始化硬件、初始化应用程序执行环境、跳转至主函数;
发生其他异常时ARM通过硬件机制将PC指向异常向量表对应的位置,进而跳转至相应的异常处理函数。
复位异常的处理函数完成的功能我们下面会讲解(2至5大点),在此不再赘述,我们来分别看一下其他异常处理函数是怎么处理的。
UID87327帖子42精华1积分96贡献点1382阅读权限30在线时间6小时注册时间2010-3-20最后登录2011-6-15查看详细资料TOP
1.1未定义异常处理函数
当ARM处理器不认识当前指令时,它就将该指令发送至协处理器,如果协处理器也不能识别此指令,就产生未定义异常。
发生未定义异常之后,CPU会将函数的返回地址及此时的CPSR保存至未定义异常专用的R14和SPSR,然后再修改CPSR使系统进入未定义异常模式运行,PC被强制指向0x00000004,然后程序就跳转至未定义异常处理函数。
上述过程都是通过硬件机制实现的,我们无需干预。
如果应用中没有特殊的要求,比如用未定义异常来仿真某些器件的功能(例如浮点运算),我们可以将此处理函数编写为一个简单的死循环。
程序的代码如下:
HandlerUndef
BHandlerUndef
1.2软中断异常处理函数
软件中断异常是由用户定义的中断指令,它可以用于用户模式下的程序调用特权模式下的操作指令;
在实时操作系统中可以通过该机制实现系统功能调用。
ARM处理器一共有7种模式,他们分别是用户模式、系统模式、管理模式、中止模式、未定义模式、中断模式和快速中断模式,七种模式之中除了用户模式以外其它六种被称为特权模式,特权模式下可以对CPSR进行修改,用户模式则只能读取CPSR的值而无法对其进行修改。
在应用中一般所有任务都是在用户模式下运行的,但是用户模式下无法修改CPSR,所以无法切换到其它模式。
在这种情况下我们可以通过SWI指令来解决这一问题,SWI指令可以强迫处理器从用户模式切换至管理模式,SWI是从用户模式切换到特权模式的唯一途径。
处理器响应SWI异常的流程如下:
发生SWI异常以后处理器自动将函数返回地址及CPSR保存至SWI模式专用的R14和SPSR,然后通过修改CPSR进入管理模式、禁止IRQ中断,最后强制PC指向0x00000008,此时程序会跳转至软中断异常处理函数。
在本文档涉及的启动代码中,软中断异常处理函数的作用是控制IRQ、FIQ中断的开启和关闭。
在初始化系统堆栈时,最后使系统运行在系统模式(见第2大点详述),并且IRQ和FIQ是默认关闭的,如果我们在程序中需要使用IRQ或FIQ,在使用之前需要先通过SWI将相应的中断开启。
下面来了解一下。
相关的代码段:
(1)
……
intMain(void)
{
//请在此进行中断初始化
IRQEnable();
//开启IRQ中断
//在此等待中断发生
}
(2)
//使能/禁能IRQ、FIQ中断
__swi(0x00)voidSwiHandle1(intHandle);
#defineIRQDisable()SwiHandle1(0)
#defineIRQEnable()SwiHandle1
(1)
#defineFIQDisable()SwiHandle1
(2)
#defineFIQEnable()SwiHandle1(3)
(3)
HandlerSWI
CMPR0,#4//R0保存的是软中断函数传递的参数,比其值和4,原因是预设了4个软中断,要根据实际需要设置
LDRLOPC,[PC,R0,LSL#2]//如果小于4则执行SwiFunction
MOVSPC,LR//如果大于等于4则返回
SwiFunction//软中断散转函数,根据软中断号来执行相应的处理函数
DCDIRQDisable//参数为0的处理函数
DCDIRQEnable//参数为1的处理函数
DCDFIQDisable//参数为2的处理函数
DCDFIQEnable//参数为3的处理函数
IRQDisable//关闭IRQ中断
MRSR0,SPSR//将SPSR的值(软中断前的CPSR)放入寄存器R0
ORRR0,R0,#IRQMSK//将此值修改,屏蔽IRQ中断
MSRSPSR_c,R0//将此值修改入SPSR,注意_c代表只能修改低8位
MOVSPC,LR//返回原来的程序
IRQEnable//使能IRQ中断
MRSR0,SPSR
BICR0,R0,#IRQMSK
MSRSPSR_c,R0
MOVSPC,LR
FIQDisable//关闭FIQ中断
ORRR0,R0,#FIQMSK
FIQEnable//使能FIQ中断
BICR0,R0,#FIQMSK
先来看一下代码段
(2),这一代码段位于启动代码中。
__swi是ADS编译器的关键字,用它做前缀可以声明一个软中断调用,格式为:
__swi(功能号)返回值类型名称(参数列表)
功能号:
软中断功能号,类似软中断指令中的立即数,即软中断号
名称:
即调用软中断时用于描述软中断的函数名称
参数:
软中断函数的参数,根据ATPCS规则,如果软中断函数有不超过4个参数时,通过寄存器R0~R3传递,超过4个参数时用堆栈来传递。
在代码段
(2)中是这样来编写的:
__swi(0x00)voidSwiHandle1(intHandle)。
其中0x00为软中断功能号;
软中断函数名称为SwiHandle1;
函数只有一个参数,则使用寄存器R0来传递;
函数没有返回值。
紧接着这句代码定义了4个宏,分别表示禁能IRQ函数、使能IRQ函数、禁能FIQ函数、使能IFQ函数,其实这四个宏调用的软中断函数都是一样的,只是参数不同而已。
如代码段
(1)所示,在用户程序中调用“IRQEnable();
”时,处理器会产生软中断,软中断函数的参数为1。
代码段(3),这一段也位于启动代码中,当发生软中断时,PC被强制指向0x00000008,这个地址中存放的是一条跳转到软中断异常处理函数的指令,所以程序会跳转至标号“HandlerSWI”处执行(即软中断异常处理函数)。
HandlerSWI函数的功能是判断寄存器R0的值(R0的值为软中断函数传递过来的参数)是否小于4,如果小于4则程序跳转至标号“SwiFunction”执行,如果不是则函数返回。
SwiFunction函数是一个散转函数,它的功能是根据寄存器R0的值跳转至对应的函数处执行,即如果参数为1,则函数会跳转至IRQEnable处执行,将IRQ中断使能。
代码段(3)的四个函数的流程都是一样的,先将SPSR(SPSR_svc)的值放入一个寄存器,SPSR的值为发生软中断以前的CPSR的值,发生软中断以后,处理器的模式切换为管理模式,CPSR的值的模式位会改变,不过在改变以前,原来模式的CPSR值会保存在当前模式的SPSR中。
SPSR的值放入寄存器以后再将寄存器的值进行修改,然后再写入SPSR,函数返回时SPSR的值会拷贝至CPSR。
此时读者可能注意到了我们使用的是SPSR_c,这是什么意思呢?
在使用MSR指令对PSR进行操作的时候,我们在PSR的后面加一些标志来对可修改的位进行限定,目的是避免对某些位进行操作时会影响其他位的值。
这些标志代表的意思如下:
PSR_c:
只会修改PSR[7:
0]位,控制位包括模式位、T位(状态位)和中断禁止位;
PSR_x:
只会修改PSR[15:
8]位,保留位;
PSR_s:
只会修改PSR[23:
16]位,保留位;
PSR_f:
只会修改PSR[31:
24]位,高4位为条件代码标志位,其余为保留位。
需要提醒两点:
只有异常模式才有SPSR寄存器,用户模式和系统模式是没有的。
对PSR的修改推荐使用“读------修改------写”的方式。
UID87327
1.3取指中止异常处理函数
ARM9采用的是5级流水线的哈佛结构,这5级流水线是:
取指、译码、执行、缓冲/数据、回写,如果处理器预取的指令的地址不存在,或者该地址不允许当前指令访问,则当该预取的指令被执行时就会产生指令预取中止异常。
取指异常的发生及处理过程如下:
指令预取时,如果发现目标地址非法,该指令会被标记,但该指令之前的指令继续执行,当执行至被标记的指令时,处理器就产生取指中止异常中断。
发生取指中止异常之后,处理器将函数返回地址(PC-4,即被标记的指令的地址+4)及CPSR保存至取指中止异常专用的R14和SPSR,然后通过修改CPSR将处理器切换至取指异常状态,最后将PC强制指向0x0000000C,这时程序会自动跳转至取指异常处理函数。
如果应用中不涉及MMU,取指异常处理函数可以设计成一个简单的死循环即可。
具体程序如下:
HandlerPabort
BHandlerPabort
1.4数据中止异常处理函数
ARM指令集和Thumb指令集中都有专门的数据处理指令,如果数据访问指令的目标地址不存在或者该地址不允许此指令访问,处理器就会产生数据中指指令。
发生数据中止异常后,处理器将函数的返回地址及CPSR保存至数据中止异常模式专用的R14和SPSR,然后通过修改CPSR的值进入数据访问中止模式,最后强制PC指向0x00000010,程序就会跳转至数据中止异常处理函数执行。
如果应用中不涉及MMU,数据中止异常的处理函数可设计为简单的死循环。
具体的程序如下:
HandlerDabort
BHandlerDabort
1.5IRQ中断异常处理函数
ARM内核为了处理外部设备向CPU发出的服务请求,特别是一些紧急事件,而特别设计了中断处理机制,当发生中断时,处理器暂停正在执行的程序,然后跳转至中断处理函数去执行,中断处理函数执行完毕后再返回至原来的程序跳转处继续向下执行。
当处理器的外部中断请求引脚被拉低,并且CPSR寄存器中的I位被清零时,处理器就会产生IRQ中断异常。
产生IRQ中断以后,ARM内核将函数返回地址保存至IRQ模式专用的R14,将CPSR保存至IRQ模式专用的SPSR,然后修改CPSR禁止新的IRQ产生,并将处理器模式切换至IRQ模式,最后强制PC指向0x00000018,程序跳转至IRQ中断处理函数执行。
在编写IRQ中断处理函数的时候我们需要注意中断的可重入性,即在有些IRQ的中断处理函数中,可以允许新的IRQ中断产生,但这种情况下需要注意,原先的中断处理程序使用的一些寄存器值会被新的中断给刷新,比如LR,所以还需要做一些准备工作来避免这种情况出现。
满足这些条件的中断处理函数叫做可重入的中断处理函数(中断嵌套)。
为了使程序简单化便于大家理解,我们在此介绍的是不可重入的中断。
IRQ的处理函数可参照下列代码段编写,它是在别的文件中用C语言来实现的:
void__irqIRQHandle(void)
void(*p)(void);
//定义一个函数指针
intirq_no;
//中断号
uint32intpnd;
//中断挂起号
intpnd=rINTPND;
//从中断挂起寄存器读取中断号
//将中断挂起号转换为中断号
for(irq_no=0;
irq_no<
32;
irq_no++)
{
intpnd=intpnd>
>
1;
if(intpnd==0)
break;
}
//利用函数指针取得中断服务程序的地址
p=(void(*)(void))VICVectAddr[irq_no];
p();
//运行中断服务程序
在汇编语言实现的启动代码中,需要在异常向量表之前引入这个函数,可以通过以下代码实现:
IMPORTIRQHandle
因为上述的IRQ处理函数不具有可重入性,所以我们可以使用关键字__irq来说明,使用这个关键字可以自动实现进入中断处理函数时一些关键寄存器的保存及函数处理完毕后的自动返回,程序编写者不用自己编码实现。
上述程序只是获得中断源以后激活相应的中断服务程序,具体的中断服务程序还需要程序员自己实现,可以参考以下例子来实现。
例如一个应用中使用了两个外部中断,一个是EINT0,另一个是EINT1,两个中断的处理函数分别为:
VoidIRQ_Eint0(void)
//中断处理的内容自己编写
VoidIRQ_Eint1(void)
编写完成中断处理函数以后还需要以下两个语句来使其和IRQ异常处理函数建立联系:
VICVectAddr[0]=(uint32)IRQ_Eint0;
VICVectAddr[1]=(uint32)IRQ_Eint1;
经过上述处理之后,发生外部中断以后系统就能正确反应了。
有几点需要注意:
ARM有32个外部IRQ中断源,S3C2440的外部中断8-23是占用一个IRQ源的,使用到这个范围内的多个外部中断时,需要再添加判断语句;
中断处理程序的最后需要将中断标志清掉,以免系统不断的响应。
具体的程序可以见附带的工程模板。
在一些对实时性要求严格的应用中,特别是使用操作系统时可能需要使用可重入的IRQ异常处理。
使用可重入的IRQ中断时,必须在中断服务程序中重新使能IRQ中断,因为内核进入IRQ中断时默认的是关闭IRQ中断。
还需要将“老”IRQ用到的一些寄存器的值保存至堆栈中,防止“新”的IRQ将其中的数据破坏。
可重入的IRQ中断请大家参阅有关资料,在此不再赘述。
1.6FIQ异常中断处理函数
在某些具体应用中,系统对实时性比较敏感,比如数据传输或通道处理时就需要非常短的中断响应时间,于是ARM在IRQ外还设计了一种更快速的中断类型:
FIQ。
ARM在硬件结构及资源分配上都给予了FIQ足够的支持,比如FIQ模式具有8个专用的寄存器,当系统从其他模式切换为FIQ模式时,这些寄存器的值无需进栈,节省了寄存器入栈的时间;
另外FIQ异常的入口处于异常向量表的最后,我们可以紧接着异常向量表就进行FIQ异常处理,无需跳转,这样也可以节省反应时间。
当处理器的外部快速中断引脚被拉低,并且CPSR寄存器中的F位被清零时,处理器就会产生FIQ中断异常。
FIQ的中断优先级最高,当系统进入FIQ中断异常后,其他的所有外部中断就会被屏蔽。
发生FIQ异常以后,ARM内核将函数返回地址及CPSR保存至FIQ异常模式专用的R14及SPSR中,然后通过修改CPSR切换至FIQ模式并禁止FIQ和IRQ,然后强制PC指向0x0000001C,程序就会跳转至FIQ异常处理函数。
FIQ异常处理函数可以参照以下函数段来编写:
HandlerFIQ
STMFDSP!
{R0-R3,LR}//保存返回地址
BLFIQ_Exception//FIQ中断处理
LDMFDSP!
{R0-R3,LR}//保存的寄存器出栈
SUBSPC,LR,#4//返回
其中“FIQ_Exception”是在别的文件中用C语言实现的FIQ处理函数,参照以下函数段编写:
voidFIQ_Exception(void)
while
(1);
//这一句替换为自己的代码
当然在汇编语言实现的启动代码中需要在HandlerFIQ前先引入这个函数,代码为:
IMPORTFIQ_Exception
在实际应用中不宜安排过多的FIQ中断,这样需要在FIQ异常处理函数中增加中断源判断,使反应
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Mini2440 启动 代码 编写