http://www.tuicool.com/articles/eQ7nEn
最終到了HLS部分。HLS是High Level Synthesis的縮寫,是一種能夠将進階程式設計語言C,C++。SystemC綜合為RTL代碼的工具。
生産力的發展推動了設計模式。在電子技術0基礎階段,人們關注的是RLC電路。通過建立微分方程求解電路響應。
門級電路是對RLC的初步封裝,人們進而採用布爾代數、卡諾圖進行電路設計與分析。之後随着內建電路進一步發展。門電路能夠內建為寄存器、觸發器、ROM等宏單元。設計工具也變得更為高度子產品化。算法級别的電路設計,則一直沒有特别好的工具,直到出現了HLS。
HLS能夠将算法直接映射為RTL電路,實作了高層次綜合。從這個層面上講,System Generator也是一種高層次綜合工具。由于它将matlab算法描寫叙述綜合為RTL代碼。假設今後機器學習、人工智能獲得重大突破,也許會出現将人類自然語言綜合為RTL代碼的工具,不知我們能否見證它的面世。
HLS的學習資源能夠參考 http://xilinx.eetrend.com/article/5096 。本節給出較為通用的矩陣與向量相乘樣例,從全串行到全并行進行了一步步優化實作。
矩陣實驗室Matlab是比較經常使用的數學仿真軟體。
本部落客用的是R2013a版本号。為了驗證矩陣向量相乘正确性。我們先用matlab生成測試矩陣和向量。并利用matlab計算結果。代碼例如以下:
clear;
clc;
close all;
N = 5;
A = randi([1,100],N,N);
b = randi(100,N,1);
c = A*b;
KKK_SaveToCHeaderFile(A,'A.h');
KKK_SaveToCHeaderFile(b,'b.h');
KKK_SaveToCHeaderFile(c,'c.h');
這裡給出的是A*b = c的簡單樣例,A為5X5矩陣。b為5X1向量,結果c為5X1向量。當中KKK_SaveToCHeaderFile()是将矩陣、向量儲存為C語言數組的子函數。定義例如以下:
function [] = KKK_SaveToCHeaderFile(var,fn)
fid = fopen(fn,'w');
var = reshape(var.',1,[]);
fprintf(fid,'%d,\r\n',var);
fclose(fid);
給出測試例程中,A例如以下:
82 10 16 15 66
91 28 98 43 4
13 55 96 92 85
92 96 49 80 94
64 97 81 96 68
76
75
40
66
18
9800
15846
16555
23124
22939
執行matlab腳本之後,生成三個檔案:A.h。b.h。c.h,這些是作為HLS程式的輸入資料和參考結果。以下我們用HLS工具實作上述矩陣X向量的功能。第一步,執行Vivado HLS。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLi0zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwkzX39GZhh2csATMflHLwEzX4xSZz91ZsADMx8FdsYkRGZkRG9lcvx2bjxSa2EWNhJTW1AlUxEFeVRUUfRHelRHL2EzXlpXazxyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3PnVGcq5CZ5I2NygTZhFWZxYGZ4UzNjJWYidzMykDN1QTZzIGZw8CXxAzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL5M3Lc9CX6MHc0RHaiojIsJye.jpeg)
選擇第一項,Create New Project,建立新projectMatrixMultiply
輸入路徑和project名之後,點Next。
加入頂層子產品檔案。這裡我們Top Functions輸入MatrixMultiply,然後New File...,建立一個.c檔案。命名為MatrixMultiply.c(字尾不要省略!
),然後點Next
加入頂層檔案測試腳本。這裡New一個檔案TestMatrixMultiply.c(字尾不要省略!
),然後Add前面用Matlab生成的A.h。b.h,c.h,例如以下圖所看到的:
點Next,選擇解決方式配置,例如以下圖所看到的
其餘保持預設,僅僅改動Part Selection部分,改為ZedBoard。
改完後。Finish就可以進入主界面,例如以下圖所看到的
能夠看出。Vivado HLS界面非常像非常像Xilinx SDK,不同的是前者負責PL部分開發。後者負責PS軟體編寫。定位不同決定了二者今後的路必定走向分歧。
将MatrixMultiply.c内容改為:
typedef int data_type;
#define N 5
void MatrixMultiply(data_type AA[N*N],data_type bb[N],data_type cc[N])
{
int i,j;
for(i = 0;i<N;i++)
{
data_type sum = 0;
for(j = 0;j<N;j++)
{
sum += AA[i*N+j]*bb[j];
}
cc[i] = sum;
}
}
将TestMatrixMultiply.c内容改為:
#include <stdio.h> typedef int data_type; #define N 5
const data_type MatrixA[] = { #include "A.h" }; const data_type Vector_b[] = { #include "b.h" }; const data_type MatlabResult_c[] = { #include "c.h" };
data_type HLS_Result_c[N] = {0}; void CheckResult(data_type * matlab_result,data_type * your_result); int main(void) { printf("Checking Results:\r\n"); MatrixMultiply(MatrixA,Vector_b,HLS_Result_c); CheckResult(MatlabResult_c,HLS_Result_c); return 0; }
void CheckResult(data_type * matlab_result,data_type * your_result) { int i; for(i = 0;i<N;i++) { printf("Idx %d: Error = %d \r\n",i,matlab_result[i]-your_result[i]); } }
首先進行C語言仿真驗證,點這個button:
結果例如以下:
從C仿真輸出看到,仿真結果與matlab計算結果一緻,說明我們編寫的C程式MatrixMultiply是正确的。
接下來進行綜合。按C仿真後面那個三角形button,得到結果例如以下:
注意到,計算延遲為186個時鐘周期。
這是未經過優化的版本号,記為版本号1。
為了提高FPGA并行計算性能,我們接下來對它進行優化。
打開MatrixMultiply.c,點Directives頁面,能夠看到我們能夠優化的對象。
注意到矩陣和向量相乘是雙層for循環結構。我們先展開最内層for循環,過程例如以下:
右鍵點選最内側循環,右鍵,然後Insert Directive...
彈出對話框例如以下,Directives選擇UNROLL,OK就可以。後面全部都保持預設。
再次綜合後,結果例如以下
可見效果很明顯,延遲縮短到51個時鐘周期。
用相同方法,展開外層循環。綜合後結果例如以下:
計算延遲又減少了1/3。!
。
但是代價呢?細心的你可能發現占用資源情況發生了較大變化,DSP48E1由最初的4個變為8個後來又成為76個。!
!
FPGA設計中,延遲的減少,即速度提高,必定會導緻面積的增大!
循環展開是優化的一個角度,還有一個角度是從資源出發進行優化。
我們打開Analysis視圖。例如以下所看到的:
從分析視圖能夠看出各個子產品的執行順序,進而為優化提供更為明白的指引。我們發現AA_load導緻了延遲,假設全部AA的值都能一次性并行取出,勢必會加快計算效率!
回到Synthetic視圖,為AA添加Directives:
選擇Resources,再點Cores後面的方框,進入Vivado HLS core選擇對話框
按上圖進行選擇。使用ROM是由于在計算矩陣和向量相乘時,AA為常數。确認。
仍然選擇AA。添加Directives,例如以下圖:
選擇數組分解,mode選擇全然complete,綜合後結果例如以下圖:
延遲進一步減少,已經降到11個時鐘周期了!。!是否已經達到極限了呢???
答案是否定的。我們進入Analysis視圖。看一下還有哪些地方能夠優化的。經過對照發現bb也須要分解,于是依照上面的方法對bb進行資源優化,也用ROM-2P類型,也做全分解,再次綜合,結果例如以下:
發現延遲進一步減少到8個時鐘周期了。。!
老師。能不能再給力點?
能夠的!!
我們進入分析視圖,發現cc這個回寫的步驟堵塞了總體流程,于是我們将cc也進行上述資源優化。僅僅隻是資源類型要變為RAM_2P,由于它是須要寫入的。
綜合結果:
總體延遲已經減少到6個clk周期了!!!
再看Analysis視圖:
延遲已經被壓縮到極限了。
。。
老師。還能再給力點嘛?
答案是能夠的。!
我們前面的全部運算都是基于整形數int,假設将數值精度減少,将大大節省資源。
注意如今DSP48E1須要100個!
看我們怎樣将資源再降下來。
這就須要借助“随意精度”資料類型了。
HLS中除了C中定義的char。shrot,int。long。long long 之外。還有随意bit長度的int類型。
我們将代碼開頭的data_type定義改為:
#include <ap_cint.h>
typedef uint15 data_type;
因為matlab生成的随機數在1~100以内。乘積範圍不會超過10000。于是取15bit就能滿足要求。
首先驗證下結果的正确性,用C Simulation試一下。結果例如以下:
看來結果是正确的(當然也不排除數位不夠,溢出後的結果相減也是0,須要你自己決定數值位寬)
綜合一下。結果例如以下:
延遲縮短了一半,DSP48E1降低到原來的1/4!!
!!!。!!!!
。!!!
。!
!。!。!!!。。!!!
!!
。。!!。!!
我和我的小夥伴們都震驚了!
。!!
。。。!
!!。!
!!!!。。!
!。!。!
!!!!!
。!!。。
再看Analysis視圖
能夠發現我們的資源使用率已經達到極緻,時序已經壓縮到無以複加,實作了全并行計算。系統時鐘全然能夠達到100MHz。延遲僅3CLK。約30ns,相比matlab。得到約數百倍加速(matlab進行矩陣——向量相乘時採用浮點計算)。
通過本文實驗,能夠發現利用Vivado HLS實作從最初的C串行實作到全并行實作的步步優化。總結一下優化步驟:
(1)粗優化(循環展開、子函數内聯)
(2)訪存優化(塊存儲分散化、多port存取)
(3)精優化(數值位寬優化、流水線優化)
(4)總線化(利用AXI4、AXI-Stream總線接口,減少總體訪存需求)
利用HLS能夠将原來的C算法高速部署到FPGA上,降低直接進行硬體程式設計的工作量。在非常多情況下,優化手段能夠和CUDA進行類比,互相借鑒。CUDA事實上更接近軟體接口,而HLS更接近硬體程式設計接口,也許今後兩者會在新的層次上融合為統一架構語言。