ucosiii 源码分析之上下文切换---针对M3内核分析

Ucos  操作系统核心功能点重要两块,任务调度和上下切换,对于移植系统人员来说主要关注上下文的切换,上下文切换主要涉及当前映像的保存,主要涉及三动作

  • 关闭中断,调整当前BASEPRI 可进入中断的级别。保存现场(就是cpu 中的寄存器入栈),开启中断
  • 当前优先及级指向最高优先级,当前的OSTCBCurPtr 指向最高优先级(切任务控制块的指针)
  • 从当前的任务栈中恢复数据到cpu 寄存器中,恢复psp 指针,恢复basepri 写0 ,不使能屏蔽高于某个等级的中断
  • exc_return 机制,LR寄存器写LR=0XFFF_FFFD返回线程模式,进入异常模式,cm3 会自动设置lr 寄存器的值

对于ucos 任务,任务切换是在中断中进行上下文进行切换,也就是pendsv ,优先级最低,所以有必要了解下中断相关知识

中断行为

        中断嵌套

                这块可以自行在《cm3内核权威指南》了解

        咬尾中断

                由主程序进入中断模式,首先进行保存现场,也就是R0-R3 ,R12,R14,R15 XPSR,寄存器入栈,当退出之后自动弹出,考虑以下场景,一个低优先级ISR1的中断正在执行,此时一个高优先级中断被触发,cm3 会优先响应高优先级中断,此时cm3 保存的寄存器不会立马弹出,待回到ISR1,退出后一起弹出,减少一次出栈入栈动作 (保存到当前使用的任务堆栈,如果psp 就直接保存到psp,msp 就是到msp 堆栈)

                

        中断退出,相关设置如下

        

OSTaskStkInit: 栈帧初始化,构造初始化栈帧,程序启动时候需要按照 中断保存现场压入 r0-r3 xpsr PC LR 寄存器值,以便在保证中断进行任务切换时候,自动出栈一致

试想下,如果在初始时候这几个值未按照,中断自动保存寄存器顺序,那么在任务创建完成后第一次进行任务调度,从当前任务控制块中恢复寄存器的值:
1、pendsv 手动控制部分 R0-R11 
2 、自动弹栈部分 xpsr pc r14 r12 R0-R3 

一致性得不到保证,必然导致 程序异常 pc 指针可能都飞掉了

OSTaskStkInit:

CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,

                         void          *p_arg,

                         CPU_STK       *p_stk_base,

                         CPU_STK       *p_stk_limit,

                         CPU_STK_SIZE   stk_size,

                         OS_OPT         opt)

{

    CPU_STK    *p_stk;


 

    (void)opt;                                                  /* 'opt' is not used, prevent warning                   */

    p_stk = &p_stk_base[stk_size];                              /* Load stack pointer                                   */

                                                                /* Align the stack to 8-bytes.                          */

    p_stk = (CPU_STK *)((CPU_STK)(p_stk) & 0xFFFFFFF8u);

                                                                /* Registers stacked as if auto-saved on exception      */

    *(--p_stk) = (CPU_STK)0x01000000u;                          /* xPSR                                                 */

    *(--p_stk) = (CPU_STK)p_task;                               /* Entry Point                                          */

    *(--p_stk) = (CPU_STK)OS_TaskReturn;                        /* R14 (LR)                                             */

    *(--p_stk) = (CPU_STK)0x12121212u;                          /* R12                                                  */

    *(--p_stk) = (CPU_STK)0x03030303u;                          /* R3                                                   */

    *(--p_stk) = (CPU_STK)0x02020202u;                          /* R2                                                   */

    *(--p_stk) = (CPU_STK)p_stk_limit;                          /* R1                                                   */

    *(--p_stk) = (CPU_STK)p_arg;                                /* R0 : argument                                        */

    *(--p_stk) = (CPU_STK)0xFFFFFFFDuL;                         /* R14: EXEC_RETURN; See Note 5                         */

                                                                /* Remaining registers saved on process stack           */

    *(--p_stk) = (CPU_STK)0x11111111uL;                         /* R11                                                  */

    *(--p_stk) = (CPU_STK)0x10101010uL;                         /* R10                                                  */

    *(--p_stk) = (CPU_STK)0x09090909uL;                         /* R9                                                   */

    *(--p_stk) = (CPU_STK)0x08080808uL;                         /* R8                                                   */

    *(--p_stk) = (CPU_STK)0x07070707uL;                         /* R7                                                   */

    *(--p_stk) = (CPU_STK)0x06060606uL;                         /* R6                                                   */

    *(--p_stk) = (CPU_STK)0x05050505uL;                         /* R5                                                   */

    *(--p_stk) = (CPU_STK)0x04040404uL;                         /* R4                                                   */

    return (p_stk);

}

    任务上下文切换如下: 此代码为正点原子官方例程里面的代码,理论上不需要保存R14寄存器的值,CM3权威指南指出,R14寄存器的值  会根据进入异常前的堆栈和模式设置相应的值,测试过在保存堆栈 R4-R11,R14 去除R14,程序不能跑起来,把R14 换成R2 寄存器的可以跑起来,个人感觉这块保存动作为了保证栈帧一致性,如果把创建任务执行的 OSTaskStkInit 中栈帧初始化中R14入栈也去掉,任务切换函数中的(OS_CPU_PendSVHandler )R14相关寄存器操作都可以去除

以下代码,理论上应该可以去除,可以分析代码逻辑,理论上R14 最终是从栈里面恢复的

MOV     R4, LR                                              ; Save LR exc_return value

ORR     LR,  R4, #0x04  


OS_CPU_PendSVHandler
    CPSID   I                                                   ; 关中断
    MOV32   R2, OS_KA_BASEPRI_Boundary                          ; OS_KA_BASEPRI_Boundary 地址存到R2
    LDR     R1, [R2]                                            ; 存储器地址R2 的值读取到R1,也就是R1=OS_KA_BASEPRI_Boundary
    MSR     BASEPRI, R1                                         ; 设置 BASEEPRI 寄存器 值等于64 ,优先级数字比这个大的都屏蔽掉,数字越低优先级越高
    DSB
    ISB
    CPSIE   I                                                   ;使能中断

    MRS     R0, PSP                                             ; R0指向psp
    IF {FPU} != "SoftVFP"
                                                                ; Push high vfp registers if the task is using the FPU context
    TST       R14, #0x10
    IT        EQ
    VSTMDBEQ  R0!, {S16-S31}
    ENDIF

    STMFD   R0!, {R4-R11, R14}                                  ; 保存R4-R11,R14 寄存器的值到栈

    MOV32   R5, OSTCBCurPtr                                     ; R5 存OSTCBCurPtr 地址
    LDR     R1, [R5]                                            ; R1 是OSTCBCurPtr->sp 地址
    STR     R0, [R1]                                            ; OSTCBCurPtr->sp = sp

                                                                ; At this point, entire context of process has been saved
    MOV     R4, LR                                              ; Save LR exc_return value
    BL      OSTaskSwHook                                        ; Call OSTaskSwHook() for FPU Push & Pop

    MOV32   R0, OSPrioCur                                       ; OSPrioCur   = OSPrioHighRdy;
    MOV32   R1, OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    MOV32   R1, OSTCBHighRdyPtr                                 ; OSTCBCurPtr = OSTCBHighRdyPtr;
    LDR     R2, [R1]
    STR     R2, [R5]

    ORR     LR,  R4, #0x04                                      ; 线程模式置位,理论上来说不需要,因为pendsv 是优先级最低的
    LDR     R0,  [R2]                                           ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
    LDMFD   R0!, {R4-R11, R14}                                  ; 此地方又重新覆盖了LR 寄存器,不是特别明白
    IF {FPU} != "SoftVFP"
                                                                ; Pop the high vfp registers if the next task is using the FPU context
    TST       R14, #0x10
    IT        EQ
    VLDMIAEQ  R0!, {S16-S31}
    ENDIF

    MSR     PSP, R0                                             ; 设置psp 寄存器的值

    MOV32   R2, #0                                              ; 不做特殊优先级屏蔽动作,所有中断都可以响应
    CPSID   I                                                   ; Cortex-M7 errata notice. See Note #5
    MSR     BASEPRI, R2
    DSB
    ISB
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

    ALIGN                                                       ; Removes warning[A1581W]: added <no_padbytes> of padding at <address>

    END


参考文档:

Cortex-M3权威指南Cn.pdf

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值