Chinaunix首页 | 论坛 | 博客
  • 博客访问: 553079
  • 博文数量: 99
  • 博客积分: 4010
  • 博客等级: 上校
  • 技术积分: 1117
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-23 15:17
文章分类

全部博文(99)

文章存档

2011年(4)

2010年(13)

2009年(82)

我的朋友

分类: LINUX

2009-09-22 19:50:24

15:08 2009-9-22
今天分析了一把4020的IRQ进入和退出过程,发现不少东东,按照常理进IRQ的过程是硬件完成的,而出中断的过程则需要根据具体过程进行加减步骤了。

正常的中断进入过程是:
(1)拷贝CPSR到SPSR_
(2)设置正确的CPSR位,arm自动硬件完成下面三项工作:切换到ARM状态,切换到异常模式,禁止中断。
(3)保存返回地址在LR_
(4)设置PC到异常向量地址

以上这四个操作是硬件完成的,另外我发现当模式换到IRQ模式下,此时的堆栈指针sp已经在中断栈指针(提前指定好的)上加8了,为啥呢??
而我们在真正操作中还需要将其他寄存器入栈,这个过程是我们自己写的。
(5)将r0-r12压栈
这样实现的是不能中断嵌套的过程,如果想实现中断嵌套,还需要将r14,spsr,也压栈。

出中断时,我们自己需要做以下操作:
(1)r0-r12出栈
(2)从SPSR_恢复CPSR
(3)从LR_恢复PC值SUBS pc, lr, #4

一般(2)(3)过程是一下完成的,用  LDMFD sp!,{pc} ^   即可完成,用它代码实现是比较麻烦的。

原来在SEP4020的中断处理代码如下,可以看出是相当麻烦的。
IRQ_DO     
 
 stmfd sp!, {r0,r1}          

 ldr r0, =IRQ_R1         
 str r1, [r0]
 
 ldmfd sp!, {r0}
 ldr r1, =IRQ_R0          
 str r0, [r1]                ;保存R0和R1寄存器(因为这两个寄存器再后面要用到)
 
 add r13, r13, #4  ;恢复中断栈针
 
 sub r14, r14, #4             ;使r14指向中断返回的下条语句
 mov r0, r14                 ;LR_irq(R14)减4并保存在R0
 
 mrs r1, spsr
 orr r1, r1, #0x80           ;将SPSR_irq的中断屏蔽位置‘1’(屏蔽中断),并保存再R1 中
 msr cpsr_cxsf, r1  ;将模式切换到中断前的模式,即这个时候就又回到了SVC模式了,但此时是不允许中断的。
;---------------现在又进入SVC模式了------------------------------------------------------------------------------ 
 bic r1, r1, #0x80  ;将原先保存的SPSR_irq的R1的中断屏蔽位清零(允许中断)
 
 stmfd sp!, {r0}
 stmfd sp!, {r14}
 stmfd sp!, {r1}               ;依次将R0,R14,R1的值压入中断前模式下的堆栈(当前R0,R14,R1中存放的分别是LR_irq-4,中断前模式下的LR,SPSR_irq)
 
 ldr 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全部压入中断以前模式即SVC模式下的堆栈

;-----------------------------     
 IMPORT int_vector_handler
 bl int_vector_handler      ;跳转到中断源判断和中断处理程序

;-----------------中断返回------------          ;restore the register
 ldmfd sp!, {r0-r12}           ;恢复原先保存的R0-R12
 ldmfd sp!, {r14}
 msr cpsr_cxsf, r14          ;恢复SVC下的cpsr,并且允许中断
 ldmfd sp!, {r14}              ;将原先保存的SPSR_irq恢复到CPSR中
 ldmfd sp!, {pc}


我后来自己改的一个流程,这个是中断允许嵌套的做法
 IRQ_DO     
 
 stmfd sp!, {r0,r1}          

 ldr r0, =IRQ_R1      ;加载    常数IRQ_R1的地址到r0
 str r1, [r0]
 
 ldmfd sp!, {r0}
 ldr r1, =IRQ_R0          
 str r0, [r1]                ;保存R0和R1寄存器(因为这两个寄存器再后面要用到)
 
 ;add r13, r13, #4  ;restore the sp_irq top to original irq top
 
 sub r14, r14, #4
 mov r0, r14                 ;LR_irq(R14)减4并保存在R0
 
 mrs r1, spsr               ;保存SVC模式下的cpsr到r1中,准备压栈保存,以便返回时用
  
 stmfd sp!, {r0}       ;lr-4
 stmfd sp!, {r14}      ;r14
 stmfd sp!, {r1}          ;spsr_irq   

 ldr 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全部压入中断以前模式下的堆栈

 ;mrs r5,cpsr
 ;bic r5,r5,#0x80
 ;msr cpsr_cxsf,r5         ;使中断能够嵌套,但不能在此用,因为中断源还没别清,这是开中断又会进入中断了
;-----------------------------     
 IMPORT int_vector_handler
 bl int_vector_handler      ;跳转到中断源判断和中断处理程序

;-----------------------------          ;restore the register
 ldmfd sp!, {r0-r12}           ;恢复原先保存的R0-R12
 ldmfd sp!, {r14}
 msr spsr_cxsf, r14
 ldmfd sp!, {r14}              ;将原先保存的SPSR_irq恢复到CPSR中
 ldmfd sp!, {pc}^
                                      
 EXPORT ENABLE_IRQ
;----------下面是函数栈,可以对比下中断栈于函数栈的区别-----------------------------
ENABLE_IRQ
 stmfd sp!,{r5,r14}         ;函数进入时,函数压栈
     mrs r5,cpsr
 bic r5,r5,#0x80
 msr cpsr_cxsf,r5        ;使中断能够嵌套,
 ldmfd sp!,{r5,pc}       ;函数返回
;可以发现中断栈与函数栈的区别在于,中断栈要保存cpsr,而函数栈不用管cpsr了,因此返回时也不用切换模式了。

在中断函数中将irq打开即可再次引起中断,用ENABLE_IRQ即可,而由于这时候上次的中断现场都已经保存所以再次进中断是可以的,但前提是上次中断已经清除了。

阅读(1942) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~