陶常勇 高彥釗 王元磊 張興明
(1.天津市濱海新區(qū)信息技術創(chuàng)新中心 天津 300450;2.解放軍信息工程大學 鄭州 450000)
RISC-V最初是從2010年開始,由UCB的Krste等人開始著手研究的,并于2015年左右最終開發(fā)出了一套完整的新指令集,還包括對應的編譯器和工具鏈[1]。在近兩年的時間里,RISC-V的發(fā)展得到了學術界和工業(yè)界的大量關注,UCB已經建立了一個名為Rocket-chipGenerator的開源項目[2],在國內也成立了開放指令生態(tài)聯盟。RISC-V的優(yōu)勢在于它真正做到了免費開放,并且其指令集丟棄了歷史包袱,支持模塊化,可高效實現各種微結構和大量定制、加速功能[3]。
在計算密集型和數據密集型的高性能計算中,如果采用馮諾依曼架構的通用處理器完成計算,因涉及到大量的數據需要在ALU和內存之間反復搬移,計算效率會大打折扣[4]。一種可能的思路是將數據與算粒就近放置,實現近數據計算的計算架構。本文根據RISC-V開源架構的特點,結合高性能計算對數據和算粒就近放置的需求,提出了一種基于RISC-V的近數據計算協處理器加速陣列與系統(tǒng),在文中,首先介紹了乘加計算需求,接著重點描述了協處理器加速陣列的電路和微碼表項的結構,緊接著完成了針對該電路的RISC-V自定義指令設計,最后以一個實例對系統(tǒng)進行了定性的評估。
針對有規(guī)律的乘累加計算,完全可以將計算過程中的運算部分和控制部分相分離,控制部分在RISC-V的內核中實現,而運算部分在一個協處理器陣列中以流式計算的形式完成。在本部分,我們首先介紹了乘加運算的計算需求,然后完成了協處理器陣列的邏輯結構設計,同時詳細介紹了協處理器陣列中微碼表項的組織結構。
在信號處理或深度學習領域中,大量涉及有規(guī)律的乘累加計算,比如在矩陣乘法中,其計算公式的基本形態(tài)為
其中,i,j的值為0,1,…K;K為矩陣的維數。
再比如在卷積神經網絡的卷積運算中,其可能的計算公式[5]
其中,i,j的值為0,1,…N,N為特征圖像尺寸;K為卷積核尺寸。
再比如,在基2的某一級FFT運算中,其可能的計算公式為
(3)
其中,i的值為0,1,…,N/2-1;k的取值與FFT運算的基數有關[6]。
通過觀察上述公式,對于各種形式的乘累加運算,可總結為如公式(4)的計算格式
yi,j=∑aia,ja·bib,jb
(4)
其中,乘數a的角標和被乘數b的角標ia、ja、ib,jb均是為了描述a和b在矩陣中的相對位置,為了方便硬件處理,假設每個時鐘周期完成一次乘加計算,把上述公式中針對乘數a與被乘數b的角標隨時鐘周期的變化關系轉換為四組長度相同的數列來描述,記每組數列的當前值分別為ia_c、ja_c、ib_c、jb_c,每組數列的上一時鐘周期的值分別為ia_p、ja_p、ib_p、jb_p。則ia_c、ja_c、ib_c、jb_c的值可由式(5)計算得到
z=x+y+A
(5)
其中,A為一個立即數,x、y描述了參與計算的角標的索引,其取值關系可如下表所示,表中,Null表示當前值索引為數值0,x、y為2’b00時表示選擇Null。
表1 乘數與被乘數角標公式選值關系
傳統(tǒng)的馮諾依曼架構的處理方式需要產生大量的數據搬移,帶來了額外的功耗和性能的損失。為此,本文所設計的協處理器陣列采用存算一體的計算結構實現,將待計算數據分散存儲在多個RAM塊中,并將乘加計算算子盡量靠近RAM放置,如圖1所示為協處理器陣列架構框圖。
圖1 協處理器陣列邏輯框架
圖1中,RAM塊與乘加算粒一一配對組成一個存算一體化的近數據計算協處理器,相鄰協處理器間通過NOC路由控制節(jié)點相互連接,組成一個片上網絡。在每個NOC路由控制節(jié)點中可以有四個數據通路與周邊協處理器的路由控制節(jié)點相連接,主要實現數據在相鄰協處理器間的移動;其內部有兩個本地總線分別與數據RAM和乘加算粒相連接,主要實現待計算數據在數據RAM與乘加算粒之間的數據交互。RISC-V的內核通過協處理器接口與各路由控制節(jié)點通信,實現用戶自定義指令與協處理之間的交互。圖中為表述方便,只畫出了由四個協處理器組成的一個2DFull-mesh片上網絡結構,實際工程實現時,協處理器的數量可以根據硬件資源與計算需求情況進行增刪。
根據1.1章節(jié)中對有規(guī)律乘累加計算算法的分析,當待計算數據在RAM塊中存儲時,假定待計算數據均以矩陣形式進行存儲,則對矩陣中某一元素的尋址方式可采用mtx.d_ij的形式進行索引,其中mtx表示數據存儲在哪個矩陣中,i與j表示數據在矩陣中的行列角標。
為了實現數據在片上網絡中的傳輸,如圖1所示需要對每個NOC路由控制節(jié)點按照它所在的行列位置進行編號,同時還需要按照圖2所示的幀格式將待傳輸數據進行封裝。
圖2 路由節(jié)點間數據傳輸幀結構
圖2中,
1)s_rij表示該幀是從哪個NOC路由控制節(jié)點發(fā)出的;
2)d_rij表示該幀需要發(fā)往哪個NOC路由控制節(jié)點;
3)Type指示幀類型是請求幀還是響應幀;
4)rd_wr指示幀操作是讀操作還是寫操作;
5)mtx.dij指示元素在矩陣中的具體位置;
6)dir表示矩陣中的數據在RAM塊中是按行存儲還是按列存儲;
7)d_len指示幀中有效數據的長度;
8)data為幀中需要傳輸的數據。
當一個數據幀進入到某個NOC路由控制模塊時,首先NOC路由控制模塊判斷該幀中的d_rij是否與自己所在的行列號相同,若不相同,則根據d_rij與自己所在的行列號進行大小判斷,從4個數據通路中的某一個轉發(fā)走。若相同,則表明該數據幀是與自己相關的,此時需要根據mtx_dij和dir的指示轉換為RAM的物理地址空間。為了實現這一點,還需要在每個路由控制模塊中維護一個矩陣與RAM地址的映射關系表,表中包含矩陣第一個元素在矩陣中的偏移地址,以及矩陣的行列維數、存儲方式等信息。
根據1.1章節(jié)對規(guī)律性乘累加計算的描述,NOC路由控制節(jié)點除了實現待計算數據的流動外,更重要的是要針對乘累加規(guī)律性運算進行加速,這就涉及到乘累加計算過程中待計算數據角標的產生與控制,下面著重介紹NOC路由控制節(jié)點模塊中對角標規(guī)律的控制方法。
NOC路由控制節(jié)點的內部結構如圖3所示。
圖3 路由控制節(jié)點內部結構框圖
圖3中,
1)交換轉發(fā)模塊用于實現相鄰路由控制節(jié)點之間的數據交換,因通常NOC網絡采用2Dfull-mesh結構,因此一個路由控制節(jié)點可與周邊4個路由控制節(jié)點產生互聯。
2)幀解析和幀生成模塊負責完成路由控制節(jié)點之間傳輸的數據幀的解析與生成。
3)RAM讀寫控制模塊根據得到的數據角標,按序從RAM中讀取數據,并送入到乘加算粒中。同時接收乘加算粒返回的結果,并將結果或保存到本節(jié)點的RAM中,或通過幀生成和交換轉發(fā)模塊傳輸到其他協處理器中。
4)算式間循環(huán)控制和算式內循環(huán)控制實現了規(guī)律性乘加運算角標的產生,可以看出這兩個模塊是路由控制節(jié)點的關鍵模塊。下面詳細說明這兩個模塊的工作原理。
1.4.1 算式間循環(huán)控制模塊
算式間循環(huán)控制模塊主要實現對計算過程中計算結果的角標控制,其內部邏輯結構如圖4所示。
圖4 算式間循環(huán)控制模塊邏輯結構
內循環(huán)計數器模塊主要實現內層循環(huán)次數控制功能,當檢測到start信號的上升沿后,Loop_in循環(huán)計數器模塊內的計數器開始自動增加,每增加一次,向內循環(huán)指令存儲器中發(fā)出一次rd_in讀請求信號;當計數值與loop_in的值相等時,向外循環(huán)計數器給出flag_in指示。
Loop_mode控制內外循環(huán)之間是否為嵌套關系,當為嵌套關系時,外循環(huán)計數器模塊每收到一次flag_in信號加1,同時給出restart信號重新啟動內循環(huán)計數器工作。當為非嵌套關系時,外循環(huán)計數器收到flag_in之后會連續(xù)增加。外循環(huán)計數器每增加1輸出一個rd_ex脈沖信號,當外循環(huán)計數器的計數值與loop_ex的值相等時,表明循環(huán)控制結束。
內循環(huán)指令存儲器和外循環(huán)指令存儲器均是由RAM實現,RAM中存儲了針對1.1章節(jié)中描述的角標計算的微碼指令,其格式如圖5所示。
圖5 算式間循環(huán)微碼表結構
其中,微碼指令分為兩部分,i微碼指令用于控制i角標的變化。j微碼指令用于控制j角標的變化。兩條微碼指令合并為一條表項存儲在內外循環(huán)微碼存儲器中。
1)x_sel和y_sel的取值原則與表 2中指示的x和y的取值規(guī)律相符;
2)X_inv和y_inv指示x_sel和y_sel索引的數據在計算時,是否需要將符號位取反;
3)A為微碼中的立即數。
在系統(tǒng)工作時,可預先在內循環(huán)或外循環(huán)微碼指令存儲器中預置多條微碼表項,這些微碼表項可與多種計算場景對應。當某一場景需要工作時,由cal_rule作為內外循環(huán)微碼存儲器的地址,將對應的微碼表項讀出。
讀出的兩條微碼表項需要經過指令隊列FIFO送到i指令執(zhí)行器和j指令執(zhí)行器,如果在同一個時鐘周期同時從內循環(huán)微碼存儲器和外循環(huán)微碼存儲器中讀出兩個表項,則此時需要由loop_seq指示這兩條表項需要執(zhí)行的先后順序,這兩條表項按照先后順序存入指令隊列FIFO中。
最后,i指令執(zhí)行器和j指令執(zhí)行器完成角標的計算后,輸出具體的計算結果的i角標和j角標值。到此算式間循環(huán)控制模塊的工作過程就介紹完了。
1.4.2 算式內循環(huán)控制
算式內循環(huán)控制主要實現一個計算公式內,乘數a和被乘數b的角標ia、ja、ib、jb產生的方法,算式內循環(huán)控制模塊的工作原理與算式間循環(huán)控制類似,其內部結構如圖6所示。
圖6中,因為要對一個計算公式的乘數a和被乘數b進行循環(huán)展開所花費的時鐘周期較長,因此需要一個隊列FIFO來緩存算式間循環(huán)控制輸出的啟動指示。
乘數a和被乘數b的角標變化的微碼指令存儲在算式內微碼存儲器中,其表項內容如圖7所示。
圖6 算式內循環(huán)控制內部邏輯結構框圖
圖7 算式內循環(huán)微碼表結構
圖7中,一條微碼表項被分為5個區(qū)域,其中ia微碼指令、ja微碼指令、ib微碼指令和jb微碼指令的格式和含義與算式間循環(huán)控制模塊的內容類似,在此不做贅述。
微碼循環(huán)控制區(qū)域中由兩個位域組成,其中times字段指示從當前微碼條目開始,需要循環(huán)執(zhí)行多少次。scop指示從當前微碼指令開始,需要循環(huán)執(zhí)行的微碼條數有多少條。微碼順序控制器根據times和scop的指示,決定了讀取算式內微碼存儲器的地址指針是否需要跳回,以及跳回多少。當times為1時,地址指針自動加1即可,當times大于1,scop為1時,地址指針需要停滯times所指示的周期數,當times大于1,scop大于1時,地址指針就需要有跳回操作了。如果將一個scop所指示的微碼指針空間稱為一個微碼循環(huán)段,則多個微碼循環(huán)段之間有可能發(fā)生重疊,此時就需要在微碼順序控制器中設置一個??臻g,處理微碼循環(huán)的嵌套問題,比如在卷積運算中,通常需要至少支持兩層嵌套。
從算式內微碼存儲器中讀出的微碼指令,有四個角標生成器并行執(zhí)行,從而得到了每個計算公式中乘數與被乘數角標的具體數據。
在上一部分的內容中,我們介紹了協處理器加速陣列的邏輯結構,以及其中的微碼表項的結構與工作原理,在本部分的內容中,我們將結合RISC-V指令集架構的特點,從微碼表配置、數據搬移和運算控制三個方面著重介紹RISV-V用戶自定義指令的設計。
自從RISC-V架構誕生以來,在全世界范圍內已經出現了數十個版本的RISC-V架構處理器,其中,最具代表性的Rocketcore是伯克利開發(fā)的一款64位的開源RISC-V處理器核,其性能可與ARMCortex-A5對標。更重要的是,在相關的以RISC-V為基礎的內核中配置了可擴展指令接口,可供用戶擴展協處理器指令[7]。
在RISC-V的指令集支持各種不同的指令長度,以最常用的32位指令集為例,如表2所示為32位指令集的opcode表,其中,定義了4組custom指令類型,分別為custom-0/1/2/3,每種custom均有自己的opcode。
表2 RISC-V指令opcode空間劃分
本文所采用的32位custom指令的編碼格式如圖8所示,圖中,xs1、xs2和xd比特位分別用于控制是否需要讀取源寄存器rs1、rs2和寫目標寄存器rd,rs1、rs2和rd為寄存器組的索引地址,funct7作為額外的編碼,用于編碼更多的指令。
圖8 custom指令格式
數據搬移指令實現待計算數據矩陣或計算結果矩陣在協處理器節(jié)點間的移動,如圖9所示,為微碼表配置指令的具體格式。
圖9 數據搬移指令結構
圖9中,
1)opcode的值為7’b0001011表示采用custom-0的指令空間。
2)i_type的值為2’b00,表示該指令為數據搬移指令。
3)i_len表示數據搬移的數據長度,最多一條指令可搬移32個數據。
4)xs1和xs2的值均為1,表示數據搬移時,協處理陣列需要用到rs1和rs2所指示的通用寄存器中的數值。xd的值為0,表示數據搬移時,協處理器陣列無需返回執(zhí)行結果。
5)rs1和rs2所指示的通用寄存器的格式定義相同,rs1指示待搬移數據的來源,rs2指示待搬移數據的目的。其中的mtx.dij指示待數據元素在哪個矩陣中的哪個起始位置上,rij表示數據元素在哪個協處理器節(jié)點上,dir指示從mtx.dij開始,i分量和j分量是否遞增。step指示i分量和j分量遞增的步進值,core_f指示數據搬移的源或目的是否通過RISC-V內核的存儲器訪問接口進行。
微碼表配置指令用來完成對每個協處理器節(jié)點中的微碼表項的配置,因每條微碼表配置指令只能配置32bit的表項,而一條微碼表項的寬度可達88bit,因此,一條微碼表項有可能需要多條微碼指令才能完成配置。其指令格式如圖10所示。
圖10 微碼表配置指令結構
圖10中,
1)opcode的值為7’b0001011表示采用custom-0的指令空間。
2)i_type的值為2’b01,表示該指令為微碼配置指令。
3)offset的為5bit的值,表示該條指令需要配置一條微碼表項的哪一部分,因微碼表的寬度超過32bit位寬,該字段指示本條微碼指令是微碼表項中的第幾個32bit。最大可支持的微碼寬度為1024bit。
4)xs1和xs2的值均為1,表示微碼表配置時,協處理陣列需要用到rs1和rs2所指示的通用寄存器中的數值。xd的值為0,表示微碼表配置時,協處理器陣列無需返回執(zhí)行結果。
5)rs1所指示的通用寄存器中,rij指示需要配置哪個協處理器節(jié)點中的表項。table_sel用來控制將表項配置到算式內微碼存儲器、內循環(huán)微碼存儲器還是外循環(huán)微碼存儲器中。addr指示表項的具體地址。
6)rs2所指示的通用存儲器中,table_content指表項的具體內容。
運算控制指令用來實現對計算啟動的控制,其指令格式如圖11所示。
圖11 運算控制指令結構
圖11中,
1)opcode的值為7’b0001011表示采用custom-0的指令空間。
2)i_type的值為2’b10,表示該指令為運算控制指令。
3)loop_sel用來控制算式間循環(huán)控制器內外循環(huán)微碼指令執(zhí)行的順序。
4)loop_mode用來控制算式間循環(huán)控制器內外層循環(huán)是否嵌套。
5)xs1和xs2的值均為1,表示運算控制指令執(zhí)行時,協處理陣列需要用到rs1和rs2所指示的通用寄存器中的數值。xd的值為0,表示運算控制指令執(zhí)行時,協處理器陣列需要返回執(zhí)行結果。
6)rs1中,rij指示需要啟動哪個協處理器節(jié)點中的計算。loop_in用來控制算式間循環(huán)控制內循環(huán)的次數。loop_ex用來控制算式間循環(huán)控制器中外循環(huán)的次數。cal_rule用來控制內循環(huán)微碼存儲器和外循環(huán)微碼存儲器的地址。
7)rs2中,mtx.dij給出了算式間循環(huán)控制器中y_i指令執(zhí)行器和y_j指令執(zhí)行器的初始值。
8)rd中,finish指示算式間循環(huán)控制器的循環(huán)是否執(zhí)行完畢。
在本部分,我們以典型矩陣乘法為例,說明協處理器工作的具體過程。首先介紹了矩陣乘法所需的計算公式,然后分析了針對矩陣乘法所需要的微碼表項和相關指令,最后定性評估了執(zhí)行過程中對risv-v內核與協處理器之間對微碼和指令執(zhí)行速率的要求。
假設將兩個72×72維的矩陣相乘,則有公式(6)
(6)
通過分析公式(6),在算式間循環(huán)控制器中,采用內循環(huán)和外循環(huán)嵌套的方式進行,在協處理器中配置的微碼表如表3所示。
表3 矩陣乘法微碼配置表
在完成了上述微碼表現配置以后,在RISC-V內核中只需執(zhí)行2條指令,即可完成72×72維的矩陣乘法,首先執(zhí)行一條數據搬移指令,將待計算數據搬移到指定協處理器中,然后再執(zhí)行一條運算控制指令,啟動協處理器乘法計算過程??梢钥闯觯c沒有協處理器參與的72×72維矩陣乘法相比,由于乘加計算均通過協處理器完成,在RISC-V內核中指令執(zhí)行數量得到大幅縮減。
計算的硬件加速方法多種多樣,常見硬件加速方法有以谷歌TPU為代表的脈動陣列結構[8]、以清華Thinker為代表的可重構計算[5]等。前者通過將計算中間結果進行直接復用的方式減輕訪存帶來的內存墻問題;后者強調了算力的可重構特性,但本質上仍然需要仔細設計控制流與數據流的緊密配合問題。而本文采用的是數據分散存儲在算粒附近方法,強調的是一種近數據計算陣列的實現方法。
在本文中,我們基于RISC-V內核提出了一種近數據計算的協處理器陣列結構,待計算數據分散存儲在各協處理器陣列的節(jié)點中,在每個協處理器中算式間循環(huán)控制器和算式內循環(huán)控制器的控制下,通過將RISC-V的自定義指令與協處理器中的微碼表項有機結合,實現了有規(guī)律乘加運算的可重構近數據計算,從而減少了訪存次數。通過定性分析論證,將有規(guī)律的乘加運算均放置到協處理器中運行,在RISC-V的僅保留規(guī)律運算的過程控制部分,可以極大減少RISC-V內核的負擔。