ARM微处理器的编程模型之:异常中断处理
有些情况下,系统0x0地址不一定是ROM。如果0x0地址为RAM,那么就系统将中断向量表从ROM复制RAM,下面的例子显示了这样一个过程。
MOV R8, #0
ADR R9, Vector_Init_Block
LDMIA R9!,{r0-r7} ;复制中断向量表 (8 words)
STMIA R8!,{r0-r7}
LDMIA R9!,{r0-r7} ;复制由伪操作 DCD定义的地址
STMIA R8!,{r0-r7}
注意 | 可以使用Scatter文件定义加载向量表的地址,这样上述代码的拷贝工作由C库函数完成。 |
2.使用C语言安装异常处理程序
程序中有时需要在main()函数中使用C语言安装中断向量表。这就要求指令经编译后的解码能安装在内存的正确位置。
(1)向量表中使用跳转指令的情况
如果在向量表中使用跳转指令,使用下面的步骤完成向量表的安装。
① 读取异常处理程序的地址。
② 从异常处理程序地址中减去向量表中的偏移。
③ 为适应指令流水线,将上一步得到的地址减8。
④ 将得到的结果右移2位,得到以字为单位的地址偏移量。
⑤ 将结果的高8位清零,得到跳转指令的24位偏移量。
⑥ 将上一步得到的结果和0xea000000(无条件跳转指令编码)做逻辑与操作,从而得到要写到向量表中的跳转指令的正确编码。
下面的例子显示了这样一个标准过程。
unsigned Install_Handler (unsigned routine, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((routine - (unsigned)vector - 0x8)>>2);
if ((vec 0xFF000000))
{
/* diagnose the fault */
prinf (Installation of Handler failed);
exit (1);
}
vec = 0xEA000000 | vec;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
(2)在向量表中使用加载PC指令
在向量表中使用加载PC指令,按照下面的步骤完成。
① 读取异常处理程序地址。
② 从异常处理程序地址中减去向量表中的偏移。
③ 为适应指令流水线,将上一步得到的地址减8。
④ 保留结果的后12位。
⑤ 将结果与0xe59ff000(LDR PC, [PC,#offset])做逻辑或操作,从而得到要写到向量表中的跳转指令的正确编码。
⑥ 将异常处理程序的地址放到相应的存储单元。
下面的例子显示了一个标准的C语言过程。
unsigned Install_Handler (unsigned location, unsigned *vector)
{ unsigned vec, oldvec;
vec = ((unsigned)location - (unsigned)vector - 0x8) | 0xe59ff000;
oldvec = *vector;
*vector = vec;
return (oldvec);
}
3.4.7 FIQ和IRQ中断处理函数的设计
1.中断分支
ARM内核只有两个外部中断输入信号nFIQ和nIRQ。但对于一个系统来说,中断源可能多达几十个。为此,在系统集成的时候,一般都会有一个异常控制器来处理异常信号,如图3.8所示。
图3.8 中断系统
这时候用户程序可能存在多个IRQ/FIQ的中断处理函数。为了使从向量表开始的跳转始终能找到正确的处理函数入口,需要设置一套处理机制和方法。
多数情况下是由软件来处理异常分支的,因为软件可以通过读取中断控制器来获得中断源的信息,如图3.9所示。
有些芯片可能支持特殊的硬件分支功能,这需要查看具体的芯片说明。
因为软件的灵活性,可以设计出比图3.9更好的流程控制方法,如图3.10所示。
Int_vector_table是用户自己开辟的一块存储器空间,里面按次序存放异常处理函数的地址。IRQ_Handler()从中断控制器获取中断源信息,然后再从Int_vector_table中的对应地址单元得到异常处理函数的入口地址,完成一次异常响应的跳转。这种方法的好处是用户程序在运行过程中,能够很方便地动态改变异常服务内容。
图3.9 软件控制中断分支
图3.10 灵活的软件分支设计
进入异常处理程序后,用户可以完全按照自己的意愿来进行程序设计,包括调用Thumb状态的函数等。但对于绝大多数的系统来说,有两个步骤必须处理,一是现场保护,二是要把中断控制器中对应的中断状态标识清除,表明该中断请求已经得到响应,否则,中断函数退出以后,又会被再一次触发,从而进入周而复始的死循环。
评论