廖漢松,吳朝暉,李 斌
(1.華南理工大學微電子學院,廣州510641;2.人工智能與數字經濟廣東省實驗室(廣州),廣州510330)
RISC-V 是一種基于精簡指令集計算(Reduced Instruction Set Computing,RISC)原則的開源指令集架構,由于其相較于x86、ARM 架構可以更低成本地針對不同應用領域進行定制和優(yōu)化,因此成為近年來的研究熱點[1-3]。另一方面,人工智能現已滲透到各行各業(yè),出現了諸多智慧化產品。在主流的運算平臺中,GPU 功耗和成本過高[4],FPGA 資源和速度有限,ASIC 通用性較差[5-7],技術積累深厚的CPU 平臺更適合對成本敏感、算法靈活多變和計算步驟復雜的領域,但目前通用的CPU 仍難以滿足神經網絡大規(guī)模計算的要求。
由于登納德縮放比例定律失效和摩爾定律減緩,物聯網領域常用的CPU 平臺難以單純地通過提高處理器工作頻率、集成更多核心等手段來滿足神經網絡算法對端側算力日益增長的需求[8]。采用領域專用架構(Domain Specific Architecture,DSA),面向領域應用定制處理器核心和集成異構核心能夠更有效地提高能效,然而此方案卻受限于主流x86、ARM 架構的商業(yè)局限性而難以普及,而具有先進設計理念優(yōu)勢的開源架構RISC-V 則能有效避免這些問題。
為應對計算強度特別高的算法(如神經網絡算法),本文參考領域專用處理器架構,基于RISC-V 指令集,針對特定應用領域的需求設計專用拓展指集處理器,利用卷積神經網絡(Convolutional Neural Network,CNN)的并行性對常見網絡運算進行硬件加速,使其同時具備CPU 的通用性、靈活性和ASIC 的高能效優(yōu)勢。
RISC-V 是2010年由伯克利加州大學研究者研發(fā)的開源指令集架構,相比于x86 和ARM 等架構,其無需考慮前向兼容性,可對多年來舊架構暴露的問題和缺陷進行完善改進,而無須將一些冗余低效甚至錯誤的設計保留在其中[2],如去除分支延遲槽、去除使用頻率低卻影響處理器亂序執(zhí)行的條件執(zhí)行指令、僅實現一種簡潔的內存尋址模式等。編譯器軟件隨著多年發(fā)展得以完善,許多使用硬件優(yōu)化的手段可被軟件取代,硬件設計可以更加簡潔。以往依賴于硬件加速的設計,可能已經不再發(fā)揮作用,甚至阻礙優(yōu)化。RISC-V 還采用了不同尋常的模塊化設計,將不同的指令劃分成可選的拓展。使用基礎指令集+拓展指令集的模式,除了基礎指令集必須實現外,還可以根據應用的需求、高性能或是低功耗,選擇是否添加這些拓展指令集。而且這個開放的指令集中保留了大量的操作碼空間,以便添加用于領域專用加速器的自定義指令。RISC-V 的這種設計既能滿足軟件生態(tài)的兼容,又能在保證處理器盡可能精簡的同時添加領域專用加速器。
本文定制的Rocket 處理器是由伯克利大學的ASANOVI? 等人開發(fā)的一款經典5 級流水線的單發(fā)射標量處理器核心[9]。該處理器由基于Chisel(Constructing Hardware in a Scala Embedded Language)構建的開源SoC 設計平臺Rocket Chip 生成。面向對象語言使得開發(fā)硬件更加靈活,通過Rocket Chip平臺,可以對處理器進行高度定制化。除了面向特定應用開發(fā)專屬的自定義模塊,只需要調整參數即可對已有的處理器核心數、指令集拓展、Cache 配置、總線等進行調整,最后編譯出所需的定制處理器。由于專用指令集處理器的設計十分復雜,生產成本也很高,為確??煽啃?,一般都會采用模擬器對處理器進行建模和驗證。由于Rocket Chip 平臺原本就由高級語言進行構建,因此能夠快速生成配套的C++模擬器,大幅縮短處理器的驗證時間,降低開發(fā)成本。
此外,Rocket Chip 平臺還提供RoCC(Rocket Custom Coprocessor)接口用于加入自定義的加速器。加速器可使用以下3 種方式接入:
1)接入處理器的指令處理流水線。Rocket 核將相關指令發(fā)射到RoCC 接口,加速器只能通過指令和通用寄存器獲取命令與數據,完成特定運算后給出響應并回傳數據到通用寄存器。
2)增加一級數據高速緩存(L1 D-Cache)訪問接口。L1 D-Cache 中緩存了處理器頻繁訪問的數據,加速器直接通過高速緩存讀寫數據,能夠更高效地獲取所需處理數據和回寫結果。
3)完全解耦。通過總線直連到外部內存系統,更適用于數據龐大且重用性低的情況。
為獲得更好的性能,神經網絡研究向更深、更復雜的方向發(fā)展。然而在使用神經網絡落地解決實際問題時,受限于終端設備的算力、內存大小和低功耗、低成本的應用場景,很多經典的網絡模型都難以實現。因此,研究者著手于網絡優(yōu)化,通過以1×1 和3×3 卷積核取代大尺寸的卷積核、以全局池化層取代全連接層等手段簡化模型的復雜度,減少網絡參數的數量[10-12]。經典網絡模型與輕量化網絡模型比較[13-15]如表1所示??梢钥闯觯瑴蚀_率相近的SqueezeNet網絡與AlexNet網絡參數量相差約50 倍,前者顯著降低了計算時對算力的需求和內存的消耗。
表1 經典網絡模型與輕量化網絡模型的對比Table 1 Comparison of classical network model and lightweight network model
本文提出如圖1所示的專用指令集處理器設計方案,主要分為兩個部分,即Rocket 核和用于卷積神經網絡運算的加速器。為使處理器盡可能精簡,在Rocket 核部分選擇RV32IMAFC 架構,其中32 位寬的處理器足以覆蓋物聯網設備的內存范圍。Rocket 核負責完成算法的邏輯控制和圖像預處理,在此基礎上使用自定義拓展指令觸發(fā)加速器完成CNN 中的密集計算。通過自定義拓展指令配置卷積神經網絡各層的信息,對輸入數據進行分組并靈活調整加速器的數據通路,從而適應不同大小的輸入數據,減少片上緩存需求,同時適應輕量化網絡的常用運算操作和結構。加速器接收到相關指令后,從片外存儲器中獲取相應數據,根據指令完成CNN 中卷積、激活、局部池化和全局池化等耗時操作,并將結果回寫存儲器以待后續(xù)處理。加速器沒有接入D-Cache,而是以更大位寬的獨立讀寫端口對片外存儲器進行訪問,完成與Rocket核的數據傳遞。由于卷積神經網絡數據量龐大,因此若不能匹配Cache 大小,則會導致訪存命中率低和數據頻繁進出,從而降低效率。
圖1 本文處理器系統框圖Fig.1 System block diagram of the proposed processor
本設計中處理器并未使用高性能和過多配置,目的是節(jié)省更多空間留給CNN 加速器運算單元,具體配置如表2所示。
表2 處理器配置Table 2 Configuration of processor
本文設計了如圖2所示的2種指令用于CNN加速,包括用于配置加速器寄存器的設置類指令和用于計算的操作類指令。指令中第0 位~第6 位為opcode 字段,處理器解碼得到0001011 后會將此類指令發(fā)射到加速器模塊,第28 位~第31 位的funct4 字段用于標識不同的指令類型,最高位取0 表示為設置類指令,取1 表示為操作類指令。位操作類指令的第28 位~第30 位分別用于指定加速器是否進行卷積、池化操作,第15 位~第19位的rs1字段、第20位~第24位的rs2字段、第25位~第27 位的offset3 字段則用于指定主處理器通用寄存器堆中的編號。其中,offset3 為相對rs2 指定寄存器編號的偏移量,即第3 個寄存器通過[rs2+offset3]來尋址,因此,每條指令最多可傳遞3 個32 位的源操作數對加速器進行配置。第12 位的xs2 字段、第13 位的xs1 字段、第14 位的xd 字段用于標識是否使用相應字段指定的通用寄存器進行數據傳遞。xs1 設置為1 表示使用rs1、rs2 指定的通用寄存器傳遞數據。操作指令中xs2設置為0,表示不需要配置第3 個通用寄存器數據。由于設計中加速器的運算數據通過獨立訪存端口進行傳遞,并不需要回傳到主處理器的通用寄存器堆,因此xd設置為0,且修改原本用于指定目的寄存器的第7 位~第11 位字段用于傳遞更多信息。設置類指令中修改為addr 字段,用于指定3 組加速器中用于配置的寄存器,分別存儲輸入數據、輸出數據和網絡參數信息。操作類指令中修改為info 字段,用于指定各計算操作的類型,如卷積和池化操作的窗口大小、運算步長、數據四周是否補零。加速器并未實現太細顆粒度加速,而是以每層為單位進行加速。雖然會犧牲一定靈活性,但是細顆粒不利于進一步優(yōu)化運算,且數據頻繁進出加速器影響效率。
圖2 卷積神經網絡專用拓展指令Fig.2 Special extension instructions of convolutional neural network
2.2.1 數據量化
為在有限資源下實現CNN 加速器設計,本文采用英偉達的使用飽和截取的線性量化方案[16],將網絡權重和激活值量化為8 bit。經SqueezeNet 網絡驗證,與浮點卷積相比,量化后雖然Top1 準確率下降了0.58%,Top5 準確率下降了1.39%,但仍能保持較高準確率。
2.2.2 卷積運算單元設計
卷積模塊運算單元如圖3所示,采用內存帶寬占用相對較低的二維PE 陣列,由16×16 個PE 陣列組成,支持并優(yōu)化輕量化網絡中常用的1×1 和3×3 兩種大小的卷積,通過不同映射實現多種并行方式和數據復用。此外,為精簡常規(guī)PE 陣列之間繁瑣的互連,本模塊以廣播數據的方式,將權值、輸入特征圖分別從水平方向、垂直方向加載到PE,并且PE 計算結果僅會在一行內累加。
圖3 卷積模塊Fig.3 Convolution module
卷積過程采用保留卷積核的方式,以1×1 卷積為例,即完成當前16 輸入通道內所有數據與卷積核的卷積、卷積核徹底用完,才加載下一組16 通道輸入特征圖和卷積核。遍歷所有輸入通道后才能獲得完整的輸出特征圖,中間產生的部分和數據需要保存在輸出緩存中。此種方式能夠更好地提高數據重用性,卷積時可復用輸入緩存內和PE 內的輸入特征圖,也便于之后模塊實現3×3 的池化操作。
為方便闡述該結構實現卷積的運算過程,對PE 陣列進行簡化,并分別對1×1 和3×3 兩種卷積映射進行分析。
1×1卷積映射如圖4(a)所示。每個PE 都可以獨立完成運算,并行計算6 通道輸入、6 通道輸出。PE 陣列不需要分組,數據也不需要變換。6 通道輸入特征圖分別并行加載至6 列PE,計算結果輸出至每行加法樹,可同時輸出6 通道輸出特征圖。由于1×1 卷積不需要復用多行數據,PE 內緩存不需要進行額外控制,計算完該6通道輸入后,向緩存請求下一組權值和6通道輸入,因此遍歷所有通道輸入特征圖并完成部分和疊加,即可得到6 通道完整輸出特征圖。
3×3卷積映射如圖4(b)所示。以步長為3將PE陣列劃分為4組,同一列的組處理相同輸入特征圖,同一行的組處理相同輸出特征圖。該陣列可并行計算2 通道輸入、2 通道輸出。每組內3 行PE 分別計算同一輸入特征圖通道內的3 個卷積窗口,行內每個PE 并行計算卷積窗口內3 個乘法。為簡化控制邏輯,每列PE 數據都是廣播寫入,因此,緩存內寫入數據都相同。根據PE位置的不同,卷積時按不同的順序使用緩存內數據。PE 內有3 個輸入特征圖局部緩存,最多可緩存3 行輸入特征圖,實現3×3 卷積行間數據的復用。
圖4 PE 陣列映射示意圖Fig.4 PE array mapping diagram
圖5 展示了PE 組Group(0,0)計算步長為1 的3×3 卷積時的過程。計算卷積第1 行時,該行的卷積核固定,在#0 時刻這9 個PE 可計算出前3 個卷積結果的中間值。5 個周期后完成該行輸入特征圖計算,切換到第2 行卷積核與第2 行輸入特征圖相乘,第3 行執(zhí)行相同步驟。分割位寬16 的特征圖經核大小為3、步長為1 的卷積后有效數據只有14 個,因此,#4 時刻會有一項無效數據。3 行特征圖乘積共15 個周期,中間值疊加后即得到完整的一行14 個卷積結果。卷積過程中的3 行輸入特征圖在PE 內分3 片局部緩存存放,除卷積開始前需要提前加載3 行數據,計算下一行卷積時,只需要將第1 行輸入數據替換成第4 行輸入數據,即可減少片內數據的重復傳輸。
從圖5 可以看出,每行PE 計算時進行了不同的映射,如在#0 時刻時,第1 個卷積窗口的數據D(0,0)、D(0,1)、D(0,2)分配到PE(0,0)、PE(0,1)、PE(0,2),而第2個卷積窗口的數據D(0,1)、D(0,2)、D(0,3)則分配到PE(1,1)、PE(1,2)、PE(1,0)。兩個卷積窗口間是彼此重疊的,PE(0,1)與PE(1,1)、PE(0,2)與PE(1,2)以及PE(1,0)與下一時刻的PE(0,0)使用的是相同的數據。使用該方法排序后,同一列PE使用的是相同的輸入特征圖,數據傳輸變得非常規(guī)則,可簡化PE陣列的互連與控制、同時減少PE內部局部緩存的大小。針對卷積步長為2 的運算,橫向上數據流向不做調整,后續(xù)模塊可通過數據有效信號過濾無效數據,而縱向上修改輸入特征圖的數據請求邏輯,當每個卷積窗口計算完時共替換兩行數據,減少數據計算量。若是進行四周補零后再卷積的運算,數據請求時在輸入特征圖局部緩存首位加入數據0,即行首補零,而行末補零乘積結果為0,通過禁用該列PE 實現,即拉低該列PE 使能信號。卷積過程中首尾兩行補零行不寫入輸入特征圖局部緩存,調整緩存讀出控制減少計算量,疊加卷積中間值時也少疊加一次。
圖5 PE 陣列組內卷積計算過程Fig.5 Convolution calculation process in PE array group
2.2.3 局部池化運算單元設計
池化是一種降采樣操作,常用于提取特征信息和縮小特征圖尺寸,常見有平均池化和最大池化。以最大池化為例,局部池化運算單元設計如圖6所示,其中,左半部分用于當前行數據比較,右半部分用于完成多行數據比較及復用,支持2×2 和3×3 兩種大小池化窗口。2×2 池化時,池化窗口間沒有重疊,每兩周期進行一次比較。第1 行比較時,數據較大者直接壓入FIFO。當下一行數據兩兩比較后,再讀出FIFO 中的值進行比較,即可完成一個池化窗口的運算,然后比較數據直接寫入輸出緩存模塊,并清空FIFO。3×3 池化時,增加下方通路比較第3 個數據與前兩個數據的較大者,將結果壓入FIFO。計算完池化窗口第3 行后,輸出當前池化結果,并將第3 行的比較結果寫入FIFO。由于3×3 池化存在數據重疊,因此計算下一行池化窗口可直接從第2 行數據開始請求,并復用FIFO 中的結果,減少計算量和片內數據的重復傳輸。使用此結構進行局部池化,每通道輸入特征圖只需要請求一次。此外,若池化時輸入特征圖尺寸除不盡,結果需要向上取整,即輸入特征圖的右側與下側需要補零,此時通過控制Valid 信號,填充數據0 到運算單元中以完成一次完整的池化。
圖6 池化模塊Fig.6 Pooling module
2.2.4 訪存控制與片內緩存設計
加速器使用更大的片內緩存可以減少片外訪存需求,提高系統能效。但由于CNN 的特征圖、參數量大,限于面積、功耗的成本,因此片內緩存不能設計太大。為適應不同大小的特征圖并實現高效的并行運算,設計如圖7所示結構對數據進行分塊,其中,(i,j,c)表示第c通道第i行第j列數據。讀寫時特征圖以8 通道、寬度16 進行分組,并以行方向、列方向、通道方向的順序進行連續(xù)讀寫。由于3×3卷積、3×3 池化以及同時進行卷積與池化時存在數據重疊,因此分塊時需要根據情況重復讀取部分數據。
圖7 特征圖分塊示意圖Fig.7 Block diagram of feature map
卷積神經網絡運算中需要頻繁讀取大量的數據,為降低加速器與片外存儲器間讀寫耗時的感知,片內緩存采用乒乓結構。如圖8所示,輸入和輸出緩存均由兩塊緩存組成,通過重疊讀寫操作和數據運算時間,增加訪存吞吐量。對于輸入緩存,當其中一塊保存的數據徹底使用完畢且另一塊寫入一組完整數據塊后,交換兩塊緩存使用權,而輸出緩存中還存在卷積得到的部分和數據,需要在計算下一數據塊時讀出疊加。因此,在最終結果寫滿一塊緩存且另一塊緩存數據完全寫入片外存儲后,才切換輸出緩存。
圖8 乒乓緩存示意圖Fig.8 Schematic diagram of Ping-Pong cache
輸入特征圖經上述方法分塊后,若卷積層中包含所有通道的數據塊能夠存在一塊輸入緩存,則緩存中數據可以多次使用。輸入緩存所有通道數據遍歷一次后無需切換到另一塊緩存,也無需從片外存儲重新讀取,直到輸出特征圖所有通道計算完成。若包含所有通道的數據塊能夠在兩塊輸入緩存內存下,則可在兩塊緩存間切換以完成所有通道輸出特征圖的計算,也不需要從片外存儲反復讀取相同數據。
為保證有足夠的數據滿足運算單元的需求,輸入輸出緩存包括多個讀寫端口。如圖9所示,每塊片內緩存拆分為16 片并列的RAM 緩存區(qū),緩存區(qū)數量與運算單元相關。緩存區(qū)使用相同地址訪問以簡化緩存讀寫邏輯。片外存儲讀寫時將16 片緩存區(qū)分為兩組,每次并行寫入8 片輸入緩存區(qū),從8 片輸出緩存區(qū)并行讀出。
圖9 片內緩存示意圖Fig.9 Schematic diagram of on-chip cache
不同的運算操作加速器內數據流向和緩存存儲數據的順序不同。進行1×1 卷積時PE 陣列可以并行計算16 通道特征圖,因此,當輸入緩存寫完8 通道上述特征圖數據塊后,下一數據塊寫入另一組8 片緩存區(qū)中,保證16 通道的輸出。如果加速器同時完成卷積和池化運算,激活模塊輸出16 通道的輸出特征圖后,流向局部池化模塊,通過16 個池化運算單元并行完成池化操作,然后并行寫入輸出緩存?;貙懫饩彺鏁r輸出緩存中數據的讀出順序與寫入輸入緩存相同。3×3 卷積時,每組緩存區(qū)存滿完整通道的數據后才切換至另一組緩存區(qū)。PE 陣列每次最多并行計算5 通道特征圖,8 片緩存區(qū)分成兩次進行。與1×1 卷積不同,PE 陣列中每三列PE 的數據都由同一片緩存區(qū)提供。3×3 卷積時PE 陣列每次最多輸出5 通道數據,若數據流向池化模塊,最多也只會同時使用5 個池化運算單元。輸出緩存的寫入也同樣是分兩次寫入8 通道特征圖數據,寫滿完整通道的數據后切換至另一組緩存區(qū)。
只進行局部池化時輸入特征圖不進行量化,輸入緩存占用較大,當輸入數據過大時可使用兩片緩存區(qū)進行存儲。從輸入緩存讀取8 通道數據,并行完成池化運算后,直接寫入輸出緩存。經局部池化后的數據縮小了4 倍,輸出緩存的寫入順序與3×3 卷積相同,寫滿完整通道的數據后切換至另一組緩存區(qū)。而全局池化層常位于CNN 的最后一層,此時的特征圖尺寸小、通道多,并且每通道輸入都被轉化為1×1 輸出。因此,采用與1×1 卷積相同的讀寫順序,同時從輸入緩存讀出16 通道數據,運算完成后數據同樣并行寫入16 片輸出緩存區(qū)。
對本文設計的RISC-V 架構卷積神經網絡專用指令集處理器進行驗證測試。首先在軟件層面上完成對定制的CNN 拓展指令的實現和驗證。然后調用拓展指令完成SqueezeNet網絡分類算法的程序編程。通過仿真工具仿真處理器加速卷積神經網絡計算的過程,完成處理器的功能驗證。最后在FPGA 平臺上實現,對系統資源占用、功耗、性能等進行評估。
本文設計的CNN 拓展指令采用匯編指令的形式嵌入到C 語言程序中,取代CNN 算法中的耗時操作以達到加速目的[17]。通過指定格式在C 語言代碼中將拓展指令的匯編語句嵌入,指定傳入的數據、通用寄存器,最后編譯成相應的RISC-V 指令。完成算法程序編寫后,使用RISC-V 交叉編譯工具鏈進行編譯,生成程序的二進制文件。
將本文使用的FPGA 驗證平臺為含Xilinx Kintex-7系列FPGA 芯片的Genesys 2 開發(fā)板。將處理器工作頻率約束在100 MHz,經Vivado 軟件綜合以及布局布線后,FPGA 內部資源使用情況如表3所示,處理器建立時間裕度為0.189 ns,片上功耗為1.966 W,其中動態(tài)功耗占91%。
表3 FPGA 資源使用情況Table 3 FPGA resource utilization
不同卷積神經網絡模型的結構各異,其計算的結果可能相差很大。為更好地對本文設計的處理器進行資源和性能評估,此處選用推理相同網絡的文獻進行對比,如表4所示。因為本文處理器面向物聯網終端和移動端設備,考慮到低成本、低功耗的限制,使用了較小規(guī)模的PE 陣列、片上緩存,資源消耗較少。相較于文獻[18],雖然推理計算速度上沒有那么快,但是本文設計具有更好的性能功耗比,提高約25.81%,同時也優(yōu)于其他對比處理器。
表4 FPGA 平臺加速器性能對比Table 4 Performance comparison of FPGA accelerators
本文處理器與常見通用處理器、GPU 和手機處理器推理計算SqueezeNet 網絡模型的性能對比如表5所示,其中,AMD Ryzen7 3700X 與英偉達RTX2070 Super 均在Caffe 框架下計算,驍龍835 則在為手機端優(yōu)化的推理框架NCNN 下計算。由于輕量化卷積神經網絡前向推理過程的計算量和參數量對RTX2070 Super 來說負載較小,并不能充分利用內部所有運算單元,因此實際功耗有所下降。通過對比可知,在相同卷積神經網絡模型推理上,在FPGA 上實現的本文處理器比驍龍835 單核計算耗時減少40.64%,且性能功耗比是通用CPU Ryzen7 3700X 的25.39 倍、GPU RTX2070 Super 的1.04 倍、手機處理器驍龍835 單核計算時的3.05 倍、四核計算時的1.16 倍。
表5 不同平臺性能對比Table 5 Performance comparison of different platforms
本文基于開源指令集RISC-V 設計卷積神經網絡專用指令集處理器。面向物聯網應用場景,針對輕量化網絡模型的基本運算操作定制RISC-V 拓展指令。同時考慮物聯網設備的算力與資源限制,對加速器采用量化網絡模型、優(yōu)化運算單元、靈活調整數據通路、設計相應數據結構和片上緩存等方法設計加速方案?;贔PGA 平臺的實驗結果表明,在100 MHz 工作頻率下推理SqueezeNet 網絡,處理器計算性能達到19.08 GOP/s,功耗為1.966 W,比手機處理器單核計算速度更快,并且在性能功耗比上較其他平臺更具優(yōu)勢。下一步將針對具體應用對主處理器核心進行深度定制,開發(fā)相應的編譯器,同時使用高級程序語言提高使用效率,保證軟件兼容性。