FreeRTOS 任务上下文切换核心函数:xPortPendSVHandler详解

源代码:

cpp 复制代码
__asm void xPortPendSVHandler( void )
{
    extern uxCriticalNesting;
    extern pxCurrentTCB;
    extern vTaskSwitchContext;

/* *INDENT-OFF* */
    PRESERVE8

    mrs r0, psp        ; 获取进程栈指针(当前任务的栈)
    isb                ; 指令同步屏障
    /* Get the location of the current TCB. */
    ldr r3, =pxCurrentTCB        ; 获取当前任务控制块指针
    ldr r2, [ r3 ]                ; r2 = pxCurrentTCB

    /* Is the task using the FPU context?  If so, push high vfp registers. */
    tst r14, #0x10            ; 检查EXC_RETURN bit 4
    it eq                        ; 如果bit4=0,说明使用了FPU
    vstmdbeq r0!, {s16-s31}        ; 保存FPU高寄存器s16-s31

    /* Save the core registers. */
    stmdb r0!, {r4-r11, r14}    ; 保存CPU寄存器 r4-r11, r14(EXC_RETURN)

    /* Save the new top of stack into the first member of the TCB. */
    str r0, [ r2 ]               ; 更新TCB中的栈顶指针
    
    /* 以下为执行任务切换 */
    stmdb sp!, {r0, r3}        ; 临时保存 r0, r3
    mov r0, # configMAX_SYSCALL_INTERRUPT_PRIORITY
    msr basepri, r0            ; 提升中断优先级(屏蔽某些中断)
    dsb                        ; 数据同步屏障
    isb                        ; 指令同步屏障
    bl vTaskSwitchContext      ; 调用C函数选择下一个任务
    mov r0, # 0                
    msr basepri, r0            ; 恢复中断屏蔽
    ldmia sp!, {r0, r3}        ; 恢复 r0, r3

    /* The first item in pxCurrentTCB is the task top of stack. */
    ldr r1, [ r3 ]       ; r1 = 新的pxCurrentTCB     
    ldr r0, [ r1 ]        ; r0 = 新任务的栈顶指针

    /* Pop the core registers. */
    ldmia r0!, {r4-r11, r14}    ; 恢复CPU寄存器

    /* Is the task using the FPU context?  If so, pop the high vfp registers
     * too. */
    tst r14, # 0x10        ; 检查是否需要恢复FPU上下文
    it eq
    vldmiaeq r0!, {s16-s31}    ; 恢复FPU高寄存器

    msr psp, r0            ; 更新进程栈指针
    isb
    #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */
        #if WORKAROUND_PMU_CM001 == 1
            push { r14 }
            pop { pc }
            nop
        #endif
    #endif

    bx r14                ; 异常返回
/* *INDENT-ON* */
}

解析:

上下文切换核心:切换前保存所有相关内核寄存器,切换后恢复所有相关内核寄存器;这里的所有相关内核寄存器就是指:R0-R12,SP,LR,PC,PSR以及有FPU的则还包括FPU相关寄存器;

其中R0-R3,R12,SP,LR,PC,PSR已经在中断执行的时候由硬件机制入栈了,所以只需要将R4-R11,R14手动入栈就行了,这里之所以要再将R14入栈一次,是因为硬件机制入栈的R14是指LR,已经处于中断中时R14是表示EXCRETURN,代表的意义不一样;

所以,以上代码的流程总结就是:

1.保存内核寄存器

2.更新新的栈顶指针(因为有入栈)

3.切换下一个任务(实际上就是将pxCurrentTCB 指向新任务)

4.获取新任务的栈顶指针

5.恢复内核寄存器

6.异常返回(执行之后硬件会自动POP R0-R3等寄存器)

注意:上诉代码能有效的还一个重要信息就是: TCB的第一个字保存的是该任务栈的栈顶地址

相关推荐
LuminousCPP20 小时前
数据结构 - 线性表第四篇:C 语言通讯录优化升级全记录(踩坑 + 思考)
c语言·开发语言·数据结构·经验分享·笔记·学习
浩浩测试一下1 天前
汇编 标志位寄存器 (逆向分析 )
c语言·汇编·逆向·windows编程·标志寄存器
SuperByteMaster1 天前
uart中断发送和接收处理
c语言
szxinmai主板定制专家1 天前
RK3568 + CODESYS+实时系统运动控制器PLC,支持 AI 视觉目标检测,预测性维护,混合多系统部署,多路模拟量采集
arm开发·人工智能·嵌入式硬件·fpga开发
社交怪人1 天前
【浮点数相除的余】信息学奥赛一本通C语言解法(题号1029)
c语言·开发语言
辣椒思密达1 天前
Python爬虫中如何正确配置住宅IP代理?新手避坑指南
c语言·python
番茄灭世神1 天前
PN学堂GD32教程第21篇——WiFiIOT
c语言·stm32·单片机·嵌入式·gd32
我还记得那天1 天前
C语言递归实现汉诺塔问题
c语言·开发语言
LuminousCPP1 天前
数据结构 - 线性表第三篇:基于顺序表实现 C 语言通讯录(基础功能篇)
c语言·数据结构·经验分享·笔记·算法
kkeeper~1 天前
0基础C语言积跬步之内存函数
c语言·开发语言