上篇博客我們遺留了一些問題,這篇博客算是對上篇博客的完善。
一、Normal(Standard)模式的FIFO
上篇博客,我們最后得到如下的波形:
1、換行數據的問題
在換行時數據比較怪,如第 4 行數據的第一個矩陣是{10,10,11,20,20,21,30,30,31},這是什么鬼?我們將數據放到正常的圖片數據上來分析,可以看到,在換行時是借用了上一行最后一個矩陣的一列數據。
實驗推理:圖像最左邊出現一個違和的豎條,隱隱約約像是圖片最右邊的圖形。
2、換幀數據的問題
我們也按上面那種方法分析。換幀后的第一個矩陣數據為{30,30,30,40,40,40,50,50,1},這個3x3矩陣借用了上一幀圖片末尾的最后一列數據。不僅如此,整個的第一排矩陣都是借用了上一幀圖片末尾的最后一個矩陣的一列數據。
實驗推理:圖像最上邊出現一個違和的橫條,隱隱約約像是圖片最右下角的圖形。
3、實際上板效果
本次上板采用的圖像處理為均值濾波,關於均值濾波的具體實現我會另開一篇博客說明。此外我將選取的圖片的左上角畫了一個黑點,右下角畫了一個白點。如下所示:
經過處理后得到如下圖片:
明顯的看到最上邊出現一條白線,正是右下角的白點造成的。而最左邊則隱隱約約出現了最右邊的圖形,尤其左下角那里很容易觀察到。實驗現象和我們的推理一致。
二、show-ahead(first world fall through)模式的FIFO
用這種模式的話,只是矩陣數據的選取時就不需要打拍了,normal 模式的整個設計耗費 2 個時鍾周期,而 show-ahead 模式的整個設計就只需要耗費 1 個時鍾周期。
//矩陣數據選取 //--------------------------------------------------- assign row_1 = q_2; assign row_2 = q_1; assign row_3 = din; //打拍形成矩陣,矩陣順序歸正,1clk //--------------------------------------------------- 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
其仿真波形如下所示:
1、換行數據的問題
第4行數據的第一個矩陣是{11,11,11,21,21,21,30,30,31},數據主要是復制了本矩陣的最后一列,即圖像數據的第一列。此外還借用了 2 次上一行最后一個矩陣的 1 個數據。
實驗推理:圖像最左邊出現一個不怎么違和的豎條,隱隱約約像是第一列的復制。
2、換幀數據的問題
換幀后一開始的矩陣數據為{30,30,30,40,40,50,50,1},這個矩陣借用了上一幀圖片末尾的最后一列數據,第5個矩陣后則開始全部借用本幀圖片開頭的第一個數據。
結論:最上邊出現一個違和的橫條,橫條最左邊隱隱約約像是圖片最右下角的圖形,橫條的其他部分隱隱約約像是圖片最左上角的圖形。
3、實際上板效果
上板后看到最左邊沒有那么違和了,最上邊基本是黑色,但其實最上邊開頭處是有一丁點的白色的,全圖不好觀察,我專門照一下這個角:
這樣就能看到了,實驗結果我們的推理也是一致的。
4、normal模式和show-ahead模式對比
(1)normal 模式的矩陣數據選取時需打拍才能對齊,打拍加生成矩陣共耗費 2 個時鍾周期,而 show-ahead 模式選取矩陣數據時不需要打拍,只是生成矩陣時耗費 1 個時鍾周期。
(2)normal模式轉行時借用前一行最后一列像素,而show-ahead模式轉行時多是復制自身,轉行時 show-ahead 模式表現較好。
(3)轉幀時,兩種模式的表現都很垃圾。
三、FIFO法改進:邊界補0
邊界補 0 法即是在轉行和轉幀時的用0來替代這些怪怪的數據,normal 模式和 show-ahead 模式都差不多,最后的波形都一樣,下面僅整理 show-ahead 模式的邊界補 0 改進方法。
1、代碼
邊界補 0 法的要點在於矩陣數據選取時做文章,如下所示:
//矩陣數據選取 //--------------------------------------------------- assign row_1 = rd_en_2 ? q_2 : 8'd0; assign row_2 = rd_en_1 ? q_1 : 8'd0; assign row_3 = din_vld ? din : 8'd0;
2、波形
得到的波形如下所示:
拿圖像數據來看的話則如下圖所示:
實驗推理:在像素構成上,0代表黑色,因此處理后的第一行和第一列圖形偏黑,看上去就是圖像的上邊和左邊出現了一條黑邊。
3、實際上板:
上板后可以看到最左邊和最上邊確實出現了黑邊,由於天黑了,拍照不太容易照清邊界,實際上是可以看見黑邊的。
四、FIFO法改進:邊界復制
邊界補 0 終究不是太好,最后圖像的上邊和左邊出現了黑邊。最好的方法是鏡像或者復制,由於 FPGA 處理是線性的,鏡像法的實現比較困難,這里只探究復制法的實現。
邊界復制法用 normal 模式或 show-ahead 模式都差不多,原理都是一樣的,無非是 normal 的數據和計數得再打一拍才能使用。下面僅展示 show-ahead 模式下的邊界復制改進辦法。
1、代碼
邊界復制法的要點在於生成 3x3 矩陣,如下所示:
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 //------------------------------------------------------------------------- 第1排矩陣 else if(cnt_row == 0)begin if(cnt_col == 0) begin //第1個矩陣 {matrix_11, matrix_12, matrix_13} <= {row_3, row_3, row_3}; {matrix_21, matrix_22, matrix_23} <= {row_3, row_3, row_3}; {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3}; end else begin //剩余矩陣 {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_3}; {matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_3}; {matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3}; end end //------------------------------------------------------------------------- 第2排矩陣 else if(cnt_row == 1)begin if(cnt_col == 0) begin //第1個矩陣 {matrix_11, matrix_12, matrix_13} <= {row_2, row_2, row_2}; {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2}; {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3}; end else begin //剩余矩陣 {matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_2}; {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 //------------------------------------------------------------------------- 剩余矩陣 else begin if(cnt_col == 0) begin //第1個矩陣 {matrix_11, matrix_12, matrix_13} <= {row_1, row_1, row_1}; {matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2}; {matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3}; 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 end
這代碼......看起來很蠢是吧,但我實在想不到別的辦法了。這段代碼與其說是設計,不如說是拼湊。如果誰有更優雅的寫法歡迎告知。
2、波形
仿真波形如下所示:
由於這次設計的波形太長,因此只展示第二幀的圖像數據仿真波形。從波形上看數據很難看懂,我們也是一列列矩陣放到圖像數據上來看,如圖所示:
3、實際上板:
上板后發現圖片非常完美,最上邊和最左邊都很和諧,完全沒有違和感。直接看圖片非常像邊界補 0 的操作,但邊界補0法上邊和左邊都是黑邊,而邊界復制法不是黑邊。此次設計的實驗可以通過按鍵切換圖像效果,在邊界補0法切換為邊界復制法的瞬間,可以看到圖片的最左邊和最上邊突然放大了一點點,其實就是邊界補 0 法的黑邊消失了。
后記:
本篇博客解決了上篇博客遺留下來的問題,比較了 FIFO 生成矩陣后的各種處理,最后得出結論: show-ahead 模式的代碼最簡潔,邊界復制法的效果最完美。
實際上在 Quartus 、ISE、Vivado 中有專門用於生成矩陣的 shift IP 核,下篇博客我們再來介紹。