【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通用寄存器

可能有用的參考中斷與子程序調用問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM