《Cortex™-A系列編程者指南(V3.0)》第4章筆記


文檔來源DEN0013C_cortex_a_series_PG.pdf
文檔說明:前三章略過,值得關注的是Linaro網站:www.linaro.org


  本章介紹ARM處理器的基礎特性,包括寄存器工作模式指令集的細節。我們也會涉及一些處理器實現細節,包括指令流水線分支預測


  ARMv7架構是一個32位處理器架構。它是一種load/store架構,意味着數據處理指令操作通用寄存器中的值。只有加載(load)和存儲(store)指令訪問存儲器。通用存儲器也是32位的。本書中,字(word)代表32位,雙字(doubleword)代表64位,半字(halfword)代表16位寬。


  儘管ARMv7架構是一種32位架構,單獨的處理器對所有模塊和內部連接的實現不一定必須都是32位寬。例如,對於指令獲取和數據訪問,具有64位或更寬的路徑是可能的。


  實現ARMv7-A架構的處理器不具有由架構固定的存儲器映射。處理器能訪問4GB的虛擬地址空間,存儲器和外設都可以在那個空間範圍之內自由地映射。我們將會在第9章和第10章中講述存儲器管理,那裏我們會看到緩存和內存管理單元(MMU)。



4.1 指令集


  大多數ARM處理器支持超過一種指令集:

 >ARM--- 一種32位指令集
 >Thumb--- 一種16位指令集,具有更好的代碼密度(但是相比ARM代碼,性能有所降低)
在程序的控制之下,處理器可以在這兩種指令集之間來回切換。


  所有的Cortex-A系列處理器實現了Thumb-2技術,它擴展了Thumb指令集。混合使用32位和16位指令,以Thumb指令集的代碼密度和接近ARM指令集的性能。自從所有的Cortex-A系列處理器支持這一擴展,針對它們的軟件常被編譯成Thumb指令集。(本文作者注:按照文章《如何使用Thumb-2改善代碼密度和性能》所述,Thumb-2代碼大小約爲ARM代碼的74%,性能則爲其98%)



4.2 模式


  ARM架構有9種處理器模式,在表4-1中所總結。有8種特權模式一種非特權模的用戶模式。在用戶模式,在某些操作上具有限制,例如MMU訪問,修改操作模式是一種特權操作。注意,模式與異常事件有聯繫,將會在第12章異常處理中講述。



4.3 寄存器


  ARM架構具有一批通用寄存器,提供處理器內部的數據存儲。除了通用寄存器,還有R15程序計數器,和包含ALU標誌位與其它執行狀態信息的程序狀態寄存器。這些寄存器中很多是分組的,那就是,除非是在特定處理器模式,否則處理器不能訪問。當進入一個不同的處理器模式,這些分組的寄存器自動地切入和換出。每個異常模式(不包括系統模式)附加有一個備份程序狀態寄存器,包含一份異常觸發時程序狀態寄存器的拷貝。


  因此,在圖4-1中,如果處理器是在IRQ模式,我們可以看見R0,R1...R12(與在用戶模式看到的相同的寄存器),加上SP_IRQ和LR_IRQ(僅在IRQ模式中可以訪問的寄存器)和R15(程序計數器,PC)。我們通常不必指定模式中的寄存器名。如果我們在一行代碼中引用R13,處理器會訪問當前模式對應的SP寄存器


  用戶模式程序狀態寄存器(CPSR)的受限形式被稱爲應用程序狀態寄存器(APSR)。R15是程序計數器,保持當前程序地址(實際上,在ARM狀態,它總是指向提前8個字節當前指令的位置;在Thumb狀態,它總是指向提前4個字節當前指令的位置)。


  我們可以寫值到PC以修改程序流。LR鏈接寄存器,存放函數或異常的返回地址。R13,通常被用作堆棧指針R0-R12通用寄存器。一些16位Thumb指令可訪問的寄存器受限---可訪問的子集被稱爲低寄存器(low registers),由R0-R7組成。圖4-2顯示了對通用數據處理指令可見的寄存器子集。


  R0-R14的復位值不可預測。堆棧指針(SP),在使用堆棧之前,必須被啓動代碼初始化。AAPCS或AEABI(見第17章 應用二進制接口)指定了軟件應該如何使用通用寄存器,爲了不同工具鏈或編程語言之間的互操作。


4.3.1 程序狀態寄存器


  程序狀態寄存器形成了一組額外的分組寄存器集。每中異常模式有它自己的備份程序狀態寄存器(Saved Program Status Register ,SPSR),當一個異常發生時自動保存一份異常前CPSR拷貝


  《ARM架構參考手冊》(ARM Architecture Reference Manual)描述了程序狀態是如何在32位應用程序狀態寄存器(Application Program Status Register ,APSR)中報告的,其它狀態和控制位(系統級信息)任然保留在CPSR。本書中涉及的ARMv7-A架構APSR實際上同CPSR是相同的寄存器,儘管它們有分開的名字。APSR只能訪問N/Z/C/V/Q和GE[3:0]位。這些位通常不能直接訪問,由條件代碼設置指令設置,並由有條件執行的指令測試。重命名因此是嘗試清理舊的ARM架構中CPSR的混合訪問,圖4-3顯示了CPSR的組成。


  獨立位代表如下:
 > N --- 來自ALU的負結果
 > Z --- 來自ALU的零結果
 > C --- ALU操作進位
 > V --- ALU操作溢出
 > Q --- 累積飽和
 > J --- 指示處理器是否在加速狀態
 > GE[3:0] --- 由一些SIMD指令使用
 > IT[7:2] --- Thumb-2指令組的If-Then條件執行
 > E --- 控制load/store字節序
 > A --- 禁止異步中止
 > I --- 禁止IRQ
 > F --- 禁止FIQ
 > T --- 指示處理器是否在Thumb狀態
 > M[4:0] --- 指定處理器模式(FIQ,IRQ等)


  處理器可以在模式間切換通過指令直接寫CPSR模式位(用戶模式不能)。更爲常見的,處理器修改模式異常事件的結果


4.4 指令流水線


  指令流水線是一種被用於處理器設計的技術,提升指令的吞吐量。信息的處理由一連串對輸入數據不同的操作組成。爲了優化性能,處理器核被設計爲級聯的基本功能單元塊,每個塊由前面單元的輸出作爲輸入:在每個時鐘,單元N處理單元N-1的輸出數據。這麼選擇主要的原因是簡單的處理塊能夠比複雜的運行得快,因此流水線的結構能夠讓數字電路設計工作在GHz時鐘頻率。每個指令從一步移動到另一步,通過幾個時鐘週期。每個流水線階段處理執行一個指令過程的一部分,因此在給定的任何時鐘週期,一些不同的指令可能會在流水線的不同階段。處理器的總體速度由最慢一步的速度決定,這比執行所有步驟需要的時間極大地減少。一個非流水線架構較爲低效,因爲處理器內一些模塊會在指令執行期間被閒置。


  經典的流水線由三個階段組成 --- 取指、譯碼和執行,如圖4-4中所示。更爲普遍地,一個指令流水線會被分成下面廣泛的定義:
 > 指令預取:決定將會被獲取指令在內存中的位置,執行相關總線訪問
 > 指令獲取:從內存系統中讀取讀取將被執行的指令
 > 指令譯碼:得出將被執行的指令,爲數據路徑生成合適的控制信號
 > 寄存器獲取:提供正確的寄存器值
 > 發出:發出指令到合適的執行單元
 > 執行:實際的ALU或乘法器操作等
 > 內存訪問:執行數據加載或存儲
 > 寄存器寫回:用結果更新處理器寄存器


  在單獨的處理器實現時,這些步驟的一些可被組合成一個單一的流水線階段,或一些步驟可被分佈在幾個週期。較長的流水線意味着每個流水線之間的關鍵路徑中更少的邏輯門,從而更快地執行。然而,典型地指令之間會有依賴。如果一個指令依賴於前一指令的結果,控制邏輯需要插入位置(或泡沫)到流水線,直到依賴解決。需要附加的邏輯來檢測和解決這樣的依賴。


  一般來說,ARM架構試圖對編程者隱藏流水線影響。這意味着,編程者只能通過閱讀處理器手冊來決定流水線結構。然而,一些流水線工件仍然存在。例如,程序計數器寄存器(R15)在ARM狀態時,指向超前於當前正執行指令兩個指令的指令,最初ARM1處理器三階段流水線的遺留物。


  長流水線一個更近一步的缺點是有時來自內存的指令的串行執行會被打斷。這可能會發生在一個分支指令執行的結果,或被一個異常事件(例如一箇中斷)。當此發生時,處理器無法決定下一條應該被獲取指令的正確位置,直到分支被解決。在典型的代碼中,很多分支指令是有條件的,作爲循環或者if聲明的結果。因此,分支是否會被執行不能再指令獲取時決定。如果我們獲取的指令緊跟着一個分支,並且分支被執行,流水線必須清空,來自分支目的地的指令新指令集必須從內存獲取。隨着流水線變長,分支懲罰的代價變得更高


  Cortex-A系列處理器具有分支預測邏輯,目的是減少分支懲罰的影響。本質上,處理器預測一個分支是否會被執行,獲取的指令要麼來自緊跟分支之後的指令(如果預測表明條件分支不會被執行),要麼來自分支的目標指令(如果預測表明分支會被執行)。如果預測正確,分支不會清空流水線如果預測錯誤流水線必須清空,並且獲取來自正確位置的指令來充填。我們將在下面的分支預測裏看到更多細節。

4.4.1 多發佈流水線


  一個精緻的處理器流水線是我們可以有超過一個硬件單元來處理流水線階段。在ARM11處理器家族,例如,有三個並行後端流水線 --- 一個ALU流水線,一個load/store流水線和一個乘法流水線。指令可被髮布進這些中的任何流水線。這個想法的一個合乎邏輯的發展是,有多個執行硬件的實例 --- 例如兩個ALU流水線。我們就可以在一個週期發佈超過一個指令到這些並行流水線 --- 一個指令級並行的例子。這樣的處理器被稱爲超標量體系結構(superscalar)Cortex-A8Cortex-A9Cortex-A15處理器超標量處理器 --- 它們可以在一個時鐘週期中潛在地譯碼和發佈超過一個指令。Cortex-A5和Cortex-A7處理器相對受限制,只能雙發射特定指令的組合 --- 例如,一個分支和一個數據處理指令可以在同一週期內發佈。指令仍然從內存中的串行指令流中發佈。需要額外的硬件邏輯來檢查指令間的依賴性,例如,在一個指令必須等待其它指令結果的情況下。


  亂序執行爲提升流水線效率提供了空間。如果指令被串行執行,一個指令在下一個處理之前完全退休。在亂序處理中,多內存訪問可以是立刻顯著的,並且可以以不同的順序完成。


  經常地一個指令必須停滯,由於依賴(例如,需要使用來自前面指令的結果)。我們可以執行下面的沒有共享這一依賴的指令,提供指令之間的邏輯危害是嚴格推崇的。Cortex-A9Cortex-A15處理器使用這一技術實現非常高等級的效率和指令吞吐量。它們可以被認爲是可變長度的流水線,因爲流水線長度取決於一個指令使用的後端執行流水線。它們可以獨立地執行指令,並且在每個時鐘可以譯碼兩個指令,但是有在一個獨立時鐘週期發佈最高四條指令的能力。這可以提高性能,如果流水線已經變得暢通,前面由於一些原因停滯了。


4.4.2 寄存器重命名


  Cortex-A9處理器有一個有趣的微架構實現,使用了一個寄存器重命名方案。作爲標準ARM架構一部分的寄存器集對程序員可見,但是處理器的硬件實現實際上有一個更爲大的物理寄存器池,邏輯上動態地映射程序員可見寄存器到物理寄存器。圖4-5顯示了分開的架構寄存器池和物理寄存器池。


  考慮一種情況,代碼寫一個寄存器的值到外部存儲器緊接着其後讀取一個不同外部存儲器位置的值到相同的寄存器。這在前面的處理器中可能會引起流水線停滯,儘管在此特殊的例子中,沒有實際上的數據依賴。寄存器重命名避免這個問題,通過確保兩個R0實例被重命名到不同的物理寄存器,移除了依賴。這允許了一個編譯器或彙編程序員在沒有內部指令之間的依賴時,重用寄存器,而不需要考慮因爲重用寄存器導致的架構上的懲罰。重要的是,這允許了write-after-write和write-after-read序列的亂序執行。(write-after-write的危害出現在兩個獨立的指令寫值到相同的寄存器。處理器必須確保來自這兩條指令之後的指令看到的是後一條指令的結果)


  爲避免與標誌位設置和比較相關的指令間的依賴,APSR標誌也使用相同的技術


4.5 分支預測


  如我們已看到的,分支預測邏輯在Cortex-A系列處理器中對實現高吞吐量是一個重要的因素。沒有分支預測,我們不得不等待知道一個條件分支執行,在我們能夠決定獲取下一條指令位置之前。


  一個條件跳轉指令第一次被獲取時,沒有關於下一條指令地址的可依賴信息。舊的ARM處理器使用靜態分支預測。這是最簡單的分支預測方法,因爲它不需要關於分支的先前信息。我們推測向後的分支會被採用向前的分支不會。向後的分支具有一個目標地址,低於它自己的地址。我們因此可以看一個單一的操作位來決定分支方向。這個技術可以給與合理的預測精度,歸因於代碼中循環的普遍,循環代碼總是包含後向分支,並且後向總比前向多很多。由於Cortex-A系列處理器的流水線長度,我們通過使用更爲複雜的給予更好預測精度的分支預測方法,可以獲得更好的性能。


  動態分支預測可以進一步減少平均分支懲罰,通過使用在前面執行時條件分支是否被採用的歷史信息Cortex-A8處理器中的分支目標地址緩存(Branch Target Address Cache ,BTAC),也稱爲分支目標緩衝(Branch Target Buffer ,BTB),是一個緩存保存有前次分支指令執行的信息。它使得硬件能夠推測一個條件分支是否會被採用。


  處理器仍然必須評估附帶在分支指令上的條件代碼。如果分支預測硬件預測正確,流水線不必停滯。如果分支預測硬件評估錯誤,處理器必須清空流水線並充填它。


4.5.1 返回堆棧(return stack)


  在分支預測中的描述看到了處理器可以用於預測分支是否會被採用的策略。對絕大多數分支指令,目標地址固定並編碼在指令中。然而,有一類分支,分支目標的目的地不能通過看指令決定。例如,如果我們執行一個數據處理操作,它修改了PC(例如,MOV,ADD or SUB),我們必須等待ALU來評估結果,在我們能夠直到分支目標之前。類似的,如果我們從內存加載PC,使用一個LDR,LSM,或POP指令,我們不能知道目標地址直到加載完成。


  通常,這樣的分支(常稱爲間接分支)不能在硬件上被預測。然而,有一個常見的情況可以很有用地被優化,在預取硬件中使用一個後進先出堆棧(return stack)。任何時候一個函數調用(BL或BLX)指令被執行,我們進入壓入接下來的指令到這個堆棧。任何時候當我們遇到一個指令,它可以被識別爲函數返回指令(BX LR,或一個在寄存器列表中包含PC的堆棧pop操作),我們可以投機地從堆棧pop一個條目硬件比較指令生成的地址和堆棧預測的地址。如果未命中,流水線被清空,我們從正確的位置重新開始。


  返回堆棧有固定的大小(例如,在Cortex-A8或Cortex-A9處理器中有8個條目)。如果一個特殊的代碼序列包含大量嵌套的函數調用,一個8條目返回堆棧可以預測前8個函數返回。


4.5.2 程序員的角度


  對於大多數應用級的程序員,分支預測是硬件實現的一部分,可以安全的被忽略。然而,處理器分支行爲的知識可以是有用的,當編寫高度優化的代碼時硬件性能監視器計數器可以生成關於分支正確與否預測數目的信息。這一硬件在第19章被進一步描述。


  分支預測邏輯在復位時被禁止。啓動代碼序列的一部分典型地會被用於設置CP15:SCTLR(系統控制寄存器)的Z位,這使能了分支預測。有一個其它的情形編程者需要注意。當在一個地址移動或修改代碼,在此地址上的代碼已在系統中被執行,從分支歷史邏輯中移除舊的條目是必要的(並且總是明智的),通過使用CP15指令作廢所有條目。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章