MCU在執行指令的過程中,是如何選擇堆棧指針呢?

2023-07-12 11:21:39 來源:一起學嵌入式

我們知道 Cortex-M3 系列單片機內部有雙堆棧機制。即 Cortex‐M3 擁有兩個堆棧指針:主堆棧(MSP)和進程堆棧(PSP)。任一時刻只能使用其中的一個。通過控制寄存器 CONTROL 中的選擇位進行控制。

兩個堆棧指針如下:

主堆棧指針( MSP):復位后缺省使用的堆棧指針,用于操作系統內核以及異常處理例程(包括中斷服務例程)進程堆棧指針( PSP):由用戶的應用程序代碼使用。

RTOS移植到 Cortex-M3 系列單片機上后,任務堆棧用的是 PSP,然而任務切換是在中斷處理函數 PendSV() 中完成的。


(資料圖)

那么在任務切換期間,MCU在執行指令的過程中,是如何選擇堆棧指針呢?

下面逐步進行分析。

堆棧的基本操作

堆棧操作就是對內存的讀寫操作,其地址由 SP 給出。寄存器的數據通過 PUSH 操作存入堆棧,以后用 POP 操作從堆棧中取回。在 PUSH 與 POP 的操作中, SP 的值會按堆棧的使用法則自動調整,以保證后續的 PUSH 不會破壞先前 PUSH 進去的內容。

堆棧的功能就是把寄存器的數據放入內存,當一個任務或一段子程序執行完畢后,能夠恢復繼續執行。正常情況下, PUSH 與 POP 必須成對使用,而且參與的寄存器,不論是身份還是先后順序都必須完全一致。當 PUSH/POP 指令執行時, SP 指針的值也根著自減/自增。

Cortex‐M3 使用的是“向下生長的滿棧”模型。堆棧指針 SP 指向最后一個被壓入堆棧的 32位數值。在下一次壓棧時, SP 先自減 4,再存入新的數值。

POP 操作剛好相反:先從 SP 指針處讀出上一次被壓入的值,再把 SP 指針自增 4 。

在進入 ESR 時, CM3 會自動把一些寄存器壓棧,這里使用的是發生本異常的瞬間正在使用的 SP 指針(MSP 或者是 PSP)。離開 ESR 后,只要 ESR 沒有更改過 CONTROL[1],就依然使用發生本次異常的瞬間正在使用的 SP 指針來執行出棧操作。

堆棧使用控制

已經知道了 CM3 的堆棧是分為兩個:主堆棧和進程堆棧, 具體使用哪個堆棧(MSP 還是 PSP) 通過特殊寄存器 CONTROL[1] 來控制。

控制寄存器 CONTROL,有兩個用途:其一用于定義特權級別,其二用于選擇當前使用哪個堆棧指針。

當 CONTROL[1]=0 時,只使用 MSP,此時用戶程序和異常 handler 共享同一個堆棧。這也是復位后的缺省使用方式。

當 CONTROL[1]=1 時,線程模式將不再使用 MSP,而改用 PSP(handler 模式永遠使用 MSP)。這樣做的好處在哪里?原來,在使用 OS 的環境下,只要 OS 內核僅在 handler 模式下執行,用戶應用程序僅在用戶模式下執行,這種雙堆棧機制防止用戶程序的堆棧錯誤破壞 OS 使用的堆棧。

再介紹一下兩個操作模式,Cortex-M3 支持 兩個模式和兩個特權等級:

兩個模式:handler模式和線程模式兩個特權等級:特權級和用戶級

處理器處在線程狀態下時,既可以使用特權級,也可以使用用戶級;另一方面, handler 模式總是特權級的。在復位后,處理器進入線程模式+特權級。

在特權級下的代碼可以通過置位 CONTROL[0]來進入用戶級。而不管是任何原因產生了任何異常,處理器都將以特權級來運行其服務例程,異常返回后,系統將回到產生異常時所處的級別。

用戶級下的代碼不能再試圖修改 CONTROL[0]來回到特權級。它必須通過一個異常 handler,由那個異常handler 來修改 CONTROL[0],才能在返回到線程模式后拿到特權級。

運行在線程模式的用戶代碼使用 PSP,而異常服務例程則使用 MSP。這兩個堆棧指針的切換是智能全自動的,就在異常服務的始末由 CM3 硬件處理。

中斷處理過程

響應異常的第一個行動,就是自動保存現場的必要部分:依次把 xPSR, PC, LR, R12 以及 R3-R0 由硬件自動壓入適當的堆棧中。

當響應異常時,如果當前的代碼正在使用 PSP,則壓入 PSP,也就是使用進程堆棧;否則就壓入MSP,使用主堆棧。

一旦進入了服務例程,就將一直使用主堆棧。

在進入異常服務程序后,將自動更新 LR(鏈接寄存器R14) 的值為特殊的 EXC_RETURN。這是一個高28位全為1的值,只有 [3:0] 的值有特殊含義,如下表所示。當異常服務例程把這個值送往 PC 時,就會啟動處理器的中斷返回序列。因為LR 的值是由 CM3 自動設置的,所以只要沒有特殊需求,就不要改動它。

總結一下,可以得出三個合法的 EXC_RETURN 值 :

如果主程序在線程模式下運行,并且在使用MSP時被中斷,則在服務例程中 LR=0xFFFF_FFF9(主程序被打斷前的LR已被自動入棧)。

如果主程序在線程模式下運行,并且在使用 PSP 時被中斷,則在服務例程中 LR=0xFFFF_FFFD(主 程序被打斷前的LR已被自動入棧)。

PendSV 中斷介紹

SVC(系統服務調用,亦簡稱系統調用)和 PendSV(可懸起系統調用),它們多用于在操作系統之上的軟件開發中。 SVC 用于產生系統函數的調用請求。

SVC 異常通過執行 ”SVC” 指令來產生。 該指令需要一個立即數, 充當系統調用代號。SVC異常服務例程稍后會提取出此代號, 從而解釋本次調用的具體要求, 再調用相應的服務函數。

例如,

SVC 0x3 ; 調用 3 號系統服務

在 SVC 服務例程執行后,上次執行的 SVC 指令地址可以根據自動入棧的返回地址計算出。找到了 SVC 指令后, 就可以讀取該 SVC 指令的機器碼,從機器碼中萃取出立即數,就獲知了請求執行的功能代號。

如果用戶程序使用的是 PSP, 服務例程還需要先執行 MRSRn,PSP指令來獲取應用程序的堆棧指針 。通過分析 LR 的值,可以獲知在 SVC 指令執行時,正在使用哪個堆棧

PendSV(可懸起的系統調用)和 SVC 協同使用。

SVC 異常是必須立即得到響應的(對于 SVC 異常來說,若因優先級不比當前正處理的高,或是其它原因使之無法立即響應,將造成成硬 fault ), 應用程序執行 SVC 時都是希望所需的請求立即得到響應。

PendSV 則不同,它是可以像普通的中斷一樣被懸起的。 OS 可以利用它“緩期執行” 一個異常——直到其它重要的任務完成后才執行動作。 懸起 PendSV 的方法是: 手動往 NVIC 的 PendSV 懸起寄存器中寫 1。 懸起后, 如果優先級不夠 高,則將緩期等待執行。

PendSV 的典型使用場合是在上下文切換時(在不同任務之間切換)。 例如, 一個系統中 有兩個就緒的任務,上下文切換被觸發的場合可以是:

執行一個系統調用系統滴答定時器(SYSTICK)中斷,(輪轉調度中需要)

PendSV 異常會自動延遲上下文切換的請求,直到其它的 ISR 都完成了處理后才放行。為實現這個機制,需要把 PendSV 編程為最低優先級的異常。

如果 OS 檢測到某 IRQ 正在活動并且被 SysTick 搶占,它將懸起一個 PendSV 異常,以便緩期執行上下文切換。

任務 A 呼叫 SVC 來請求任務切換(例如,等待某些工作完成)OS 接收到請求,做好上下文切換的準備,并且 pend 一個 PendSV 異常。當 CPU退出 SVC 后,它立即進入 PendSV,從而執行上下文切換。當 PendSV 執行完畢后,將返回到任務 B,同時進入線程模式。發生了一個中斷,并且中斷服務程序開始執行在 ISR 執行過程中,發生 SysTick 異常,并且搶占了該 ISR。OS 執行必要的操作,然后 pend 起 PendSV 異常以作好上下文切換的準備。當 SysTick 退出后,回到先前被搶占的 ISR 中, ISR 繼續執行ISR 執行完畢并退出后, PendSV 服務例程開始執行,并且在里面執行上下文切換當 PendSV 執行完畢后,回到任務 A,同時系統再次進入線程模式

RTOS系統中雙堆棧操作

一個真正健壯的 CM3 軟件系統是要使用實時操作系統內核的,通常會符合如下的要求:

服務例程使用 MSP盡管異常服務例程使用 MSP,但是它們在形式上返回后,內容上卻可以依然繼續——而且此時還能使用 PSP,從而實現“可搶占的系統調用”,大幅提高實時性能通過 SysTick,實時內核的代碼每隔固定時間都被調用一次,運行在特權級水平上,負責任務的調度、任務時間管理以及其它系統例行維護用戶應用程序以線程的形式運行,使用 PSP,并且在用戶級下運行內核在執行關鍵部位的代碼時,使用 MSP,并且在輔以 MPU 時, MSP 對應的堆棧只允許特權級訪問

在操作系統中,對于 EXC_RETURN 的修改,只是再尋常不過基本需求。在開始調度用戶程序后,一定還伴隨著 SysTick 異常,它周期性把執行權轉入操作系統,從而使例行的系統管理以及必要輪轉調度得以維持,任務切換過程如圖所示:

上圖為 SysTick 異常推動時間片輪轉調度模式圖。在這里,使用 PendSV(一個優先級最低的異常)來執行上下文切換,從而消滅了在中斷服務例程中出現上下文切換的可能。

在 SysTick 中斷例程中執行必要的操作,然后懸掛起 PendSV 異常以作好上下文切換的準備。退出 SysTime 中斷處理函數后,PendSV 服務例程開始執行,并且在里面執行上下文切換。

在任務-1 和 任務2 程序執行過程中使用的是 PSP(線程堆棧)。進入中斷服務程序后(SysTime 和 PendSV)在內部使用的是MSP(主堆棧)。

標簽:

上一篇:NAND閃存內部結構解析
下一篇:最后一頁