在有中断发生时,程序跳到OS_CPU_IRQ_ISR处执行。OS_CPU_IRQ_ISR函数在OS_CPU_A.S中。
OS_CPU_IRQ_ISR
STR R3, [SP, #-4]! 1 ; PUSH WORKING REGISTERS ONTO IRQ STACK
STR R2, [SP, #-4]! 2
STR R1, [SP, #-4]! 3
MOV R1, SP 4 ; Save IRQ stack pointer
ADD SP, SP,#12 5 ; Adjust IRQ stack pointer
SUB R2, LR,#4 6 ; Adjust PC for return address to task
MRS R3, SPSR 7 ; Copy SPSR (i.e. interrupted task's CPSR) to R3
MSR CPSR_c, #(NO_INT | SYS32_MODE) ;8 Change to SYS mode
; SAVE TASK'S CONTEXT ONTO TASK'S STACK
STR R2, [SP, #-4]! 9; Push task's Return PC
STR LR, [SP, #-4]! 10; Push task's LR
STR R12, [SP, #-4]! 11; Push task's R12-R4
STR R11, [SP, #-4]! 12
STR R10, [SP, #-4]! 13
STR R9, [SP, #-4]! 14
STR R8, [SP, #-4]! 15
STR R7, [SP, #-4]! 16
STR R6, [SP, #-4]! 17
STR R5, [SP, #-4]! 18
STR R4, [SP, #-4]! 19
LDR R4, [R1], #4 20 ; Move task's R1-R3 from IRQ stack to SYS stack
LDR R5, [R1], #4 21
LDR R6, [R1], #4 22
STR R6, [SP, #-4]! 23
STR R5, [SP, #-4]! 24
STR R4, [SP, #-4]! 25
STR R0, [SP, #-4]! 26; Push task's R0 onto task's stack
STR R3, [SP, #-4]! 27 ; Push task's CPSR (i.e. IRQ's SPSR)
; HANDLE NESTING COUNTER
LDR R0, =OSIntNesting 28 ; OSIntNesting++;
LDRB R1, [R0] 29
ADD R1, R1,#1 30
STRB R1, [R0] 31
CMP R1, #1 32 ; if (OSIntNesting == 1) {
BNE OS_CPU_IRQ_ISR_1 33
LDR R4, =OSTCBCur 34 ; OSTCBCur->OSTCBStkPtr = SP
LDR R5, [R4] 35
STR SP, [R5] 36 ; }
OS_CPU_IRQ_ISR_1
MSR CPSR_c, #(NO_INT | IRQ32_MODE) 37; Change to IRQ mode (to use the IRQ stack to handle interrupt)
LDR R0, =OS_CPU_IRQ_ISR_Handler 38 ; OS_CPU_IRQ_ISR_Handler();
MOV LR, PC 39
BX R0 40
MSR CPSR_c, #(NO_INT | SYS32_MODE) 41; Change to SYS mode
BL OSIntExit 42 ; OSIntExit();
; RESTORE TASK'S CONTEXT and RETURN TO TASK
LDR R4, [SP], #4 43; pop new task's CPSR
MSR CPSR_cxsf, R4 44
LDR R0, [SP], #4 45 ; pop new task's context
LDR R1, [SP], #4 46
LDR R2, [SP], #4 47
LDR R3, [SP], #4 48
LDR R4, [SP], #4 49
LDR R5, [SP], #4 50
LDR R6, [SP], #4 51
LDR R7, [SP], #4 52
LDR R8, [SP], #4 53
LDR R9, [SP], #4 54
LDR R10, [SP], #4 55
LDR R11, [SP], #4 56
LDR R12, [SP], #4 57
LDR LR, [SP], #4 58
LDR PC, [SP], #4 59
在分析这个移植代码前,先了解下发生IRQ中断时,ARM的处理流程:
1、 会把当前的CPSR的值拷贝到SPSR_irq
2、 把PC的值拷贝到LR_irq
3、 强制进入IRQ异常模式
4、 强制进入ARM状态
5、 禁止IRQ中断
6、 PC=0X18,跳转到OS_CPU_IRQ_ISR处
上面这些都是硬件自动完成的。
在PC跳转到OS_CPU_IRQ_ISR后,已经进入IRQ模式,这时的SP指向的是IRQ模式下的堆栈,也就是说SP的实际物理地址是R13_irq。
下面分析程序:
1-3:把R1,R2,R3的值保存到IRQ的堆栈,只保存这3个寄存器的原因是下面使用到了这3个寄存器。
4:把当前SP的值保存在R1中
5:因为在保存R1,R2,R3进堆栈时,SP减了12,所以这里让SP+12,重新指向IRQ堆栈的起始位置。
6:LR_irq减4得到的值保存在R2中,LR_irq的值不变。这里减4和ARM的3级流水线取指有关系。
7:把SPSR_irq的值保存进R3,SPSR_irq的值是进入中断前的CPSR值
8:修改CPSR的值,进入系统模式
9:因为系统模式和用户模式的寄存器一样,所以系统模式的SP就是用户模式的SP。把R2的值压入堆栈,这句是保存用户程序的返回PC值
10:保存任务的LR值进堆栈
11-19:保存任务的R12-R4寄存器值进堆栈
20-25:把保存在IRQ堆栈里的R1,R2,R3寄存器的值保存到系统堆栈里,也就保存到用户任务堆栈。对应第1-4条指令就可以看明白。
26:把R0的值保存进堆栈
27:把R3保存的值,也就是SPSR_irq的值保存进堆栈。SPSR_irq的值就是进入中断前的CPSR值。
到这里为止,所有的寄存器值都已经保存到了任务堆栈里。
28-31:OSIntNesting加1,OSIntNesting是内核为中断嵌套的层数定义的一个全局变量。每进入一次中断,该变量加1,退出一次中断,该变量减一。中断全部处理完,该变量为0
32-36:判断是否是中断的第一层,如果是,立即把堆栈指针保存到这个任务的任务控制块OS_TCB中。如果有中断嵌套,跳转到OS_CPU_IRQ_ISR_1处执行。
这么做是UCOS中规定的。中断第一层要保存任务堆栈指针,这个好理解,如果发生中断嵌套,整个处理流程是怎样的?还没完全弄清楚,下次再写篇文章单独探讨这个问题。
37:进入IRQ模式,利用IRQ模式下的堆栈来处理中断程序。
38:把函数OS_CPU_IRQ_ISR_Handler()的地址传给R0。OS_CPU_IRQ_ISR_Handler()是执行中断处理的函数,下面会讲到。
39:把当前PC的值保存进LR_irq。作为下面调用OS_CPU_IRQ_ISR_Handler()函数后的返回地址。
40:跳转到OS_CPU_IRQ_ISR_Handler()处执行。
OS_CPU_IRQ_ISR_Handler()不是UCOS-II自带的函数,是用户自己加的。我是在LPC2214上移植的,下面的寄存器都是LPC2214的中断寄存器。函数如下:
void OS_CPU_IRQ_ISR_Handler(void)
{
PFNCT pfnct;
pfnct = (PFNCT)VICVectAddr; /* Read the interrupt vector from the VIC */
while (pfnct != (PFNCT)0) { /* Handle ALL interrupting devices */
(*pfnct)(); /* Call ISR for interrupting device */
pfnct = (PFNCT)VICVectAddr; /* Read the interrupt vector from the VIC */
}
}
VICVectAddr寄存器存的就是中断处理函数的地址,当这个值不为0时,就跳到对应的中断处理函数处执行。这个值为0,说明所有的中断处理函数都已经执行完成,程序返回到第41条指令处。
41:进入系统模式
42:调用OSIntExit函数。注意使用BL指令跳转时,会自动把下一条指令地址拷贝到LR中,而使用BX不会。在OSIntExit函数中,OSIntNesting会减1,同时判断是否执行任务调度。如果要进行任务调度,会调用函数OSIntCtxSw();执行OSIntCtxSw()后,就会跳转到新的任务去执行,不会执行43条以下的指令了。
43-59:如果没有进行任务调度。就把任务堆栈中的保存的寄存器值全部恢复到寄存器中。在任务中断的断点处继续运行。
文章评论(0条评论)
登录后参与讨论