Chinaunix首页 | 论坛 | 博客
  • 博客访问: 646627
  • 博文数量: 329
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 693
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-05 23:37
个人简介

Do not panic!

文章存档

2021年(1)

2018年(3)

2017年(7)

2016年(98)

2015年(220)

我的朋友

分类: 嵌入式

2015-05-07 00:03:13

//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保存的什么值)

阅读(1261) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~