前兩篇博客整理了雙 FIFO 生成 3x3 矩陣的方法,本篇博客整理一下 Quartus II 軟件下的 shift ip 核及如何生成 3x3 矩陣。
要求:模擬一張分辨率為 10x5 的圖片,圖片的數據為 1~50,用 Verilog 對其生成 3x3 矩陣,以便后面的圖像處理。
testbench:數據的使能和數據對齊,每隔 10 個數據就空閑小段時間,每隔 50 個數據又空閑一段時間,模仿圖像幀的樣子,如下所示:


一、Shift IP 核原理解析
還是直接拿官方例程來說明吧。在官方手冊上,展示了一個案例,首先是配置說明:

這段配置在 Shift ip 核中的具體設置如下圖所示:

由此進行仿真可得到如下波形:

官方解釋:“ 這個例子展示的效果為:當全部 12 個數據都移到移位寄存器中時,如何同時使用第 1 - 4 - 7 - 10 個數據(其次是第 2-5-8-11 個和第 3-6-9-12個數據)。”
看到這肯定是一臉懵逼的,完全不知道他表達的什么意思,因此我們還是自己再梳理一下圖中的信息:
1、有 12 個數據作為 shift ip 核的輸入即 shiftin信號。
2、和 shiftin一起的還有使能信號 clken,clken 和 shiftin是對齊的。
3、出來了 4 串數據。shiftout、taps0x,taps1x,taps2x,taps3x,其中 shiftout 信號和 taps3x信號完全一致。
4、有一個異步清 0 信號aclr,當其拉高有效時,出來的 4 串數據立馬為 0。
得到這些信號后稍微清楚了一點,但還是不太理解,那就繼續看數據手冊:


這個示意圖解釋了 shift ip 核的內部原理。shiftout 信號和 taps3x信號相同。進去 1 串數據就出來平行的 4 串數據,f8 、13、b5、54在時序上是同一時刻出來的,同樣的 b8,84,3b,0e是同一時刻出來的,d0,67,6e,44也是同一時刻出來的。由此我們可以看出 shift ip 核很像是一個 RAM,但是這個 RAM 里面有好幾道溝槽,當一個溝槽填滿后就轉移到下一個溝槽,同時每個溝槽都有一個接口引出來,利用這些溝槽的接口就能做很多事情了,例如輕松的實現 3x3、5x5等矩陣。
這個圖理解了,但實際運用時還是會迷糊,因為看起來 shiftin 在最上面,而最下面的數據反而是最開始進去的。如果是圖片數據,那完全就是倒過來了,此外shiftin的位置也比較模糊,因此我制作一張倒過來的圖,方便后面生成 3x3 矩陣時的理解。

由於 shiftout 和 taps3x 信號是完全相同的,因此沒有畫出,實際使用時也不需要使用 shiftout 信號。而 shiftin 信號在官方原版圖中容易讓人誤解,正確的位置如圖所示。倒過來后就清楚的知道,最先開始的數據在左上角,就像一張圖片的第一個像素一樣,后面的像素順序排列,排滿一行換下一行,而最下面的信號則是shiftin,即端口中的輸入信號。
二、Shift IP 核生成 3x3 矩陣
1、IP核生成
(1)打開 Quartus II 的 IP核生成向導,輸入 shift,點擊如圖所示的 IP核並命名。

(2)本次設計是仿真 10x5 像素的圖片,數據為 8bit,因此位寬選擇為8,taps 選擇為 2,即 2 個 taps 和 din 就已經滿足 3 條平行數據了。默認是未勾選 “taps分組” 選項,這樣出來的是一個大 taps,而我們需要的是多個 taps 數據,所以必須勾選上 “taps分組” 選項。taps之間的距離為一行的數據個數 10。再勾選時鍾使能接口,這個實際上就是寫使能。異步清 0 信號不用勾選,這里用不到。順便說一下,是沒有讀使能的,ip核內數據存儲到一定數量就會自動吐出。

2、IP核調用
shift_ip u_shift_ip
(
.clken (din_vld ),
.clock (clk ),
.shiftin (din ),
.shiftout ( ),
.taps0x (taps0x ),
.taps1x (taps1x )
);
到這一步實際上相當於完成了之前雙 FIFO 法生成 3x3 矩陣的 “IP核調用 + 行列計數規划 + FIFO讀寫信號設置”,只能說 shift ip 核太適合用來生成矩陣了。
3、生成3x3矩陣
和雙 FIFO 的 show-ahead 模式完全一樣,就是要注意一下矩陣的數據選取,腦子得轉個 180 度的彎來,一旦轉過來了就非常簡單,沒轉過來的話就難理解了。
//矩陣數據選取 //--------------------------------------------------- assign row_1 = taps1x; assign row_2 = taps0x; assign row_3 = din; //打拍形成矩陣,矩陣順序歸正 //--------------------------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) begin {matrix_11, matrix_12, matrix_13} <= {8'd0, 8'd0, 8'd0}; {matrix_21, matrix_22, matrix_23} <= {8'd0, 8'd0, 8'd0}; {matrix_31, matrix_32, matrix_33} <= {8'd0, 8'd0, 8'd0}; end else begin {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_1}; {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2}; {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3}; end end
三、仿真分析
此次仿真腳本和之前的雙 FIFO 是完全一樣的,即模擬一張 5x10 分辨率的圖片,最終波形如下所示:

波形是對齊了的,還是和雙 FIFO 一樣再來分析一下換行和換幀的情況吧。
1、換行數據的問題
取第4行數據的第一個矩陣來看,其數據為{11,11,11,21,21,21,30,30,31},數據主要是復制了本矩陣的最后一列,即圖像數據的第一列。此外還借用了 2 次上一行最后一個矩陣的 1 個數據。和 show-ahead 模式的雙FIFO法是一模一樣的情況。

實驗推理:圖像最左邊出現一個不怎么違和的豎條,隱隱約約像是第一列的復制。
2、換幀數據的問題
換幀后一開始的矩陣為{31,31,31,41,41,41,50,50,1},借用了上一幀圖片末尾的數據。 整個第一排矩陣的數據都是借用了上一幀圖片末尾的最后兩行數據。

實驗推理:最上邊出現一個違和的橫條,隱隱約約像是圖片最下邊的圖形。
四、實際上板
這次選擇一張最下邊有一些白點的圖片,以均值濾波的實現來測試這次的 3x3 矩陣效果。關於均值濾波后面會單獨拿出來講解。原圖如下:

經過處理后得到如下圖片:

隱約看到最左邊有一條模糊的線,但不怎么違和。而最上邊出現了一些白色線段,剛好對應了最下方的那些白點。實驗最終和我們的推理一致。
五、后記
shift ip 核生成 3x3 矩陣比雙 FIFO 簡單太多了,代碼量極少。但是它也有局限性,如果要改進為邊界補 0 或邊界復制的話還不如就用 雙FIFO 來實現。但 shift ip 核勝就勝在簡潔,而且我們做圖像處理時不會在乎邊界的那一丁點問題,重點還是關注圖像處理算法本身以及最后實現的圖像整體效果。
在 ISE 和 Vivado 中其實也有 shift ip 核,和本篇博客略有差異。關於生成 3x3 矩陣的方法就整理到這,足夠用了。
參考資料:
[1]CrazyBingo 圖像處理教程
[2]NingHechuan 圖像處理教程
