Nucleus源代码分析 - Task(2)

来源:互联网 发布:js储存key和value 编辑:程序博客网 时间:2024/05/08 18:50

Nucleus Task的中断与恢复

以下是以TI的源代码进行的分析
一般来说最有可能中断Task的就是irq中断了。在ARM下到有中断发生时,ARM会自动切换到IRQ模式,此时ARM的寄存器已经是IRQ模式下的寄存器了。

view plaincopy to clipboardprint?
  1. INT_IRQ  
  2. STMDB   sp!,{r0-r5}                 ; Save a1-a4 on temporary IRQ stack  
  3. MRS     a1,spsr                     ; check for the IRQ bug:  
  4. TST     a1,#080h                    ; if the I - flag is set,  
  5. BNE     IRQBUG                      ; then postpone execution of this IRQ  
  6. SUB     r4,lr,#4                    ; Save IRQ's lr (return address)  
  7. <strong>注意r4的内容lr-4(被中断的PC,-4是因为ARM放入的是下一条指令+4)</strong>  
  8. BL      _TCT_Interrupt_Context_Save ; Call context save routine  
  9. BL      _IQ_IRQ_isr             ; Call  int. service routine  
  10. B       _TCT_Interrupt_Context_Restore  
  11.   
  12. ;BUG correction 2nd part  ------------------  
  13. IRQBUG: LDMFD  sp!,{r0-r5}                  ; return from interrup. Fix for locosto reset problem. We have pushed 6 regs and popping 4.  
  14. SUBS   pc,r14,#4  
  15. ;BUG correction 2nd part end  --------------  

以上就是一个irq的完整的处理过程了,我们慢慢来分析。

view plaincopy to clipboardprint?
  1. ;VOID  TCT_Interrupt_Context_Save(vector)  

.def _TCT_Interrupt_Context_Save
_TCT_Interrupt_Context_Save
……….
LDR r1,TCT_Int_Count ; Pickup address of interrupt count
LDR r2,[r1] ; Pickup interrupt counter
ADD r2,r2,#1 ; Add 1 to interrupt counter
STR r2,[r1] ; Store new interrupt counter value
CMP r2,#1 ; Is this first interrupt?
BNE TCT_Nested_Save ; If not first interrupt, do nested save

.if NU_FIQ_SUPPORT

; Check for a special nested case. A special nested case occurs when
; a second interrupt goes off before TCT_Int_Count is incremented.
……….

.else

B TCT_Normal_Save

.endif ; NU_FIQ_SUPPORT

TCT_Nested_Save
……
TCT_Normal_Save

; Determine if a thread was interrupted.

LDR r1,TCT_Current_Thread1 ; Pickup current thread ptr address
LDR r1,[r1] ; Pickup the current thread pointer
CMP r1,#0 ; Is it NU_NULL?
BEQ TCT_Idle_Context_Save ; If no, no real save is necessary
以下就是Task被中断时所做的操作…
TCT_Thread_Save
MOV r1,sp ; Put the exception sp into r1
MOV r2,lr ; Move the return address for the caller
; of this function into r2
MRS r3,spsr ; Put the exception spsr into r3

; Adjust the exception stack pointer for future exceptions
24是因为 STMDB sp!,{r0-r5} ?
ADD sp,sp,#24 ; sp will point to interrupt mask register(s)

; Switch CPU modes to save context on thread’s stack
这个是进行Task上下文保护的关键,切回svc模式,是因为Task的运行是在svc模式下进行的,在irq模式下要取得Task运行是ARM的各个寄存器的值然后进行保护,唯一的途径就是在切回svc模式下,只有这样才能取得.
MRS r5,CPSR ; Pickup the current CPSR
BIC r5,r5,#MODE_MASK ; Clear the mode bits
ORR r5,r5,#SUP_MODE ; Change to supervisor mode (SVC)
MSR CPSR,r5 ; Switch modes
; Store the thread’s sp into r5 so the sp can be saved as is

MOV r5,sp

; Save the exception return address on the stack (PC)
R4就是我们在irq模式下的那个放pc的r4,ARM的R0-R7寄存器是unbanked register,也就是说各个模式下都是同一个寄存器。
STMDB r5!,{r4}
; Save r6-r14 on stack

STMDB r5!,{r6-r14}
; Switch back to using sp now that the original sp has been saved.

MOV sp,r5

; Get r0-r5 off exception stack and save on thread’s stack

LDMIA r1!,{r5-r10}
STMDB sp!,{r5-r10}
此时Task运行模式时的R0-PC已经储存在了相应的堆栈上.
; Save the SPSR on the system stack (CPSR)

STMDB sp!,{r3}

; Save stack type to the task stack (1=interrupt stack)
设置中断原因,为如何恢复设置变量。
MOV r1,#1
STMDB sp!,{r1}

; Save the thread’s stack pointer in the control block.
将新的sp地址放到Task的结构中,这个在恢复时会做相应的读取。
LDR r1,TCT_Current_Thread1 ; Pickup current thread ptr address
LDR r3,[r1] ; Pickup current thread pointer
STR sp,[r3, #TC_STACK_POINTER] ; Save stack pointer

; Switch to the system stack / system stack limit
切换回svc模式下的系统堆栈,注意这个堆栈是和task的堆栈是不一样的。Task的堆栈是task创建是需指定的一个内存区域,供task函数调用用到,task在运行时会先将task的堆栈赋值给arm的sp,此时task函数调用就使用的是指定的那块内存了。而各个模式下都必须设置一个系统堆栈以作为系统使用。
LDR r1,TCT_System_Stack ; Pickup address of stack pointer
LDR r3,TCT_System_Limit1 ; Pickup address of stack limit ptr
LDR sp,[r1] ; Switch to system stack
LDR r10,[r3] ; Setup system stack limit

…..
TCT_Save_Exit

; Return to caller ISR.

BX r2

到此task的上下文保护完成。

运行对应的中断函数.

view plaincopy to clipboardprint?
  1. BL      _IQ_IRQ_isr             ; Call  int. service routine  

Task的恢复。

  1. B       _TCT_Interrupt_Context_Restore  

;VOID TCT_Interrupt_Context_Restore(VOID)

.def _TCT_Interrupt_Context_Restore
_TCT_Interrupt_Context_Restore
LDR r0,TCT_Current_Thread1 ; Pickup current thread ptr address
LDR r0,[r0] ; Pickup current thread pointer
CMP r0,#0 ; Determine if a thread is active
BEQ TCT_Nested_Restore ; If not, just do a nested restore

; A thread was active during this special situation
; Switch to this thread’s stack

LDR sp,[r0,#TC_STACK_POINTER]

; Adjust the SP below the stack type

ADD sp,sp,#4

LDR r1,[sp], #4 ; Pickup the saved CPSR

MSR SPSR,r1 ; Place into saved SPSR

; Return to the point of interrupt.
将原来储存的各个寄存器恢复,task就从中断的地方继续运行了。
LDMIA sp,{r0-r15}^

原创粉丝点击