UCOS-II中OS_CPU_IRQ_ISR移植过程分析
OS_CPU_IRQ_ISR
STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers
MOV R1, SP
ADD SP, SP, #12 ;Adjust IRQ stack pointer
SUB R2, LR, #4 ;Adjust PC for return address to task
MRS R3, SPSR ; Copy SPSR (Task CPSR)
MSR CPSR_cxsf, #SVCMODE|NOINT ;Change to SVC mode
; SAVE TASKS CONTEXT ONTO OLD TASKS STACK
STMFD SP!, {R2} ; Push tasks PC
STMFD SP!, {R4-R12, LR} ; Push tasks LR,R12-R4
LDMFD R1!, {R4-R6} ; Load Tasks R1-R3 from IRQ stack
STMFD SP!, {R4-R6} ; Push Tasks R1-R3 to SVC stack
STMFD SP!, {R0} ; Push Tasks R0 to SVC stack
STMFD SP!, {R3} ; Push tasks CPSR
LDR R0,=OSIntNesting ;OSIntNesting++
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
CMP R1,#1 ;if(OSIntNesting==1){
BNE %F1
LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;
LDR R5,[R4]
STR SP,[R5] ;}
1 MSR CPSR_c,#IRQMODE|NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
LDR R0, =INTOFFSET
LDR R0, [R0]
LDR R1, IRQIsrVect
MOV LR, PC ; Save LR befor jump to the C function we need return back
LDR PC, [R1, R0, LSL #2] ; Call OS_CPU_IRQ_ISR_handler();
MSR CPSR_c,#SVCMODE|NOINT ;Change to SVC mode
BL OSIntExit ;Call OSIntExit
LDMFD SP!,{R4} ;POP the tasks CPSR
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Tasks context
IRQIsrVect DCD HandleEINT0
这个函数是irq中断的通用处理形式,我对其中的代码做一下简要的分析和讨论。
首先我需要简要的分析一下中断处理过程中我们应该完成的任务,首先cpu会自动完成一些操作,其中包括中断的关闭已经返回地址的保存,还有模式的切换等。我们程序员则需要完成一些寄存器的保存工作以及跳转到具体的处理函数中,最后完成返回控制。
需要注意的是,本文中的UC/OS-II任务都运行在SVC模式下,而不是SYS模式下。任务栈中保存的寄存器也是这种模式下对应的值。
我按照注释号对代码进行解释:
2、STMFD SP!, {R1-R3}; 主要是完成几个寄存器的压栈操作,为什么这么做呢?因为我们接下来将要使用这写寄存器。为什么需要呢?这是因为当前CPU工作的irq模式下,因此这里的SP并不是SVC模式下的SP指针,但是在UC/OS-II的移植过程中通常将各个任务工作在SVC模式下,同时还需要对寄存器的保存,在中断发生以后,需要保存所有的寄存器以及CPSR的值,而当前的SP并不是任务的栈,此时除了SP、R14的值发生了变化以外,其他的寄存器并没有发生变化。但是由于后面需要使用这些寄存器,因此需要压栈。
3、MOV R1, SP; 将R14_irq的值保存到R1中,这个SP的保存主要是为了通过这个值访问IRQ模式下的堆栈空间,实现对数据的访问。
4、ADD SP, SP, #12; 调整IRQ模式下的堆栈指针SP_irq,将这个指针指向IRQ堆栈的开始位置,方便下一次中断的处理操作。
5、SUB R2, LR, #4 ;这个操作主要是调整返回地址,了解异常返回地址的理解其中的含义,这时候的返回地址实质上就是任务的返回地址,也就是将来需要加载到PC中的值。
6、MRS R3, SPSR;因为发生了IRQ中断,此时CPU进入IRQ模式中,这时的SPSR_irq中保存了svc模式下的CPSR状态。而任务堆栈中保存的刚好是SVC模式下的状态寄存器,因此需要将SVC模式下的状态寄存器首先读出来,然后保存进任务的堆栈中,因此用R3来保存CPSR值。
7、MSR CPSR_cxsf, #SVCMODE|NOINT; 因为任务的堆栈空间位于SVC模式下,因此首先需要将CPU的状态切换到SVC模式下,然后进行任务情景的切换操作。
9、STMFD SP!, {R2}; 这时候的SP是指在SVC模式下的R13_svc。中断发生以后,SVC模式下的SP并没有发生改变,返回以后,该值仍然存在,仍然指向任务的栈顶位置。根据任务栈空间的分布,首先需要保存PC值,然后是R14-R0,CPSR的值,最后是保存SP到任务中,这种分布状态是和堆栈初始化过程的分布一致的。而通过调整的返回地址刚好就保存在了R2中,因此需要压栈保存PC值。
10、STMFD SP!, {R4-R12, LR};因为在之前的一系列操作中,并没有对R4-R12,R14的值进行破坏,因此可以直接进行压栈操作,实现任务堆栈中R4-R12,LR的保存操作。
接下来的几句代码是重点:实现了在对其他模式下堆栈空间的访问问题。
11、LDMFD R1!, {R4-R6};是指将R1地址处加载一些数据到R4-R6,这三个寄存器的值已经被压入栈中,对他们的修改并不会导致错误的产生,因此这三个寄存器实质上是作为中间地址。其中加载的顺序满足高地址对应高的高编号的寄存器值。R1我在前面就强调了用来访问IRQ模式堆栈空间的参考地址。从这个地址向上分别保存了压入栈中的R1-R3寄存器的值,也就是被中断任务R1-R3的值。
12、STMFD SP!, {R4-R6};也就是完成了对任务寄存器R1-R3的压栈操作。
13、STMFD SP!, {R0} ;前面的代码中并没有修改R0的值,因此其中的值仍然是任务的R0值,因此也需要压栈操作。通过上面的几句代码就完成了任务的R0-R15所有寄存器的保存操作。接下的就应该完成CPSR的保存。
14、STMFD SP!, {R3};当前的R3中保存了实际上是前面所说的SPCR_irq中的值,也就是SVC模式下的状态寄存器的值,因此可以认为就是完成了任务的状态寄存器的保存。
16-19、接下来的操作本应该是完成将SP的值保存到任务堆栈空间的,但是在UC/OS-II中存在一个全局变量OSIntNesting,它表明了中断嵌套的次数,因此需要对这个值进行一次加1操作。
21、接下来的操作就是判断是否在中断嵌套中,也就是对全局变量进行比较操作,如果这个值是1,则认为只有一个中断产生,如果不等于1,则认为实在中断嵌套中。
UCOS-IIOS_CPU_IRQ_ISR移植过 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)