ARM微处理器的编程模型之:异常中断处理
2.ARM编译器对中断处理函数编写的扩展
考虑到中断处理函数在现场保护和返回地址的处理上与普通函数的不同之处,不能直接把普通函数体连接到异常向量表上,需要在上面加上一层封装,下面是一个例子。
IRQ_Handler ;中断相应函数
STMFD SP!,{r0-r12,lr} ;保护现场,一般只需要保护{r0-r3,LR}
BL IrqHandler ;进入普通处理函数,C或汇编均可
……
LDMFD sp!,{r0-r12,LR} ;恢复现场
SUBS pc,lr,#4 ;中断返回,注意返回地址
为了方便使用高级语言直接编写异常处理函数,ARM编译器对此做了特定的扩展,可以使用函数声明关键字_irq,这样编译出来的函数就可以满足异常响应对现场保护和恢复的需要,并且自动加入LR减4的处理,符合IQR和FIQ中断处理的要求。
下面的例子显示了使用_irq对中断处理函数产生的影响。
C语言源程序如下。
__irq void IRQHandler (void)
{
volatile unsigned int *base = (unsigned int *) 0x80000000;
if (*base == 1)
{
/*调用C语言中断处理函数*/
C_int_handler();
}
/*清楚中断标志*/
*(base+1) = 0;
}
使用armcc编译出的汇编代码如下。
IRQHandler PROC
STMFD sp!,{r0-r4,r12,lr}
MOV r4,#0x80000000
LDR r0,[r4,#0]
SUB sp,sp,#4
CMP r0,#1
BLEQ C_int_handler
MOV r0,#0
STR r0,[r4,#4]
ADD sp,sp,#4
LDMFD sp!,{r0-r4,r12,lr}
SUBS pc,lr,#4
ENDP
如果不使用_irq子程序声明关键字,编译出的汇编代码如下。
IRQHandler PROC
STMFD sp!,{r4,lr}
MOV r4,#0x80000000
LDR r0,[r4,#0]
CMP r0,#1
BLEQ C_int_handler
MOV r0,#0
STR r0,[r4,#4]
LDMFD sp!,{r4,pc}
ENDP
3.可重入中断设计
在缺省情况下,ARM中断是不可重入的。因为一旦进入异常响应状态,ARM自动关闭中断使能。如果在异常处理过程中,简单地打开中断使能而发生中断嵌套时,显然新的异常处理将破坏原来的中断现场而导致出错。但有时需要中断必须是可重入的,因此要通过程序设计来解决这个问题。其中有两个关键问题。
① 新中断使能之前,必须要保护好前一个中断的现场信息。比如LR_irq和SPSR_irq等,这一点比较容易做的。
② 中断处理过程中对BL进行保护。
在中断处理函数中发生函数调用BL是很常见的,假设有下面一种情况。
IRQ_Handler:
……
BL Foo
ADD
其中,
Foo:
STMFD SP!,{r0-r3,LR}
……
LDMFD SP!{r0-r3,PC}
上述程序,在IRQ处理函数IRQ_Handler()中调用了函数Foo()。若是在IRQ_Handler()里面中断可重入的话,可能发生问题,考察下面的情况:当新的中断请求恰好在“BL Foo”指令执行完成后发生。这时候LR_irq寄存器(因在IRQ模式下,所以是LR_irq)的值将调整为BL指令的下一条指令(ADD)地址,使其能从Foo()正确返回;但是因为这时候发生了中断请求,接下来要进行新中断的响应,处理器在新中断响应过程中也要进行LR_irq保存。这次对LR_irq的操作发生了冲突,当新中断返回后,往下执行STMFD指令,这时候压栈的LR已不是原来的ADD指令地址,从而使子程序Foo()无法正确返回。
这个问题无法通过增加额外的现场保护指令来解决。一个办法就是在重新使能中断之前改变处理器模式,也就是使上面程序的“BL Foo”指令不要运行在IRQ模式下。这样当新的中断发生时,就不会造成LR寄存器的冲突。考虑ARM的所有运行模式,采用SYSTEM模式是比较合适的,因为它是特权模式,不是IRQ模式,与中断响应无关。
下面的例子显示了标准的IRQ/FIQ异常中断处理程序。
PRESERVE8
AREA INTERRUPT, CODE, READONLY
IMPORT C_irq_handler
IRQ
SUB lr, lr, #4 ;跳转返回地址
STMFD sp!, {lr} ;保存返回地址
MRS r14, SPSR ;读取SPSR
STMFD sp!, {r12, r14} ;保存寄存器
; 清除中断源
MSR CPSR_c, #0x1F ;切换到SYSTEM模式,
STMFD sp!, {r0-r3, lr} ;保存lr_USR 和其他使用到的寄存器
BL C_irq_handler ;跳转到C中断处理函数
LDMFD sp!, {r0-r3, lr} ;恢复用户模式寄存器
MSR CPSR_c, #0x92 ;切换回irq模式
LDMFD sp!, {r12, r14}
MSR SPSR_cf, r14
LDMFD sp!, {pc}^
END
评论