指令漫游流水線CPU奇遇記

作者:    所在單位:中國科學(xué)技術(shù)大學(xué)

前言

本文可以看作計(jì)算機(jī)專業(yè)學(xué)生的“計(jì)算機(jī)組成原理”一課內(nèi)容的總結(jié)概括。手搓CPU可謂是計(jì)算機(jī)專業(yè)學(xué)生的一大浪漫,因?yàn)樵谖唇佑|這門課以前,我們總是將CPU看得非常的高大上。無論哪家科技公司,只要有芯片相關(guān)業(yè)務(wù),他們的各種型號(hào)的處理器的命名都十分的“炫酷”。“酷?!保膀旪垺?,“銳龍”......仿佛CPU完全是一個(gè)魔法造就的神秘黑盒,但是有著無與倫比的運(yùn)行速度并且牢牢占據(jù)著我們對(duì)一臺(tái)電子設(shè)備的關(guān)注焦點(diǎn)。但是今天我們可以從底層來打開這個(gè)黑盒,發(fā)現(xiàn)其中的奇妙世界以及理解脈沖,信號(hào)之間協(xié)同工作,嚴(yán)絲合縫運(yùn)轉(zhuǎn)的巧妙之處。

正文

嗨!各位科技迷們!今天我們要揭秘的是計(jì)算機(jī)世界里的一顆璀璨明珠——流水線CPU!它可是決定計(jì)算機(jī)運(yùn)行性能的大殺器哦!

首先,讓我們穿越時(shí)空,回到上世紀(jì)60年代。那時(shí)候,計(jì)算機(jī)還比較“憨憨”,一次只能做一件事。直到人們開始意識(shí)到,如果能同時(shí)處理多個(gè)任務(wù),那豈不是美滋滋?于是,流水線概念應(yīng)運(yùn)而生!

流水線CPU就像一條裝配線,它把任務(wù)拆成一小塊一小塊,然后交給不同的處理器段來同時(shí)處理。就好像工廠里的工人,各司其職,高效無比!這么做有什么好處呢?首先,大大提高了計(jì)算機(jī)的處理速度!它能夠同時(shí)進(jìn)行多個(gè)任務(wù),讓我們的電腦跑得飛快!其次,它還讓計(jì)算機(jī)更節(jié)約能源,不浪費(fèi)一點(diǎn)兒嘞!

首先,我們有“取指令階段(IF)”這個(gè)階段就像是在超市拿購物清單一樣,告訴計(jì)算機(jī)接下來要做啥。

接下來是“解碼指令階段(ID)”這一環(huán)節(jié)就是將指令翻譯成計(jì)算機(jī)能聽懂的語言,讓它知道具體要做啥。

然后是“執(zhí)行指令階段(EX)”這相當(dāng)于工人們照著清單去操作機(jī)器,將任務(wù)具體完成。

最后,我們有“寫回結(jié)果階段(WB)”這就像是把購物清單上的物品帶回家一樣,把結(jié)果存起來,供后續(xù)使用。

所以說,流水線CPU就像是一支充滿活力的樂隊(duì),各司其職,合作無間,讓我們的計(jì)算機(jī)在各種任務(wù)面前游刃有余!歷經(jīng)時(shí)光的洗禮,它已經(jīng)成為了計(jì)算機(jī)世界的明日之星!毫不夸張地說,在現(xiàn)代所有類型的CPU中都占據(jù)絕對(duì)的統(tǒng)治地位!

接下來,讓我們分階段來更加詳細(xì)的揭開流水線處理器的秘密吧!

第一節(jié) 流水線從取指令開始

晚上剛送完指令的取指單元現(xiàn)在很悠閑,他的工作很簡單,在整個(gè)流水線中央處理器中都是有名的閑職:每天早上從PC寄存器拿到一份貼著地址的空紙箱,然后沿著線網(wǎng)送到不遠(yuǎn)處的指令高速緩存(ICache)倉庫門口就可以了,PC是program counter的縮寫,盡管這并沒有準(zhǔn)確傳達(dá)其功能的含義——它的作用是指示程序執(zhí)行到的位置,但是已經(jīng)成為了約定俗成的名字?,F(xiàn)在的取指單元正維持著電平不變,已經(jīng)進(jìn)入了夢鄉(xiāng)。它的延遲很低,也可以說工作很少,所以可以很快上床躺平,只要明天早上時(shí)鐘網(wǎng)絡(luò)一拉高,然后經(jīng)過不長的線延遲后就可以把工作完成了。只是窗外偶爾傳來的忙碌加班的聲音漸漸地傳入了進(jìn)入取值單元的夢鄉(xiāng),這是別的關(guān)鍵路徑上的部門還在加班干活,但是又很快地消失了。

這里有一個(gè)定死的規(guī)矩,所有人都必須在時(shí)鐘電平跳變之間進(jìn)入穩(wěn)定狀態(tài),也就是必須把活干完然后在第二天時(shí)鐘拉起來的上升沿之前去睡覺,不得時(shí)序違例。這個(gè)規(guī)矩有多么嚴(yán)格呢?曾經(jīng)有一個(gè)部門十分臃腫,由于招了太多人,攬了太多活,天天加班007,結(jié)果某個(gè)月的任務(wù)(指令)非常密集,導(dǎo)致某天晚上這個(gè)部門還在加班,一直到時(shí)鐘拉起,它前面的部門的今天的快遞都已經(jīng)塞進(jìn)大門了,但是這個(gè)違例的部門內(nèi)部的電平還是沒有穩(wěn)定下來,導(dǎo)致兩天的工作混在了一塊。當(dāng)設(shè)計(jì)師發(fā)現(xiàn)了刺眼的critical warning后氣不打一處來,當(dāng)場就把這個(gè)部門解散了。

早睡早起身體好,這是取指單元不被裁員的座右銘。

但是取指單元的歲月靜好,是由于指令緩存的負(fù)重前行,我們很快就會(huì)看到這一點(diǎn)。

第二節(jié) 指令高速緩存

?ICache,指令高速緩存部門一大早就看到了門口的貼著PC的快遞盒?!斑@個(gè)取指單元的爺真是爺,天天除了吃就是睡,沒別噠,我們部門都忙不過來了,四路倉庫按索引查都沒有這個(gè)PC指示的地址的指令,這下又得等好長時(shí)間從主存里調(diào)貨了”。但是接到的PC不能不接,否則會(huì)丟指令。ICache派了個(gè)寄存器先存著這個(gè)指令,然后對(duì)信號(hào)傳遞使能的保安allowin說:"下次碰到那個(gè)老頭不要接他的單,什么時(shí)候我們的指令從主存里取出來了或者我們的四路組相聯(lián)的庫里還有存貨(指令)再允許他派單"。allowin是一個(gè)很聽話的保安,第二天取指單元大爺來送貼著PC的快遞盒時(shí),allowin禮貌而堅(jiān)定地拒收了,大爺只好悻悻地把空紙盒又拿回去了。

至于調(diào)貨的事情,就需要ICache通過一種事先定好的協(xié)議,比如AXI協(xié)議和主存溝通了。AXI是一個(gè)非常講究儀式而又慢性子的中介,與他溝通不僅兩人要先握手寒暄一下,然后給定好數(shù)據(jù)傳輸?shù)牡刂泛烷L度,之后經(jīng)過數(shù)十個(gè)甚至上百個(gè)周期,來自遠(yuǎn)方主存的數(shù)據(jù)才會(huì)姍姍來遲。這是一種叫做存儲(chǔ)層次結(jié)構(gòu)的深層原因所導(dǎo)致的,在CPU內(nèi)部,只需要用寄存器,或者緩存就可以以接近時(shí)鐘的頻率讀寫數(shù)據(jù),但是這種方法所能使用的空間有限,而且單位造價(jià)也很昂貴,于是如果使用稍慢”一點(diǎn)“的內(nèi)存的話,雖然讀寫速度會(huì)降低一個(gè)或兩個(gè)數(shù)量級(jí),但是可以獲得更大的存儲(chǔ)空間。這種關(guān)系同樣存在與內(nèi)存和硬盤之間:速度快,空間大以及價(jià)格低是一組不可能三角,如果你想速度快,空間大,那么英特爾昂貴的傲騰存儲(chǔ)也許能滿足你,如果想要速度快,價(jià)格低,那么只有小空間的片上寄存器資源可以供你使用,否則就是速度慢但是價(jià)格低,容量大的硬盤,磁帶等存儲(chǔ)介質(zhì)了。

高速緩存有兩家部門,一家是在前端的指令高速緩存,另一家是在后端的數(shù)據(jù)高速緩存,他們在CPU中占有重要地位,擔(dān)任著提高性能的主要責(zé)任。

流水線中央處理器的時(shí)間節(jié)奏是很快的,即使是很佛系的處理器,也是以百兆赫茲的頻率快速地更替日月。而從消費(fèi)級(jí)一直到企業(yè)級(jí)的英特爾或者AMD處理器的時(shí)鐘頻率可以達(dá)到GHZ,也就是不到一納秒的時(shí)間。這是什么概念呢?如果一個(gè)人一秒算一次加減法的話,現(xiàn)代CPU可以算10億次,而且是很保守的估計(jì)。但是與之相連的存儲(chǔ)和外設(shè)速度卻要慢若干個(gè)數(shù)量級(jí),因此ICache取指和DCache訪存如果不命中,那么就不得不花費(fèi)大量時(shí)間。這個(gè)部門因此會(huì)花費(fèi)大量時(shí)間并阻塞,所以增大倉庫容量,增加路數(shù)是提高命中率的常規(guī)操作。

當(dāng)然,如果倉庫里查到PC所對(duì)應(yīng)的指令的話,皆大歡喜,指令很快地就可以打包然后繼續(xù)送到下一站:譯碼單元。

第三節(jié) 指令譯碼

譯碼單元里有許許多多的選擇器,這些選擇器通過識(shí)別指令來標(biāo)注指令類型,目標(biāo)寄存器和源寄存器編號(hào)等信息。這些快遞盒通過譯碼器去后會(huì)被分成各種類型,有ALU算數(shù)邏輯指令,有訪存指令,有跳轉(zhuǎn)指令,甚至有特權(quán)指令。有時(shí)候指令的長度甚至有不一樣長,導(dǎo)致快遞盒大小差異很大,比如x86指令集就是這樣。這會(huì)給譯碼帶來額外的困難,因?yàn)榇笮〔灰坏目爝f盒可能超出了安檢機(jī)的入口大小,所以不得不分幾次來譯碼。有時(shí)譯碼單元會(huì)發(fā)現(xiàn)按照指令集手冊查不到當(dāng)前指令的任何信息,那么譯碼單元會(huì)毫不猶豫地給這個(gè)快遞件貼上”指令不存在例外“的異常標(biāo)簽。

指令譯碼單元是CPU里出名的血汗工廠,里面擠著大量的選擇器工人,他們的工位往往僅能看到不長的三五個(gè)比特,然后就要根據(jù)設(shè)計(jì)師定好的選擇邏輯來確定指令的一部分信息。這里的工人沒有時(shí)間睡覺,因?yàn)樗麄兌际墙M合邏輯,所以沒有寄存器可以讓他們在時(shí)鐘跳變間隙休息。許多工人忙到汗流浹背,彼此之間空位很小,你仿佛可以看到譯碼單元里產(chǎn)生的熱氣在密密麻麻的線網(wǎng)中逐漸地氤氳......

第四節(jié) 讀寄存器,執(zhí)行運(yùn)算

譯碼出的寄存器編號(hào)隨后放在寄存器堆門口,寄存器堆顧名思義就是一堆寄存器堆在一起,不同寄存器之間有不同的編號(hào)來區(qū)分。里面存放的值一般隨后用來作為執(zhí)行階段的操作數(shù),可以進(jìn)行尋址,加減,乘除,移位等操作。

執(zhí)行階段聽起來很像個(gè)龐然大物,畢竟各種類型的指令需要使用大量的硬件資源來計(jì)算。但是這部分可能反而是最好實(shí)現(xiàn)的部分,因?yàn)楦鞣NIP和模版已經(jīng)十分成熟,設(shè)計(jì)師實(shí)現(xiàn)某個(gè)類型的運(yùn)算可能只是一行代碼的工作量。

深入到硬件看,這里同樣也是十分熱鬧。還記得之前譯碼器標(biāo)記上的類型嗎?毫無疑問,運(yùn)算的類型由譯碼器指定,隨后從寄存器堆得到的數(shù)據(jù)或者伴隨指令的立即數(shù)會(huì)作為各種運(yùn)算的操作數(shù)進(jìn)入到運(yùn)算單元。雖然在高級(jí)語言內(nèi)我們可以使用諸如三角函數(shù),指數(shù)函數(shù),對(duì)數(shù)函數(shù)等等算術(shù),但是CPU只會(huì)進(jìn)行簡單的幾種基本運(yùn)算。但是別擔(dān)心,我們的程序可以根據(jù)各種級(jí)數(shù)展開將復(fù)雜的函數(shù)轉(zhuǎn)換成CPU內(nèi)部若干條基本運(yùn)算的迭代循環(huán)。這些基本運(yùn)算一般都包括加減,乘除,和一些位運(yùn)算。

這個(gè)過程中,還有許許多多別的細(xì)節(jié),比如除零運(yùn)算我們可能認(rèn)為需要停止計(jì)算并給這個(gè)指令標(biāo)記上算術(shù)運(yùn)算錯(cuò)然后后傳,但是也有處理器允許計(jì)算結(jié)果是不確定的值并且不觸發(fā)任何例外;還有越界時(shí)的處理,比如乘法結(jié)果太大無法用數(shù)據(jù)寄存器完整保存,那么一般就是截?cái)鄶?shù)據(jù)。

這個(gè)運(yùn)算單元的結(jié)果還另外有大用,不僅要正常向后傳遞,也需要經(jīng)過前遞回傳到執(zhí)行單元的輸入選擇器上。這是對(duì)于流水線的CPU,盡管早先進(jìn)入的指令還沒有執(zhí)行結(jié)束,但是新的指令已經(jīng)進(jìn)入了流水線,新指令所需要的操作數(shù)可能已經(jīng)被臨近的舊指令修改了,但是還沒有來得及寫回使新指令獲得最新的值,所以需要使用前遞來保證結(jié)果的正確性。如果不前遞會(huì)怎么樣?也不會(huì)怎么樣,無非就是CPU算出一個(gè)錯(cuò)值(問題大了)或者每有一對(duì)相鄰的相關(guān)指令,就不得不等到新的值更新完畢再繼續(xù)傳遞指令。這樣看來前遞還是十分重要的,但是總有一些指令的相關(guān)問題是前遞都解決不了的,我們只能退而求其次,等若干個(gè)周期,寧肯慢,絕不錯(cuò)。

我們知道,所有程序都可以由三種基本類型的結(jié)構(gòu)所實(shí)現(xiàn):順序結(jié)構(gòu),分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)。其中分支和循環(huán)結(jié)構(gòu)就是對(duì)應(yīng)指令中的分支和跳轉(zhuǎn)指令。分支指令往往需要比較兩個(gè)數(shù)的大小,如果滿足條件,比如這個(gè)指令類似BLT(Branch less than),如果兩個(gè)操作數(shù)的大小關(guān)系確實(shí)是小于的話,程序的PC就會(huì)跳轉(zhuǎn)到指定的位置。這個(gè)指定的位置可能是當(dāng)前的PC加上一個(gè)立即數(shù),也可能是從一個(gè)寄存器中獲得要跳轉(zhuǎn)的位置。還有一些跳轉(zhuǎn)是無條件的,他們可以有更大的跳轉(zhuǎn)范圍但是只能使用特定的寄存器。

再次提醒,我們的CPU是流水線的,指令不管分支結(jié)果怎么樣已經(jīng)填入了CPU,我們怎么保證分支指令后的指令是正確的呢?很遺憾,沒有辦法百分百地確保這一點(diǎn)。我們能做的就是如果錯(cuò)了就發(fā)一個(gè)令CPU聞之色變的信號(hào):flush。也可以理解為清空前半段的流水線,無效掉由于分支預(yù)測失敗讀進(jìn)來的錯(cuò)誤指令來保護(hù)CPU的正確性,這樣做的代價(jià)就是指令高速緩存可能需要重新與主存溝通,同時(shí)流水線之前的大部分工作都白費(fèi)了。

等等,分支預(yù)測失???如果我不失敗,幾乎次次都猜對(duì),那不是很好嗎?

確實(shí)如此,即使使用很簡單的局部分支預(yù)測器,比如”吃一塹長一智“的方法,或者說只要一次預(yù)測失敗就記住這個(gè)位置,下次改變跳轉(zhuǎn)的采取與否選擇,也可以大大提高循環(huán)體結(jié)構(gòu)中的預(yù)測準(zhǔn)確率。也有另外一種方法,也許可以稱之為”吃兩塹長一智“,也叫2bit飽和預(yù)測,正如字面所說,即使預(yù)測失敗,還給予一次機(jī)會(huì)做出與上次相同的預(yù)測。分支預(yù)測器放置在取指單元之前,每一拍都會(huì)根據(jù)當(dāng)前PC預(yù)測下一次PC,大部分情況下都是+4字節(jié)(32位處理器),也就是連續(xù)取指。

?另外對(duì)于訪存指令來說,同樣有一個(gè)數(shù)據(jù)高速緩存來進(jìn)行對(duì)接。數(shù)據(jù)高速緩存(DCache)相比與ICache,需要新增臟位標(biāo)識(shí),來指示數(shù)據(jù)不一致的情況。為什么ICache不需要呢?因?yàn)橹噶钜话闶枪潭ǖ模瑥膬?nèi)存中讀出來后不會(huì)被改寫,所以可以認(rèn)為是只讀的,所以不會(huì)產(chǎn)生內(nèi)存和緩存數(shù)據(jù)不一致的情況。除臟塊的寫回以外,ICache和DCache的架構(gòu)就沒有什么更大的區(qū)別了。讀取數(shù)據(jù)同樣使用地址的低位作為索引查到到對(duì)應(yīng)的行,比較每一路的標(biāo)簽,如果命中就返回對(duì)應(yīng)位置的數(shù)據(jù)。但是如果是寫數(shù)據(jù),要寫的目標(biāo)地址正好在緩存的塊中,也還好辦,但是如果不在,而且現(xiàn)在索引對(duì)應(yīng)的幾路全都”臟“了該怎么辦呢?那么就需要把臟行寫回到主存,然后從主存取出正確位置所在的一行,然后再在緩存中寫。這樣看起來與主存的溝通不僅要寫還要讀,顯然比ICache要復(fù)雜一些,同時(shí)要花費(fèi)更多的時(shí)間與主存溝通。問題不僅如此,數(shù)據(jù)段和指令段的讀寫都要經(jīng)過AXI協(xié)議的話,究竟誰先誰后呢?這個(gè)過程還需要仲裁一下,一般是寫數(shù)據(jù)優(yōu)先,因?yàn)镈Cache位于流水線的后段,所以對(duì)應(yīng)的是先執(zhí)行的指令,當(dāng)然要讓寫數(shù)據(jù)的效果對(duì)后執(zhí)行的指令可見。

我們不妨再看看一類特殊的指令:特權(quán)指令。這類指令是觸發(fā)例外最頻繁的因素之一。特權(quán)指令有很高的權(quán)限,可以改變一般指令不能改變的狀態(tài)寄存器的值和CPU的工作模式,所以如果不在特權(quán)模式下執(zhí)行特權(quán)指令會(huì)報(bào)特權(quán)等級(jí)錯(cuò)例外。你可能發(fā)現(xiàn)我們上述所講的CPU似乎除了從主存中拿出指令然后循環(huán),計(jì)算一通寫回主存后沒有別的與外部交流的方式了,這與我們實(shí)際生活中能放視頻,操作界面,播放音樂,打印文件的個(gè)人電腦差了很多。其實(shí)只要補(bǔ)上最后一片拼圖:中斷,就可以完成上述所有的一切;而中斷怎樣處理,正是由特權(quán)指令所設(shè)置的。你想要打印一串字符?把字符串的起始位置存到指定的寄存器里,把打印程序的指令起始位置設(shè)置好,然后觸發(fā)系統(tǒng)調(diào)用例外,隨后的事情就交給中斷處理程序和外設(shè)吧!從這個(gè)角度看,其他的交互都只不過是執(zhí)行中斷程序和提供數(shù)據(jù)(比如要打印的字符,屏幕某個(gè)像素點(diǎn)的顏色,位置)后,實(shí)際功能外包給各種外設(shè)罷了。當(dāng)然各種驅(qū)動(dòng)程序怎么實(shí)現(xiàn),傳輸速率如何的問題,那就屬于程序員適配軟件和工程師測試硬件的范疇了。

第五節(jié) 寫回與異常處理

天下沒有不散的筵席,也沒有執(zhí)行不完的指令。指令即使不斷地被flush沖刷或者不停地被阻塞,在經(jīng)過執(zhí)行單元后,還是會(huì)進(jìn)入到寫回段。寫回段需要根據(jù)指令的類型來判斷其是否需要更新寄存器堆。也許你會(huì)疑問為什么不早點(diǎn)更新,不是不想早一些,而實(shí)在是做不到??!

如果剛從寄存器讀出來就更新的話,不好意思,數(shù)據(jù)甚至都沒有算好,更新個(gè)寂寞。也許我們需要再次說明一下,在宏觀上CPU執(zhí)行某條指令就像是一個(gè)周期就瞬間算好了,但是微觀上只有工程師才知道在時(shí)序,頻率和資源權(quán)衡下,布線之后的真實(shí)CPU中信號(hào)的傳遞是一拍一拍的。換一個(gè)視角,宇宙間最快的速度——光速,在10GHZ的頻率下,每一個(gè)時(shí)鐘周期期間也只能移動(dòng) 的長度,你可以想一想一塊計(jì)算機(jī)CPU的邊長是不是接近這個(gè)頻率對(duì)應(yīng)的信息傳遞上限,再加上內(nèi)部布線的彎彎繞繞,是不是覺得現(xiàn)代計(jì)算機(jī)的頻率已經(jīng)接近到了某種不可思議的程度?

?那么如果從執(zhí)行單元出來就更新,這下數(shù)據(jù)都準(zhǔn)備好了,沒有理由不更新寫回到寄存器堆了吧?還真有。本來執(zhí)行段就有自己的任務(wù),如果直接把數(shù)據(jù)接回去,這不是給執(zhí)行單元這個(gè)部門加班了嗎?別忘了我們的規(guī)矩!在時(shí)鐘周期之間要把任務(wù)都執(zhí)行完,如果加班要不然會(huì)造成時(shí)序違例,要不然只能降低時(shí)鐘頻率,而降頻就意味著損失性能。幸運(yùn)的是,我們并沒有非這樣不做的理由,或者說在執(zhí)行段后寄存一級(jí),讓指令以及他們攜帶著的一大堆數(shù)據(jù)睡一覺,第二天再上路也是非常合理的。這里可以體現(xiàn)出流水線設(shè)計(jì)的精髓:找到耗時(shí)最長的單元,我們往往稱之為關(guān)鍵路徑,切一刀分成兩部分,讓每個(gè)單元在同一個(gè)時(shí)鐘周期內(nèi)做更少的事來減少延遲,是提高流水線頻率的基本操作。

在寫回段還有另一件重要的事,那就是異常處理。我們已經(jīng)見過指令不存在例外,特權(quán)等級(jí)錯(cuò)例外,以及系統(tǒng)調(diào)用例外,但是還有一些此處未提到過的例外。處理這些例外時(shí)我們可以通過隨指令一起的標(biāo)簽來判斷例外類型。不過一般我們都需要讓PC跳轉(zhuǎn)到例外入口地址處去處理。例外入口地址的設(shè)置有不同的實(shí)現(xiàn)方法,在龍芯精簡指令集中是使用特權(quán)指令來實(shí)現(xiàn)。入口的那一邊是什么?當(dāng)然就是處理例外的程序了,當(dāng)這段程序處理完后,還需要回到產(chǎn)生例外的原來的位置繼續(xù)執(zhí)行。不出意外的,原來的位置需要使用特權(quán)指令來讀取。我們可能發(fā)現(xiàn)并不容易區(qū)分異常和中斷兩種機(jī)制的處理流程的區(qū)別,但實(shí)際上也確實(shí)如此。他們的處理流程都是離開原來的程序流,進(jìn)入到預(yù)先設(shè)置好的處理程序,執(zhí)行完后再回到原來的位置,就好像在調(diào)用一段函數(shù)一樣。我們也將中斷分為外中斷和內(nèi)中斷,由外設(shè)引起的中斷可以叫外中斷,而各種異常也叫做內(nèi)中斷。

第六節(jié) 流水線處理器的優(yōu)化

到此為止,我們已經(jīng)見識(shí)了一個(gè)可以作為本科生課程實(shí)驗(yàn)程度的簡易流水線是如何工作的。如果你曾經(jīng)自己實(shí)現(xiàn)過一個(gè)這樣的處理器,燒錄到FPGA上并且真正執(zhí)行了一段程序時(shí),內(nèi)心會(huì)很有成就感。可能還不由得鄙夷AMD,INTEL這樣的大廠,什么檔次,我們實(shí)現(xiàn)流水線CPU,他們也實(shí)現(xiàn)流水線CPU。但是請保持謙遜,別忘了人家的流水線可以達(dá)到GHZ的頻率,同時(shí)有大小核,多核并行的并行技術(shù)。即使只拿出人家三十年前的處理器,也已經(jīng)采用了亂序超標(biāo)量的技術(shù)。什么?你沒有聽說過亂序超標(biāo)量嗎?不要緊,簡單理解,我們之前的講述都只是一條一條指令在斷斷續(xù)續(xù)地執(zhí)行,但是超標(biāo)量可以同時(shí)發(fā)射兩條以上的指令(盡管也可能是斷斷續(xù)續(xù)地)。直覺上是可以想到,如果有一串指令序列,奇數(shù)位置PC的指令與偶數(shù)位置PC的指令使用的寄存器,訪存的地址都互不干擾,也不改變執(zhí)行順序,那么同時(shí)發(fā)射兩條就可以很暴力地提高一倍的性能。但是會(huì)帶來一系列的問題,因?yàn)轱@然實(shí)際情況中我們的假設(shè)并不總是成立,甚至大部分時(shí)間都不成立,那么請你想一想,如果不成立,是否有方法解決呢?至少我們總是可以用空泡(即不會(huì)產(chǎn)生任何有意義變化的無意義指令)來填充那些沒辦法同時(shí)執(zhí)行的指令。即使如此簡單的處理,也會(huì)帶來一系列的小問題,這些問題單獨(dú)拿出來都不難,但是非常繁雜,實(shí)際實(shí)現(xiàn)時(shí)會(huì)以你意料不到的方式不斷地摧毀你的奇思妙想與天才設(shè)計(jì)。我們以上所說的都是順序發(fā)射的處理器,那么什么是亂序呢?當(dāng)然就是在不影響正確性的前提下,應(yīng)發(fā)盡發(fā),充分地利用執(zhí)行單元的資源,不發(fā)白不發(fā),發(fā)了還想發(fā)。這需要引入新的技術(shù),比如記分板,重命名和重排序技術(shù)。對(duì)于高速緩存的設(shè)計(jì)技術(shù),還有大量的技巧,比如很典型的使用多級(jí)緩存,比如L2 Cache甚至L3 Cache,或者使用異構(gòu)混合的緩存,比如使用一種叫做Victim Cache的結(jié)構(gòu)來彌補(bǔ)路數(shù)不足的缺陷。此外,還有指令緩存(不是ICache)可以用來放在ICache的下一段,其實(shí)就是一個(gè)先進(jìn)先出的隊(duì)列,可以讓前端在不命中而取指令的時(shí)間后端從隊(duì)列中拿出指令還有活干;或者后端阻塞的情況下前端還能源源不斷的去取出指令,總之就是更充分的壓榨流水線和存儲(chǔ)的性能。

本文省略了一些重要的部件的相關(guān)內(nèi)容,比如TLB(快表),因?yàn)闆]有區(qū)分虛地址和實(shí)地址,進(jìn)而地沒有介紹地址翻譯模式,連帶著地址翻譯相關(guān)的常見異常也沒有介紹。各種指令集中往往有各自獨(dú)特的特色指令,雖然使用頻率不高,但是卻有可能對(duì)CPU的設(shè)計(jì)架構(gòu)產(chǎn)生關(guān)鍵的影響,比如柵障指令,計(jì)數(shù)器讀指令等等,更不用說難以歸類的一些雜項(xiàng)指令了。

寫在后面

盡管CPU中有數(shù)不清的設(shè)計(jì)方法和時(shí)序優(yōu)化技巧弄得人眼花繚亂,但是有幾個(gè)重要的基本概念,可以說深刻影響了CPU的現(xiàn)狀,過去以及未來。一是層次化存儲(chǔ)結(jié)構(gòu),如果沒有這種結(jié)構(gòu)金字塔的存在,我們完全沒有必要設(shè)計(jì)緩存,并且與之類似的各種緩存技術(shù)將毫無存在意義。二是流水線結(jié)構(gòu),從單個(gè)指令的執(zhí)行過程來看,流水線并不能減少單個(gè)指令從取指到寫回所需要的時(shí)間,但是在大量指令的運(yùn)行過程中,由于流水線深度填充而產(chǎn)生的影響將被無限地抹平,看起來完全就是一條指令剛進(jìn)入流水線,寫回段就完成了一條指令,因此毫不夸張地說流水線是人類偉大的工程設(shè)計(jì)思想。三是沒有最好的設(shè)計(jì),只有更好的設(shè)計(jì)。小容量的Cache容易導(dǎo)致頻繁不命中而大大降低程序運(yùn)行速度,但是大容量的Cache又會(huì)占用大量資源,影響布線進(jìn)而降低頻率,同樣會(huì)降低運(yùn)行速度。選擇合適容量和設(shè)計(jì)的緩存是一門藝術(shù),也是一種實(shí)驗(yàn)科學(xué)??偠灾悴粫?huì)找到一種可以完美解決所有方案的設(shè)計(jì),但是在與無窮無盡的問題的斗爭中,我們設(shè)計(jì)CPU的技藝將不斷精進(jìn),能夠取得在有限資源下兼顧能耗和速率的更加優(yōu)秀的設(shè)計(jì),這也是計(jì)算機(jī)科學(xué)的魅力之一。

參考資料:

中國科學(xué)技術(shù)大學(xué) 王超老師 PPT,張俊霞老師 PPT

《計(jì)算機(jī)組成原理與設(shè)計(jì)》 David A. Patterson,John L.Hennessy

龍芯杯指令集手冊 龍芯中科有限公司

《計(jì)算機(jī)體系結(jié)構(gòu)》 龍芯中科

《超標(biāo)量處理器設(shè)計(jì)》,姚永斌