FPGA排序模組與verilog實現【含原始碼!!!】

JOHN9636發表於2020-08-11

基於FPGA的排序

關於排序,通常大家熟悉的都是基於CPU時序結構的排序演算法,比如氣泡排序、快速排序等等。但在FPGA上有時也需要進行排序,比如想得到FFT輸出的若干點中最大的幾十個點,或者我們只關心輸出中那些較大的值的情況。如果我們只需要最大的一個值,這比較好辦,用一個比較器樹就行了。但是如果我們需要取出例如前128個最大值或者最小值,那麼通常需要採用排序模組。

在FPGA上實現一個固定輸入個數的排序,通常使用排序網路(Sorting Network),一個不錯的排序網路就是雙調排序網路(Bitonic Sorting Network),這可以在wiki上查到。這個排序網路很好理解,下面簡單介紹一下。

看一下下面這個同樣是來自wiki的圖

Bitonic Sorting Network
它很清楚地介紹了雙調排序網路的結構。這是一個16輸入的排序網路。所謂雙調,就是指這個網路的兩組輸入都要是單調的,也就是排好序的。例如最後面的一個框裡的16輸入網路,它可以對兩個分別是單調遞增的8個排好序的資料和單調遞減的8個排好序的資料進行排序;同樣地,在第三級的藍框和綠框裡(籃框表示最終輸出是單調遞增的,綠框表示最終輸出是單調遞減的),每部分的輸入都是由兩組增長特性相反的排好序的輸出組成的。所以這就是所謂的雙調排序網路。

如果從前往後看,資料首先進行兩兩比較,調整次序,然後再如圖所示地進行13、24比較,之後再12、23比較,這樣得到的4個輸出資料就是排好序的了。以此類推。

設計排序網路的一個重點在於證明該網路對於任意順序的輸入得到的排序結果都是有效的、正確的,這一點在此不展開了,因為雙調排序網路已經得到了證明。

verilog程式碼實現

原始碼在我的github倉庫裡,歡迎造訪。

因為我模擬的時候為了方便用了一系列工具,包括Icarus, myHDL and gtkwave,而且是在ubuntu16.04下做的,用python寫的testbench。如果你看了嫌麻煩,給你一個懶人傳送門,直接看到4個verilog程式碼,確定排序資料個數、位寬、每個輸入對應的索引或者是標籤(label)的位寬、遞增還是遞減、有符號還是無符號,改改top模組裡對應的5個parameter,放入vivado,enjoy。關於引數的細節,可以看下面配置引數部分。

如果想了解這個模擬怎麼用,找臺linux機器,按照那三個工具的網站上的手冊安裝好,應該就可以愉快地使用了。如果他們用起來實在不夠友好,之後我再優化。

如果使用者體驗不佳,告訴我改進方向,我再繼續完善。

verilog實現細節和配置引數

如果你不僅想使用該模組,還想知道模組是怎麼實現的,那就接著往下看。

起先我想直接一個模組一個模組地編下去,於是就有了nonrecursion(非遞迴)那個資料夾下的rtl和模擬。我從頭往後編,嘗試實現一個16輸入的模組。先來一個2輸入的,升降序可調、有無符號可選、位寬和label寬度可調,測一下,沒問題;再來一個4輸入的,測一下,沒問題;再寫8輸入的,沒問題;寫到16輸入發現輸入訊號實在太多了,所以把所有輸入放到了一個向量裡,label也放到了一個向量裡,輸出也一樣。最終,16輸入地非遞迴實現的排序網路誕生了。但是如果要實現一個256輸入的排序網路豈不是要崩潰。

於是,recursion(遞迴)那個資料夾下的rtl和模擬誕生了。只需要4個verilog檔案!你就可以實現任意多的輸入排序!只要你的FPGA有足夠多的硬體資源,想要多少輸入,就有多少輸入!只需4個verilog檔案!4個!4個!只需4個!喜歡的話別忘了點個贊哦!

在遞迴實現中,除了非遞迴中實現的升降序可調、有無符號可選、位寬和label寬度可調,又增加了一個LOG_INPUT_NUM引數,這是用來選定通道數的,正是它讓你想要多少輸入,就能有多少輸入。所以輸入x向量的寬度就是DATA_WIDTH*(2LOG_INPUT_NUM),也就是輸入數為 2LOG_INPUT_NUM個,每個數的位寬為DATA_WIDTH;同樣地輸入的x_label的寬度就是LABEL_WIDTH*(2LOG_INPUT_NUM);輸出向量也一樣。比如說,4輸入網路,位寬8bit,label位寬4bit,那麼x[7:0]就是第一個輸入,x_label[3:0]就是第一個輸入對應的標籤或者索引;第二個輸入就是x[15:0]和x_label[7:4]。

如果你想搞清楚硬體實現的方式,就看非遞迴的,因為比較好理解;如果你想搞個比如256輸入的排序,那還是從遞迴的下手改改引數更方便。

模組效能

不難算出,當輸入數量為K時,該模組延遲的週期數為 (log⁡K*(log⁡K+1))/2 。這裡面log的底數是2,這個可以通過16輸入的情況推算出來和驗證。最終需要的2輸入比較器的數量為 (log⁡K*(log⁡K+1))/2 * K/2 = (K * logK * (log⁡K+1))/4 個,具體到了FPGA上會對映出多少資源,大體上應該正比於比較器數量。這個排序網路效能還是不錯的,做個512輸入或者256輸入的排序,一般的資源消耗還可以接受,延遲也不高。

如有任何疑問,歡迎交流指正。

相關文章