//zz//##############################################################################
zz-arm9-rtos的中断汇编代码分析helper2416-rawos.txt
arm9-rtos的中断汇编代码分析.txt
helper2416-rawos
///########
arm9-rtos的中断汇编代码分析helper2416-rawos
zz-write
@2014-8-2 23:22:38
///########
REF:
raw-os-helper2416-master.zip
KeyWord:
汇编 任务切换 CONTEXT_SWITCH()
//zz//##############################################################################
1.
在Helper2416的rawos的Keil工程中,IRQ中断入口函数的汇编代码分析
先看 Start.s 启动文件的代码,看到如下这段
exception_vector;
LDR PC,=reset_exception_process; //复位异常
LDR PC,=Undef_exception_process_asm; //未定义指令异常
LDR PC,=SWI_exception_process; //软中断异常
LDR PC,=Pabort_exception_process; //取指中止异常
LDR PC,=Dabort_exception_process; //数据中止异常
LDR PC,. ; //保留
LDR PC,=raw_os_interrupt; //zz// IRQ中断异常for raw-os
LDR PC,=FIQ_exception_process; //FIQ中断异常
以后再搞明白 exception_vector 是怎么被设置/调用的吧,如上这些都是给PC赋值,跳转到相应的中断函数中去
raw_os_interrupt这个是IRQ中断的总入口(EINT? 定时器等中断都从这里进入)
今天只分析 raw_os_interrupt 汇编代码做了些什么
切换任务task,执行EINT?等中断ISR,跳回IRQ中断前的Context继续执行
raw_os_interrupt
;//zz// irq 的堆栈sp上压栈R1-R3
STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers
;//zz// R1指向压栈后的堆栈地址
MOV R1, SP
;//zz// SP指向加12/4=3,irq的堆栈回到压R1-R3之前
ADD SP, SP, #12 ;Adjust IRQ stack pointer
;//zz// 3级流水线,中断前LR=PC指向了EXEC+8处(下两条),而LR需要是EXEC+4(下一条指令)
SUB R2, LR, #4 ;Adjust PC for return address to task
MRS R3, SPSR ; Copy SPSR (Task CPSR)
;##############################################################
;//zz//切到SVC模式,SP(R13),LR(R14)及CPSR都是SVC模式下的了
MSR CPSR_cxsf, #SVCMODE:OR:NOINT ;Change to SVC mode
; SAVE TASK''S CONTEXT ONTO OLD TASK''S STACK
;//zz//将IRQ中断前打断的PC(LR=>R2),存到SVC做的堆栈里面来
STMFD SP!, {R2} ; Push task''s PC
STMFD SP!, {R4-R12, LR} ; Push task''s LR,R12-R4
;//zz//以R1为堆栈指针,出栈之前存的R1-R3
LDMFD R1!, {R4-R6} ; Load Task''s R1-R3 from IRQ stack
;//zz//再把进IRQ中断前的现场R1-R3压入R1-R3
STMFD SP!, {R4-R6} ; Push Task''s R1-R3 to SVC stack
STMFD SP!, {R0} ; Push Task''s R0 to SVC stack
;//zz//压入CPSR(SVC模式之前存到R3中的)
STMFD SP!, {R3} ; Push task''s CPSR
;//zz// 切换任务(其实就是换SP的值到指定的任务去)
LDR R4,=raw_task_active
LDR R5,[R4]
STR SP,[R5]
;//zz//检测当前task的SP是否有效
PUSH {R14}
bl raw_stack_check
POP {R14}
BL raw_enter_interrupt
;//zz// 切到IRQ模式下,完成ISR中断服务函数的调用
MSR CPSR_c,#IRQMODE:OR:NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
BL irq_process
MSR CPSR_c,#SVCMODE:OR:NOINT ;Change to SVC mode
BL raw_finish_int
//zz// 弹出CPSR-PC,回到进入IRQ前保存的中断现场
LDMFD SP!,{R4} ;POP the task''s CPSR
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Task''s context
//zz//##############################################################################
2.
关于中断现场Context,堆栈SP的内存结构,具体参看 port.c 下的函数port_stack_init()
RAW_VOID *port_stack_init(PORT_STACK *p_stk_base,
RAW_U32 stk_size,
RAW_VOID *p_arg,
RAW_TASK_ENTRY p_task)
{
RAW_U32 *stk;
RAW_U32 temp = (RAW_U32)(p_stk_base + stk_size - 4);
stk = (RAW_U32 *)temp;
*(stk) = (RAW_U32)p_task; /* Entry Point PC R15*/
//zz// R14 is just SP...
*(--stk) = (RAW_U32)0; /* LR R14*/
*(--stk) = (RAW_U32)0; /* R12 */
*(--stk) = (RAW_U32)0; /* R11 */
*(--stk) = (RAW_U32)0; /* R10 */
*(--stk) = (RAW_U32)0; /* R9 */
*(--stk) = (RAW_U32)0; /* R8 */
*(--stk) = (RAW_U32)0; /* R7 */
*(--stk) = (RAW_U32)0; /* R6 */
*(--stk) = (RAW_U32)0; /* R5 */
*(--stk) = (RAW_U32)0; /* R4 */
*(--stk) = (RAW_U32)0; /* R3 */
*(--stk) = (RAW_U32)0; /* R2 */
*(--stk) = (RAW_U32)0; /* R1 */
*(--stk) = (RAW_U32)p_arg; /* R0 argument */
*(--stk) = (RAW_U32)0x13; /* CPSR SVC mode */
return stk;
}
push压栈的顺序是:
高地址 PC => LR => R12 ... R1 => R0 => CPSR 低地址
pop出栈的顺序是(从内存地址来看是一样的):
低地址 CPSR => R0 => R1 ... R12 => LR => PC 高地址
//zz//##############################################################################
3.
其他几段汇编代码在对 2. 中栈的结构有了解后,就很好理解了..
port_task_switch //任务切换
raw_int_switch //rawos开机启动最高优先级任务
OS_CPU_SR_Save //CPSR状态寄存器保存,之后关闭中断
OS_CPU_SR_Restore //CPSR状态寄存器恢复(是否恢复中断使能,要看Save保存的什么值)