中断体系结构.docx
《中断体系结构.docx》由会员分享,可在线阅读,更多相关《中断体系结构.docx(15页珍藏版)》请在冰豆网上搜索。
![中断体系结构.docx](https://file1.bdocx.com/fileroot1/2023-1/22/c64ab366-98be-4639-9d02-df53112646b0/c64ab366-98be-4639-9d02-df53112646b01.gif)
中断体系结构
中断体系结构
ARM体系CPU有以下7种工作模式。
.用户模式(usr):
ARM处理器正常的程序执行状态。
.快速中断模式(fiq):
用于高速数据传输或通道处理。
.中断模式(irq):
用于通用的中断处理。
.管理模式(svc):
操作系统使用的保护模式。
.数据访问终止模式(abt):
当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
.系统模式(sys):
运行具有特权的操作系统任务。
.未定义指令中止模式(und):
当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。
可以通过软件来进行模式切换,或者发生各类中断、异常时CPU自动进入相应的模式。
除用户模式外,其它6种工作模式都属于特权模式。
大多数程序运行于用户模式,进入特权模式是为了处理中断、异常,或者访问被保护的系统资源。
ARM920T有31个通用的32位寄存器和6个程序状态寄存器。
这37个寄存器分为7组,进入某个工作模式时就使用它那组的寄存器。
有些寄存器,不同的工作模式下有自己的副本,当切换到另一个工作模式时,那个工作模式的寄存器副本将被使用:
这些寄存器被称为备份寄存器。
在ARM状态下,每种工作模式都有16个通用寄存器和1个(或2个,这取决于工作模式)程序状态寄存器。
图中R0~R15可以直接访问,这些寄存器中除R15外都是通用寄存器,即它们既可以用于保存数据也可以用于保存地址。
另外,R13~R15稍有些特殊。
R13又被称为栈指针寄存器,通常被用于保存栈指针。
R14又被称为程序连接寄存器或连接寄存器,当执行BL子程序调用指令时,R14得到R15(程序计数器PC)的备份。
而当发生中断或异常时,对应的R14_svc、R14_irq、R14_fiq、R14_abt或R14_und中保存R15返回值。
快速中断模式有7个备份寄存器R8_fiq~R14_fiq,这使得进入快速中断模式执行很大部分程序时,甚至不需要保存任何寄存器(只要它们不改变R0~R7)。
每种工作模式除R0~R15共16个寄存器外,还有第17个寄存器CPSR,即“当前程序状态寄存器”。
CPSR中一些位被用于标识各种状态,一些位被用于标识当前处于什么工作模式。
除CPSR外,还有快速中断模式、中断模式、管理模式、数据访问终止模式和未定义指令中止模式等5种工作模式和一个寄存器——SPSR,即“程序状态保存寄存器”。
当切换进入这些工作模式时,在SPSR中保存前一个工作模式的CPSR值,这样,当返回前一个工作模式时,可以将SPSR的值恢复到CPSR中。
综上所述,当一个异常发生时,将切换进入相应的工作模式(为表述方便,下文中将它称为异常模式),这时ARM920TCPU核将自动完成如下事情。
a.在异常工作模式的连接寄存器R14中保存前一个工作模式的下一条,即将执行的指令的地址。
对于ARM状态,这个值是当前PC值加4或加8.
b.将CPSR的值复制到异常模式的SPSR。
c.将CPSR的工作模式位设为这个异常对应的工作模式。
d.令PC值等于这个异常模式在异常向量表中的地址,即跳转去执行异常向量表中的相应指令。
相反地,从异常工作模式退回到之前的工作模式时,需要通过软件完成如下事情。
a.前面进入异常工作模式时,连接寄存器中保存了前一个工作模式的一个指令地址,将它减去一个适当的值后赋值给PC寄存器。
b.将SPSR的值复制回CPSR。
ARM处理器对异常的响应过程可用伪代码描述如下:
R14_=returnlink
SPSR_=CPSR
CPSR[4:
0]=exceptionmodenumber
CPSR[5]=0/*当运行于ARM状态时*/
if==ResetorFIQthen
CPSR[6]=1/*禁止新的FIQ中断*/
CPSR[7]=1/*禁止新的IRQ中断*/
PC=exceptionvectoraddress
注意:
异常发生时异常模式R14的定义是PC-4。
IRQ、FIQ和ABT(指令预取)异常中断处理程序的返回:
发生IRQ或者FIQ异常中断时,指令已经执行完毕,PC指向当前指令后面的第3条指令。
因此IRQ或者FIQ的异常中断发生时,处理器将程序计数器的计算值(PC-4)保存到LR_IRQ或者LR_FIQ寄存器中。
这时LR_IRQ或者LR_FIQ寄存器的值指向当前指令后的第2条指令。
在指令预取时如果目标地址是非法的,该指令将被标记成有问题的指令,处理器产生指令预取ABT异常。
此刻PC的值还没有更新,它指向当前指令后的第2条指令。
指令预取ABT异常中断发生时,处理器将程序计数器的计算值(PC-4)保存到异常模式LR_ABT。
这时LR_ABT寄存器的值指向当前指令后的第1条指令。
当IRQ、FIQ和ABT(指令预取)异常中断处理程序退出时,前两种情况下应该执行断点的下一条指令,后一种情况下程序应该返回到有问题的指令处(即断点指令),重新读取并执行。
无论上述三种情况的那一种,返回操作都应该通过SUBSPC,LR,#4指令实现。
该指令将寄存器LR中的值复制到程序计数器PC中,实现程序返回,同时将SPSR_SVC或者SPSR_UND寄存器的内容复制到CPSR中。
不论何种CPU,中断的处理过程是相似的。
(1)中断控制器汇集各类外设发出的中断信号,然后告诉CPU。
(2)CPU保存当前程序的运行环境(各个寄存器等),调用中断服务程序(ISR)来处理这些中断。
(3)在ISR中通过读取中断控制器、外设的相关寄存器来识别这是哪个中断,并进行相应的处理。
(4)清除中断:
通过读写中断控制器和外设的相关寄存器来实现。
(5)最后恢复被中断程序的运行环境(即上面保存的各个寄存器等),继续执行。
SUBSRCPND和SRCPND寄存器表明有哪些中断被触发了,正在等待处理;SUBMASK和MASK用于屏蔽某些中断。
“Requestsource(withoutsub-register)”中的中断源被触发之后,SRCPND寄存器中相应位被置1,如果此中断没有被INTMASK寄存器屏蔽或者快速中断的话,它将被进一步处理。
“Requestsource(withsub-register)”中的中断源被触发之后,SUBSRCPND寄存器中的相应位被置1,如果此中断没有被INTSUBMASK寄存器屏蔽的话,它在SRCPND寄存器中的相应位也被置1,之后的处理过程就和“Requestsource(withoutsub-register)”一样了。
在SRCPND寄存器中,被触发的中断的相应位被置1,等待处理。
如果被触发的中断有快速中断(FIQ)——MODE(INTMODE寄存器,FIQ只能分配一个,即INTMOD中只能有一位设为1)中为1的位对应的中断是FIQ,则CPU进入快速中断模式(FIQMode)进行处理。
对于一般中断IRQ,可能同时有几个中断被触发,未被INTMASK寄存器屏蔽的中断经过比较后,选出优先级最高的中断,此中断在INTPND寄存器中的相应位被置1,然后CPU进入中断模式(IRQMode)进行处理。
中断服务程序可以通过读取INTPND寄存器或者INTOFFSET寄存器来确定中断源。
使用中断的步骤如下:
(1)设置好中断模式和快速中断模式下的栈:
当发生中断IRQ时,CPU进入中断模式,这时使用中断模式下的栈;当发生快速中断FIQ时,CPU进入快速中断模式,这时使用快速中断模式下的栈。
InitStacks:
mrsr0,cpsr
bicr0,r0,#MODEMASK
orrr1,r0,#UNDEFMODE|NOINT
msrcpsr_c,r1@UndefMode
ldrsp,=UndefStack@UndefStack=0x33FF_5C00
orrr1,r0,#ABORTMODE|NOINT
msrcpsr_c,r1@AbortMode
ldrsp,=AbortStack@AbortStack=0x33FF_6000
orrr1,r0,#IRQMODE|NOINT
msrcpsr_c,r1@IRQMode
ldrsp,=IRQStack@IRQStack=0x33FF_7000
orrr1,r0,#FIQMODE|NOINT
msrcpsr_c,r1@FIQMode
ldrsp,=FIQStack@FIQStack=0x33FF_8000
bicr0,r0,#MODEMASK|NOINT
orrr1,r0,#SVCMODE
msrcpsr_c,r1@SVCMode
ldrsp,=SVCStack@SVCStack=0x33FF_5800
(2)准备好中断处理函数。
a.异常向量表中设置好当进入中断模式或快速中断模式时的跳转函数,它们的异常向量地址分别为0x00000018、0x0000001c。
bReset
@0x04:
未定义指令中止模式的向量地址
bHandlerUndef
@0x08:
管理模式的向量地址,通过SWI指令进入此模式
bHandlerSWI
@0x0c:
指令预取终止导致的异常的向量地址
bHandlerPrefetchAbort
@0x10:
数据访问终止导致的异常的向量地址
bHandlerDataAbort
@0x14:
保留
bHandlerNotUsed
@0x18:
中断模式的向量地址
bHandlerIRQ
@0x1c:
快中断模式的向量地址
bHandlerFIQ
跳转到具体的异常处理函数:
HandlerFIQ:
HANDLERHandleFIQ
HandlerIRQ:
HANDLERHandleIRQ
HandlerUndef:
HANDLERHandleUndef
HandlerSWI:
HANDLERHandleSWI
HandlerDataAbort:
HANDLERHandleDabort
HandlerPrefetchAbort:
HANDLERHandlePabort
HandlerNotUsed:
b.
HANDLER是一个宏定义,它将跳转到具体的函数进行执行。
.macroHANDLERHandleLabel
subsp,sp,#4
stmfdsp!
{r0}
ldrr0,=\HandleLabel
ldrr0,[r0]
strr0,[sp,#4]
ldmfdsp!
{r0,pc}
.endm
b.中断服务程序(ISR)。
IRQ、FIQ的跳转函数,最终将调用具体中断的服务函数。
对于IRQ,读取INTPND寄存器或INTOFFSET寄存器的值来确定中断源,然后分别处理。
IsrIRQ:
sublr,lr,#4@计算返回地址
stmfdsp!
{r0-r12,lr}@保存使用到的寄存器
subsp,sp,#4@保留pc寄存器的值
stmfdsp!
{r8-r9}@把r8r9按入堆栈
ldrlr,=int_return@设置调用ISR即EINT_Handle函数后的返回地址
ldrr9,=INTOFFSET@把中断偏移INTOFFSET的地址装入r9里面
ldrr9,[r9]@取出INTOFFSET单元里面的值给r9
ldrr8,=HandleEINT0@向量表的入口地址赋给r8
addr8,r8,r9,lsl#2@求出具体中断向量的地址
ldrr8,[r8]@中断向量里面存储的中断服务程序的入口地址赋给r8
strr8,[sp,#8]@按入堆栈
ldmfdsp!
{r8-r9,pc}@堆栈弹出,跳转到相应的中断服务程序
int_return:
ldmfdsp!
{r0-r12,pc}^@中断返回,^表示将spsr的值复制到cpsr
对于FIQ,因为只有一个中断可以设为FIQ,无须判断中断源。
c.清除中断:
如果不清除中断,则CPU会误以为中断又一次发生了。
可以在调用ISR之前清除中断,也可以在调用ISR之后清除中断,这取决于在ISR执行过程中,这个中断是否可能继续发生、是否能够丢弃。
如果在ISR执行过程中,这个中断可能发生并不能丢弃,则在调用ISR之前清除中断,这样在ISR执行过程中发生的中断能够被各寄存器再次记录并通知CPU;如果在ISR执行过程中,这个中断不会发生或者可以丢弃,则在调用ISR之后清除中断。
清除中断时,从源头开始:
首先,需要的话,操作具体外设清除中断信号;其次,清除SUBSRCPND(用到的话),SRCPND寄存器中相应位(往相应位写1即可);最后,清除INTPND寄存器中的相应位(往相应位写1即可),最简单的方法就是“INTPND=INTPND”。
(3)进入、退出中断模式或快速中断模式时,需要保存、恢复被中断程序的运行环境。
a.对于IRQ,进入和退出的代码如下:
sublr,lr,#4//计算返回地址
stmdbsp!
{r0-r12,lr}//保存使用到的寄存器
……//中断处理
ldmiasp!
{r0-r12,pc}^//中断返回,^表示将spsr的值赋给cpsr
b.对于FIQ,进入和退出的代码如下:
sublr,lr,#4//计算返回地址
stmdbsp!
{r0-r7,lr}//保存使用到的寄存器
……//中断处理
ldmiasp!
{r0-r7,pc}^//中断返回,^表示将spsr的值赋给cpsr
(4)根据具体中断,设置相关外设。
(5)对于“Requestsources(withsub-register)”中的中断,将INTSUBMASK寄存器中相应位设为0。
(6)确定使用此中断的方式:
FIQ或IRQ。
如果是FIQ,则在INTMODE寄存器中设置相应位为1.如果是IRQ,则在PRIORITY寄存器中设置优先级。
(7)如果是IRQ,将INTMASK寄存器中相应位设为0(FIQ不受INTMASK寄存器控制)。
(8)设置CPSR寄存器中的I-bit(对于IRQ)或F-bit(对于FIQ)为0,使能IRQ或FIQ。
以下是一个按键的中断测试程序。
主要做以下几件事:
初始化中断,中断函数注册,开中断,如果中断测试完毕则关掉相应的中断。
基于bootloader的设计,在写中断的时候,只需要将中断函数的地址写到我们规定的地址即可。
关于bootloader的设计,后续再作介绍。
#defineGPB5_out(1<<(5*2))//LED1
#defineGPB6_out(1<<(6*2))//LED2
#defineGPB7_out(1<<(7*2))//LED3
#defineGPB8_out(1<<(8*2))//LED4
#defineGPF4_eint(2<<(4*2))//K2,EINT4
#defineGPF2_eint(2<<(2*2))//K3,EINT2
#defineGPF1_eint(2<<(1*2))//K1,EINT1
#defineGPF0_eint(2<<(0*2))//K4,EINT0
voidinit_led(void)
{
GPBCON=GPB5_out|GPB6_out|GPB7_out|GPB8_out;
GPBDAT=0xffff;
}
/*
*初始化GPIO引脚为外部中断
*GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)
*/
voidinit_irq(void)
{
GPFCON=GPF0_eint|GPF1_eint|GPF2_eint|GPF4_eint;
EINTMASK&=(~(1<<4));
PRIORITY=(PRIORITY&((~0x01)|(0x3<<7)))|(0x0<<7);
INTMSK&=(~(1<<0))&(~(1<<1))&(~(1<<2))&(~(1<<4));
}
voidEINT_Handle()
{
unsignedlongoft=INTOFFSET;
switch(oft)
{
//K4被按下
case0:
{
GPBDAT|=(0x0f<<5);//所有LED熄灭
GPBDAT&=~(1<<8);//LED4点亮
break;
}
case1:
{
GPBDAT|=(0x0f<<5);//所有LED熄灭
GPBDAT&=~(1<<5);//LED3点亮
break;
}
//K3被按下
case2:
{
GPBDAT|=(0x0f<<5);//所有LED熄灭
GPBDAT&=~(1<<7);//LED3点亮
break;
}
//K1或K2被按下
case4:
{
GPBDAT|=(0x0f<<5);//所有LED熄灭
GPBDAT&=~(1<<6);//K2被按下,LED2点亮
break;
}
default:
break;
}
//清中断
if(oft==4)
EINTPEND=(1<<4);
SRCPND=1<INTPND=1<}
voidKeyScan_Test(void)
{
charc;
init_led();//led初始化
init_irq();
printf("\nKeyScanTest,pressESCkeytoexit!
\n");
ClearPending(BIT_EINT0|BIT_EINT1|BIT_EINT2|BIT_EINT4_7);
pISR_EINT0=pISR_EINT1=pISR_EINT2=pISR_EINT4_7=(int)EINT_Handle;//注册中断函数
EnableIrq(BIT_EINT0|BIT_EINT1|BIT_EINT2|BIT_EINT4_7);
//while
(1);
while((c=get_c())!
=0x1b);
DisableIrq(BIT_EINT0|BIT_EINT1|BIT_EINT2|BIT_EINT4_7);
}
对于pISR_EINT0、pISR_EINT1、pISR_EINT2、pISR_EINT4_7等相关的二级中断地址我们已经有了定义。
#define_ISR_STARTADDRESS0x31ffff00
//Exceptionvector
#definepISR_RESET(*(unsigned*)(_ISR_STARTADDRESS+0x0))
#definepISR_UNDEF(*(unsigned*)(_ISR_STARTADDRESS+0x4))
#definepISR_SWI(*(unsigned*)(_ISR_STARTADDRESS+0x8))
#definepISR_PABORT(*(unsigned*)(_ISR_STARTADDRESS+0xc))
#definepISR_DABORT(*(unsigned*)(_ISR_STARTADDRESS+0x10))
#definepISR_RESERVED(*(unsigned*)(_ISR_STARTADDRESS+0x14))
#definepISR_IRQ(*(unsigned*)(_ISR_STARTADDRESS+0x18))
#definepISR_FIQ(*(unsigned*)(_ISR_STARTADDRESS+0x1c))
//Interruptvector
#definepISR_EINT0(*(unsigned*)(_ISR_STARTADDRESS+0x20))
#definepISR_EINT1(*(unsigned*)(_ISR_STARTADDRESS+0x24))
#definepISR_EINT2(*(unsigned*)(_ISR_STARTADDRESS+0x28))
#definepISR_EINT3(*(unsigned*)(_ISR_STARTADDRESS+0x2c))
#definepISR_EINT4_7(*(unsigned*)(_ISR_STARTADDRESS+0x30))
#definepISR_EINT8_23(*(unsigned*)(_ISR_STARTADDRESS+0x34))
#definepISR_CAM(*(unsigned*)(_ISR_STARTADDRESS+0x38))
#definepISR_BAT_FLT(*(unsigned*)(_ISR_STARTADDRESS+0x3c))
#definepISR_TICK(*(unsigned*)(_ISR_STARTADDRESS+0x40))
#definepISR_WDT_AC97(*(unsigned*)(_ISR_STARTADDRESS+0x44))
#definepISR_TIMER0(*(unsigned*)(_ISR_STARTADDRESS+0x48))
#definepISR_TIMER1(*(unsigned*)(_ISR_STARTADDRESS+0x4c))
#definepISR_TIMER2(*(unsigned*)(_ISR_STARTADDRESS+0x50))
#definepISR_TIMER3(*(unsigned*)(_ISR_STARTADDRESS+0x54))
#definepISR_TIMER4(*(unsigned*)(_ISR_STARTADDRESS+0x58))
#definepISR_UART2(*(unsigned*)(_ISR_STARTADDRESS+0x5c))
#definepISR_LCD(*(unsigned*)(_ISR_STARTADDRESS+0x60))
#definepISR_DMA0(*(unsigned*)(_ISR_STARTADDRESS+0x64))
#definepISR_DMA1(*(u