Arm架構是一種弱序內存架構,允許以不同於程序順序的順序觀察和完成內存訪問。本章的以下部分提供了ARMv8內存模型的完整定義,本文的介紹並不是要與這些部分中的定義相矛盾。一般來說,ARMv8內存模型的基本原則是:
· 提供與高級編程語言(如C或Java)所使用的內存模型有類似弱點的內存模型。例如,通過允許獨立的內存訪問將被重新排序,如其他觀察員所見。
· 避免在大多數內存類型中要求多複製原子性。
· 提供指令和內存屏障,以彌補在需要多複製原子性的情況下的不足。
· 在創建順序時使用地址、數據和控制依賴項,以避免在程序員或編譯器需要某種順序的常見情況下出現過多的屏障或其他顯式指令。
Address space
地址計算使用64位寄存器執行。但是,監視軟件可以將前8位地址配置為標籤,如果這樣做了,地址位[63:56]:
· 在確定地址是否有效時不考慮。
· 從不傳播到程序計數器。
監視軟件確定有效的地址範圍。試圖訪問無效的地址將生成一個MMU錯誤。
簡單的指令順序執行可能會溢出有效地址範圍。
內存訪問使用Mem[]函數。此函數訪問所需的類型。如果監視軟件將前8位地址配置為標記,則將忽略前8位地址。
AccType{}枚舉定義了不同的訪問類型。
內存類型概述
ARMv8提供了以下互斥的內存類型:
Normal:
這通常用於大容量內存操作,包括讀/寫操作和只讀操作。
Device:
Arm架構禁止對任何類型的設備內存進行推測性讀取。這意味著設備內存類型適合於對讀敏感的位置。
分配給外圍設備的內存映射位置通常分配給設備內存屬性。
設備內存有以下效果的附加屬性:
· 它們防止讀寫的聚合,保持指定內存訪問的數量和大小。
· 它們保留了訪問順序和同步需求,包括對單個外設的訪問和對一個或多個內存讀寫訪問的可觀察性的同步需求。
· 它們指示除了在結束點之外,是否可以確認寫入。
Arm架構中的原子性
原子性是內存訪問的一個特性,稱為原子訪問。Arm架構描述涉及兩種類型的原子性:單拷貝原子性和多拷貝原子性。在Armv8架構中,對內存訪問的原子性要求取決於內存類型,以及訪問是顯式的還是隱式的。
單拷貝原子性的要求
對於從異常級別生成的顯式內存訪問,應用以下規則:
由加載指令生成的讀操作是單拷貝原子操作,該指令加載一個通用寄存器,並與指令中的讀操作大小對齊。
由存儲單個通用寄存器的存儲指令生成並與指令中的寫大小對齊的寫是單拷貝原子的。
由加載兩個通用寄存器並與每個寄存器的負載大小對齊的加載對指令生成的讀取被視為兩個單拷貝原子讀取,每個讀取的寄存器對應一個原子讀取。
由存儲兩個通用寄存器的存儲對指令生成並與每個寄存器的存儲大小對齊的寫操作被視為兩個單拷貝原子寫操作,每個寄存器對應一個原子寫操作。
兩個32位數量的負載獨佔對指令和32位數量的存儲獨佔對指令是單拷貝原子的。
當使用兩個64位數量的Load-Exclusive/Store-Exclusive對指令的Store-Exclusive成功時,將導致整個內存位置的單拷貝原子更新被更新。
翻譯表遍歷生成對翻譯表項的讀操作,這種讀操作是單拷貝原子的。
獲取指令的原子性
對SIMD和浮點寄存器的讀取是單64位或更小的,並且與所加載的數量的大小保持一致,這被視為單拷貝原子讀取。
從SIMD和浮點寄存器中寫入一個64位或更小的數量(與存儲的數量大小一致)被視為單拷貝原子寫入。
將元素或結構讀入SIMD和64位或更小的元素的浮點寄存器(其中每個元素與所加載元素的大小對齊),將每個元素視為單拷貝原子讀。
元素或結構從SIMD和64位或更小的元素的浮點寄存器寫入,其中每個元素與存儲的元素的大小對齊,將每個元素視為單拷貝原子存儲。
對內存中對齊為64位的128位值的SIMD和浮點寄存器的讀操作被視為一對單拷貝64位原子讀操作。
從SIMD和內存中對齊為64位的128位值的浮點寄存器的寫操作被視為一對單拷貝64位原子寫操作。
所有其他的內存訪問都被看作是對字節的訪問流,架構保證了不同字節之間的原子性。
對任何字節的所有訪問都是單拷貝原子的。
如果根據這些規則,一條指令以訪問序列的形式執行,那麼在該序列中可以執行異常(包括中斷),而不管訪問的內存類型是什麼。如果使用這些異常的首選返回地址返回其中任何一個異常,則會重新執行生成訪問序列的指令,因此在採取異常之前執行的任何訪問都會重複。
這些多重訪問指令的異常行為意味著它們不適合用於軟件同步目的的內存寫操作。
ARMv8.4中對單拷貝原子性的更改
當滿足以下條件時,ARMv8.4-RCpc中引入的指令是單拷貝原子:
· 所有被訪問的字節都在相同的16字節數量內,並對齊到16字節。
· 訪問是內部寫回,外部寫回正常的可緩存內存
否則,實現定義它們是否是單拷貝原子。
如果實現ARMv8.4-LSE,當滿足以下條件時,所有加載和存儲都是單拷貝原子的:
· 訪問不與數據大小對齊,而是在一個與16字節對齊的16字節數量內進行對齊。
· 訪問是內部寫回,外部寫回正常的可緩存內存。
否則,不管訪問小於16字節的LDP、LDNP或STP指令是單拷貝原子指令,都要定義實現。
單拷貝原子訪問的屬性
單拷貝原子內存訪問指令具有以下屬性:
對於一對重疊的單拷貝原子存儲指令,其中一個存儲生成的所有重疊寫操作都是一致的——在另一個存儲生成相應的重疊寫操作之後。
單份原子負荷指令L1重疊單份原子存儲指令S2,如果其中一個重疊的讀取由L1程序讀取一個重疊寫入由S2,然後生成的重疊寫道S2都在相應的重疊讀取由L1。
多拷貝原子性
在多處理系統中,如果下列條件都為真,則寫入內存位置是多拷貝原子的:
· 對同一位置的所有寫操作都是序列化的,這意味著所有的觀察者都以相同的順序觀察它們,儘管有些觀察者可能不會觀察所有的寫操作。
· 在所有觀察者都觀察到寫之前,一個位置的讀操作不會返回寫操作的值。
多拷貝原子性的要求
對於普通內存,寫不需要是多拷貝原子的。
對於設備內存,寫不需要是多拷貝原子的。
ARMv8內存模型是另一個多拷貝原子模型。
指令的併發修改和執行
Armv8架構限制了一個執行線程可以執行的指令集,因為它們正在被另一個執行線程修改,而不需要顯式的同步。
併發修改和執行的指令可能會導致產生的指令執行任何行為可以通過執行任何可執行的指令序列相同的異常水平,除了在每個指令修改前和修改後的指令是一個B,BL,BRK, HVC,ISB,NOP, SMC或SVC指令。
對於B、BL、BRK、HVC、ISB、NOP、SMC、SVC指令,體系結構保證指令修改後的行為與其中任何一個的執行是一致的:
· 指令最初獲取。
· 修改後的指令的取回
如果一個執行線程將一個條件分支指令(如B或BL)更改為另一個條件指令,而更改同時影響條件字段和分支目標,則在同步更改之前由另一個執行線程執行更改的指令可能導致:
· 與新目標地址關聯的舊條件
· 與舊目標地址關聯的新條件。
不管條件是在分支指令更改之前還是之後,這些可能性都適用於always條件。
對於所有其他指令,為了避免不可預測或受約束的不可預測行為,必須在執行指令修改之前顯式地同步它們。所需同步如下:
· 當另一個PE修改指令時,任何PE都不能執行該指令。
· 為了確保修改後的指令是可見的,寫指令的PE必須發出以下指令和操作序列:
在同一個內部可共享域內對數據和指令訪問的一致性示例。
輸入這段代碼,
存在可緩存空間中,位於Xn指向的位置。
STR Wt, [Xn]
DC CVAU, Xn ; 通過VA清理數據緩存到統一點(PoU)
DSB ISH ; 確保從緩存中清除的數據的可見性
IC IVAU, Xn ; 從VA到PoU使指令緩存失效
DSB ISH ; 確保失效的完成
如果內存區域是不可緩存的或寫通過緩存的,則不需要DC CVAU操作。
如果映射之間的物理內存內容不同,那麼將VAs映射更改為PAs會導致一個PE併發地修改指令,另一個PE執行指令。如果修改影響的指令不是那些列出的修改可接受的指令,那麼必須使用同步來避免不可預測的或受約束的不可預測的行為。
在多處理器系統中,IC IVAU被廣播到運行該序列的PE的內部可共享域內的所有PEs。但是,當修改指令可見時,每一個執行修改指令的PE必須發出以下指令,以保證修改指令的執行:
ISB ; 同步獲取的指令流
使用原子指令的可能實現限制
在某些實現中,對於某些內存類型,原子性的屬性只能通過PE之外的功能來滿足。有些系統實現可能不支持內存所有區域的原子指令。這特別適用於:
· 系統中不支持硬件緩存一致性的任何類型的內存。
· 設備,不可緩存的內存,或在支持硬件緩存一致性的實現中被視為不可緩存的內存。
在這些實現中,由系統定義:
· 相對於訪問內存的其他代理而言,原子指令是否是原子性的。
· 如果原子指令相對於訪問內存的其他代理而言是原子指令,則此指令適用於哪些地址範圍或內存類型。
實現可以選擇將哪種內存類型視為不可緩存的。
在架構上保證原子指令將是原子的內存類型有:
· 內部可共享,內部寫回,外部寫回正常內存與讀分配提示和寫分配提示,而不是瞬態。
· 外部可共享,內部寫回,外部寫回正常內存與讀分配提示和寫分配提示,而不是瞬態。
如果相對於訪問內存的其他代理而言,原子指令不是原子指令,那麼對這樣的位置執行原子指令可能會產生以下一種或多種效果:
· 該指令生成一個同步外部中止。
· 指令產生一個系統錯誤中斷。
該指令生成一個使用ESR_ELx. DFSC = 110101的數據中止錯誤狀態代碼報告的MMU錯誤定義的實現。
對於EL1和0轉換機制,如果由於在翻譯的第一階段定義的內存類型而不支持原子指令,或者翻譯的第二階段未啟用,則此異常為第一階段中止,並被帶到EL1。否則,異常是第二個階段中止並被帶到EL2。
該指令被視為NOP
雖然執行了指令,但不能保證對內存的訪問是原子性的,與訪問內存的其他代理無關。在這種情況下,指令也可能產生系統錯誤中斷。
閱讀更多 嵌入式ing 的文章