一、ARM架构分析
在ARM有以上寄存器组,此处不再多余分析,介绍的文章比较多,USR和SYS模式公用一组寄存器组,故没有列出,SP位堆栈寄存器,通过这个寄存器我们可以实现不同任务间以及任务与操作系统间的隔离。任务用SYS寄存器组(USR组),操作系统用SVC组,中断用IRQ组,FreeRtos共用了三组以上的。
RTOS跑的任务都属于内核级线程,在SYS下,我们可以看到,RTOS保存数据以及回复数据都是在SYS模式下进行的,操作系统的模式咋SVC下进行,这与M3内核的相似,比如说M3内核的是PSP线程堆栈,而A9则是利用不同模式下堆栈指针不同进行的。看下面代码:
/* Switch to system mode. */
.type vPortRestoreTaskContext, %function
vPortRestoreTaskContext:
CPS #SYS_MODE@作用是将模式切换到SYS模式
portRESTORE_CONTEXT@将堆栈内容恢复此处便用的是堆栈指针
当进入中断后硬件会自动将CPSR模式转换为IRQ模式,而此时我们如果要保存线程数据,必须要进行切换到SYS下相应的堆栈指针。
下面分析IRQ中断,主要完成任务切换等
FreeRTOS_IRQ_Handler:
/* Return to the interrupted instruction. */
SUB lr, lr, #4 //流水线有关 PC_cur = PC_now + 8中断此时命令还没执行故-4
/* Push the return address and SPSR. */
PUSH {lr}
MRS lr, SPSR
PUSH {lr}
/* Change to supervisor mode to allow reentry. */
CPS #SVC_MODE//因为要进入任务相关的切换,操作系通内核,所以侵入SVC模式,再后面要切换到IRQ模式,因为要根据SPSR恢复CPSR
/* Push used registers. */
PUSH {r0-r4, r12}
/* Increment nesting count. r3 holds the address of ulPortInterruptNesting
for future use. r1 holds the original ulPortInterruptNesting value for
future use. */
LDR r3, ulPortInterruptNestingConst
LDR r1, [r3]
ADD r4, r1, #1
STR r4, [r3]
/* Read value from the interrupt acknowledge register, which is stored in r0
for future parameter and interrupt clearing use. */
LDR r2, ulICCIARConst
LDR r2, [r2]
LDR r0, [r2]
/* Ensure bit 2 of the stack pointer is clear. r2 holds the bit 2 value for
future use. _RB_ Does this ever actually need to be done provided the start
of the stack is 8-byte aligned? */
MOV r2, sp
AND r2, r2, #4
SUB sp, sp, r2
/* Call the interrupt handler. r4 pushed to maintain alignment. */
PUSH {r0-r4, lr}
LDR r1, vApplicationIRQHandlerConst
BLX r1
POP {r0-r4, lr}
ADD sp, sp, r2
CPSID i
DSB
ISB
/* Write the value read from ICCIAR to ICCEOIR. */
LDR r4, ulICCEOIRConst
LDR r4, [r4]
STR r0, [r4]
/* Restore the old nesting count. */
STR r1, [r3]
/* A context switch is never performed if the nesting count is not 0. */
CMP r1, #0
BNE exit_without_switch
/* Did the interrupt request a context switch? r1 holds the address of
ulPortYieldRequired and r0 the value of ulPortYieldRequired for future
use. */
LDR r1, =ulPortYieldRequired
LDR r0, [r1]
CMP r0, #0
BNE switch_before_exit
exit_without_switch:
/* No context switch. Restore used registers, LR_irq and SPSR before
returning. */
POP {r0-r4, r12}
CPS #IRQ_MODE
POP {LR}
MSR SPSR_cxsf, LR
POP {LR}
MOVS PC, LR
switch_before_exit:
/* A context swtich is to be performed. Clear the context switch pending
flag. */
MOV r0, #0
STR r0, [r1]
/* Restore used registers, LR-irq and SPSR before saving the context
to the task stack. */
POP {r0-r4, r12}
CPS #IRQ_MODE
POP {LR}
MSR SPSR_cxsf, LR
POP {LR}
portSAVE_CONTEXT
/* Call the function that selects the new task to execute.
vTaskSwitchContext() if vTaskSwitchContext() uses LDRD or STRD
instructions, or 8 byte aligned stack allocated data. LR does not need
saving as a new LR will be loaded by portRESTORE_CONTEXT anyway. */
LDR R0, vTaskSwitchContextConst
BLX R0
/* Restore the context of, and branch to, the task selected to execute
next. */
portRESTORE_CONTEXT
上述代码再最后调用了IRQ代码的vApplicationIRQHandler,执行终端的程序,每次中断都会判段是否需要切换任务,这与M3实现大相径庭,M3的实现是在PENDSv内判断是否需要,这与硬件架构也有关系。如果需要那么变进行切换。