• 【RTOS】堆栈与任务栈



    前言

    本笔记基于 stm32+FreeRTOS。


    概念

    双堆栈指针

    Cortex-M3 和 M4内核具有双堆栈指针。MSPPSP

    • MSP:主堆栈指针
    • PSP:进程栈指针

    要点

    • 用户程序、中断和中断嵌套都是用 MSP
    • M3 内核所有寄存器压栈时有64B
    • M4 内核所有寄存器压栈时有200B。(因为FPU(浮点运算单元)也要压栈)

    Cortex-M3寄存器介绍

    寄存器图

    通用寄存器组:

    特殊功能寄存器组:

    简要介绍

    • 通用寄存器 R0-R7

      • R0-R7被称为低组寄存器。
      • 所有指令都能访问它们。
      • 字长全是32位,复位后不可预料。
    • 通用寄存器 R8-R12

      • R0-R7被称为高组寄存器。
      • 只有很少16位Thumb指令能访问它们,32位指令不受限制。
      • 字长全是32位,复位后不可预料。
    • 堆栈指针 R13(SP)

      • CM3处理器内核中央有两个堆栈指针,分别是MSPPSP
      • 当前使用MSP,则PSP只能通过特殊指令(MRS,MSR指令)来访问。
      • MSP:主堆栈指针
        • 缺省的堆栈指针,由OS内核、异常服务例程及所有需要特权访问的应用程序代码来使用。
      • PSP:线程堆栈指针
        • 用于常规的应用程序代码。
        • 注意:并不是每个应用都需要两个堆栈指针,简单的应用程序只需要MSP即可
    • 连接寄存器 R14(LR)

      • 保存调用子程序时返回的地址
    • 连接寄存器 R15(PC)

    知识

    出入栈

    出入栈用于上下文切换。

    • 例子:stm32F103自动出入栈的寄存器有 R0-R3,R12,LR,PC,xPSR

    入栈(压栈)

    入栈:上文保存

    1. 先自动压栈(进入异常时,硬件自动完成
    2. 再手动压栈

    出栈

    出栈:下文加载

    1. 先手动出栈
    2. 再自动出栈(退出异常时,硬件自动完成

    重点知识

    异常的响应序列*

    参考《Cortex-M3 权威指南》第九章

    当CM3开始响应一个中断时,会在它看不见的体内奔涌起三股暗流:

    • 入栈:把8个寄存器的值压入栈(硬件完成)
    • 取向量:从向量表中找出对应的服务程序入口地址
    • 选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC
    入栈
    取向量
    更新寄存器

    在入栈和取向量的工作都完毕之后,执行服务例程之前,还要更新一系列的寄存器:

    • SP:在入栈中会把堆栈指针(PSP或MSP)更新到新的位置。在执行服务例程后,
      将由MSP负责对堆栈的访问。
    • PSR:IPSR位段(地处PSR的最低部分)会被更新为新响应的异常编号。
    • PC:在向量取出完毕后,PC将指向服务例程的入口地址,
    • LR:LR的用法将被重新解释,其值也被更新成一种特殊的值,称为“EXC_RETURN”,并且在异常返回时使用。EXC_RETURN的二进制值除了最低4位外全为1,而其最低4位则有另外的含义。 
      以上是在响应异常时通用寄存器的变化。另一方面,在NVIC中,也伴随着更新了与之相关的若干寄存器。例如,新响应异常的悬起位将被清除,同时其活动位将被置位。
    小结知识*
    • Cortex-M3 中发生异常时,会硬件压栈
    • Cortex-M3 函数调用时是不会硬件压栈的,需要软件压栈。但是,用C语言时编译器会自动生成压栈的汇编语句。若用汇编编写,则需要手写压栈。
      • 网友一句很好理解的话:
        • 当程序跳到中断向量表时就会硬件自动压栈
    • 进入异常后,异常不用LR寄存器(R14)保存返回地址,但是需要使用LR寄存器触发异常返回机制

    FreeRTOS任务切换源码分析

    __asm void xPortPendSVHandler( void )
    {
    	extern uxCriticalNesting;
    	extern pxCurrentTCB;
    	extern vTaskSwitchContext;
    
    	PRESERVE8
    
    	mrs r0, psp
    	isb
    
    	ldr	r3, =pxCurrentTCB		/* Get the location of the current TCB. */
    	ldr	r2, [r3]
    
    	stmdb r0!, {r4-r11}			/* Save the remaining registers. */
    	str r0, [r2]				/* Save the new top of stack into the first member of the TCB. */
    
    	stmdb sp!, {r3, r14}
    	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
    	msr basepri, r0
    	bl vTaskSwitchContext
    	mov r0, #0
    	msr basepri, r0
    	ldmia sp!, {r3, r14}
    
    	ldr r1, [r3]
    	ldr r0, [r1]				/* The first item in pxCurrentTCB is the task top of stack. */
    	ldmia r0!, {r4-r11}			/* Pop the registers and the critical nesting count. */
    	msr psp, r0
    	isb
    	bx r14
    	nop
    
    • stmdb sp!, {r3, r14}
      • r3 压栈保护,因为调用函数vTaskSwitchContext时可能会用到 r3 寄存器。
      • r14 压栈保护,因为调用函数vTaskSwitchContext时,r14将被改写。

    话语

    • 任务切换的时候,在PendSV中断函数中,由于任务使用 PSP ,中断使用 MSP。如果在中断函数中调用函数或者中断嵌套时,压栈2,会覆盖 R14。PendSV结束返回时找不到正确的返回机制。

    参考

    重要参考 《Cortex-M3权威指南》
    主要参考野火安富莱
    主要参考链接Cortex-M3通用寄存器

    可能有用的参考中断与子程序调用问题

  • 相关阅读:
    Remove Element leetcode java
    Remove Duplicates from Sorted Array leetcode java
    Container With Most Water leetcode java
    Divide Two Integers leetcode java
    N-Queens II leetcode java
    N-Queens leetcode java
    Pow(x,n) leetcode java
    Single Number II leetcode java
    Single Number leetcode java
    ROS第一次开网站跳转到公告页(任意地址跳转)方法
  • 原文地址:https://www.cnblogs.com/lizhuming/p/13811237.html
Copyright © 2020-2023  润新知