使用Vitis HLS創(chuàng)建屬于自己的IP
副標題-FPGA高層次綜合HLS(三)-Vitis HLS創(chuàng)建Vivado IP
高層次綜合(High-level Synthesis)簡稱HLS,指的是將高層次語言描述的邏輯結(jié)構(gòu),自動轉(zhuǎn)換成低抽象級語言描述的電路模型的過程。
對于AMD Xilinx而言,Vivado 2019.1之前(包括),HLS工具叫Vivado HLS,之后為了統(tǒng)一將HLS集成到Vitis里了,集成之后增加了一些功能,同時將這部分開源出來了。Vitis HLS是Vitis AI重要組成部分,所以我們將重點介紹Vitis HLS。
官方指南:
https://docs.xilinx.com/r/_lSn47LKK31fyYQ_PRDoIQ/root
重要術(shù)語
LUT 或 SICE
LUT 或 SICE是構(gòu)成了 FPGA 的區(qū)域。它的數(shù)量有限,當它用完時,意味著您的設(shè)計太大了!
BRAM 或 Block RAM
FPGA中的內(nèi)存。在 Z-7010 FPGA上,有 120 個,每個都是 2KiB(實際上是 18 kb)。
Latency延遲
設(shè)計產(chǎn)生結(jié)果所需的時鐘周期數(shù)。
循環(huán)的延遲是一次迭代所需的時鐘周期數(shù)。
Initiation Interval (or II, or Interval間隔)
在接受新數(shù)據(jù)之前必須執(zhí)行的時鐘周期數(shù)。
循環(huán)的間隔是可以開始循環(huán)迭代的最大速率,以時鐘周期為單位。
之前,我們一直在使用Vivado給我們提供的IP或者使用硬件描述語言制作 IP 。今天我們將講解如何使用HLS-高級綜合語言來創(chuàng)建屬于我們自己的IP。我們將使用的工具稱為Vitis HLS,此后稱為 HLS。HLS 采用 C 和 C++ 描述并將它們轉(zhuǎn)換為自定義硬件 IP,完成后我們就可以在 Vivado 項目中使用該IP。
Vitis HLS
創(chuàng)建一個新的 HLS 項目:
通過從Linux 終端鍵入 vitis_hls 或從 Windows 開始菜單運行 HLS 。
PS:Linux系統(tǒng)下可能并沒有安裝到命令行,所以可能需要使用下面完整命令才能運行HLS:
/opt/york/cs/net/xilinx_vitis-2020.2/Vitis_HLS/2020.2/bin/vitis_hls
選擇創(chuàng)建新項目并為其指定合適的名稱和位置。同樣,請記住 Xilinx 工具不允許路徑或者名稱中有空格或者中文。點擊下一步。
將頂部函數(shù)設(shè)置為 toplevel。點擊下一步。
將時鐘周期設(shè)置為 10(以納秒為單位,因此對應(yīng)于 100MHz 時鐘頻率,這是提供給 FPGA 架構(gòu)的默認時鐘頻率)。
選擇FPGA到xc7z020clg484-2。
單擊完成。
現(xiàn)在我們就能看到一個 HLS 項目,但它是空的。創(chuàng)建兩個名為toplevel.cpp和toplevel.h
toplevel.cpp
#include"toplevel.h" //Inputdatastorage #defineNUMDATA100 uint32inputdata[NUMDATA]; //Prototypes uint32addall(uint32*data); uint32subfromfirst(uint32*data); uint32toplevel(uint32*ram,uint32*arg1,uint32*arg2,uint32*arg3,uint32*arg4){ #pragmaHLSINTERFACEm_axiport=ramoffset=slavebundle=MAXI #pragmaHLSINTERFACEs_axiliteport=arg1bundle=AXILiteS #pragmaHLSINTERFACEs_axiliteport=arg2bundle=AXILiteS #pragmaHLSINTERFACEs_axiliteport=arg3bundle=AXILiteS #pragmaHLSINTERFACEs_axiliteport=arg4bundle=AXILiteS #pragmaHLSINTERFACEs_axiliteport=returnbundle=AXILiteS readloop:for(inti=0;i
toplevel.h
#ifndef__TOPLEVEL_H_ #define__TOPLEVEL_H_ #include#include #include //Typedefs typedefunsignedintuint32; typedefintint32; uint32toplevel(uint32*ram,uint32*arg1,uint32*arg2,uint32*arg3,uint32*arg4); #endif
檢查文件
toplevel.cpp 包含整個 EMBS 結(jié)構(gòu)。toplevel函數(shù)有五個uint32類型的參數(shù)。在typedef.h中描述寬度為32位的無符號整數(shù)。
該文件還包含被HLS稱為指令的雜注。指令用于告訴HLS如何制作硬件。這里的指令告訴HLS創(chuàng)建AXI主接口和AXI從接口。主接口允許組件訪問主存儲器,從接口允許ARM內(nèi)核傳入一些變量,并啟動、復(fù)位和停止組件。一旦構(gòu)建完成并導(dǎo)出硬件IP,IP將在Vivado中顯示如下:
注意三個重要的事情:
沒有 main 函數(shù)。
我們已經(jīng)聲明了一個函數(shù) toplevel。這將是硬件的“入口點”。
代碼中的循環(huán)被賦予了標簽(readloop、addloop和subloop)。軟件中大多數(shù)程序員都不這樣做,但它在HLS中很有用,稍后您我們將講解。
測試組件
硬件綜合需要的時間比較長。因此,在構(gòu)建硬件之前,應(yīng)該充分的驗證我們設(shè)計的硬件是正確的。測試平臺是其中的重要組成部分。測試平臺測試代碼的功能屬性,以確保它不包含任何邏輯錯誤并且它大致符合要求。因為測試平臺是在軟件中模擬的,所以無法測試最終硬件的速度。
在 HLS 中,右鍵單擊左側(cè)資源管理器中的“Test Bench”,然后選擇“New File”。命名testbench.cpp 并將其放在合理的地方(與之前的toplevel.h文件相同的文件夾中,否則將需要編輯 #include 以獲得頭文件的相對路徑)。
復(fù)制以下代碼:
testbench.cpp
#include"toplevel.h" #defineNUMDATA100 uint32mainmemory[NUMDATA]; intmain(){ //Createinputdata for(inti=0;i
注意事項:
我們將一塊內(nèi)存聲明為“主內(nèi)存”。在實際系統(tǒng)中,這是 Z7 板上的 1GB DDR 內(nèi)存,但對于測試平臺,我們只需分配一個足夠大的ARRAY即可滿足我們的目的。
如果一切正常,測試平臺應(yīng)該返回 0。它將從硬件返回的值與預(yù)先計算的值對比,以確保是正確的。
要運行測試平臺,請選擇 Project | 運行 C Simulation 并在出現(xiàn)的對話框中單擊確定。應(yīng)該會看到 HLS 做了很多工作,但最終會看到測試平臺的輸出。
Sumofinput:12950 Values1to99subtractedfromvalue0:3050 INFO:[SIM211-1]CSimdonewith0errors. INFO:[SIM211-3]***************CSIMfinish*************** INFO:[HLS200-111]FinishedCommandcsim_designCPUusertime:0seconds.CPUsystemtime:0seconds.Elapsedtime:4.582seconds;currentallocatedmemory:191.719MB. FinishedCsimulation.
我們已經(jīng)驗證了我們的設(shè)計。(如果需求,應(yīng)該更嚴格地測試一個真實的設(shè)計?。?/p>
高層次綜合
所以現(xiàn)在我們有了一個仿真好的設(shè)計,我們需要研究如何將其轉(zhuǎn)化為硬件。在窗口的右上角,應(yīng)該看到一行三個按鈕-Debug, Synthesis, 和 Analysis(調(diào)試、合成和分析)。
這些是透視圖,我們將通過單擊它們切換。單擊合成按鈕以確保處于合成透視圖中。
打開 toplevel.cpp. 現(xiàn)在單擊Solution | Run C Synthesis | C Synthesis(或單擊工具欄中的綠色箭頭)。將開始進行綜合(綜合是將 C++ 描述轉(zhuǎn)化為硬件的過程)。
綜合完成后,將打開綜合報告窗口。這會告訴有關(guān)剛剛構(gòu)建的設(shè)計的所有信息。在 Performance Estimates下 查看 Latency 摘要。這應(yīng)該類似于:
該報告說明設(shè)計具有 709 個時鐘周期的總體延遲(從第一個數(shù)據(jù)輸入到最后一個數(shù)據(jù)輸出的時間)。它的間隔是 710,這是從一次運行的第一個數(shù)據(jù)到設(shè)計能夠接受另一次運行的第一個數(shù)據(jù)的周期數(shù)。
在toplevel行下方,它顯示了代碼中的三個循環(huán)(說明為什么給它們標簽是有用的)。循環(huán)的延遲是完成所需的周期數(shù)。有時 HLS 不會知道這一點(例如,如果循環(huán)變量不是靜態(tài)的)。迭代延遲是一次迭代所花費的周期數(shù)。啟動間隔僅對流水線循環(huán)有效(見下文),行程計數(shù)是將計算的迭代總數(shù)。
與性能一樣重要的是利用率。進一步查看利用率摘要,將看到設(shè)計在 FF(觸發(fā)器)和 LUT(查找表)中的用法。這些是可重構(gòu)邏輯的度量。還有 DSP(數(shù)字信號處理)單元和 BRAM(Block RAM)。Block RAM 是整個 FPGA 架構(gòu)中非常高速的內(nèi)存小塊??梢栽趩蝹€時鐘周期內(nèi)讀取或?qū)懭胨鼈?,但每個時鐘周期每個 Block RAM 最多可訪問兩次。這些數(shù)字脫離上下文可能有點無意義,因此可以單擊表格上方的 % 符號將這些數(shù)字轉(zhuǎn)換為 FPGA 的百分比。
上表中主要注意的是 BRAM 和 FF/LUT。此表有助于對大部分設(shè)計使用資源的位置進行粗略分類匯總。隨著設(shè)計變得越來越復(fù)雜,可以進一步檢查資源以獲取更詳細的信息。
使用指令調(diào)整綜合
上邊的設(shè)計整體看還可以,但可以通過指令進行進一步優(yōu)化。首先,讓我們仔細看看設(shè)計是如何實現(xiàn)的。單擊“ Analysis”按鈕(右上角)轉(zhuǎn)到“ Analysis”透視圖。這應(yīng)該會打開一個性能選項卡。
這些行是來自已編譯代碼的操作。列是狀態(tài),因此垂直查看會顯示并行發(fā)生的所有進程,如果兩件事在同一列中,它們會并行發(fā)生。目前我們可以看到addloop和subloop循環(huán)不重疊,因此它們不是并行完成的。還要注意,addloop和subloop有兩種狀態(tài)。這對應(yīng)于我們從綜合報告中得到的性能估計,該報告告訴我們這兩個循環(huán)的迭代延遲都是2。
這是因為我作弊了,對不起!
HLS 實際上會做得更好,但我在上面的代碼中包含了一些指令,故意關(guān)閉一些優(yōu)化,以便我們更好地了解它們的作用。我們將在下一節(jié)中撤消它??梢酝耆归_每個循環(huán)以查看在每個狀態(tài)下發(fā)生的各個操作。還可以右鍵單擊操作并選擇 Goto Source 以查看創(chuàng)建它的 C++ 代碼行,或者將創(chuàng)建實際 FPGA 硬件的生成的 Verilog 或 VHDL 行。
流水線循環(huán)
我們要做的第一個優(yōu)化是告訴 HLS 流水線 addloop 和 subloop,它們都有兩種狀態(tài),因此它們可以同時處理兩個數(shù)據(jù)元素。沒有流水線,一次只能運行一個迭代,如這些圖所示。
關(guān)閉性能報告并返回Synthesis透視圖。打開 toplevel.cpp并選擇右側(cè)的 Directive 選項卡(或 Window → Show View → Directive)。此選項卡顯示源文件中可以附加指令的項目。查找addloop,并注意我包含了指令“HLS PIPELINE off”。這告訴它不要PIPELINE循環(huán)。
雙擊該指令并在彈出的對話框中取消選中“關(guān)閉”。接下來,重復(fù)subloop指令。
保存文件并重新運行綜合(單擊綠色箭頭)。完成后查看報告。我們立即看到設(shè)計的延遲現(xiàn)在是 512 個周期,低于之前的710個。這是因為兩個循環(huán)同時處理多個數(shù)據(jù)項。
在綜合報告中,將看到兩個循環(huán)現(xiàn)在已流水線化,它們的 Initiation Interval 現(xiàn)在為 1。這意味著每個時鐘周期都可以將數(shù)據(jù)項推入循環(huán)。它們的行程計數(shù)(它們執(zhí)行的次數(shù))是 100 和 99,因此它們的延遲是 100 和 99 個周期,低于之前的 200 和 198。
單擊Analysis透視圖。流水線還是沒有使循環(huán)并行發(fā)生。速度提升來自于數(shù)據(jù)項被更快地推入循環(huán)的事實。關(guān)閉 Performance 選項卡并返回 Synthesis 透視圖。
展開循環(huán)
比流水線循環(huán)更有效地操作是展開它們。UNROLL 指令告訴 HLS 嘗試并行執(zhí)行循環(huán)的各個迭代。 這是非??斓模鶕?jù)展開的級別可能會花費更多的硬件資源。
在 Synthesis 透視圖中, toplevel.cpp 右鍵單擊 addloop 和 subloop 循環(huán)上的 PIPELINE 指令并刪除它們。有時,上述操作會導(dǎo)致 HLS 弄亂源代碼,如果是,請修復(fù)它。
右鍵單擊 addloop 指令面板中的循環(huán),然后選擇插入指令,選擇 UNROLL。
添加指令時,可以選擇將指令放在源文件中(作為#pragma指令)或單獨的指令文件。我更喜歡使用指令文件,但總的來說沒關(guān)系。
我們可以在此處添加一個因素來限制展開,但讓其留空以表示盡可能展開。單擊subloop并重復(fù)上訴操作 ,再綜合。
現(xiàn)在我們的設(shè)計延遲降低到大約 415 個周期,這意味著我們的整體運行速度幾乎是原始設(shè)計(沒有指令)的兩倍。然而,我們現(xiàn)在使用了大約 7200 個 LUT——我們的大小超過了 3 倍!HLS 還決定使用 4 個 Block RAM 作為內(nèi)存而不是 1 個,以便可以并行訪問更多數(shù)據(jù)。這是一個經(jīng)典的速度/資源權(quán)衡,請注意,如果在綜合報告中展開“Loop”,則現(xiàn)在只有 readloop。另外兩個不見了,因為它們已經(jīng)完全展開。
單擊Analysis透視圖。我們的設(shè)計看起來完全不同!
我們注意到的第一件事是函數(shù)是可見的。這是因為以前函數(shù)非常簡單,HLS已經(jīng)自動內(nèi)聯(lián)了它們?,F(xiàn)在它們是巨大的硬件,所以它沒有。因此,硬件在啟動subfromfirst函數(shù)之前完成addall函數(shù)。讓我們強制它內(nèi)聯(lián)這些函數(shù),這樣它就可以將兩個函數(shù)的操作安排在一起。將INLINE指令添加到addall和subfromfirst函數(shù)中,然后重新綜合。
現(xiàn)在我們減少到 364 個周期,并且我們節(jié)省了一些硬件,因為 HLS 已經(jīng)能夠優(yōu)化這兩個功能。盡管如此,我們?nèi)匀豢梢宰龅酶茫?/p>
為 LUT 交換 Block RAM
讓我們告訴 HLS 不要使用 Block RAM,而只使用普通寄存器。在某些設(shè)計中,Block RAM 非常 昂貴,但它將允許真正的并行訪問。 關(guān)閉 Performance 選項卡并返回 Synthesis 透視圖。右鍵單擊 inputdata 指令選項卡并選擇插入指令。插入 ARRAY_PARTITION 類型的指令 complete。它將詢問將其應(yīng)用于哪個功能。選擇 toplevel。
此外,也適用 UNROLL 于 readloop ,因此我們可以完全利用分布式 RAM。
你應(yīng)該有上面的指令,再綜合。
我們現(xiàn)在只有 333 個周期的微小設(shè)計延遲。因為設(shè)計讀取 100 個數(shù)據(jù)項,我們知道我們的設(shè)計永遠不會快于 101 個周期,所以這非常好!另請注意,現(xiàn)在我們的 Block RAM 數(shù)量減少了,我們的LUT 使用量再次增加。通常ARRAY_PARTITION會顯著增加 LUT 的使用,但在這種情況下,我們之前的設(shè)計有很多中間存儲寄存器,我們基本上已經(jīng)在這樣做了,所以增加并不算太糟糕。請記住,第一次展開使我們的 LUT 使用量增加了 3 倍。這表明了試驗指令的重要性,并使用分析視角來計算并行發(fā)生的事情。
所以我們現(xiàn)在有一個非常快的設(shè)計,但如果我們需要通過流水線而不是展開來使其更小(和更慢),我們也知道如何使其更小(和更慢)。
真正實現(xiàn)它
我們現(xiàn)在將使用測試平臺純粹在 HLS 內(nèi)部工作。以后的實踐將采用 HLS 設(shè)計并將它們連接到 ARM 處理系統(tǒng)。
總結(jié)
這是《FPGA高層次綜合HLS》系列教程第三篇,后面會按照專題繼續(xù)更新,文章有什么問題,歡迎大家批評指正~感謝大家支持。
審核編輯:彭靜
-
硬件
+關(guān)注
關(guān)注
11文章
3211瀏覽量
66064 -
電路模型
+關(guān)注
關(guān)注
1文章
43瀏覽量
9546 -
Vivado
+關(guān)注
關(guān)注
19文章
804瀏覽量
66224
原文標題:使用Vitis HLS創(chuàng)建屬于自己的IP
文章出處:【微信號:Open_FPGA,微信公眾號:OpenFPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論