本章介紹ARM處理器的基礎特性,包括寄存器、工作模式和指令集的細節。我們也會(huì )涉及一些處理器實(shí)現細節,包括指令流水線(xiàn)和分支預測。
ARMv7架構是一個(gè)32位處理器架構。它是一種load/store架構,意味著(zhù)數據處理指令操作通用寄存器中的值。只有加載(load)和存儲(store)指令訪(fǎng)問(wèn)存儲器。通用存儲器也是32位的。本書(shū)中,字(word)代表32位,雙字(doubleword)代表64位,半字(halfword)代表16位寬。
盡管ARMv7架構是一種32位架構,單獨的處理器對所有模塊和內部連接的實(shí)現不一定必須都是32位寬。例如,對于指令獲取和數據訪(fǎng)問(wèn),具有64位或更寬的路徑是可能的。
實(shí)現ARMv7-A架構的處理器不具有由架構固定的存儲器映射。處理器能訪(fǎng)問(wèn)4GB的虛擬地址空間,存儲器和外設都可以在那個(gè)空間范圍之內自由地映射。我們將會(huì )在第9章和第10章中講述存儲器管理,那里我們會(huì )看到緩存和內存管理單元(MMU)。
4.1 指令集
大多數ARM處理器支持超過(guò)一種指令集:
>ARM--- 一種32位指令集
>Thumb--- 一種16位指令集,具有更好的代碼密度(但是相比ARM代碼,性能有所降低)在程序的控制之下,處理器可以在這兩種指令集之間來(lái)回切換。
所有的Cortex-A系列處理器實(shí)現了Thumb-2技術(shù),它擴展了Thumb指令集;旌鲜褂32位和16位指令,以Thumb指令集的代碼密度和接近ARM指令集的性能。自從所有的Cortex-A系列處理器支持這一擴展,針對它們的軟件常被編譯成Thumb指令集。(本文作者注:按照文章《如何使用Thumb-2改善代碼密度和性能》所述,Thumb-2代碼大小約為ARM代碼的74%,性能則為其98%)
4.2 模式
ARM架構有9種處理器模式,在表4-1中所總結。有8種特權模式和一種非特權模的用戶(hù)模式。在用戶(hù)模式,在某些操作上具有限制,例如MMU訪(fǎng)問(wèn),修改操作模式是一種特權操作。注意,模式與異常事件有聯(lián)系,將會(huì )在第12章異常處理中講述。
4.3 寄存器
ARM架構具有一批通用寄存器,提供處理器內部的數據存儲。除了通用寄存器,還有R15,程序計數器,和包含ALU標志位與其它執行狀態(tài)信息的程序狀態(tài)寄存器。這些寄存器中很多是分組的,那就是,除非是在特定處理器模式,否則處理器不能訪(fǎng)問(wèn)。當進(jìn)入一個(gè)不同的處理器模式,這些分組的寄存器自動(dòng)地切入和換出。每個(gè)異常模式(不包括系統模式)附加有一個(gè)備份程序狀態(tài)寄存器,包含一份異常觸發(fā)時(shí)程序狀態(tài)寄存器的拷貝。
因此,在圖4-1中,如果處理器是在IRQ模式,我們可以看見(jiàn)R0,R1...R12(與在用戶(hù)模式看到的相同的寄存器),加上SP_IRQ和LR_IRQ(僅在IRQ模式中可以訪(fǎng)問(wèn)的寄存器)和R15(程序計數器,PC)。我們通常不必指定模式中的寄存器名。如果我們在一行代碼中引用R13,處理器會(huì )訪(fǎng)問(wèn)當前模式對應的SP寄存器。
在用戶(hù)模式,程序狀態(tài)寄存器(CPSR)的受限形式被稱(chēng)為應用程序狀態(tài)寄存器(APSR)。R15是程序計數器,保持當前程序地址(實(shí)際上,在A(yíng)RM狀態(tài),它總是指向提前8個(gè)字節當前指令的位置;在Thumb狀態(tài),它總是指向提前4個(gè)字節當前指令的位置)。
我們可以寫(xiě)值到PC以修改程序流。LR是鏈接寄存器,存放函數或異常的返回地址。R13,通常被用作堆棧指針。R0-R12是通用寄存器。一些16位Thumb指令可訪(fǎng)問(wèn)的寄存器受限---可訪(fǎng)問(wèn)的子集被稱(chēng)為低寄存器(low registers),由R0-R7組成。圖4-2顯示了對通用數據處理指令可見(jiàn)的寄存器子集。
R0-R14的復位值不可預測。堆棧指針(SP),在使用堆棧之前,必須被啟動(dòng)代碼初始化。AAPCS或AEABI(見(jiàn)第17章 應用二進(jìn)制接口)指定了軟件應該如何使用通用寄存器,為了不同工具鏈或編程語(yǔ)言之間的互操作。
4.3.1 程序狀態(tài)寄存器
程序狀態(tài)寄存器形成了一組額外的分組寄存器集。每中異常模式有它自己的備份程序狀態(tài)寄存器(Saved Program Status Register ,SPSR),當一個(gè)異常發(fā)生時(shí)自動(dòng)保存一份異常前CPSR拷貝。
《ARM架構參考手冊》(ARM Architecture Reference Manual)描述了程序狀態(tài)是如何在32位應用程序狀態(tài)寄存器(Application Program Status Register ,APSR)中報告的,其它狀態(tài)和控制位(系統級信息)任然保留在CPSR。本書(shū)中涉及的ARMv7-A架構,APSR實(shí)際上同CPSR是相同的寄存器,盡管它們有分開(kāi)的名字。APSR只能訪(fǎng)問(wèn)N/Z/C/V/Q和GE[3:0]位。這些位通常不能直接訪(fǎng)問(wèn),由條件代碼設置指令設置,并由有條件執行的指令測試。重命名因此是嘗試清理舊的ARM架構中CPSR的混合訪(fǎng)問(wèn),圖4-3顯示了CPSR的組成。
獨立位代表如下:
> N --- 來(lái)自ALU的負結果
> Z --- 來(lái)自ALU的零結果
> C --- ALU操作進(jìn)位
> V --- ALU操作溢出
> Q --- 累積飽和
> J --- 指示處理器是否在加速狀態(tài)
> GE[3:0] --- 由一些SIMD指令使用
> IT[7:2] --- Thumb-2指令組的If-Then條件執行
> E --- 控制load/store字節序
> A --- 禁止異步中止
> I --- 禁止IRQ
> F --- 禁止FIQ
> T --- 指示處理器是否在Thumb狀態(tài)
> M[4:0] --- 指定處理器模式(FIQ,IRQ等)
處理器可以在模式間切換通過(guò)指令直接寫(xiě)CPSR模式位(用戶(hù)模式不能)。更為常見(jiàn)的,處理器修改模式是異常事件的結果。
4.4 指令流水線(xiàn)
指令流水線(xiàn)是一種被用于處理器設計的技術(shù),提升指令的吞吐量。信息的處理由一連串對輸入數據不同的操作組成。為了優(yōu)化性能,處理器核被設計為級聯(lián)的基本功能單元塊,每個(gè)塊由前面單元的輸出作為輸入:在每個(gè)時(shí)鐘,單元N處理單元N-1的輸出數據。這么選擇主要的原因是簡(jiǎn)單的處理塊能夠比復雜的運行得快,因此流水線(xiàn)的結構能夠讓數字電路設計工作在GHz時(shí)鐘頻率。每個(gè)指令從一步移動(dòng)到另一步,通過(guò)幾個(gè)時(shí)鐘周期。
每個(gè)流水線(xiàn)階段處理執行一個(gè)指令過(guò)程的一部分,因此在給定的任何時(shí)鐘周期,一些不同的指令可能會(huì )在流水線(xiàn)的不同階段。處理器的總體速度由最慢一步的速度決定,這比執行所有步驟需要的時(shí)間極大地減少。一個(gè)非流水線(xiàn)架構較為低效,因為處理器內一些模塊會(huì )在指令執行期間被閑置。
經(jīng)典的流水線(xiàn)由三個(gè)階段組成 --- 取指、譯碼和執行,如圖4-4中所示。更為普遍地,一個(gè)指令流水線(xiàn)會(huì )被分成下面廣泛的定義:
> 指令預取:決定將會(huì )被獲取指令在內存中的位置,執行相關(guān)總線(xiàn)訪(fǎng)問(wèn)
> 指令獲取:從內存系統中讀取讀取將被執行的指令
> 指令譯碼:得出將被執行的指令,為數據路徑生成合適的控制信號
> 寄存器獲取:提供正確的寄存器值
> 發(fā)出:發(fā)出指令到合適的執行單元
> 執行:實(shí)際的ALU或乘法器操作等
> 內存訪(fǎng)問(wèn):執行數據加載或存儲
> 寄存器寫(xiě)回:用結果更新處理器寄存器
在單獨的處理器實(shí)現時(shí),這些步驟的一些可被組合成一個(gè)單一的流水線(xiàn)階段,或一些步驟可被分布在幾個(gè)周期。較長(cháng)的流水線(xiàn)意味著(zhù)每個(gè)流水線(xiàn)之間的關(guān)鍵路徑中更少的邏輯門(mén),從而更快地執行。然而,典型地指令之間會(huì )有依賴(lài)。如果一個(gè)指令依賴(lài)于前一指令的結果,控制邏輯需要插入位置(或泡沫)到流水線(xiàn),直到依賴(lài)解決。需要附加的邏輯來(lái)檢測和解決這樣的依賴(lài)。
一般來(lái)說(shuō),ARM架構試圖對編程者隱藏流水線(xiàn)影響。這意味著(zhù),編程者只能通過(guò)閱讀處理器手冊來(lái)決定流水線(xiàn)結構。然而,一些流水線(xiàn)工件仍然存在。例如,程序計數器寄存器(R15)在A(yíng)RM狀態(tài)時(shí),指向超前于當前正執行指令兩個(gè)指令的指令,最初ARM1處理器三階段流水線(xiàn)的遺留物。
長(cháng)流水線(xiàn)一個(gè)更近一步的缺點(diǎn)是有時(shí)來(lái)自?xún)却娴?/font>指令的串行執行會(huì )被打斷。這可能會(huì )發(fā)生在一個(gè)分支指令執行的結果,或被一個(gè)異常事件(例如一個(gè)中斷)。當此發(fā)生時(shí),處理器無(wú)法決定下一條應該被獲取指令的正確位置,直到分支被解決。在典型的代碼中,很多分支指令是有條件的,作為循環(huán)或者if聲明的結果。因此,分支是否會(huì )被執行不能再指令獲取時(shí)決定。如果我們獲取的指令緊跟著(zhù)一個(gè)分支,并且分支被執行,流水線(xiàn)必須清空,來(lái)自分支目的地的指令新指令集必須從內存獲取。隨著(zhù)流水線(xiàn)變長(cháng),分支懲罰的代價(jià)變得更高。
Cortex-A系列處理器具有分支預測邏輯,目的是減少分支懲罰的影響。本質(zhì)上,處理器預測一個(gè)分支是否會(huì )被執行,獲取的指令要么來(lái)自緊跟分支之后的指令(如果預測表明條件分支不會(huì )被執行),要么來(lái)自分支的目標指令(如果預測表明分支會(huì )被執行)。如果預測正確,分支不會(huì )清空流水線(xiàn)。如果預測錯誤,流水線(xiàn)必須清空,并且獲取來(lái)自正確位置的指令來(lái)充填。我們將在下面的分支預測里看到更多細節。
4.4.1 多發(fā)布流水線(xiàn)
一個(gè)精致的處理器流水線(xiàn)是我們可以有超過(guò)一個(gè)硬件單元來(lái)處理流水線(xiàn)階段。在A(yíng)RM11處理器家族,例如,有三個(gè)并行后端流水線(xiàn) --- 一個(gè)ALU流水線(xiàn),一個(gè)load/store流水線(xiàn)和一個(gè)乘法流水線(xiàn)。指令可被發(fā)布進(jìn)這些中的任何流水線(xiàn)。這個(gè)想法的一個(gè)合乎邏輯的發(fā)展是,有多個(gè)執行硬件的實(shí)例 --- 例如兩個(gè)ALU流水線(xiàn)。我們就可以在一個(gè)周期發(fā)布超過(guò)一個(gè)指令到這些并行流水線(xiàn) --- 一個(gè)指令級并行的例子。這樣的處理器被稱(chēng)為超標量體系結構(superscalar)。Cortex-A8,Cortex-A9和Cortex-A15處理器是超標量處理器 --- 它們可以在一個(gè)時(shí)鐘周期中潛在地譯碼和發(fā)布超過(guò)一個(gè)指令。Cortex-A5和Cortex-A7處理器相對受限制,只能雙發(fā)射特定指令的組合 --- 例如,一個(gè)分支和一個(gè)數據處理指令可以在同一周期內發(fā)布。指令仍然從內存中的串行指令流中發(fā)布。需要額外的硬件邏輯來(lái)檢查指令間的依賴(lài)性,例如,在一個(gè)指令必須等待其它指令結果的情況下。
亂序執行為提升流水線(xiàn)效率提供了空間。如果指令被串行執行,一個(gè)指令在下一個(gè)處理之前完全退休。在亂序處理中,多內存訪(fǎng)問(wèn)可以是立刻顯著(zhù)的,并且可以以不同的順序完成。
經(jīng)常地,一個(gè)指令必須停滯,由于依賴(lài)(例如,需要使用來(lái)自前面指令的結果)。我們可以執行下面的沒(méi)有共享這一依賴(lài)的指令,提供指令之間的邏輯危害是嚴格推崇的。Cortex-A9和Cortex-A15處理器使用這一技術(shù)實(shí)現非常高等級的效率和指令吞吐量。它們可以被認為是可變長(cháng)度的流水線(xiàn),因為流水線(xiàn)長(cháng)度取決于一個(gè)指令使用的后端執行流水線(xiàn)。它們可以獨立地執行指令,并且在每個(gè)時(shí)鐘可以譯碼兩個(gè)指令,但是有在一個(gè)獨立時(shí)鐘周期發(fā)布最高四條指令的能力。這可以提高性能,如果流水線(xiàn)已經(jīng)變得暢通,前面由于一些原因停滯了。
4.4.2 寄存器重命名
Cortex-A9處理器有一個(gè)有趣的微架構實(shí)現,使用了一個(gè)寄存器重命名方案。作為標準ARM架構一部分的寄存器集對程序員可見(jiàn),但是處理器的硬件實(shí)現實(shí)際上有一個(gè)更為大的物理寄存器池,邏輯上動(dòng)態(tài)地映射程序員可見(jiàn)寄存器到物理寄存器。圖4-5顯示了分開(kāi)的架構寄存器池和物理寄存器池。
考慮一種情況,代碼寫(xiě)一個(gè)寄存器的值到外部存儲器,緊接著(zhù)其后,讀取一個(gè)不同外部存儲器位置的值到相同的寄存器。這在前面的處理器中可能會(huì )引起流水線(xiàn)停滯,盡管在此特殊的例子中,沒(méi)有實(shí)際上的數據依賴(lài)。寄存器重命名避免這個(gè)問(wèn)題,通過(guò)確保兩個(gè)R0實(shí)例被重命名到不同的物理寄存器,移除了依賴(lài)。這允許了一個(gè)編譯器或匯編程序員在沒(méi)有內部指令之間的依賴(lài)時(shí),重用寄存器,而不需要考慮因為重用寄存器導致的架構上的懲罰。重要的是,這允許了write-after-write和write-after-read序列的亂序執行。(write-after-write的危害出現在兩個(gè)獨立的指令寫(xiě)值到相同的寄存器。處理器必須確保來(lái)自這兩條指令之后的指令看到的是后一條指令的結果)
為避免與標志位設置和比較相關(guān)的指令間的依賴(lài),APSR標志也使用相同的技術(shù)。
4.5 分支預測
如我們已看到的,分支預測邏輯在Cortex-A系列處理器中對實(shí)現高吞吐量是一個(gè)重要的因素。沒(méi)有分支預測,我們不得不等待知道一個(gè)條件分支執行,在我們能夠決定獲取下一條指令位置之前。
一個(gè)條件跳轉指令第一次被獲取時(shí),沒(méi)有關(guān)于下一條指令地址的可依賴(lài)信息。舊的ARM處理器使用靜態(tài)分支預測。這是最簡(jiǎn)單的分支預測方法,因為它不需要關(guān)于分支的先前信息。我們推測向后的分支會(huì )被采用,向前的分支不會(huì )。向后的分支具有一個(gè)目標地址,低于它自己的地址。我們因此可以看一個(gè)單一的操作位來(lái)決定分支方向。這個(gè)技術(shù)可以給與合理的預測精度,歸因于代碼中循環(huán)的普遍,循環(huán)代碼總是包含后向分支,并且后向總比前向多很多。由于Cortex-A系列處理器的流水線(xiàn)長(cháng)度,我們通過(guò)使用更為復雜的給予更好預測精度的分支預測方法,可以獲得更好的性能。
動(dòng)態(tài)分支預測可以進(jìn)一步減少平均分支懲罰,通過(guò)使用在前面執行時(shí)條件分支是否被采用的歷史信息。Cortex-A8處理器中的分支目標地址緩存(Branch Target Address Cache ,BTAC),也稱(chēng)為分支目標緩沖(Branch Target Buffer ,BTB),是一個(gè)緩存,保存有前次分支指令執行的信息。它使得硬件能夠推測一個(gè)條件分支是否會(huì )被采用。
處理器仍然必須評估附帶在分支指令上的條件代碼。如果分支預測硬件預測正確,流水線(xiàn)不必停滯。如果分支預測硬件評估錯誤,處理器必須清空流水線(xiàn)并充填它。
4.5.1 返回堆棧(return stack)
在分支預測中的描述看到了處理器可以用于預測分支是否會(huì )被采用的策略。對絕大多數分支指令,目標地址固定并編碼在指令中。然而,有一類(lèi)分支,分支目標的目的地不能通過(guò)看指令決定。例如,如果我們執行一個(gè)數據處理操作,它修改了PC(例如,MOV,ADD or SUB),我們必須等待ALU來(lái)評估結果,在我們能夠直到分支目標之前。類(lèi)似的,如果我們從內存加載PC,使用一個(gè)LDR,LSM,或POP指令,我們不能知道目標地址直到加載完成。
通常,這樣的分支(常稱(chēng)為間接分支)不能在硬件上被預測。然而,有一個(gè)常見(jiàn)的情況可以很有用地被優(yōu)化,在預取硬件中使用一個(gè)后進(jìn)先出堆棧(return stack)。任何時(shí)候一個(gè)函數調用(BL或BLX)指令被執行,我們進(jìn)入壓入接下來(lái)的指令到這個(gè)堆棧。任何時(shí)候當我們遇到一個(gè)指令,它可以被識別為函數返回指令(BX LR,或一個(gè)在寄存器列表中包含PC的堆棧pop操作),我們可以投機地從堆棧pop一個(gè)條目,硬件比較指令生成的地址和堆棧預測的地址。如果未命中,流水線(xiàn)被清空,我們從正確的位置重新開(kāi)始。
更多詳情可咨詢(xún)劉老師:電話(huà)18365409359,QQ3311615775深圳(南山+民治+龍崗+西鄉),廣州,鄭州,長(cháng)沙,南寧八大實(shí)訓基地統一授課中!
|