先概述下中断发生时的软硬件处理流程。
硬件:中断源->中断控制器筛选->CPU核保存当前工作状态,跳到对应异常向量表
软件:根据中断号,执行对应中断服务程序,执行完毕恢复各类寄存器值,并返回。
AREA INT, CODE, READONLY
IMPORT RST_DO
ENTRY
b RST_DO
b EXTENT_INSTRU
b SWI_DO
b ABORT_PREFETCH_DO
b ABORT_DATA_DO
mov R1, R1
b IRQ_DO
b FIQ_DO
刚开始看到这部分代码会觉得比较蒙,这些指令都是无条件跳转,我们怎么知道它的具体实现逻辑呢?这就需要看技术文档结合ARM核的硬件执行过程分析。
首先,这里的八行代码对应ARM七种异常(有一行 mov R1, R1 保留)。这就是ARM对应的中断向量表,每行占4个字节,从0x00000000开设存放各类异常的发生时执行的跳转指令。当有异常发生,ARM核自动完成的事件包括:1)异常模式下的LR保存程序当前地址PC;2)异常模式的SPSR保存当前状态寄存器CPSR;3)设置PC值跳到异常向量表执行对应的指令。*也就是说一旦异常发生如IRQ中断发生,硬件会自动执行上述b IRQ_DO*
中断控制器是中断源和CPU核之间的中介。当有事件发生,硬件会设置某个寄存器,CPU每执行完一条指令都会通过硬件查看这个寄存器,根据这个寄存器的情况CPU决定是否中断当前程序流程,跳到固定的地址去处理这个事件。中断源会有很多,而ARM核只有IRQ、FIQ两者,所以需要中断控制器暂存并筛选出某个中断源给CPU核。
IRQ(普通中断)可能有多个中断源到达,中断控制器要判断哪些中断源被屏蔽掉了,再从没被屏蔽掉的中断源中选出优先级最高的中断源。FIQ(快速中断)MODE=1时表示是快速中断。FIQ每次只有一个中断能够进来所以流程更为简单。
软件需要完成的工作包括1)保存下面程序可能用到的通用寄存器;2)跳到中断服务程序进行处理;3)处理完毕,恢复通用寄存器值,把SPSR恢复到CPSR,处理LR使它为异常返回的地址给PC。
IRQ_DO
stmfd sp!, {r0,r1}
ldr r0, =IRQ_R1
str r1, [r0]
ldmfd sp!, {r0}
ldr r1, =IRQ_R0
str r0, [r1]
add r13, r13, #4
sub r14, r14, #4
mov r0, r14
mrs r1, spsr
orr r1, r1, #0x80
msr cpsr_cxsf, r1
stmfd sp!, {r0}
stmfd sp!, {r14}
stmfd sp!, {r1}
dr r0, =IRQ_R1
ldr r1, [r0]
stmfd sp!, {r1}
ldr r1, =IRQ_R0
ldr r0, [r1]
stmfd sp!, {r0}
ldmfd sp!, {r0,r1} ;恢复原先保存的R0和R1
stmfd sp!, {r0-r12} ;将r0--r12全部压入中断以前模式下的堆栈
IMPORT int_vector_handler
bl int_vector_handler ;跳转到中断源判断和中断处理程序
ldmfd sp!, {r0-r12} ;恢复原先保存的R0-R12
ldmfd sp!, {r14}
msr cpsr_cxsf, r14
ldmfd sp!, {r14} ;将原先保存的SPSR_irq恢复到CPSR中
ldmfd sp!, {pc}
中断处理是个多级的流程,跳到上述的中断处理程序bl int_vector_handler 进行中断源判断进而选出到底执行哪个中断服务程序。
定义结构体:中断源号,和其对应的中断服务程序
typedef struct int_vector{
U8 IntNum;
void (*handler)(void);
}INT_VECTOR;
根据中断源号码选出对应的中断服务函数,并执行
void int_vector_handler(void)
{
U32 intnum;
U8 i = 0;
intnum = *(RP)(INTC_IFSR);
while (intnum != 1)
{
intnum = intnum >> 1;
i++;
}
(*vector[i].handler)(); //通过调用相应的处理函数
}