流水線設計
基本概念
流水線處理源自現代工業生產裝配線上的流水作業,是指將待處理的任務分解為相對獨立的、可以順序執行的而又相互關聯的一個個子任務。流水線處理是高速設計中的一個常用設計手段,如果某個設計的處理流程分為若干步驟,并且整個數據處理是“單流向”的,即沒有反饋或者迭代運算,前一個步驟的輸出是下一個步驟的輸入,那么可以考慮采用流水線設計方法來提高系統頻率。流水線設計結構如圖所示。
其基本結構是將適當劃分的n個操作步驟單流向串聯起來。流水線操作的最大特點是數據流在各個步驟的處理從時間上看是連續的順序操作,與此同時各個步驟又是同時并行的在運作。
在處理器架構上,一個單核處理器只能一次處理一個任務,是順序的執行,如要實現并行操作需要多個處理器來執行。
FPGA中典型的流水線設計
流水線處理采用面積換取速度的思想,可以大大提高電路的工作頻率,尤其對于圖像處理任務中的二維卷積運算、FIR及FFT濾波器等,采用流水線設計可以保證一個時鐘輸出一個像素,相對于全并行處理電路占用資源又不會太多。對于大部分的圖像處理任務而言,處理過程基本上也是一個“串行”的處理思路。因此,流水線設計無疑是最好的設計方式。如下圖所示是一個典型的圖像處理任務流程圖。
并行陣列
在并行陣列型電路中,多組并行排列的子電路同時接收整體數據的多個部分進行并行計算。并行陣列型電路中的子電路本身可以是簡單的組合電路,也可以是復雜的時序電路例如上面提到的流水線型電路。如果受邏輯資源限制,無法同時處理全部數據,那么也可以依次處理部分數據直到完成全部數據的處理。
和流水線共享電路的思路不同,并行陣列電路對于每個處理數據都生成一個處理電路,這無疑更大地提高了電路的處理速度,但是也帶來了更大的資源消耗,是用面積換取速度原則的又一體現。如果系統設計對資源消耗相對不敏感,但是又需要較快的處理速度時,那么我們會選擇并行結構來完成。
并行陣列的一個典型應用是多通道像素同時進行處理,對一個串行輸入的RGB通道或是YCbCr通道的視頻流,首先做一個串并轉換,接著復制處理邏輯對三個通道同時做處理。這樣理論上可以得到3倍的速度提升。
計算技術
計算技術也是圖像處理的核心技術之一。在軟件算法設計和調試完成之后,需要將軟件的算法映射到FPGA中去,由于軟件和硬件的設計差異性,相當一部分算法在映射前需要通過等效轉換,近似計算等硬件計算技術來轉換成硬件易于實現的方式,從而達到邏輯資源消耗和時序,以及誤差與消耗的平衡。本節將介紹幾種常用的硬件計算技術。
算法轉換
在乘法和除法運算中,經常會遇到乘數、被乘數或分子與分母是常數的情況。直接調用乘法器或除法器當然可以解決這個問題,但是這會消耗一定的DSP運算單元,而DSP單元往往是FPGA里面比較少的資源。對于定常數,可以通過一定的轉換將其轉換為移位和加法運算,從而減少乘法器和除法器的使用。下面列舉幾個常用的例子。
乘法運算的實現:
dout =? din × 255
轉換后:
dout =? din ×(256-1)=(din<<8)-din
除法運算的實現:
在這里擴展了10bit的位寬,這個位寬約大,精度約高,但也會消耗相對更多的資源,實際應用中根據精度需求進行選擇。
近似計算
與算法轉換不同的是,算法轉換是不會帶來任何原理的誤差,而近似則會帶來一定的計算誤差。通常情況下,在誤差允許的范圍內,采用近似計算帶來的明顯優勢是計算復雜度的降低及資源消耗的降低。在FPGA中常見的近似計算是截斷。
截斷就是用位數較少的近似值來代替位數較多或無限位數的數時,要有一定的取舍法則。在數值計算中,為了適應各種不同的情況,須采用不同的截取方法。
經常使用的截斷方法就是四舍五入。實際上,對于FPGA來講,處理的都是二進制數據。因此,在小數位的第一位的值是0還是1決定了是否對結果進行進位。
同樣,以除法的例子為例:
將位寬擴展10bit后,再進行近似運算,近似運算直接去掉了小數部分,引入的誤差僅為0.04/210。如果din是圖像中的一個像素,其最大值為256,誤差僅為0.01,該近似運算幾乎不影響圖像像素。相對無損失的運算轉換,大大減輕了運算量。
增量更新
增量更新是指在進行更新操作時,只更新需要改變的地方,不需要更新或者已經更新過的地方則不會重復更新,增量更新與完全更新相對。增量更新在流水線處理中,特別是二維卷積處理中特別有用。這是由于在兩個連續的卷積窗口中有大量的相同元素。
假定要計算連續5個數據流的和,在上一個時刻,這5個待計算的數值是a0,a1,a2,a3,a4, ?在本時刻待計算的數值是a1,a2,a3,a4,a5。中間有4個值是相同元素。如此如果每次計算都將5個數重新相加,就有點浪費資源。正確的做法是加上一個新值,再減去一個最老的值。
對應的增量計算方法如下圖所示,當然5個數值求和,可以擴展到10個數值求和,但增量運算的運算量不變:
浮點計算
大部分運算可以通過擴位和近似的方式轉換為定點運算。但有些算法在設計在設計的過程中就涉及大量的浮點運算,在轉換為定點運算時比較麻煩,會帶來龐大的工作量。此外,在某些應用中,定點算法是不可行的,動態范圍要求使用浮點算法的一個常見的例子是矩陣求逆運算。本節將介紹如何使用FPGA來實現浮點運算以便減少移植的工作量。
浮點數的定義
浮點數(float)是屬于有理數中某特定子集的數的數字表示,在計算機中用以近似表示任意某個實數。具體來說,這個實數由一個整數或定點數(即尾數)乘以某個基數(計算機中通常是2)的整數次冪得到,這種表示方法類似于基數為10的科學記數法。
IEEE二進制浮點數算術標準(IEEE 754)是20世紀80年代以來最廣泛使用的浮點數運算標準,為許多CPU與浮點運算器所采用。這個標準定義了表示浮點數的格式(包括負零-0)與反常值(denormal number)),一些特殊數值(無窮(Inf)與非數值(NaN)),以及這些數值的“浮點數運算符”;它也指明了四種數值舍入規則和五種例外狀況(包括例外發生的時機與處理方式)。
IEEE 754規定了四種表示浮點數值的方式:單精確度(32位)、雙精確度(64位)、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上,通常以80位實現)。只有32位模式有強制要求,其他都是選擇性的。大部分編程語言都有提供IEEE浮點數格式與算術,但有些將其列為非必需的。例如,IEEE 754在問世之前就有的C語言,現在有包括IEEE算術,但不算作強制要求(C語言的float通常是指IEEE單精確度,而double是指雙精確度)。
1)單精度浮點數
單精度浮點數用32位二進制表示,其中最高位Bit[31],MSB為符號位,即sign域。Bit[30:23]為exponent域,這8位數據表示指數;最低的23位Bit[22:0]為fraction域,用于表示浮點數的小數部分。
2)雙精度浮點數
單精度浮點數用64位二進制表示,其中最高位Bit[63],MSB為符號位,即sign域。Bit[62:52]為exponent域,這11位數據表示指數;最低的52位Bit[51:0]為fraction域,用于表示浮點數的小數部分
用FPGA實現浮點運算
乘法:對于乘法運算、位數相乘及指數相加,若位數結果大于2,則需要重新規范化,將其右移一位并且增加指數。由于乘積的位數可能會比表示位多,因此,需要進行舍位處理。兩個指數的和包括兩個偏移量,因此必須減掉一個。輸出值的符號位是輸入符號位的異或。需要附加的邏輯來檢測下溢出、上移除及處理其他的錯誤情況,例如,處理無窮大和非數。
除法:除法與懲罰類似,只是尾數相除,指數相減并且在重新規范化時可能包含左移。
加法和減法:實現要更加復雜。與原碼表示相同,實際操作的執行取決于輸入的符號。指數必須相同,因為數必須要對齊。根據指數位的差值,將較小的指數右移相應的位數。
浮點操作相對于定點操作無疑要消耗更多的資源,這不是因為浮點操作有多復雜,而是因為處理異常時需要很多邏輯,尤其是在需要符合IEEE標準時。大部分FPGA在進行浮點運算時,為符合IEEE 754標準,每次運算都需要去歸一化和歸一化步驟,導致了極大的性能瓶頸。因為這些歸一化和去歸一化步驟一般通過FPGA中的大規模桶形移位寄存器實現,需要大量的邏輯和布線資源。通常一個單精度浮點加法器需要500個查找表(LUT),單精度浮點要占用30%的LUT,指數和自然對數等更復雜的數學函數需要大約1000個LUT。因此,隨著DSP算法越來越復雜,FPGA性能會明顯劣化,對占用80%~90%邏輯資源的FPGA會造成嚴重的布線擁塞,阻礙FPGA的快速互連,最終會影響時序收斂。
存儲器映射
一般情況下,我們希望當數據流過FPGA時,FPGA盡可能多地處理數據,并且減少FPGA和外部設備之間的數據傳輸,采用流水線處理架構則可以很好地減少對存儲器的頻繁讀寫。然而在某些情況下,一個圖像處理算法需要像素之間的行列同步或是幀同步,這個時候就必須要緩存部分圖像或者是整幅圖像。
在軟件處理中,這個緩存通常情況下是放在內存中,需要的時候從內存進行讀取。在FPGA中,可以選擇將緩存放在FPGA內部或者外部。
幀緩存
對于幀緩存,通常情況下會將其放在片外進行讀寫。對于幀緩存,在成本不夠敏感的情況下,最好使用靜態存儲器(SRAM),尤其是用于需要頻繁和隨機地訪問這些幀緩存的地方。
靜態存儲器:相對于動態存儲器來說,通常情況下讀寫接口時序相對簡單,讀寫速度要快,并且功耗相對較低。但是,由于靜態存儲器每一位要使用6個晶體管,而動態存儲器每位只使用一個晶體管,因此靜態存儲器的價格要貴得多。這也限制了它在成本敏感場合的應用。
一個幀緩存控制電路要包括讀地址發生器、寫地址發生器及讀寫控制時序。一般情況下,這個寫地址即為輸入幀數據流ImageDin的行列地址,而讀地址為輸出流Frame_buffer的行列地址。以SRAM為基礎的幀緩存電路如圖所示。
動態存儲器:如果系統對于讀取速度沒有嚴格要求的緩存應用,那么動態存儲器無疑是更好的選擇。雖然動態存儲器存取速度較慢,從主機提供地址到數據輸出可能需要若干個時鐘,但是當動態存儲器工作在突發模式時,也可以提供較大的帶寬,這對于圖像處理這樣的大數據應用場合非常有用。
動態存儲器的接口設計相對比較復雜,這是由于動態存儲器必須要間隔一段時間對其進行刷新來保持當前的存儲器內容。此外,與靜態存儲器不同,動態存儲器的行列地址通常是分開的。因此對動態存儲器的尋址工作需要分別進行行列尋址工作。通常情況下,我們會直接采用FPGA廠家提供的IP核來實現外部的存儲器驅動,這樣可以大大提高開發的效率。
行緩存
行緩存通常情況下會放在片內。每一個行緩存有效地將輸入延遲了一行。用階數為圖像寬度的移位寄存器是可以方便地實現這種延遲。
移位寄存器:首先,移位寄存器是由一連串的移位寄存器來實現的,每個位都適用一個寄存器,而每個邏輯單元都只有一個寄存器,因此,采用移位寄存器的方式將會占用大量的邏輯資源,特別是在圖像寬度比較大的時候,用內部資源來實現行緩存往往是不明智的選擇。
片上RAM:設計行緩存時,通常會選擇利用FPGA片內的RAM塊來實現。采用RAM作為行緩存,就需要設計寫入和讀出地址產生電路。寫入地址就是輸入圖像行的列地址,讀出地址就是輸出圖像行的列地址。若采用FIFO作為行緩存,則順序寫入與讀出即可。
行緩存的理想工作狀態:流狀態是行緩存的理想工作狀態,也就是除了緩存裝載和卸載,緩存內部的數據流入和流出是平衡的,這樣才不至于破壞系統的流水線。輸入圖像數據的到來時刻是由上一級時序所控制,輸出圖像的數據流則與行緩存息息相關。
考慮一個3×3的窗口濾波器——每個輸出結果是窗口內九個像素值的一個函數。如果沒有高速緩存,每個窗口位置必須讀九個像素(對流處理來說是每個時鐘周期),并且當窗口掃描整個圖像時,每個像素需要讀取九次。在連續的時鐘周期內需要使用水平相鄰的像素,因此可以用寄存器緩存和延遲像素。這就能將每個時鐘周期讀取的像素數量減少到三個。行緩沖緩存了之前行中的像素值,避免了對這些行中像素的再次讀取。一個3×3的濾波器占據了三行:一個當前行和兩個之前行,在當前行中讀取新數據,因此需要兩個行緩沖來緩存之前兩行的像素值。
異步緩存
異步緩存主要應用在跨時鐘的場合。對于一些設計,在不同的部分使用不同的時鐘是不可避免的。這個問題主要出現在視頻輸入、視頻輸出及與外部的異步接口等場合。
一般來說,外部的視頻輸入數據流都會附帶一個視頻流的參考時鐘,而這個時鐘與本地邏輯時鐘是異步的。同時,處理完的視頻流要進行顯示,顯示驅動電路的時鐘與本地系統時鐘往往也是不同的。系統與外部的一些異步接口,例如異步存儲器等,都是跨時鐘域的場合。
異步時鐘帶來的一個問題就是有效的讀寫速率不一致。一般情況下,這種場合是讀取速度要小于寫入速度。解決異步時鐘的一個方法就是建立異步緩存器,用一個異步FIFO即可實現。使用FIFO需要注意的問題就是溢出問題,需要使FIFO的讀寫匹配,在FIFO裝滿之前,需要把FIFO讀走。
FIFO的讀寫時序比較簡單,內部結構圖如圖所示。
對于異步時鐘域,用異步FIFO作異步緩存,還能有效解決壓穩態的問題。尤其是對于不同頻不同相位的異步時鐘域,只能用異步緩存來進行同步,這是由于需要緩存來進行帶寬匹配,在這里FIFO是一個不錯的選擇。
亞穩態問題:
亞穩態狀態下,對于任何噪聲諸如環境,只要系統中有異步元件,亞穩態就是無法避免的。亞穩態主要發生在異步信號檢測、跨時鐘域信號傳輸及復位電路等常用設計中。對于壓穩態,一般的處理方法是三拍處理,使用三級寄存器同步后,亞穩態出現的給概率基本為0。
在一些對系統穩定性要求更高的場合(例如軍工領域),可能還會要求更多拍處理,以便進一步提高系統穩定性。但這種同步方式僅僅適用于同頻的異步時鐘域或對于少量錯誤不敏感的功能單元。對于異步時鐘域根可靠的方式還是使用兩組異步時鐘來進行異步緩存。
編輯:黃飛
?
評論
查看更多