ARM體系結(jié)構(gòu)之:流水線
處理器按照一系列步驟來執(zhí)行每一條指令。典型的步驟如下:
① 從存儲(chǔ)器讀取指令(fetch);
② 譯碼以鑒別它是屬于哪一條指令(dec);
③ 從指令中提取指令的操作數(shù)(這些操作數(shù)往往存在于寄存器中)(reg);
④ 將操作數(shù)進(jìn)行組合以得到結(jié)果或存儲(chǔ)器地址(ALU);
⑤ 如果需要,則訪問存儲(chǔ)器以存儲(chǔ)數(shù)據(jù)(mem);
⑥ 將結(jié)果寫回到寄存器堆(res)。
并不是所有的指令都需要上述每一個(gè)步驟,但是,多數(shù)指令需要其中的多個(gè)步驟。這些步驟往往使用不同的硬件功能,例如,ALU可能只在第4步中用到。因此,如果一條指令不是在前一條指令結(jié)束之前就開始,那么在每一步驟內(nèi)處理器只有少部分的硬件在使用。
有一種方法可以明顯改善硬件資源的使用率和處理器的吞吐量,這就是當(dāng)前一條指令結(jié)束之前就開始執(zhí)行下一條指令,即通常所說的流水線(Pipeline)技術(shù)。流水線是RISC處理器執(zhí)行指令時(shí)采用的機(jī)制。使用流水線,可在取下一條指令的同時(shí)譯碼和執(zhí)行其他指令,從而加快執(zhí)行的速度??梢园蚜魉€看作是汽車生產(chǎn)線,每個(gè)階段只完成專門的處理器任務(wù)。
采用上述操作順序,處理器可以這樣來組織:當(dāng)一條指令剛剛執(zhí)行完步驟①并轉(zhuǎn)向步驟②時(shí),下一條指令就開始執(zhí)行步驟①。圖2.1說明了這個(gè)過程。從原理上說,這樣的流水線應(yīng)該比沒有重疊的指令執(zhí)行快6倍,但由于硬件結(jié)構(gòu)本身的一些限制,實(shí)際情況會(huì)比理想狀態(tài)差一些。
2.2.2 流水線的分類從Acorn Computer公司在1983~1985年間開發(fā)的第一個(gè)3µm器件,到ARM公司在1990~1995年間開發(fā)的ARM6和ARM7,ARM整數(shù)處理器核的組織結(jié)構(gòu)變化很小,這些處理器都是采用3級流水線,而這一時(shí)期CMOS工藝的發(fā)展,幾乎將特征尺寸減少了一個(gè)數(shù)量級。因此,核的性能提高很快,但基本的操作原理大部分沒有變化。
圖2.1 流水線的指令執(zhí)行過程
從1995年以來,ARM公司推出了幾個(gè)新的ARM核。它們采用5級流水線和哈佛架構(gòu),獲得了顯著的高性能。例如,ARM9增加了存儲(chǔ)器訪問段和回寫段,這使得ARM9的處理能力可達(dá)到平均1.1 Dhrystone1 MISP/MHz,與ARM7相比,指令吞吐量提高了約13%。
注意
在許多高性能處理器內(nèi)部,一級Cache一般都設(shè)置有兩個(gè),其中,一個(gè)是指令Cache,另一個(gè)是數(shù)據(jù)Cache。這樣可以減少取指令和讀操作數(shù)的訪問沖突,這種結(jié)構(gòu)被稱為哈佛架構(gòu)。
把主存儲(chǔ)器分成兩個(gè)獨(dú)立編址的存儲(chǔ)器,一個(gè)專門存放指令,稱為指令存儲(chǔ)器,簡稱指存;另一個(gè)專門存放操作數(shù),稱為數(shù)據(jù)存儲(chǔ)器,簡稱數(shù)存。兩個(gè)存儲(chǔ)器可以同時(shí)訪問,這樣就解決了取指令和讀操作數(shù)的沖突。如果在此基礎(chǔ)上規(guī)定在執(zhí)行指令階段產(chǎn)生的運(yùn)算結(jié)果只寫到通用寄存器中,不寫到主存,那么取指令、分析指令和執(zhí)行指令就可以同時(shí)進(jìn)行。
ARM10更是把流水線增加到6級。ARM10的平均處理能力達(dá)到1.3 Dhrystone MISP/MHz,與ARM7相比,指令吞吐量提高了約34%。
注意
雖然ARM9和ARM10的流水線不同,但它們都使用了與ARM7相同的流水線執(zhí)行機(jī)制,因此ARM7上的代碼也可以在ARM9和ARM10上運(yùn)行。
1.3級流水線ARM組織3級流水線ARM組織如圖2.2所示,其主要的組成如下:
① 處理器狀態(tài)寄存器堆(Rigister Bank)。它有兩個(gè)讀端口和一個(gè)寫端口,每個(gè)端口都可以訪問任意寄存器。另外還有附加的可以訪問PC的一個(gè)讀端口和一個(gè)寫端口。
注意
PC的附加寫端口可以在取指地址增加后更新PC,讀端口可以在數(shù)據(jù)地址發(fā)出之后從新開始取指。
② 桶形移位寄存器(Barrel Shifter)。它可以把一個(gè)操作數(shù)移位或循環(huán)移位任意位數(shù)。
③ ALU。完成指令集要求的算術(shù)或邏輯功能。
圖2.2 3級流水線ARM的組織
④ 地址寄存器(Address Register)和增值器(Incrementer)??蛇x擇和保存所用的存儲(chǔ)器地址并在需要時(shí)產(chǎn)生順序地址。
⑤ 數(shù)據(jù)輸出寄存器(data-out register)和數(shù)據(jù)輸入寄存器(data-in register)。用于保存?zhèn)鬏數(shù)酱鎯?chǔ)器和從存儲(chǔ)器輸出的數(shù)據(jù)。
⑥ 指令譯碼器和相關(guān)的控制邏輯(instruction decode and control)。
例2.1顯示了一條單周期指令在流水線上的執(zhí)行過程。
【例2.1】
ADD r1,r2
指令在流水線上的執(zhí)行過程如圖2.3所示。
圖2.3 單周期指令在流水線上的執(zhí)行過程
在ADD指令中,需要訪問兩個(gè)寄存器操作數(shù),B總線上的數(shù)據(jù)移位后與A總線上的數(shù)據(jù)在ALU中組合,再將結(jié)果寫回寄存器堆。在指令執(zhí)行過程中,程序計(jì)數(shù)器的數(shù)據(jù)放在地址寄存器中,地址寄存器的數(shù)據(jù)送入增值器。然后將增值后的數(shù)據(jù)拷貝到寄存器堆的r15(程序計(jì)數(shù)器),同時(shí)還拷貝到地址寄存器,作為下一次取指的地址。
到ARM7為止的ARM處理器使用簡單的3級流水線,包括下列流水線級:
· 取指(fetch):從寄存器裝載一條指令。
· 譯碼(decode):識(shí)別被執(zhí)行的指令,并為下一個(gè)周期準(zhǔn)備數(shù)據(jù)通路的控制信號(hào)。在這一級,指令占有譯碼邏輯,不占用數(shù)據(jù)通路。
· 執(zhí)行(excute):處理指令并將結(jié)果寫回寄存器。
圖2.4顯示了3級流水線指令執(zhí)行過程。
圖2.4 3級流水線
注意
在任一時(shí)刻,可能有3種不同的指令占有這3級中的每一級,因此,每一級中的硬件必須能夠獨(dú)立操作。
當(dāng)處理器執(zhí)行簡單的數(shù)據(jù)處理指令時(shí),流水線使得平均每個(gè)時(shí)鐘周期能完成1條指令。但1條指令需要3個(gè)時(shí)鐘周期來完成,因此,有3個(gè)時(shí)鐘周期的延時(shí)(latency),但吞吐率(throughput)是每個(gè)周期一條指令。例2.2通過一個(gè)簡單的例子說明了流水線的機(jī)制。
【例2.2】
指令序列為:
ADD r1 r2
SUB r3 r2
CMP r1 r3
流水線指令序列如圖2.5所示。
圖2.5 流水線指令順序
在第一個(gè)周期,內(nèi)核從存儲(chǔ)器取出指令A(yù)DD;在第二個(gè)周期,內(nèi)核取出指令SUB,同時(shí)對ADD譯碼;在第三個(gè)周期,指令SUB和ADD都沿流水線移動(dòng),ADD被執(zhí)行,而SUB被譯碼,同時(shí)又取出CMP指令??梢钥闯觯魉€使得每個(gè)時(shí)鐘周期都可以執(zhí)行一條指令。
當(dāng)執(zhí)行多條指令時(shí),流水線的執(zhí)行不一定會(huì)如圖2.5那么規(guī)則,圖2.6顯示了有STR指令的流水線狀態(tài)。
圖2.6 含有存儲(chǔ)器訪問指令的流水線狀態(tài)
圖2.6中在單周期指令A(yù)DD后出現(xiàn)了一條數(shù)據(jù)存儲(chǔ)指令STR。訪問主存儲(chǔ)器的指令用陰影表示,可以看出在每個(gè)周期都使用了存儲(chǔ)器。同樣,在每一個(gè)周期也使用了數(shù)據(jù)通路。在執(zhí)行周期、地址計(jì)算和數(shù)據(jù)傳輸周期,數(shù)據(jù)通路都是被占用的。在譯碼周期,譯碼邏輯負(fù)責(zé)產(chǎn)生下一周期用到的數(shù)據(jù)通路的控制信號(hào)。
注意
對于STR這種存儲(chǔ)器訪問指令,實(shí)際是在地址計(jì)算時(shí)由譯碼邏輯產(chǎn)生下一周期數(shù)據(jù)傳輸所需要的數(shù)據(jù)通路控制信號(hào)。
在圖2.6中的指令序列中,處理器的每個(gè)邏輯單元在每個(gè)指令都是活動(dòng)的??梢钥闯隽魉€的執(zhí)行與存儲(chǔ)器訪問密切相關(guān)。存儲(chǔ)器訪問限制了程序執(zhí)行必須花費(fèi)的指令周期數(shù)。
ARM的流水線執(zhí)行模式導(dǎo)致了一個(gè)結(jié)果,就是程序計(jì)數(shù)器PC(對使用者而言為r15)必須在當(dāng)前指令執(zhí)行前計(jì)數(shù)。例如,指令在其第一個(gè)周期為下下條指令取指,這就意味著PC必須指向當(dāng)前指令的后8個(gè)字節(jié)(其后的第2條指令)。
當(dāng)程序中必須用到PC時(shí),程序員要特別注意這一點(diǎn)。大多數(shù)正常情況下,不用考慮這一點(diǎn),它由匯編器或編譯器自動(dòng)處理這些細(xì)節(jié)。
例2.3顯示了流水線下程序計(jì)數(shù)器PC的使用情況。
【例2.3】
指令序列為:
0x8000 LDR pc,[pc,#0]
0x8004 NOP
0x8008 DCD jumpAdress
當(dāng)指令LDR處于執(zhí)行階段時(shí),pc=address+8即0x8008。
2.5級流水線ARM組織所有的處理器都要滿足對高性能的要求。直到ARM7為止,在ARM核中使用的3級流水線的性價(jià)比是很高的。但是,為了得到更高的性能,需要重新考慮處理器的組織結(jié)構(gòu)。執(zhí)行一個(gè)給定的程序需要的時(shí)間由下式?jīng)Q定:
Tprog = (Ninst×CPI)/ fclk
式中:
Ninst:表示在程序中執(zhí)行的ARM指令數(shù);
CPI:表示每條指令的平均時(shí)鐘周期;
fclk:表示處理器的時(shí)鐘頻率。
因?yàn)閷o定程序(假設(shè)使用給定的優(yōu)化集并用給定的編譯器來編譯)Ninst是常數(shù),所以,僅有兩種方法來提供性能。
第一,提高時(shí)鐘頻率。時(shí)鐘頻率的提高,必然引起指令執(zhí)行周期的縮短,所以要求簡化流水線每一級的邏輯,流水線的級數(shù)就要增加。
第二,減少每條指令的平均指令周期數(shù)CPI。這就要求重新考慮3級流水線ARM中多于1個(gè)流水線周期的實(shí)現(xiàn)方法,以便使其占有較少的周期,或者減少因指令相關(guān)造成的流水線停頓,也可以將兩者結(jié)合起來。
3級流水線ARM核在每一個(gè)時(shí)鐘周期都訪問存儲(chǔ)器,或者取指令,或者傳輸數(shù)據(jù)。只是抓緊存儲(chǔ)器不用的幾個(gè)周期來改善系統(tǒng)系統(tǒng)性能,效果是不明顯的。為了改善CPI,存儲(chǔ)器系統(tǒng)必須在每個(gè)時(shí)鐘周期中給出多于一個(gè)的數(shù)據(jù)。方法是在每個(gè)時(shí)鐘周期從單個(gè)存儲(chǔ)器中給出多于32位數(shù)據(jù),或者為指令或數(shù)據(jù)分別設(shè)置存儲(chǔ)器。
基于以上原因,較高性能的ARM核使用了5級流水線,而且具有分開的指令和數(shù)據(jù)存儲(chǔ)器。把指令的執(zhí)行分割為5部分而不是3部分,進(jìn)而可以使用更高的時(shí)鐘頻率,分開的指令和數(shù)據(jù)存儲(chǔ)器使核的CPI明顯減少。
注意
分開的指令和數(shù)據(jù)存儲(chǔ)器。一般是分開的Cache連接到統(tǒng)一的指令和數(shù)據(jù)存儲(chǔ)器上。
在ARM9TDMI中使用了典型的5級流水線。ARM9TDMI的組織結(jié)構(gòu)如圖2.7所示。
5級流水線包括下面的流水線級:
· 取指(fetch):從存儲(chǔ)器中取出指令,并將其放入指令流水線。
· 譯碼(decode):指令被譯碼,從寄存器堆中讀取寄存器操作數(shù)。在寄存器堆中有3個(gè)操作數(shù)讀端口,因此,大多數(shù)ARM指令能在1個(gè)周期內(nèi)讀取其操作數(shù)。
· 執(zhí)行(execute):將其中一個(gè)操作數(shù)移位,并在ALU中產(chǎn)生結(jié)果。如果指令是Load或Store指令,則在ALU中計(jì)算存儲(chǔ)器的地址。
· 緩沖/數(shù)據(jù)(buffer/data):如果需要?jiǎng)t訪問數(shù)據(jù)存儲(chǔ)器,否則ALU只是簡單地緩沖一個(gè)時(shí)鐘周期。
· 回寫(write-back):將指令的結(jié)果回寫到寄存器堆,包括任何從寄存器讀出的數(shù)據(jù)。
圖2.8顯示了5級流水線指令的執(zhí)行過程。
圖2.7 5級流水線的組織結(jié)構(gòu)
圖2.8 5級流水線
在程序執(zhí)行過程中,PC值是基于3級流水線操作特性的。5級流水線中提前1級來讀取指令操作數(shù),得到的值是不同的(PC+4而不是PC+8)。這產(chǎn)生的代碼不兼容是不容許的。但5級流水線ARM完全仿真3級流水線的行為。在取指級增加的PC值被直接送到譯碼級的寄存器,穿過兩極之間的流水線寄存器。下一條指令的PC+4等于當(dāng)前指令的PC+8,因此,未使用額外的硬件便得到了正確的r15。
3.6級流水線ARM組織在ARM10中,將流水線的級數(shù)增加到6級,使系統(tǒng)的平均處理能力達(dá)到了1.3Dhrystone MISP/MHz。圖2.9顯示了6級流水線上指令的執(zhí)行過程。
圖2.9 6級流水線
2.2.3 影響流水線性能的因素1.互鎖在典型的程序處理過程中,經(jīng)常會(huì)遇到這樣的情形,即一條指令的結(jié)果被用做下一條指令的操作數(shù)。如例2.4所示。
【例2.4】
有如下指令序列:
LDR r0,[r0,#0]
ADD r0,r0,r1 ;在5級流水線上產(chǎn)生互鎖
從例2.4中可以看出,流水線的操作產(chǎn)生中斷,因?yàn)榈谝粭l指令的結(jié)果在第二條指令取數(shù)時(shí)還沒有產(chǎn)生。第二條指令必須停止,直到結(jié)果產(chǎn)生為止。
2.跳轉(zhuǎn)指令跳轉(zhuǎn)指令也會(huì)破壞流水線的行為,因?yàn)楹罄m(xù)指令的取指步驟受到跳轉(zhuǎn)目標(biāo)計(jì)算的影響,因而必須推遲。但是,當(dāng)跳轉(zhuǎn)指令被譯碼時(shí),在它被確認(rèn)是跳轉(zhuǎn)指令之前,后續(xù)的取指操作已經(jīng)發(fā)生。這樣一來,已經(jīng)被預(yù)取進(jìn)入流水線的指令不得不被丟棄。如果跳轉(zhuǎn)目標(biāo)的計(jì)算是在ALU階段完成的,那么,在得到跳轉(zhuǎn)目標(biāo)之前已經(jīng)有兩條指令按原有指令流讀取。
解決的辦法是,如果有可能最好早一些計(jì)算轉(zhuǎn)移目標(biāo),當(dāng)然這需要硬件支持;如果轉(zhuǎn)移指令具有固定格式,那么可以在解碼階段預(yù)測跳轉(zhuǎn)目標(biāo),從而將跳轉(zhuǎn)的執(zhí)行時(shí)間減少到單個(gè)周期。但要注意,由于條件跳轉(zhuǎn)與前一條指令的條件碼結(jié)果有關(guān),在這個(gè)流水線中,還會(huì)有條件轉(zhuǎn)移的危險(xiǎn)。
盡管有些技術(shù)可以減少這些流水線問題的影響,但是,不能完全消除這些困難。流水線級數(shù)越多,問題就越嚴(yán)重。對于相對簡單的處理器,使用3~5級流水線效果最好。
顯然,只有當(dāng)所有指令都依照相似的步驟執(zhí)行時(shí),流水線的效率達(dá)到最高。如果處理器的指令非常復(fù)雜,每一條指令的行為都與下一條指令不同,那么就很難用流水線實(shí)現(xiàn)。