Coder
分类: 嵌入式
2010-06-14 08:22:38
中断处理
OK,接下来,终于可以来研究中断处理了,也就是,我们辛辛苦苦添加进系统的中断处理例程被调用的整个过程。
不过,在分析源代码之前,还是让我们先了解一些原理性的东西, 我们都知道在处理中断时要保存现场,然后才能处理中断,处理完之后还要把现场状态恢复过来才能返回到被中断的地方继续执行,这里要说明的是在指令跳转到中断向量的地方开始执行之前,由CPU自动帮我们做了哪些事情:
R14_irq = 要执行的下条指令地址 + 4 //这里的下条指令是相对于被中断指令的下条。即返回地址。
SPSR_irq
= CPSR //保存的现场状态,r0到r12要由我们软件来保存(如果需要的话)。
CPSR[4:0]
= 0b10010 //进入中断模式
CPSR[5]
= 0 //在ARM模式下执行(不是Thumb下)
CPSR[7]
= 1 //关掉IRQ中断, FIQ还是开着
PC
= 0Xffff0018 //根据异常向量表的位置,跳转到特定的中断向量处去执行。
然后,我们还是要回到异常向量表去看一下。每当中断控制器发出产生一个中断请求,则CPU总是跑到异常向量表的中断向量处取指令来执行。将中断向量中的宏解开,则就像下面这个样子:
/*
* Interrupt dispatcher
*/
.macro vector_stub,
name, mode, correction=0
.align 5
vector_irq:
sub lr, lr, #4
//修正返回地址,也就是中断处理完之后要执行的指令的地址
@ Save r0, lr_
@ (parent CPSR)
stmia sp, {r0, lr} @
save r0, lr
// 保存返回地址,因为很快要使用r0寄存器,所以也要保存r0。
mrs lr, spsr
str lr, [sp, #8] @
save spsr
// 向上增长的栈。
// 此时的这个栈是中断模式下的栈,ARM下中断模式下和系统模式下的
// 栈是不同的。虽然ARM提供了七个模式,但Linux只使用了两个,一
// 个是用户模式,另一个为系统模式,所以这个栈只是一个临时性的栈。
@ Prepare for SVC32 mode. IRQs remain disabled.
mrs r0, cpsr
eor r0, r0, #(IRQ_MODE ^ SVC_MODE |
PSR_ISETSTATE)
msr spsr_cxsf, r0
//把spsr设置为管理模式
@
@ the branch table must immediately follow
this code
@
and lr, lr, #0x
mov r0, sp
// 将栈指针保存在r0中,传递为后面的中断处理过程
ldr lr, [pc, lr, lsl #2]
// 而PC寄存器是保存当前正在取值的地址,也就是当前正在执行的指令之// 后的第二条指令的地址,而不是紧接着当前指令的第一条指令的地址
// 以spsr的低4位为索引,以PC值为基地址来获得相应的中断处理
// 程序的地址
movs pc, lr @ branch to handler in SVC mode
// movs 的目的对象如果是pc的话,则还会把spsr赋值给cpsr,上面我
// 们看到spsr被设成管理模式,因此这条语句过后的代码也就跑在了管
// 理模式下。
// 可以看到该汇编代码主要是把被中断的代码在执行过程中的状态(cpsr), // 返回地址(lr)等保存在中断模式下的栈里,然后进 入到管理模式下去执// 行中断,同时令r0 = sp,这样可以在管理模式下找到该地址,进而获取// spsr等信息。该汇编代码最终根据被中断的代码所处的模式跳转到相应// 的处理程序中去。
// 注意管理模式下的栈和中断模式下的栈不是同一个。同时由于在 上面的// 代码中栈指针(sp)没有进行移位,因此即使后面的代码没对这个栈进行出// 栈操作,也不会因为不断的产生中断而导致栈溢出。
.long __irq_usr @ 0
(USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26
/ FIQ_32)
.long __irq_invalid @ 2
(IRQ_26 / IRQ_32)
.long __irq_svc @ 3
(SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
前面有提到过,这是一段很巧妙的位置无关的代码,它将中断产生时,CPSR的模式位的值作为相对于PC值的索引来调用相应的中断处理程序。如果在进入终中断时是用户模式,则调用__irq_usr例程,如果为系统模式,则调用__irq_svc,如果是其他模式,则说明出错了,则调用__irq_invalid。接下来我们分别瞧一下这些个中断处理程序。