Vivado HLS配合C語言等高級語言能幫助您在FPGA上快速實現算法。
高層次綜合(HLS)是指自動綜合最初用C、C++或SystemC語言描述的數字設計。工程師之所以對高層次綜合如此感興趣,不僅是因為它能讓工程師在較高的抽象層面上工作,而且還因為它能方便地生成多種設計解決方案。利用HLS,您能探索各種可能性,分析面積和性能特點,最終確定一個方案在FPGA芯片上實現算法。舉例來說,您能探索將存儲器映射到Block RAM(BRAM)或分布式RAM上有什么不同的影響,或者分析回路展開以及其它回路相關優化有什么效果,而且不必手動生成不同的寄存器傳輸級(RTL)設計。您所要做的僅僅是在C/C++/SystemC設計中設置相關指令而已。
賽靈思在其最新發布的Vivado?工具套件中推出了HLS工具。Vivado HLS是AutoESL工具的品牌轉型重塑,可提供眾多技術幫助您優化C/C++/SystemC代碼以實現目標性能。這樣的HLS工具就能幫助您在FPGA上快速實現算法,無需借助基于Verilog和VHDL等硬件描述語言的非常耗時的RTL設計方法。
為了幫助用戶了解Vivado HLS如何工作,我們不妨以矩陣乘法設計為例逐步剖析從設計描述(C/C++/SystemC)到FPGA實現整個端對端綜合流程。矩陣乘法在許多應用中都很常見,并廣泛用于圖像和視頻處理、科學計算和數字通信。本項目中的所有結果均使用Vivado HLS 2012.4生成,搭配使用賽靈思 ISE?軟件(14.4版)進行物理綜合和布局布線。此外,這一流程還采用了ModelSim和GCC-4.2.1-mingw32vc9進行RTL協同仿真。
圖1顯示了簡單的綜合流程,從C/C++/SystemC設計開始。C/C++/SystemC testbench用于驗證設計功能的正確性,同時還可用于RTL和C的協同仿真。協同仿真包括驗證生成的RTL設計(.v或.vhd)功能,這要使用C/C++/SystemC測試平臺而不是RTL測試平臺或者采用e或Vera驗證語言編寫的測試平臺。時鐘周期約束設置了設計應該運行的目標時鐘周期。設計將被映射到目標FPGA器件——賽靈思FPGA上。
C語言的矩陣乘法
為了充分利用我們的矩陣乘法實例,我們將探索矩陣乘法C語言實現方案的各種修訂版本,從而展示它們對綜合設計的影響。這一過程將凸顯您在使用HLS進行原型設計和實際設計時需要注意的重要問題。我們將跳過創建工程的有關步驟,因為您能很方便地在工具文檔中找到相關參考材料。我們將重點介紹設計和實現等方面。
在典型的Vivado HLS流程中,我們需要三個C/C++文件:源文件(包括待綜合的C函數)、頭文件和通過main()函數調用描述testbench的文件。
頭文件不僅包括源文件中使用的函數的聲明,也包括支持具有特定位寬的用戶定義數據類型的指令。這也使得設計人員能夠采用與C/C++所定義標準位寬不同的位寬。舉例來說,整形數據類型(int)在C語言中通常為32位長,但是在Vivado HLS中您可指定用戶定義的數據類型,例如只使用16位的“data”。
圖2顯示了用于矩陣乘法的簡單C函數。兩個矩陣mat1和mat2進行乘法。為了簡單起見,兩個矩陣大小一樣,都是兩行兩列。
在HLS流程中執行的步驟如下:
? 第一步:創建工程
? 第二步:測試功能
? 第三步:綜合
? 第四步:RTL協同仿真
? 第五步:導出RTL / RTL實現
第一步編譯工程并在不同的設計文件中測試語法錯誤等。第二步測試待實現的函數(在源文件中)功能是否正確。在這一步驟中您將使用testbench執行函數調用,驗證其功能是否正確。如果功能驗證失敗,您就需要返回來修改設計文件。
第三步進行綜合,Vivado HLS綜合源文件中定義的函數。這一步的輸出包括C函數的Verilog和VHDL代碼(RTL設計),也包括目標FPGA的資源利用率估算和時鐘周期估算。此外,Vivado HLS還可生成latency估算和回路相關的度量指標等。
第四步是使用C testbench仿真生成的RTL。這一步叫做RTL協同仿真,因為工具采用的就是之前用于驗證C源代碼的testbench,現在則測試RTL的功能正確性。要成功完成這一步,您系統(Windows或Linux)中的PATH環境變量應包含ModelSim安裝的路徑。此外,您還應在ModelSim安裝文件夾中包含GCC-4.2.1-mingw32vc9套件。
最后,第五步就要將RTL導出為IP模塊,用于更大的設計中,并由其它賽靈思工具進行處理。您可將RTL導出為IP-XACT格式的IP模塊,也可導出為System Generator IP模塊或pcore格式的IP模塊,進而用于賽靈思嵌入式設計套件。導出Vivado生成的RTL時,您可以選擇工具的“評估”選項來評估布局布線后的性能并且運行RTL實現。在此情況下, Vivado HLS工具會調用賽靈思ISE工具。要實現這一目的,您的系統PATH環境變量需包括ISE安裝路徑,Vivado HLS將會搜索ISE安裝。
當然,您也不一定非要將Vivado生成的RTL導出為以上三種格式之一的IP模塊不可。導出的格式文件可放在三個不同路徑下:此外,您也可在較大設計中使用Vivado生成的RTL,或者將其本身用作頂層設計。當較大設計中例化導出的RTL時,您應注意相關接口要求。
當綜合圖2中的C函數時,您將獲得如圖3所示的RTL級實現方案。您會發現,實現方案中的矩陣1和矩陣2的元素被讀取到函數,并且積矩陣的元素被寫出。這樣,實現方案假定“矩陣乘法”實體以外的存儲器能用來存儲矩陣1、矩陣2和乘積矩陣的元素。表1介紹了信號描述,表2則介紹了設計度量指標。
表2:用于圖3所示設計的設計度量指標
設計度量指標
器件:XC6VCX75TFF784-2
DSP48E
1
查找表
44觸發器
61實現的最佳時鐘周期(ns)
2.856時延
69吞吐量(初始間隔)
69表1:面向圖3中設計的信號描述
信號
描述
matleft_ce0
矩陣1存儲器的芯片使能
matleft_q0[15:0]
矩陣1的16位元素
matleft_address[1:0]
矩陣1存儲器的讀地址
matright_ce0
矩陣2存儲器的芯片使能
matright_q0[15:0]
矩陣2的16位元素
matright_address[1:0]
矩陣2存儲器的讀地址
product_ce0
積矩陣的存儲器的芯片使能
product_we0
積矩陣的存儲器的寫使能
product_d0[15:0]
積矩陣存儲器的寫數據
product_q0[15:0]
積矩陣存儲器的讀數據
product_address0[1:0]
積矩陣要讀寫數據的地址
ap_clk
設計的時鐘信號
ap_rst
設計的高有效同步復位信號
ap_start
開始計算的開始信號
ap_done
計算結束和輸出就緒的完成信號
ap_idle
表示實體(設計)空閑的空閑信號
ap_ready
表示設計為新輸入數據做好準備,與ap_idle配合使用
在表1中,start、done和idle信號與設計中控制數據路徑的有限狀態機(FSM)有關。您會發現,Vivado HLS生成的Verilog假定運算始于start信號,并且輸出數據在ap_done信號從低變高開始有效。Vivado HLS生成的Verilog/VHDL將始終保持至少三個基本信號:ap_start、ap_done和ap_idle,此外還有ap_clk信號。這意味著不管您使用Vivado HLS實現什么設計,設計latency都會限制您的流吞吐量。圖2中的設計latency為69個時鐘周期,目標時鐘周期為3納秒。這意味著在此特定案例中,所有積矩陣元素需要69個時鐘周期可輸出。這樣,您在至少69個時鐘周期前不能為設計提供新一組的輸入矩陣。
圖3中所示的實現方案現在可能并不是您在FPGA上實現矩陣乘法時所預想的結果。您或許希望一款實現方案能讓您輸入矩陣,并在內部進行存儲和計算,隨后讀取積矩陣元素。這顯然是圖2所示實現方案無法做到的。該實現方案需要外部存儲器提供矩陣數據的輸入和輸出。
調整代碼
圖4中的代碼能夠滿足您的需求,它是源文件的一部分,應該屬于C++文件而非此前的C文件。您應在頭文件matrixmultiply.h中包含另外兩個相關頭文件:hls_stream.h和ap_int.h。請注意,在圖2中,當源文件為C文件時,頭文件包含了ap_cint.h。頭文件ap_int.h和ap_cint.h有助于分別為C++和C源文件定義用戶定義的任意位寬的數據類型。需要頭文件hls_stream.h來充分利用流接口,并且只有在源文件為C++語言時才能使用。
圖4:用于矩陣乘法的重組源代碼
為了讓設計只接收輸入矩陣流,并輸出積矩陣流,您應在代碼中實現讀和寫數據流。流接口就像FIFO。默認情況下這個FIFO的深度為1。
表3 - 圖5中設計的信號描述
信號
描述
d_mat1_V_read
設計為矩陣1(左側矩陣)輸入做好準備時的信號
d_mat1_V_dout [15:0]
矩陣1的16位流元素
d_mat1_V_empty
通知設計矩陣1沒有更多元素的信號
d_mat2_V_read
設計為矩陣2(右側矩陣)輸入做好準備時的信號
d_mat2_V_dout [15:0]
矩陣2的16位流元素
d_mat2_V_empty
通知設計矩陣2沒有更多元素的信號
d_product_V_din [15:0]
積矩陣的16位輸出元素
d_product_V_full_n
通知設計積矩陣應該被寫入的信號
d_product_V_write
顯示積矩陣正在被寫入數據的信號
ap_clk
設計的時鐘信號
ap_rst
設計的高有效同步復位信號
ap_start
開始計算的開始信號
ap_done
計算結束和準備好信號輸出的完成信號
ap_idle
表明實體(設計)空閑的空閑信號
ap_ready
表示設計為新輸入數據做好準備,與ap_idle配合使用
表4:圖5所示設計的設計度量指標
器件:XC6VCX75TFF784-2
設計參數
無BRAM
或無分布式RAM存儲矩陣
單端口BRAM存儲矩陣
分布式RAM(LUT實現)存儲矩陣
DSP48E
1
1
1
查詢表
185
109
179
觸發器
331
102
190
BRAM
0
3
0
實現的最佳時鐘周期(納秒)
2.886
3.216
2.952
時延
84
116
104
吞吐量(初始間隔)
84
116
104
為了讓設計只接受輸入矩陣流,并輸出積矩陣流,您應在代碼中實現讀和寫數據流。代碼hls::stream<> stream_name用于為讀和寫數據流命名。這樣,d_mat1和d_mat2為讀取流而d_product為寫入流。流接口就像FIFO那樣工作。默認情況下,FIFO的深度為1。您應在Vivado HLS指令面板中通過選擇定義的數據流設置深度。對于圖4中的代碼而言,每個數據流的深度都為4個數據單元。請注意,這里的(i,j)回路在(p,q)回路之前執行,這是C++代碼的順序特性使然。因此,d_mat2數據流會在d_mat1數據流之后填滿。
完成數據流接口后,您可應用指令RESOURCE并通過指令面板選擇一個核,從而選擇將矩陣映射到BRAM。否則將用觸發器和查找表(LUT)實現矩陣。請注意,指令面板只有當源文件在綜合視圖中保持有效時才是有效的。
圖5顯示了圖4中代碼的設計實現情況。表3介紹了設計接口上可用的信號情況。在表3中,d_product_V_full_n是低有效信號,當需要通知內核積矩陣已滿時該信號為低。但在實現方案中通常不需要這樣。
表4顯示了3納秒時鐘周期約束下布局布線后的不同設計度量指標,包含了矩陣陣列映射到BRAM或分布式RAM的情況和未映射的情況。您從表4中可以看到,矩陣映射到單端口BRAM時,設計無法滿足3納秒的時序約束。表中專門包含了這個結果,說明您可用這種方法生成具有不同面積—時序參數的各種設計。此外,您也可從表1看出,雖然圖2中代碼的時延為69個時鐘周期,低于圖4中調整后的代碼的設計方案,但這種設計需要矩陣乘法實體以外的存儲器,這一點我們在上面已經解釋過了。
實現方案的精度
就這里顯示的結果而言,我將“data”這種數據類型定義為16位寬。因此,所有矩陣元素(左、右和積矩陣)都為16位寬。矩陣乘法和加法運算不能實現全精度。您可選擇在頭文件中定義另一種32位寬的數據類型data_t1,積矩陣的所有元素都采用這種數據類型,因為16位數(左側矩陣元素)乘以另一個16位數(右側矩陣元素)最多得到32位寬。這樣,資源利用率和時序結果將不同于表1和表4中的結果。
調整后的源代碼顯示出同樣的源文件會帶來多種不同設計解決方案。在本例中,一個設計解決方案采用BRAM,而另一個沒有采用。在每個Vivado HLS工程目錄中,您會看到Vivado HLS為不同的解決方案生成了不同的目錄。在每個解決方案目錄中都有一個名叫impl(也就是implementation,實現方案)的子目錄。在這個子目錄中,您會看到名為Verilog或VHDL的目錄,具體取決于RTL實現階段使用什么樣的源代碼。這個子目錄中也包含賽靈思ISE工程文件(文件擴展名為.xise)。如果Vivado HLS生成的設計是您的頂層設計,那么您可以雙擊這個文件來啟動賽靈思ISE運行這個解決方案,并生成用于門級時序和功能仿真的布局布線后模型。但您在Vivado HLS中不能做這種仿真。
在ISE中啟動解決方案后,您應給設計分配I/O引腳。隨后您可在ISE Project Navigator中選擇“Generating Programming File”以生成比特流。
在這一練習中,我們一步步完成了Vivado HLS一個實際的端對端流程,并在FPGA上實現算法。對于Vivado HLS中的許多高級特性而言,您應了解您需要什么樣的硬件架構,從而進行源代碼的調整。如需了解更多詳情,《Vivado高層次綜合教程》(UG871; )和《Vivado設計套件用戶指南》(UG002; )這兩個技術文檔對您大有裨益。
評論
查看更多