HLS開發學習-01-HLS介紹與FPGA簡單內部介紹

介紹

博主之前在全國FPGA競賽中使用過HLS的部分內容,想着今年考研也不能毫無產出吧,所以抽一點時間來碎片化學習下HLS的相關知識。

簡單來說吧,因爲傳統的開發FPGA流程相比緩慢,不如軟件開發的效率高,所以說HLS運營而生,
使用高級語言來進行轉換爲底層的硬件代碼。雖然說是支持高級語言比如說C與C++ 但是最主要的
核心來說, HLS的開發也是要求開發這對代碼的背後的硬件與FPGA內部結構相對了解,所以說本文
首先大致介紹了下HLS的相關內容,還有簡單介紹了下FPGA簡單的內部結構,(後面會更新專門的
內部結構的分析的文章這裏就不過多展開,主要針對的是HLS用到的部分做簡單說明)
		
本文內容來源網貼彙集+ 個人理解 + HLS書籍(《FPGA並行編程》)+視頻 + 自費視頻

在傳統的FPGA設計流程中,一般是自頂向下的模塊化設計,這些模塊包括用戶自己編寫的RTL或者是供應商提供的IP核。而在Xilinx新推出的高生產力設計流程中是以IP爲核心的,把所有的模塊都看做是IP,封裝爲IP,最主要的是IP的設計是基於C語言的,最後通過HLS將C語言代碼轉化爲RTL,這能極大的加快設計進程。從這段時間的學習來看,HLS綜合出來的電路比我自己寫的RTL更省資源,在時序方面可能會差一些,,但它也滿足了時序的要求。HLS還提供了非常多的經過深度優化的庫,所以我個人覺得HLS綜合的電路已經比很多剛接觸FPGA不久的工程師要好了,傳統的RTL設計可能不會消失,但很可能會越來越少了。

HLS在不添加任何優化指令的情況下默認綜合電路爲最節省資源的情況,即能使用分時複用就使用分時複用,能在一個週期中完成多個操作(滿足時鐘頻率約束的要求)則絕不流水。從這裏得到的啓發是,設計不要過度的流水,只要能滿足時鐘頻率的需求,我們在一個時鐘週期內可以進行多個操作,於是在低頻率的系統中,我們可以節省很多的寄存器資源。這樣做還有一個好處是我們可以使用assign來完成邏輯設計,然後再對輸出進行寄存,可以極大的減少代碼量,增加代碼的可讀性,HLS綜合後的RTL代碼的風格就是這樣的,當然因爲命名的原因該代碼幾乎沒有可讀性,我們可以不用刻意去讀懂它。

在學習Vivado HLS工具之前,我們需要了解HLS工具在FPGA開發流程中的作用。Vivado工具的設計理念是以IP爲核心的,所有的功能模塊都可以看做並且封裝成一個IP,最後由Vivado集成,以實現最大化的設計複用。所以Vivado HLS可以看做是一個IP封裝工具,它封裝的是由C、C++、SystemC或者OpenCL等高級語言實現的功能函數。

HLS 完成的工作

  • HLS自動分析一個算法中的潛在併發性
  • HLS自動在需要的路徑插入寄存器,並自動選擇最理想的時鐘
  • HLS自動產生控制數據在一個路徑上的出入方向的邏輯
  • HLS自動完成設計部分與系統中的其他接口
  • HLS自動映射數據到存儲單元中以平衡資源使用與帶寬
  • HLS自動將程序中計算的部分映射到對應邏輯單元,在實現等效運算的前提下選擇有效運算

大多數HLS工具需要用戶提供功能的規範,交互的描述,一個對接的計算設備,和目標優化方向。而對於Vivado HLS來說,用戶需要:

  • 一個用C/C++/System C編寫的函數
  • 一個測試平臺用於驗證結果(C testbench)
  • 一個FPGA開發版
  • 期望的時鐘週期
  • 一個簡單的實施指導

輸入程序作出以下規範

根據Vivado HLS的使用指南,我們將對我們的輸入程序作出以下規範:

  • 不使用動態內存分配(不使用malloc(),free(),new和delete())
  • 減少使用指針對指針的操作
  • 不使用系統調用(例如abort(),exit(),printf()),我們可以在其他代碼例如測試平臺上使用這些指令,但是綜合的時候這些指令會被無視(或直接刪掉)
  • 減少使用其他標準庫裏的內容(支持math.h裏常用的內容,但還是有一些不兼容)
  • 減少使用C++中的函數指針和虛擬函數
  • 不使用遞歸方程
  • 精準的表達我們的交互接口

當RTL級的設計可用時,大多數HLS工具會進行標準RTL設計流。而在賽靈思Xilinx Vivado設計套裝裏進行的是邏輯綜合,將RTL級設計轉換成一個FPGA邏輯部件的連線表,這份連線表不僅包含需要的邏輯部件還包含他們的連接方式。Vivado之後將連線表和目標設備中的可用資源相關聯,這個過程被稱作佈局及布(PAR)。產出的FPGA配置被附在比特流(bitstream)上,用戶可以將比特流上傳到FPGA以實現想要的功能。比特流實質上是用二進制表示FPGA上每一個可用資源的配置,包括邏輯部件的使用,連線的方式,和片上的內存。大型FPGA例如賽靈思UltraScale FPGA擁有超過十億個可配置比特,較小的FPGA上也至少有幾億個可配置比特。

FPGA構造

FPGA是由可編程邏輯矩陣和相連接的內存組成,通常是基於LUT(查找表,就和超市的購物小票相似,每個值都可以被索引到)。下圖大致給出了FPGA的內部構造,在可編程的邏輯塊中,現在的FPGA也已經高度集成了很多的資源。
在這裏插入圖片描述

邏輯單元

這部分主要是是實現邏輯運算的部分,基於查找表也就是LUT,實際的FPGA中大部分使用的是4–6位輸入的查找表作爲運算基礎。在邏輯單元裏,觸發器FF是最近的內存單元,我們可以利用觸發器的性質,儲存一些少量的數據。
在這裏插入圖片描述

存儲單元

前面提到了使用(FF)觸發器可以存儲少量的信息,圖中也給出了(LUT in slicem)FPGA另外一部分的存儲單元是Bram,他是支持多種內存接口,可以配置的隨機存儲器。總體來說有兩個功能:

  1. 芯片各部分的數據轉移
  2. 存儲大的數據集

同時FPGA也可以通過驅動外部的存儲器來實現上述的功能,這部分爲外部存儲,簡稱(外存)相比較來說,觸發器有最好的帶寬但是存儲容量太小;而外存的存儲密度高,但是帶寬有限,而Bram相當於兩者的中間值。

根據設計的需要,我們可以配置存儲單元爲各種形式,種類列舉如圖:
在這裏插入圖片描述

算術邏輯單元

因爲對於算法的實現來說,說白了也就是進行計算的實現,算法優化的過程就像我們小學初中學習的化簡,簡便計算等等,所以我們也要對運算邏輯單元有個簡單的瞭解。
這裏我就不過多展開,大致瞭解到DSP48中包括的部分即可。

  • 加法
  • 乘法
  • ALU運算單元
  • 選擇器

在這裏插入圖片描述

設計優化

對於HLS來說,優化基本算是他要解決的首要問題,那麼我們應該從哪些角度來思考優化呢?在開始討論怎麼去優化之前,我們先要討論一下判斷一個設計特點的標準。計算時間就是一個衡量設計好壞的重要標準。很多人把時鐘週期數作爲一個同步電路性能的指標,但實際上對於兩個使用不同時鐘
的電路這是不得當的,而時針不同又是HLS下的絕大多數情況。比如說,我們現在已經規定好了VivadoHLS的輸入時鐘限制,那麼工具根據時鐘的不同會從同一段代碼中產生不同的結構,所以這不是一個很恰當的比較方式。秒數是一個更好的對應比較指標。Vivado HLS工具會提供一個週期數和週期頻率的報告,用戶可以用此得出某段代碼的操作時間。

改變時鐘頻率有時候可以優化設計

Vivado HLS工具把時鐘頻率作爲一個輸入,所以改變一個輸入可以導致產出的結構完全不同。

我們用任務(task)這個術語來表示一個行爲的基本單位,用戶可以在Vivado HLS中發現與之對應的是調用函數。任務延遲就是任務開始到任務完成中間的這段時間。任務間隔則是任務開始到下一個任務開始之間的這段時間。所有的任務輸入,輸出和計算的時間都被算在任務延遲裏,但是任務的開始並不同於讀取輸入,同樣任務的結束也不等同於寫出輸出。在很多設計中,數據率是一個很重要的東西,它同時取決於任務間隔和函數參數的多少。
在這裏插入圖片描述
兩種不同設計的任務間隔和任務延遲,上方的弧線指示的是任務間隔,下方的弧線指示的是任務延
遲。左右兩個設計的區別在於,左邊是流水線(pipeline),右邊使用了更順序化的設計。

圖表示的是兩種設計的實施設想,橫向軸是時間軸(從左到右增大),縱向是設計中不同的函數單
位。紅色表示的是輸入有關的操作,橙色表示的是輸出有關的操作,正在活躍的運算符用深藍表示,不活躍的則用淺藍表示。每一個進入的箭頭表示的是一個任務的開始,而出去的箭頭表示任務的完成。左側的圖表示的是一個每個週期都執行新任務的結構設計。

與之對應的是完全流水(fully-pipelined)結構。右側表示的則是一個完全不一樣的結構,系統每次讀取四段輸入,處理數據,然後再合成一個4段數據的輸出。這種結構的任務延遲和任務間隔是一樣的(13個週期),並且每一週期內只有一個任務在執行。這個結構和左邊的流水形成了鮮明對比,左邊的結構在同一週期內顯然有多個任務在執行。

HLS中的流水和處理器中的流水概念相似,但是不再使用處理器中操作分5個階段並把結果寫入寄存器堆的方法,Vivado HLS工具構造的是一個只適用於特定板子,可以完成特定程序的電路,所以它能更好的調整流水的階段數量,初始間隔(連續兩組數據提供給流水之間的間隔),函數單位的數量和種類,還有所有部件之間的互聯。

Vivado HLS工具通過計算一個任務輸出到輸入之間這個過程需要的寄存器數來決定週期。因此,0週期的任務延遲是可以實現的,也就是組合邏輯下路徑上沒有任何寄存器。另一個常用的工作是計算輸入輸出並把結果存到寄存器裏,通過這些數據找到路徑上的寄存器數。這樣的計算有花費很多的週期

優化的取捨

理解編譯器如何工作其實有助於設計師寫出更高效的代碼,這點對於HLS尤其重要,因爲綜合電路的構建方式有很多種,只理解它軟件流是不夠的。比如HLS設計師需要考慮流水,內存排布,I/O接口這些軟件設計師不需要考慮的內容。

回到編譯器,理解它的關鍵問題在於:這段代碼中產生的是什麼電路?這個問題的答案分多鐘,還和你所用的HLS工具有關。

用戶可以通過在代碼中添加#pagma HLS pipeline來指導Vivado HLS工具產生函數流水結構。

複雜的設計在順序化和並行化的結構之間會有很多取捨的考慮。這些取捨在Vivado HLS中很大程度上取決於設計者的決定和代碼內容。

處理速率的限制

我們看到了很多改變架構會改變任務間隔的例子,這樣做通常來講可以提升處理速率。但是讀者需要意識到任何結構的任務間隔都是有一定的限度的。最關鍵的限制來自於遞歸和反饋循環,還有一些其他的例如資源限制也很重要。遞歸(recurrence),這裏是指某個部件的計算需要這個部件之前一輪計算的結果, 遞歸是限制產力的重要因素,即使在流水結構中也是如此[56,43]。分析算法中的遞歸併產出正確的硬件設計是非常關鍵的一步,同樣,選擇一個儘量避免很多遞歸的算法也是設計中非常關鍵的一步。遞歸在很多代碼結構中都會出現,它存在於很多順序化結構中,也有很多會隨着改編成流水結構而消失。對於順序化結構遞歸有時候不影響處理速率,但是在流水結構中是一個很不理想的狀況。

另一個影響速率的關鍵因素就是資源限制,其中一種形式是設計邊緣的跳線,因爲一個同步電路中的每根跳線在每週期只能傳送抓取1個比特的數據。因此,如果 int32_t f(int32_t x)這樣形式的函數作爲一個單獨模塊在100MHz的頻率和1的任務間隔下運行,它最大的數據處理量就是3.2G比特。另一種資源限制來自於內存,因爲大多數內存每週期只支持一定次數的訪問。還有一種資源限制來自於用戶所給的限制,如果用戶規定了在綜合中可用的操作數,這其實是給處理率添加了限制條件。

代碼要求

  1. 強調重建代碼對於高質量設計的重要性,比如在高性能和低使用面積上。
  2. 對常見的內容提供重建的代碼
  3. 討論重建對於硬件的影響
  4. 使用必要的HLS指令以實現最好的設計
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章