李金鳳 ,于德明 ,郭瑞華
(沈陽(yáng)化工大學(xué)信息工程學(xué)院,遼寧 沈陽(yáng) 110027)
RISC-V 指令集架構(gòu)的出現(xiàn)引起了業(yè)內(nèi)的廣泛關(guān)注,并產(chǎn)生了許多基于RISC-V 指令集架構(gòu)的處理器設(shè)計(jì)成果。比如,UCB 設(shè)計(jì)研發(fā)的標(biāo)量處理器Rocket 以及超標(biāo)量處理器BOOM,Vector Blox 公司設(shè)計(jì)的標(biāo)量處理器ORCA,由蘇黎世聯(lián)邦理工大學(xué)和波羅尼亞大學(xué)聯(lián)合設(shè)計(jì)的超低功耗處理器RI5CY等等[1]。在國(guó)內(nèi)也同樣涌現(xiàn)出了諸多RISC-V 架構(gòu)相關(guān)的研究成果,例如芯來(lái)科技公司的蜂鳥(niǎo)E200 系列,平頭哥公司的無(wú)劍、玄鐵等等。然而,國(guó)內(nèi)針對(duì)RISC-V 的開(kāi)源處理器的研究使用5 級(jí)流水線且加入64 位擴(kuò)展集的設(shè)計(jì)方案較少,大多數(shù)的設(shè)計(jì)方案僅考慮功能方面的實(shí)現(xiàn),對(duì)于性能的優(yōu)化略有不足[2]?;谝陨媳尘?,課題組在分析了RISC-V 指令集的基礎(chǔ)上,采用五級(jí)流水線設(shè)計(jì)RISC-V 處理器,并在Linux 系統(tǒng)上搭建了相應(yīng)的運(yùn)行環(huán)境。最終完成的基于RISC-V 指令集的64 位五級(jí)流水處理器及其運(yùn)行環(huán)境成功運(yùn)行了CoreMark 測(cè)試程序,CoreMark 測(cè)試結(jié)果為3.38 CoreMark/MHz。
本處理器采用經(jīng)典的五級(jí)流水線,五級(jí)分別稱(chēng)為取指(Instruction Fetch, lF)、譯碼(Instruction Decode, ID)、執(zhí)行(Execute, EXE)、訪存(memory,MEM)、寫(xiě)回(Write back, WB)。處理器整體框圖如圖1所示。
圖1 處理器整體框圖
取指階段主要包括指令計(jì)數(shù)器模塊和取指模塊,主要作用是在取指后將指令地址與指令傳輸?shù)阶g碼級(jí)。譯碼階段主要包括譯碼模塊和通用寄存器模塊,主要作用是將指令譯碼,使處理器明確本條指令的運(yùn)算類(lèi)型、源寄存器、源操作數(shù)、目標(biāo)寄存器等信息。執(zhí)行階段主要包括算術(shù)邏輯運(yùn)算模塊、控制與狀態(tài)寄存器模塊、訪存地址生成模塊、乘除法器模塊。主要功能是根據(jù)譯碼結(jié)果執(zhí)行相應(yīng)的運(yùn)算或者操作。訪存階段主要包括存儲(chǔ)器訪問(wèn)地址生成模塊AGU(Address Generation Unit)、存儲(chǔ)器訪問(wèn)控制模塊LSU (Load Store Unit),主要作用是對(duì)存儲(chǔ)器訪問(wèn)指令執(zhí)行完成后生成的訪存地址進(jìn)行存儲(chǔ)器讀取操作,并將數(shù)據(jù)從存儲(chǔ)器中讀出或者寫(xiě)入存儲(chǔ)器[3]。寫(xiě)回階段主要功能部件就是寄存器寫(xiě)端口,主要作用就是將指令運(yùn)算的結(jié)果寫(xiě)回到存儲(chǔ)器或者寄存器。
在RISC-V 指令集中分支跳轉(zhuǎn)指令一般分為無(wú)條件分支跳轉(zhuǎn)指令和有條件分支跳轉(zhuǎn)指令。而無(wú)條件分支跳轉(zhuǎn)指令又分為無(wú)條件直接分支跳轉(zhuǎn)指令和無(wú)條件間接分支跳轉(zhuǎn)指令,有條件分支跳轉(zhuǎn)指令只有一種,即有條件直接分支跳轉(zhuǎn)指令[4]。
通常在流水線中,取指階段以及譯碼階段無(wú)法對(duì)有條件跳轉(zhuǎn)指令做出判斷,決定是否跳轉(zhuǎn),只能等待指令執(zhí)行結(jié)束,才能判斷出指令是否滿足跳轉(zhuǎn)的條件。在這個(gè)過(guò)程中處理器將出現(xiàn)較長(zhǎng)時(shí)間的延遲,嚴(yán)重影響處理器的性能[5]。所以為了解決這一問(wèn)題,將分支指令的處理提前到譯碼階段,這樣做可以極大地降低處理分支指令時(shí)產(chǎn)生的延遲對(duì)后續(xù)流水線的影響。分支跳轉(zhuǎn)指令處理模塊如圖2所示。
圖2 分支跳轉(zhuǎn)指令處理模塊
該模塊首先使用LOGIC 模塊根據(jù)BFUN3 信號(hào)判斷跳轉(zhuǎn)指令的類(lèi)別,然后計(jì)算出跳轉(zhuǎn)指令的地址,最后將生成的這兩個(gè)信號(hào)通過(guò)一個(gè)多路選擇器傳輸給PC,最終實(shí)現(xiàn)分支跳轉(zhuǎn)指令。
流水線的冒險(xiǎn)主要包括三個(gè)方面:結(jié)構(gòu)冒險(xiǎn)、數(shù)據(jù)冒險(xiǎn)、控制冒險(xiǎn)。
結(jié)構(gòu)冒險(xiǎn)是指流水線中硬件資源的沖突,解決資源沖突可以通過(guò)復(fù)制硬件資源或者流水線停頓等待硬件資源的方法解決[6]。
數(shù)據(jù)冒險(xiǎn)是指流水線在傳播過(guò)程中,不同指令可能在同一時(shí)刻對(duì)同一寄存器產(chǎn)生讀寫(xiě)信號(hào),指令先后順序的不同可能造成讀寫(xiě)數(shù)據(jù)發(fā)生錯(cuò)誤,即產(chǎn)生數(shù)據(jù)冒險(xiǎn)[7]。在本處理器中主要會(huì)引起數(shù)據(jù)相關(guān)沖突的是先寫(xiě)后讀(Read After Write, RAW),也稱(chēng)為真相關(guān)[8]。RAW 必須等到先序指令執(zhí)行完成,再進(jìn)行后續(xù)操作,這將造成流水線停頓。本文采用數(shù)據(jù)旁路傳播技術(shù)來(lái)處理該問(wèn)題。具體實(shí)現(xiàn)思路如圖3 所示。通過(guò)數(shù)據(jù)旁路,將先序指令的數(shù)據(jù)通路生成的中間數(shù)據(jù)直接轉(zhuǎn)發(fā)到ALU 的輸出端。圖中,指令4 中ALU 的輸入端與前三條指令的中間數(shù)據(jù)使用數(shù)據(jù)旁路連接,具體實(shí)現(xiàn)如圖4所示。
圖3 使用數(shù)據(jù)旁路解決數(shù)據(jù)冒險(xiǎn)
圖4 數(shù)據(jù)旁路模塊
使用數(shù)據(jù)旁路可以解決大部分RAW 數(shù)據(jù)冒險(xiǎn),但是對(duì)于LW 指令后緊跟R 型指令或者I 型運(yùn)算指令(即Load-use 數(shù)據(jù)冒險(xiǎn))的情況只能使用前插入空操作的方式來(lái)解決。具體的實(shí)現(xiàn)需要在load 指令前插入空操作指令NOP,同時(shí)還需要讓PC 以及IF/ID 的寄存器狀態(tài)保持在一個(gè)周期。
控制冒險(xiǎn)也稱(chēng)為分支冒險(xiǎn)。因?yàn)槿〉降闹噶罨蛘咧噶畹刂返淖兓c流水線的預(yù)期不符,從而導(dǎo)致指令不能在預(yù)定的時(shí)鐘周期內(nèi)完成[9]。本文采用流水線阻塞的方式來(lái)處理控制冒險(xiǎn)。在IF/ID 的寄存器中插入一個(gè)氣泡(NOP)強(qiáng)制改寫(xiě)寄存器中的內(nèi)容。當(dāng)分支未被選中時(shí)(在ID 期間確定),提取未選中指令,繼續(xù)進(jìn)行。當(dāng)在ID 期間確定選中該分支時(shí),則在分支目標(biāo)處重新開(kāi)始提取,該分支后面的所有指令停頓1個(gè)時(shí)鐘周期。
乘法器使用了2 位Booth 算法來(lái)實(shí)現(xiàn),2 位Booth算法見(jiàn)公式(1):
由于在硬件中所有的操作數(shù)都是以補(bǔ)碼的形式存在,如果單純使用補(bǔ)碼乘法算法,則需要特地挑出第N個(gè)部分積,并使用補(bǔ)碼減法操作,這就需要實(shí)現(xiàn)一個(gè)額外的狀態(tài)機(jī)來(lái)控制,從而增加了硬件設(shè)計(jì)復(fù)雜度。但是,使用2 位Booth 算法可以顯著減少加法計(jì)算的次數(shù)[10]。
采用2 位Booth 算法,使用移位加策略實(shí)現(xiàn)64 位的乘法操作需要32 個(gè)時(shí)鐘周期,并且不支持流水操作,即第一條乘法全部完成之后才能開(kāi)始計(jì)算下一條。為了實(shí)現(xiàn)全流水、4 個(gè)時(shí)鐘周期延遲的定點(diǎn)乘法指令,就需要使用將各個(gè)部分積并行地加在一起的方式進(jìn)行計(jì)算,而不是串行迭代累加。因此,在使用Booth 算法的基礎(chǔ)上,使用不需要等待進(jìn)位信號(hào)的保留進(jìn)位加法器,并且使用華萊士樹(shù)結(jié)構(gòu)。最終完成的乘法器結(jié)構(gòu)如圖5所示。
圖5 乘法器結(jié)構(gòu)
圖5 中的第(1)部分采用2 位Booth 算法得到16個(gè)部分積,其中P 為128 位,是部分積的主體,C 為1位,表示對(duì)被乘數(shù)取反。第(2)部分采用類(lèi)似矩陣轉(zhuǎn)置連線方式,將16 個(gè)128 位部分積轉(zhuǎn)置為128 個(gè)16位等寬的數(shù),用作華萊士樹(shù)每處的輸入。第(3)部分為保留進(jìn)位加法器搭建的華萊士樹(shù)結(jié)構(gòu),是64 個(gè)1 位的華萊士樹(shù)的集合。由于兩個(gè)64 位的由符號(hào)數(shù)相乘得到的最大數(shù)值不超過(guò)126 位,所以最高位處的華萊士樹(shù)向高位的進(jìn)位直接忽略。
除法器不能像前面乘法器一樣使用多個(gè)加法器加速乘法的運(yùn)算,因?yàn)槌ǖ拿看蔚夹枰狼懊鏈p法結(jié)果的符號(hào),而乘法卻可以直接生成64 個(gè)部分積。所以除法器采用循環(huán)移位的方式進(jìn)行設(shè)計(jì)。除法計(jì)算流程如圖6 所示。
(3)加強(qiáng)對(duì)農(nóng)業(yè)循環(huán)經(jīng)濟(jì)的相關(guān)延伸,促進(jìn)多種模式的循環(huán)發(fā)展。目前很多地區(qū)發(fā)展循環(huán)經(jīng)濟(jì)的模式比較單一,僅僅停留在單項(xiàng)或者雙向流通的過(guò)程之中,應(yīng)該加大宣傳力度,推廣新的發(fā)展模式,讓更多的農(nóng)民了解并使用,從而實(shí)現(xiàn)農(nóng)業(yè)循環(huán)經(jīng)濟(jì)的更加充分發(fā)展。
圖6 除法計(jì)算流程
根據(jù)上述流程,最終實(shí)現(xiàn)的除法器結(jié)構(gòu)如圖7所示。
圖7 除法器結(jié)構(gòu)
由于在物理實(shí)現(xiàn)上存在差異,處理器與內(nèi)存的運(yùn)行速度一直存在著較大的差距。如果程序有較多地依賴訪存結(jié)果的數(shù)據(jù),那么這個(gè)差距會(huì)極大地影響處理器的性能。為了減小這一差距對(duì)處理器性能的影響,通常將存儲(chǔ)結(jié)構(gòu)劃分為四個(gè)不同的層次:高速緩存(Cache)、寄存器、IO、內(nèi)存[11]。在與處理器距離越近的位置采用存儲(chǔ)密度較小的電路,通過(guò)犧牲內(nèi)存容量來(lái)提升訪問(wèn)速度。高速緩存就是距離處理器最近的存儲(chǔ)結(jié)構(gòu),通常容量設(shè)計(jì)得較小以獲得更快的訪問(wèn)速度。設(shè)計(jì)的Cache模塊結(jié)構(gòu)如圖8所示。
圖8 Cache模塊結(jié)構(gòu)
為了避免共享Cache 產(chǎn)生的結(jié)構(gòu)沖突,設(shè)計(jì)了獨(dú)立的指令Cache(I-cache)和數(shù)據(jù)Cache(D-cache),I-cache 只用來(lái)取指,D-cache只用來(lái)訪存。
I-cache 的行內(nèi)偏移(Offset)大小為4 bit,索引(Index)大小為7 bit,標(biāo)簽(Tag)大小為21 bit。更新方式使用寫(xiě)直通。采用Tag 和數(shù)據(jù)(Data)同步訪問(wèn)的形式來(lái)降低Cache 命中情況下的執(zhí)行周期數(shù),其中Data Array 采用同步SRAM 實(shí)現(xiàn),容量大小為4 KB,Cache Line 的大小為16 byte,路數(shù)為兩路,組索引(Set Index)大小為128 bit。Tag Array 采用寄存器實(shí)現(xiàn),路數(shù)為兩路,Set Index 大小為128 bit,內(nèi)容為大小為1 bit的有效位(Valid)以及大小為21 bit的Tag。
D-cache 的結(jié)構(gòu)與I-cache 大致相同,但是由于訪存的需要,D-cache 采用了寫(xiě)回的更新方式,所以在Tag Array中還含有1 bit大小的臟塊(Dirty)用來(lái)標(biāo)識(shí)Cache Line 是否被修改。
完成處理器的硬件部分設(shè)計(jì)后,要想讓處理器運(yùn)行程序還需要完成相應(yīng)的運(yùn)行環(huán)境設(shè)計(jì)。程序的運(yùn)行都需要運(yùn)行環(huán)境的支持,包括加載、銷(xiāo)毀程序以及提供程序運(yùn)行時(shí)的各種動(dòng)態(tài)鏈接庫(kù)等。本設(shè)計(jì)中運(yùn)行環(huán)境主要包括基礎(chǔ)環(huán)境以及輸入輸出兩部分。其中,基礎(chǔ)環(huán)境主要負(fù)責(zé)程序的運(yùn)行,輸入輸出主要負(fù)責(zé)處理器與外圍設(shè)備的連接。
在處理器運(yùn)行客戶程序之前,將需要執(zhí)行的程序代碼編譯成可執(zhí)行文件。但是不能使用GCC 的默認(rèn)選項(xiàng)直接編譯,因?yàn)槟J(rèn)選項(xiàng)會(huì)根據(jù)Linux 的運(yùn)行環(huán)境將代碼編譯成運(yùn)行在Linux 下的可執(zhí)行文件。但此時(shí)的處理器并不能為客戶程序提供GNU/Linux的運(yùn)行環(huán)境,在處理器中無(wú)法正確運(yùn)行上述可執(zhí)行文件。所以,需要采用交叉編譯的方式來(lái)生成處理器可運(yùn)行的文件。
1)計(jì)算:作為程序最基本的需求,所有計(jì)算相關(guān)的代碼(順序語(yǔ)句、分支、循環(huán)、函數(shù)調(diào)用等)都會(huì)被編譯器編譯成功能等價(jià)的指令序列,最終在CPU 上執(zhí)行。
2)內(nèi)存申請(qǐng):某些程序需要在運(yùn)行時(shí)動(dòng)態(tài)地申請(qǐng)內(nèi)存來(lái)使用,通過(guò)實(shí)現(xiàn)內(nèi)存的動(dòng)態(tài)管理相應(yīng)的庫(kù)函數(shù)來(lái)實(shí)現(xiàn)。
3)結(jié)束運(yùn)行:一般程序都會(huì)有結(jié)束運(yùn)行的時(shí)候,通過(guò)RISC-V 指令集的Ebreak 指令實(shí)現(xiàn)程序結(jié)束的庫(kù)函數(shù)。
4)打印信息:輸出是程序的另一個(gè)基本需求,通過(guò)實(shí)現(xiàn)輸出字符的庫(kù)函數(shù)putch()來(lái)實(shí)現(xiàn)。
對(duì)于RISC-V 架構(gòu),輸入輸出是通過(guò)內(nèi)存映射(Memory Mapping I/O, MMIO)實(shí)現(xiàn)的。此外,使用DPI(Direct Programming Interface)-C 機(jī)制將硬件電路中寄存器的值傳輸給仿真環(huán)境,使用DPI-C 機(jī)制額外的好處就是不用修改硬件電路。
使用通用異步收發(fā)傳輸器(Universal Asynchronous Receiver/Transmitter, UART)來(lái)實(shí)現(xiàn)處理器與外圍設(shè)備的通信。實(shí)時(shí)時(shí)鐘(Real Time Clock, RTC)用來(lái)產(chǎn)生時(shí)鐘信號(hào)。
采用C語(yǔ)言編寫(xiě)測(cè)試集對(duì)RV64I的所有指令進(jìn)行測(cè)試。測(cè)試在Linux 環(huán)境下進(jìn)行,使用虛擬板卡以及開(kāi)源編譯器Verilator 進(jìn)行編譯仿真[12]。大致流程為:1)通過(guò)運(yùn)行環(huán)境將測(cè)試集內(nèi)的程序加載到虛擬內(nèi)存中,CPU 讀取并運(yùn)行程序至結(jié)束語(yǔ)句,仿真結(jié)果顯示“pass!”表明測(cè)試程序運(yùn)行成功;2)最后遍歷測(cè)試集內(nèi)的所有程序,完成對(duì)處理器以及運(yùn)行環(huán)境的功能測(cè)試。圖9 給出了乘法指令的仿真波形圖,此時(shí)運(yùn)行的指令為MUL 乘數(shù)X 與乘數(shù)Y 為0x11f42438,最終計(jì)算結(jié)果res 為0x14255a47fdfcc40,可以看出,在加入了Booth 算法以后,乘法指令的運(yùn)行過(guò)程得到簡(jiǎn)化,并且運(yùn)行結(jié)果正確。圖10 給出了除法指令的仿真波形圖,運(yùn)行的指令為DIV,除數(shù)divisor 為2,被除數(shù)dividend 為0x375F00,最終結(jié)果為0x1baf80。圖11給出了測(cè)試集內(nèi)包括上述指令的所有程序的仿真測(cè)試結(jié)果。這些仿真結(jié)果表明,CPU 通過(guò)了測(cè)試集內(nèi)的所有測(cè)試程序的測(cè)試。
圖9 乘法指令仿真波形圖
圖10 除法指令仿真波形圖
圖11 仿真測(cè)試結(jié)果
這里用CoreMark 測(cè)試程序來(lái)進(jìn)行性能測(cè)試。CoreMark 測(cè)試程序包含了大部分處理器工作時(shí)的常用算法,所以其測(cè)試結(jié)果在處理器領(lǐng)域中是處理器性能評(píng)價(jià)的重要參考指標(biāo)[13]。其跑分的計(jì)算方法為:首先,使用宏定義ITERATIONS 參數(shù)確定程序循環(huán)執(zhí)行的次數(shù),然后除以程序運(yùn)行的總時(shí)長(zhǎng)Total time,計(jì)算出來(lái)的每秒執(zhí)行CoreMark 程序的循環(huán)次數(shù),再除以處理器的主頻,就得出CoreMark 程序的跑分,單位是CoreMark/MHz。CoreMark 測(cè)試程序的測(cè)試結(jié)果如圖12 所示??梢杂?jì)算出最終得分為:
圖12 CoreMark測(cè)試程序的運(yùn)行結(jié)果
(Iteration/Total time)/50M=3.38 CoreMark/MHz。
表1 給出了處理器關(guān)鍵技術(shù)對(duì)照情況。相比較而言,本處理器性能優(yōu)于大多數(shù)5 級(jí)開(kāi)源流水線處理器。
表1 處理器關(guān)鍵技術(shù)對(duì)照表
課題組基于RISC-V 指令集搭建了5 級(jí)流水線處理器,從分支跳轉(zhuǎn)指令的處理、乘除法運(yùn)算處理、冒險(xiǎn)問(wèn)題的處理、存儲(chǔ)結(jié)構(gòu)等方面優(yōu)化處理器性能,并搭建了針對(duì)RISC-V 指令集的運(yùn)行環(huán)境。最終完成的處理器及其運(yùn)行環(huán)境在虛擬環(huán)境下通過(guò)了功能驗(yàn)證并完成了CoreMark 性能測(cè)試。結(jié)果表明,該處理器在性能方面優(yōu)于大部分同流水線深度的開(kāi)源項(xiàng)目。該處理器還可以從超標(biāo)量、多發(fā)射、動(dòng)態(tài)指令預(yù)測(cè)、非對(duì)齊指令等方面繼續(xù)優(yōu)化,從而進(jìn)一步提升性能。