1. 程式人生 > >如何用FPGA實現演算法的硬體加速

如何用FPGA實現演算法的硬體加速

當設計者試圖從演算法中獲得最佳效能但軟體方法已無計可施時,可以嘗試通過硬體/軟體重新劃分來進行加速。FPGA易於實現軟體模組和硬體模組的相互交換,且不必改變處理器或進行板級變動。本文闡述如何用FPGA來實現演算法的硬體加速

如果想從程式碼中獲得最佳效能,方法包括優化演算法、使用查詢表而不是演算法、將一切都轉換為本地字長尺寸、使用註冊變數、解開迴圈甚至可能採用彙編程式碼。如果所有這些都不奏效,可以轉向更快的處理器、採用一個不同的處理器架構,或將程式碼一分為二通過兩個處理器並行處理。不過,如果有一種方法可將那些對時間有嚴格要求的程式碼段轉換為能夠以5-100倍速度執行的函式呼叫,而且如果這一方法是一種可供軟體開發之用的標準工具,這可信嗎?現在,利用

可程式設計邏輯作為硬體加速的基礎可使這一切都變成現實。

圖1:帶定製指令的可配置處理器架構。

低成本可程式設計邏輯在嵌入式系統中應用得越來越普遍,這為系統設計者提供了一個無需對處理器或架構進行大的改動即可獲得更高效能的可選方案。可程式設計邏輯可將計算密集型功能轉換為硬體加速功能。從軟體的角度看,這只是簡單地將一個函式呼叫做進一個定製的硬體模組中,但執行速度要比通過組合語言優化的相同程式碼或將演算法轉換為查詢表要快得多。

硬體加速

首先探討一下什麼是硬體加速,以及將演算法作為定製指令來實現與採用硬體外圍電路的區別。硬體加速是指利用硬體模組來替代軟體演算法以充分利用硬體所固有的快速特性。從軟體的角度看,與硬體加速模組介面就跟呼叫一個函式一樣。唯一的區別在於此函式駐留在硬體中,對呼叫函式是透明的。

取決於演算法的不同,執行時間最高可加快100倍。硬體在執行各種操作時要快得多,如執行復雜的數學功能、將資料從一個地方轉移到另一個地方,以及多次執行同樣的操縱。本文後面將討論一些通常用軟體完成的操作,經過硬體加速後這些操作可獲得極大的效能提高。

如果在系統設計中採用FPGA,那麼在設計週期的任何時候都可以新增定製的硬體。設計者可以立刻編寫軟體程式碼,並可在最終定稿之前在硬體部分上執行。此外,還可以採取增量法來決定哪部分程式碼用硬體而不是軟體來實現。FPGA供應商所提供的開發工具可實現硬體和軟體之間的無縫切換。這些工具可以為匯流排邏輯和中斷邏輯生成HDL程式碼,並可根據系統配置定製軟體庫及include檔案。

帶一些CISCRISC

精簡指令集計算(RISC)架構的目標之一即是保持指令簡單化,以便讓指令執行得足夠快。這與複雜指令集計算(CISC)架構正好相反,後者一般不會同樣快地執行指令,但每個指令可完成更多處理任務。這兩種架構應用得都很普遍,而且各有所長。

如果能根據特定的應用將RISC的簡單和快速特性與CISC強大的處理能力結合起來,豈不兩全其美?其實這正是硬體加速所要做的。加入為某種應用而定製的硬體加速模組可以提高處理能力,並減少程式碼複雜性和密度,因為硬體模組取代了軟體模組。可以這麼說,是用硬體來換取速度和簡單性。

定製指令和硬體外圍電路方式

有兩種硬體加速模組實現方式。其一是定製指令,它幾乎可在每一個可配置處理器中實現,這是採用可配置處理器的主要優點。如圖1所示,定製指令是作為算術邏輯單元(ALU)的擴充套件而新增的。處理器只知道定製指令就像其它指令一樣,包括擁有自己的操作程式碼。至於C程式碼,巨集可自動生成,從而使得使用該定製指令跟呼叫函式一樣。

如果定製指令需要幾個時鐘週期才能完成,而且要連續呼叫它,則可以流水線式定製指令來實現。這樣可在每個時鐘週期產生一個結果,不過開始時有些延遲。

硬體加速模組的另一種實現方式是硬體外圍電路。在這一方式下,資料不是傳遞給軟體函式,而是寫入儲存器對映的硬體外圍電路中。計算是在CPU之外完成的,因此在外圍電路工作的同時CPU可以繼續執行程式碼。其實代替軟體演算法的只是一個普通的硬體外圍電路。與定製指令的另一個不同之處是硬體外圍電路可以訪問系統中的其它外圍電路或儲存器,而無須CPU介入。

根據硬體需要做什麼、怎麼工作以及需要多長時間可以決定採用是定製指令還是硬體外圍電路更合適。對於那些在幾個週期內就可完成的操作,定製指令一般更好些,因為它產生的開銷要更少。對於外圍電路,一般需要執行幾個指令來寫入控制暫存器、狀態暫存器和資料暫存器,而且需要一個指令來讀取結果。如果計算需要幾個週期,實施外圍電路比較好,因為它不會影響CPU流水線。或者,也可以實施前面所述的流水線式定製指令。

另一個區別是定製指令需要有限數目的運算元,並返回一個結果。根據處理器指令集架構的不同,運算元也各異。對某些操縱,這樣可能顯得很麻煩。此外,如果需要硬體從儲存器或儲存器中的其它外圍電路讀出和寫入,則必須採用硬體外圍電路,因為定製指令無法訪問匯流排。

圖2:16位CRC演算法的硬體實現。(Optional)

選擇程式碼

當需要優化C語言程式碼以滿足某些速度要求時,可能要執行一個程式碼仿製工具,或親自檢查該程式碼以便了解程式碼的哪個部分導致系統停滯。當然,這需要熟悉程式碼以便知道瓶頸在哪兒。

即便找出瓶頸所在,如何優化也是個挑戰。有些方案採用本地字大小的變數、帶預先計算值的查詢表,以及通用軟體演算法優化。這些技巧可產生快幾倍的執行速度效果。另一種優化C演算法的方法是用匯編語言編寫。過去這種方法可獲得很好的提高,但現今的編譯器在優化C演算法上已做得很好,因此這種效能的提高是有限的。如果需要顯著的效能提高,傳統的軟體演算法優化技巧恐怕是不夠的。

然而,利用硬體實施的演算法比軟體實施要強100倍,這不足為奇。那麼,如何確定將哪些程式碼轉為硬體實施呢?大可不必將整個軟體模組轉換為硬體,而應選擇那些在硬體中執行得特別快的操作,比如將資料從一處複製到另一處、大量的數學運算以及任何執行多次的迴圈。如果一個任務由幾個數學運算組成,還可以考慮在硬體中加速整個任務。有些時候,僅加速任務中的一個操作就可滿足效能要求。

例項:CRC演算法的硬體加速

由於大量且重複的計算,迴圈冗餘校驗(CRC)演算法或任何“校驗和”演算法都是硬體加速的不錯選擇。下面通過一個CRC演算法的優化過程來探討如何實現硬體加速。

首先,利用傳統的軟體技巧來優化演算法,然後將其轉向定製指令以加速演算法。我們將討論不同實現方法的效能比較和折衷。

CRC演算法可用來校驗資料在傳輸過程中是否被破壞。這些演算法很流行,因為它們具有很高的檢錯率,而且不會對資料吞吐量造成太大影響,因為CRC校驗位被新增進資料資訊中。但是,CRC演算法比一些簡單的校驗和演算法有更大的計算量要求。儘管如此,檢錯率的提高使得這種演算法值得去實施。

一般說來,傳送端對要被髮送的訊息執行CRC演算法,並將CRC結果新增進該訊息中。訊息的接收端對包括CRC結果在內的訊息執行同樣的CRC操作。如果接收端的結果與傳送端的不同,這說明資料被破壞了。

CRC演算法是一種密集的數學運算,涉及到二元模數除法(modulo-2 division),即資料訊息被16或32位多項式(取決於所用CRC標準)除所得的餘數。這種操作一般通過異或和移位的迭代過程來實現,當採用16位多項式時,這相當於每資料位元組要執行數百條指令。如果傳送數百個位元組,計算量就會高達數萬條指令。因此,任何優化都會大幅提高吞吐量。

程式碼列表1中的CRC函式有兩個自變數(訊息指標和訊息中的位元組數),它可返回所計算的CRC值(餘數)。儘管該函式的自變數是一些位元組,但計算要逐位來執行。該演算法並不高效,因為所有操作(與、移位、異或和迴圈控制)都必須逐位地執行。

列表1:逐位執行的CRC演算法C程式碼。
/*
* The width of the CRC calculation and result.
* Modify the typedef for a 16 or 32-bit CRC standard.
*/
typedef unsigned char crc;
#define WIDTH (8 * sizeof(crc))
#define TOPBIT (1 << (WIDTH - 1))
crc crcSlow(unsigned char const message[], int nBytes)
{
crc remainder = 0;
/*
* Perform modulo-2 division, a byte at a time.
*/
for (int byte = 0; byte < nBytes; ++byte)
{
/*
* Bring the next byte into the remainder.
*/
remainder ^= (message[byte] << (WIDTH - 8));
/*
* Perform modulo-2 division, a bit at a time.
*/
for (unsigned char bit = 8; bit > 0; "bit)
{
/*
* Try to divide the current data bit.
*/
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
}
/*
* The final remainder is the CRC result.
*/
return (remainder);
}

1.傳統的軟體優化

圖3:帶CRC外圍電路和DMA的系統模組示意圖。

讓我們看一下如何利用傳統的軟體技巧來優化CRC演算法。因為CRC操作中的一個運算元,即多項式(除數)是常數,位元組寬CRC操作的所有可能結果都可以預先計算並存儲在一個查詢表中。這樣,通過一個讀查詢表動作就可讓操作按逐個位元組執行下去。

採用這一演算法時,需要將這些預先計算好的值儲存在儲存器中。選擇ROM或RAM都可以,只要在啟動CRC計算之前將儲存器初始化就行。查詢表有256個位元組,表中每個位元組位置包含一個CRC結果,共有256種可能的8位訊息(與多項式大小無關)。

列表2示出了採用查詢表方法的C程式碼,包括生成查詢表crcInit()中數值的程式碼。

列表2:採用查詢表方法的CRC演算法C程式碼。

crc crcTable[256];
void crcInit(void)
{
crc remainder;
/*
* Compute the remainder of each possible dividend.
*/
for (int dividend = 0; dividend < 256; ++dividend)
{
/*
* Start with the dividend followed by zeros.
*/
remainder = dividend << (WIDTH - 8);
/*
* Perform modulo-2 division, a bit at a time.
*/
for (unsigned char bit = 8; bit > 0; "bit)
{
/*
* Try to divide the current data bit.
*/
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^ POLYNOMIAL;
}
else
{
remainder = (remainder << 1);
}
}
/*
* Store the result into the table.
*/
crcTable[dividend] = remainder;
}
} /* crcInit() */
crc crcFast(unsigned char const message[], int nBytes)
{
unsigned char data;
crc remainder = 0;
/*
* Divide the message by the polynomial, a byte at a time.
*/
for (int byte = 0; byte < nBytes; ++byte)
{
data = message[byte] ^ (remainder >> (WIDTH - 8));
remainder = crcTable[data] ^ (remainder << 8);
}
/*
* The final remainder is the CRC.
*/
return (remainder);
} /* crcFast() */

整個計算減少為一個迴圈,每位元組(不是每位)有兩個異或、兩個移位操作和兩個裝載指令。基本上,這裡是用查詢表的儲存空間來換取速度。該方法比逐位計算的方法要快9.9倍,這一提高對某些應用已經足夠。如果需要更高的效能,可以嘗試編寫彙編程式碼或增加查詢表容量以擠出更多效能來。但是,如果需要20、50甚至500倍的效能提高,就要考慮採用硬體加速來實現該演算法了。

表1:各種規模的資料模組下CRC演算法測試比較結果。

2.採用定製指令方法

CRC演算法由連續的異或和移位操作構成,用很少的邏輯即可在硬體中簡單實現。由於這一硬體模組僅需幾個週期來計算CRC,採用定製指令來實現CRC計算要比採用外圍電路更好。此外,無須涉及系統中任何其它外圍電路或儲存器。僅需要一個微處理器來支援定製指令即可,一般是指可配置微處理器。

當在硬體中實現時,演算法應該每次執行16或32位計算,這取決於所採用的CRC標準。如果採用CRC-CCITT標準(16位多項式),最好每次執行16位計算。如果使用8位微處理器,效率可能不太高,因為裝載運算元值及返回CRC值需要額外的週期。圖2示出了用硬體實現16位CRC演算法的核心。

訊號msg(15..0)每次被移入異或/移位硬體一位。列表3示出了在64KB資料模組上計算CRC的一些C程式碼例子。該例項是針對Nios嵌入式處理器。

列表3:採用定製指令的CRC計算C程式碼。

unsigned short crcCompute(unsigned short *data_block, unsigned int nWords)
{
unsigned short* pointer;
unsigned short word;
/*
* initialize crc reg to 0xFFFF
*/
word = nm_crc (0xFFFF, 1); /* nm_crc() is the CRC custom instruction */
/*
* calculate CRC on block of data
* nm_crc() is the CRC custom instruction
*
*/
for (pointer = data_block; pointer < (data_block + nWords); pointer ++)
word = nm_crc(*pointer, 0) return (word);
}
int main(void)
{
#define data_block_begin (na_onchip_memory)
#define data_block_end (na_onchip_memory + 0xffff)
unsigned short crc_result;
unsigned int data_block_length = (unsigned short *)data_block_end - (unsigned short
*)data_block_begin + 1;
crc_result = crcCompute((unsigned short *)data_block_begin, data_block_length);
}

採用定製指令時,用於計算CRC值的程式碼是一個函式呼叫,或巨集。當針對Nios處理器實現定製指令時,系統構建工具會生成一個巨集。在本例中為nm_crc(),可用它來呼叫定製指令。

在啟動CRC計算之前,定製指令內的CRC暫存器需要先初始化。裝載初始值是CRC標準的一部分,而且每種CRC標準都不一樣。接著,迴圈將為資料模組中的每16位資料呼叫一次CRC定製指令。這種定製指令實現方式要比逐位實現的方法快27倍。

3.CRC外圍電路方法

如果將CRC演算法作為硬體外圍電路來實現,並利用DMA將資料從儲存器轉移到外圍電路,這樣還可以進一步提高速度。這種方法將省去處理器為每次計算而裝載資料所需要的額外週期。DMA可在此外圍電路完成前一次CRC計算的時鐘週期內提供新的資料。圖3示出了利用DMA、CRC外圍電路來實現加速的系統模組示意圖。

在64KB資料模組上,利用帶DMA的定製外圍電路可獲得比逐位計算的純軟體演算法快500倍的效能。要知道,隨著資料模組規模的增加,使用DMA所獲得的效能也隨之提高。這是因為設定DMA僅需很少的開銷,設定之後DMA執行得特別快,因為每個週期它都可以傳遞資料。因此,若只有少數字節的資料,用DMA並不划算。

這裡所討論的所有采用CRC-CCITT標準(16位多項式)的演算法都是在Altera Stratix FPGA的Nios處理器上實現的。表1示出了各種資料長度的測試比較結果,以及大致的硬體使用情況(FPGA中的儲存器或邏輯單元)。

可以看出,演算法所用的硬體越多,演算法速度越快。這是用硬體資源來換取速度。

FPGA的優點

當採用基於FPGA的嵌入式系統時,在設計週期之初不必為每個模組做出用硬體還是軟體的選擇。如果在設計中間階段需要一些額外的效能,則可以利用FPGA中現有的硬體資源來加速軟體程式碼中的瓶頸部分。由於FPGA中的邏輯單元是可程式設計的,可針對特定的應用而定製硬體。因此,僅使用所需要的硬體即可,而不必做出任何板級變動(前提是FPGA中的邏輯單元足夠用)。設計者不必轉換到另一個新的處理器或者編寫彙編程式碼,就可做到這一點。

使用帶可配置處理器的FPGA可獲得設計靈活性。設計者可以選擇如何實現軟體程式碼中的每個模組,如用定製指令,或硬體外圍電路。此外,還可以通過新增定製的硬體而獲取比現成微處理器更好的效能。

另一點要知道的是,FPGA有充裕的資源,可配置處理器系統可以充分利用這一資源。

演算法可以用軟體,也可用硬體實現。出於簡便和成本考慮,一般利用軟體來實現大部分操作,除非需要更高的速度以滿足效能指標。軟體可以優化,但有時是不夠的。如果需要更高的速度,利用硬體來加速演算法是一個不錯的選擇。

FPGA使軟體模組和硬體模組的相互交換更加簡便,不必改變處理器或進行板級變動。設計者可以在速度、硬體邏輯、儲存器、程式碼大小和成本之間做出折衷。利用FPGA可以設計定製的嵌入式系統,以增加新的功能特性及優化效能。

相關推薦

如何用FPGA實現演算法硬體加速

當設計者試圖從演算法中獲得最佳效能但軟體方法已無計可施時,可以嘗試通過硬體/軟體重新劃分來進行加速。FPGA易於實現軟體模組和硬體模組的相互交換,且不必改變處理器或進行板級變動。本文闡述如何用FPGA來實現演算法的硬體加速。 如果想從程式碼中獲得最佳效能,方法包括優化演算

Python實現演算法導論中的演算法

目前正好在學習Python和《演算法導論(原書第三版)》,於是想著想用Python把書中所有演算法實現一遍。更新時間不確定,完成時間我也沒有定下來,大概有空就會寫寫,希望儘早可以完成吧~ 本篇部落格將

T型加速演算法fpga實現思想研究

用加法器實現T型曲線的理論分析   //  2017年12月28日     建立 by cofin   T型加速曲線公式:如下所示:   Vt = v0+at     

PipeCNN論文解析:OpenCL實現FPGA上的大型卷積網路加速

本文為PipeCNN的論文深度解析,轉載註明出處。 參考文獻: 論文地址: https://arxiv.org/abs/1611.02450 程式碼地址: https://github.com/doonny/PipeCNN 目錄 一、摘要:

python實現K均值演算法

import numpy as np x = np.random.randint(1,60,[30,1]) y = np.zeros(20) k = 3 #1選取資料空間中的K個物件作為初始中心,每個物件代表一個聚類中心; def initcen(x,k): return x[:k]

java實現七種排序演算法

 很多時候,聽別人在討論快速排序,選擇排序,氣泡排序等,都覺得很牛逼,心想,臥槽,排序也分那麼多種,就覺得別人很牛逼呀,其實不然,當我們自己去了解學習後發現,並沒有想象中那麼難,今天就一起總結一下各種排序的實現原理並加以實現。                         -WH 一、文章

一列數的規則如下: 1、1、2、3、5、8、13、21、34...... 求第30位數是多少, 遞迴演算法實現。//斐波那契數列

1 public class MainClass 2 { 3 public static void Main() 4 { 5 Console.WriteLine(Foo(30)); 6 } 7 public static int Foo(int i) 8 {

https是如何加密的 (知道了原理之後,希望自己能程式碼實現一下,還有用於對個人資訊和公鑰進行加密的雜湊演算法,有時間也去查一下)

由於http協議是明文傳輸資料,資料的安全性沒有保障。為了改進這種明文傳輸協議,https誕生了。   https是在應用層和傳輸層之間,增加了一層ssl加密。對於加密,請往下看:   1、對稱加密   每次在傳送資料之前,伺服器先生成一把金鑰,

基於硬體的C(C++)語言程式設計教程12:函式實現2數之和

本系列文章希望探討以硬體為平臺講述C(C++)知識的一個新的途徑,改變目前大多數C語言教程僅注重C語言本身的語法規則,而脫離其應用環境的現狀。希望讀者通過本教程的學習,能夠立刻學以致用,真正將所學知識應用到專案實踐中。 開發環境:Atmel Studio 7.0 硬體平臺:Microch

【神經網路入門】JAVA實現感知器演算法

簡述 隨著網際網路的高速發展,A(AI)B(BigData)C(Cloud)已經成為當下的核心發展方向,假如三者深度結合的話,AI是其中最核心的部分。所以如果說在未來社會,每個人都必須要學會程式設計的話,那麼對於程式設計師來說,人工智慧則是他們所必須掌握的技術(科技發展真tm快)。 這篇文章介紹

android中如何使用GPU實現硬體加速,3D渲染

已開通新的部落格,後續文字都會發到新部落格 http://www.0xfree.top --- 首先來看一些名詞解釋     GPU:Graphic  Processing Unit (圖形處理器)&nb

java實現各種排序演算法

package Com.Sort; /** * 各種高階排序方法的實現 * * @author Jane */ public class AdvanceSort { // 列印陣列 public static <T extends Comparable<?

指派問題——匈牙利Hungary演算法python實現

注:昨天剛剛看了關於python的關於陣列的簡單操作,就將匈牙利演算法用python實現了以下。其中可能有很多點可以用python中陣列本身屬性實現,但由於初學,所以不熟悉而導致步驟繁瑣的望指出~ 1.匈牙利演算法的簡單例子 (1)矩陣所表示的就是從A點到B

一個Java實現密碼演算法,使用socket引發的血案

public static void main(String[] args) throws IOException, ParseException { ServerSocket serverSocket = new ServerSocket(1

手把手教你Delphi實現硬體版hello world程式設計控制點亮電燈泡

之前我們已經給廣大愛好者或程式設計師朋友們,帶來了硬體版的或者說物聯網版本的Hello World C++Builder版的程式原始碼和教學資料,讓大家對硬體控制帶來一個嶄新的認識。今天我們再出一套兄弟版本Delphi程式語言的教程與例項原始碼。 Delphi的開發與C++B

3. 排序通常有多種演算法,如氣泡排序、插入排序、選擇排序、希爾排序、歸併排序、快速排序,請選擇任意2種java實現 [分值:20] 您的回答:(空) (簡答題需要人工評分)

3. 排序通常有多種演算法,如氣泡排序、插入排序、選擇排序、希爾排序、歸併排序、快速排序,請選擇任意2種用java實現  [分值:20] 您的回答:(空)  (簡答題需要人工評分) package com.interview; /** * 各種排序演算法 */

OpenCV實現Photoshop演算法(十): 美白磨皮(未完)

系列文章: 用OpenCV實現Photoshop演算法(四): 色階調整 十、人像美白磨皮 人像美白磨皮是一個複雜的綜合的高技術活。 基本過程是這樣的:  選取面板區域-〉磨皮-〉美白-〉銳化 相關工作涉及多種演算法: 目前我尚在研究中,有的演算法寫

前端演算法js實現楊輝三角(帕斯卡三角形)程式設計

楊輝三角,是二項式係數在三角形中的一種幾何排列,在中國南宋數學家楊輝1261年所著的《詳解九章演算法》一書中出現。 在歐洲,帕斯卡(1623-1662)在1654年發現這一規律,所以這個表又叫做帕斯卡三角形。 帕斯卡的發現比楊輝要遲393年,比賈憲遲600年。

隨機數實現插入排序演算法,並計算程式執行時間

插入排序 插入排序演算法是一種就地演算法(空間用量是一個常數) 我們希望排序的數也稱為關鍵詞(key),也就是說對一系列 key 進行排序。 輸入是以一個陣列表示的。 相對於歸併排序來說,該演算法對小規模資料的效率比較高。 插入排序演算法思想: 每

遞迴演算法實現逆序字串

題目:編寫一個函式reverse_string(char * string)(遞迴實現) 實現:將引數字串中的字元反向排列。 要求:不能使用C函式庫中的字串操作函式。 解題思路: 逆序