Cortex-M 實現互斥操做的三種方法

注:本文僅針對Cortex-M3/4 系列進行講述。架構

在傳統的ARM處理器架構中,常使用SWP指令來實現鎖的讀/寫原子操做,但從ARM v6開始,讀/寫訪問在獨立的兩條總線上進行,SWP指令已沒法在此架構下保證讀/寫訪問的原子操做,所以互斥訪問指令應運而生。本文結合項目中運用的相關方法,總結Cortex-M芯片經常使用的互斥訪問方法。函數

 

互斥訪問方式1--LDREX/STREX指令

ARM支持的互斥指令對有LDREX/STREX、LDREXB/STREXB 及 LDREXH/STREXH(專有的寄存器加載/存儲指令),其分別支持字/字節/半字訪問,本節以LDREX/STREX爲例.ui

 

語法格式

LDREX{cond} Rt, [Rn {, #offset}]atom

STREX{cond} Rd, Rt, [Rn {, #offset}] spa

其中3d

 cond: 可選狀態碼-若指令包含此狀態碼,則只有當APSR寄存器中的狀態位知足狀態碼條件時,指令纔會執行指針

 Rd: 目的寄存器-指令執行後的返回狀態,0執行成功,1執行失敗blog

 Rt: 待加載/存儲的寄存器事件

 Rn: 寄存器地址內存

 offset: 可選的地址偏移

 

基本要求

使用互斥訪問指令時,需知足如下基本要求,以防不可預期的結果出現。

 1. LDREX/STREX必須成對出現

 2. LDREX/STREX的Rn寄存器地址必須一致,操做的寄存器長度必須一致

 3. LDREX/STREX之間不得使用PC指針,操做的寄存器不使用SP指針

 4. LDREX/STREX之間的指令要儘量的簡短,offset需4字節對齊,範圍在0~1020之間(不一樣的廠商設置範圍不一樣) 

 

互斥寫失敗狀況

在知足基本要求後,互斥寫不必定成功,如互斥操做中途遇到如下狀況:

 1. 調用CLREX指令清除互斥狀態

 2. 發生上下午切換(如中斷)

 3. 以前未執行過LDREX

 4. 總線反饋的互斥錯誤

 

使用方法

以nRF52源碼中的 nrf_atomic_internal_orr() 函數爲例,該函數實現了或運算的原子操做,其中p_ptr爲初始值,value爲或運算因子,p_new爲運算後的值,函數返回原爲子操做以前的p_ptr的值。

先簡單描述上述各行代碼:

89: r0/r1/r2分別存儲的p_ptr/value/p_new的值

94:將p_ptr地址付給r4

 

97:將r4所指向的值賦給r0,r0得到了p_ptr此時的值

98:對r0存儲的值進行或運算,運算值賦給r5

99:將r5的值存儲給r4指向的地址,即更新p_ptr的值,同時將本條指令的執行結果賦給r3

 

100/101:判斷返回值r3,若不爲0,重試 97~99的操做

103/104/105:將運算值賦給r2指向的值,即獲得新值

 

代碼的關鍵在97行,需注意的是,當函數執行結束返回時,r0存儲函數的返回值,所以此函數的返回值爲原子操做以前的p_ptr值,而不是調用此函數時傳入的p_ptr值(中途可能有變)

 

以實際場景爲例,倘若存在兩個任務A和B,以及一個共享內存Mem,互斥變量Flag標記Mem是否正在被佔用(0:空閒中,1:佔用中),要如何實現呢?

 

狀況1. A/B前後訪問Mem,則

  1. A首先調用 nrf_atomic_internal_orr() 函數(Flag=0),嘗試原子操做,此時R0=0,執行結束後,由返回值R0可知,Flag成功由0->1,A佔用Mem成功

  2. 此時發生任務切換

  3. B調用 nrf_atomic_internal_orr() 函數(Flag=1),嘗試原子操做,此時R0=1,執行結束後,由返回值R0可知,Flag在置位以前已是1,B佔用Mem失敗

  注:由於只有A/B前後訪問nrf_atomic_internal_orr()函數,所以各自只須要嘗試一次原子操做便可成功。

 

狀況2.A/B同時訪問Mem,A在原子操做過程當中被B搶佔,則

  1. A首先調用 nrf_atomic_internal_orr() 函數(Flag=0),嘗試第一次原子操做,此時R0=0,此時發生任務切換

  2. A被搶佔,上下文切換退出

  3. B調用 nrf_atomic_internal_orr() 函數(Flag=0),嘗試第一次原子操做,此時R0=0,執行結束後,由返回值R0可知,Flag成功由0->1,B佔用Mem成功

  4. 此時發生任務切換

  5. A繼續執行第一次原子操做,因在LDREX/STREX之間已發生上下文切換,這次原子操做STREX返回 1,執行失敗

  6. A繼續執行第二次原子操做,注意:此時R0重載,R0=1,執行結束後,由返回值R0可知,Flag在置位以前已是1,A佔用Mem失敗

 

所以本例中,調用nrf_atomic_internal_orr() 執行原子操做後,經過判斷函數返回值可知,本次互斥操做是否搶佔資源成功。

 

互斥訪問方式2--Bit-Band操做

 在支持 「locked transfers」或僅有單個總線主機的內存系統中,使用位帶操做也可實現信號量操做。要實現互斥訪問某個資源,操做過程當中需遵循如下幾點:

 1. 系統爲每一個需互斥訪問的任務分配一個位帶bit位,

 2. 任務僅能對本身的bit位進行讀-修改-寫操做。

 2. 不能以常規的寫方式來直接修改位帶區域值,不然可能丟失已鎖定的位信息

 

具體操做過程直接上圖:

優勢:可以使用C代碼直接實現上述互斥訪問邏輯。

  

互斥訪問方式3--關中斷

最爲簡單粗暴的互斥訪問方法,FreeRTOS的信號量獲取/釋放操做便採用此方式進入臨界區。

 

關中斷實現起來雖然簡單,但也需根據具體場景來選擇關總中斷仍是外設中斷,不然可能下降系統的實時性甚至形成數據丟失。

舉例來講,在以前經歷的一個項目中,有一款MCU既須要負責USB數據的收發,同時還得處理無線數據的轉發,如在處理USB臨界區數據時選擇關總中斷,則可能致使無線數據沒法及時處理甚至致使丟包,在該場景下,若選擇只關閉USB中斷,則MCU依然可以在實現局部互斥操做的同時實時響應優先級更高的事件。

 

參考手冊

 

TI 《Cortex-M3/M4F Instruction Set》

宋巖 《Cortex -M3 權威指南》

《The Definitive guild to the ARM Cortex-M3》Second Edition》

《The Definitive guild to the ARM Cortex-M3 and Cortex-M4 Processors》Third Edition

相關文章
相關標籤/搜索