一 簡述
最近惡補基礎知識,借了<<Verilog傳奇>>,《基於FPGA的嵌入式影像處理系統設計》和<<基千FPGA的數字影像處理原理及應用>>這三本書。
<<Verilog傳奇>>是關於Verilog基礎知識的,總共九章。由於書籍內容太多沒時間看,故一般都是瞭解整本書的大致內容,遇到問題時能回憶起在那看過,再返回來仔細研究。後面兩本書是影像處理的FPGA應用,有很多基本的影像處理操作,如常見的直方圖技術,各種濾波,影像分割等。重點是影像處理演算法的FPGA對映,還有影像模擬驗證平臺。
(一)<<Verilog傳奇>>總結:
第一章用來介紹Verilog語言的基本知識, 包括髮展歷史、設計流晶片結構和可綜合性等。 這裡的重點是幫大家建立Yerilog語言與其他學科之間的聯絡。幫助大家掌握設計數字邏輯系統需要考量的有關內容。
第二章,除了介紹IEEE有關Verilog語言的標準體系及非RTL級的設計之外,還重 點說明了常量、 變數及結構化模組的內容, 是學習Verilog語言的基礎。
第三、 四章, 分別介紹了用assign和always定義的組合邏輯電路的描述方法。 其中包含各種運算子操作的寫法, 以及條件、 多選的描述。 最後, 以多路選擇器為例, 說明 瞭如何分析一個組合邏輯電路系統。
第五章, 介紹了時序邏輯電路系統的Verilog語言描述方法, 包括D觸發器及D觸 發器鏈的寫法。 本章還介紹瞭如何拆分組合電路以適應系統操作時間要求的概念。 在此基礎上, 介紹了三種系統速度與面積平衡的方法: 並行化設計、流水線設計與分時多工 設計。
第六章, 結合工程實踐, 介紹了經常遇到的若干問題, 其中包括復位系統設計、 可變移位操作、 有限狀態機設計、 多時鐘系統處理及迴圈操作的處理。
第七章, 介紹了與[P核設計有關的靈活程式設計問題。 其中涉及任務與函式的寫法、 利 用巨集定義方法改變系統引數、 利用引數方法改變系統引數及生成塊方法改變系統結構。最後綜合運用這幾種方法, 給出了一個簡單的JP核一數字分頻系統 設計的例子。
第八章, 說明了Verilog語言中不可綜合的部分。 其一為模擬所需的資料型別、複雜 運算和並行塊設計; 其二包含預編譯命令; 其三為系統任務與函式。 本章還介紹了測試向量的概念及其編寫方法, 以及Yerilog語言與其他語言介面的問題。
第九章, 以 “ 直接數字式頻率合成器” 系統為例, 綜合前面各章介紹的知識, 採用 了R OM查詢表、 折線法和CORDIC演算法分別進行了實現。本章不僅希望讀者學會如何 綜合使用Yerilog語言, 還進一步介紹了部分演算法與演算法定點化的知識。
(二)<<Verilog傳奇>>閱讀建議
首先, 粗讀/跳讀第一章到第二章的第二講, 瞭解基本概念。 本書的基本假設是讀者都掌握了《數位電子技術》這一門課程。其次詳細閱讀第二章第三講到第五章,這是基礎內容。第六章,七章為進階內容。第八章,是測試和驗證。第九章是一個複雜的例子, 重點是閱讀作者的思想、 設計過程及程式碼風格。
由於,我曾經學過Verilog語言,故直接從第六章,第七章開始。第六章(按鍵與復位,可變移位寬度的移位操作,有限狀態機及其程式碼,多時鐘系統,迴圈控制),第七章(函式與任務,巨集定義與巨集判斷,引數,生成塊,數字分頻器核的設計)。今天下午看了函式與任務,後面詳細介紹。
(三) <<基千FPGA的數字影像處理原理及應用>>總結
重點講解影像處理演算法移植到FPGA中的基本思路和方法,突出工程應用。每一章均附有C/C++實現程式碼,同時用循序漸進、自頂向下的方式設計FPGA演算法模組, 針對每一個模組設計了詳細的實現框圖,確保讀者能理解演算法設計的原理。
此外,每個演算法都配有Verilog實現方法,並給出模擬結果。本書還提出了一個通用的利用Modelsim和VS實現影像處理的模擬測試平臺。這個模擬平臺是我學習的重點,在書本的第五章,系統模擬。主要是動手搭建視訊驗證的模擬平臺,給演算法提供模擬的視訊源,聯合matlab檢視演算法處理效果。借鑑書本的思路,用在自己的影像處理專案中。
本書內容概述如下:
(I)第1~5章是基礎章節, 重點介紹數字影像處理和FPGA程式設計的基礎知識。
第1章簡單介紹了影像處理的基礎知識, 包括影像處理的發展現狀, 還地介紹了影像從獲取到顯示儲存的基本流程。
第2章首先介紹了FPGA的發展現狀, 生產廠家及其開發流程。接著介紹了基千FPGA的影像處理的基本開發流程。
第3章主要介紹了在FPGA中應用的程式語言。本章並沒有詳細介紹Verilog語法,而是從工程應用的角度介紹常用的設計方法和例項。
第4章主要介紹了把軟體演算法對映到FPGA常用的技巧。首先介紹了應用較廣泛的流水線設計方法, 接著介紹了FPGA硬體計算技術, 包括一些常用的計算轉換、查詢表、浮點計算、C ordie計算等方法。最後介紹了在影像處理中用途非常多的儲存器對映, 並提出了一些其他設計技巧。
第5章首先簡要介紹了模擬測試軟體Modelsim的使用, 接著重點介紹了一個通用的視訊影像處理模擬測試系統。這個測試系統包括完整的視訊模擬、視訊捕獲, 以及testbench設計, 並結合基於MFC 的VC上位機來實現測試系統的搭建。
(2)第6~10章主要介紹演算法實現。
第6章介紹直方圖操作, 主要介紹幾種常用直方圖操作的FPGA實現: 直方圖統計、直方圖均衡、直方圖規定及直方圖線性拉伸。
第7章介紹基千影像處理的線性濾波。首先, 介紹了均值濾波演算法、高斯濾波演算法、Sobel 運算元及FFT等常見的幾種線性濾波原理。其次, 介紹了均值濾波演算法和Sobel運算元的FPGA實現。第8章主要介紹基千影像處理的非線性濾波演算法, 包括排序濾波的基本原理及其FPGA 實現方法。
第9章主要介紹基千影像處理的形態學濾波演算法, 包括形態學濾波的基本概念,包括形態學膨脹、形態學腐蝕、開運算及閉運算等。重點介紹了基千FPGA 的Tophat濾波的原理及實現方法。
第10章主要介紹基千影像處理的常見的分割演算法,包括全域性闕值分割、區域性自適應閥值分割及Canny 運算元。重點介紹基千FPGA 的區域性自適應闕值分割和Canny 運算元的設計與實現。
第11 章主要介紹與視訊和影像處理相關的輸入/輸出介面, 包括CameraLink、火線介面、USB 介面、千兆乙太網等視訊輸入介面和CVT 標準,以及VGA, PAL, DVI,HDMI 等視訊輸出介面。其中, 給出了VGA 和PAL 介面的Verilog 程式碼實現。
二 函式
函式,理解為對於給定的輸入(一個或多個)進行處理後返回輸出值。在MATLAB中,有sum函式,max函式等。
在Verilog中,函式一是常用來計算數學公式的值。如波特率計算公式:divp10x = (10 * fsysclk) / (16 * baud)
二是函式能夠被多次呼叫,避免冗餘。
(一)函式的宣告與呼叫:
函式的宣告有兩種方式:
//express1 function[range] function_name(ports_list); begin ... end endfunction //example1 function [7:0] getbyte (input [15:0] address); begin . . . getbyte = result_expression; end endfunction //express2 function[range] function_name; ports_list; begin ... end endfunction //example2 function [7:0] getbyte; input [15:0] address; begin . . . getbyte = result_expression; end endfunction
函式只能有一個輸出,可以通過多個訊號的拼接完成多個訊號輸出。
編寫函式程式碼的原則:
(1) 函式定義只能模組中完成,不能出現在過程塊(即always)。
(2) 函式至少一個輸入埠;不能包含輸出埠和雙向埠;
(3) 在函式結構中,不能使用任何形式的時間控制語句(#,wait)也不能使用disable中止語句。
(4) 函式定義不能出現always語句。
(5) 函式內部可以呼叫函式,但不能呼叫任務;
(6) 函式呼叫即可在過程塊語句,也可以在assign賦值語句出現;
(7) 函式呼叫語句不能單獨出現,只能作為賦值語句的右端運算元;
下面用一個函式計算游泳池的面積,掌握函式的使用:
下面是計算上圖的面積的Verilog程式碼,體現了函式的宣告,呼叫。
1 module function_total 2 3 ( 4 5 input CLK, input RST, 6 7 input[7:0] width, 8 9 output reg[16:0]area 10 11 ); 12 13 14 15 //Load other module(s) 16 17 18 19 //Definition for Variables in the module 20 21 22 23 //Functions for area calculation 24 25 function[15:0] circle(input[7:0] diameter); 26 27 begin 28 29 circle = (24'd201 * {16'h0, diameter} * {16'h0, diameter}) / 256; 30 31 end 32 33 endfunction 34 35 36 37 function[15:0] square(input[7:0] width); 38 39 begin 40 41 square = {8'h0, width} * {8'h0, width}; 42 43 end 44 45 endfunction 46 47 48 49 function[16:0] total(input[7:0] width); 50 51 begin 52 53 total = {2'h0, square(width)} + {2'h0, circle(width)}; 54 55 end 56 57 endfunction 58 59 60 61 //Logical 62 63 always @(posedge CLK, negedge RST) 64 65 begin 66 67 if (!RST) 68 69 //Reset 70 71 begin 72 73 area <= 17'h0000; 74 75 end 76 77 else 78 79 //Data comes 80 81 begin 82 83 area <= total(width); 84 85 end 86 87 end 88 89 90 91 endmodule
其中的Π/4 = (24'd201/256),diameter為圓的直徑。程式碼注意點:一是體現了函式中呼叫函式。二是訊號位寬的變化。
square = {8'h0, width} * {8'h0, width};
//平方,則每個變數前補充相同輸入變數位寬,即輸出square為輸入位寬的兩倍。
total = {2'h0, square(width)} + {2'h0, circle(width)};
//為避免相加溢位,位寬又擴寬兩位,僅僅把低17位的值賦給total。
三 任務
與函式的呼叫類似,任務的呼叫只有一種形式,如表7.2 所示。與函式的呼叫不同的是:任務的呼叫是在程式碼裡單獨一行書寫的。
下面是幾點需要強調的規則:
(I) 任務的輸入、輸出埠和雙向埠數量不受限制,甚至可以沒有輸入、輸出及
雙向埠;
(2)在任務定義的描述語句中,可以使用出現不可綜合操作符合語句(使用最為頻
繁的就是延遲控制語旬),但這樣會造成該任務不可綜合;
(3)在任務中既可以呼叫其他的任務或函式,也可以呼叫自身;
(4) 在任務定義結構內不能出現initial 和always 過程塊;
(5)可以在任務中中斷正在執行的任務,但其是不可綜合的;當任務被中斷後,程
序流程將返回到呼叫任務的地方繼續向下執行;
(6)任務呼叫語句只能出現在過程塊內;
(7)任務呼叫語句和一條普通的行為描述語句的處理方法一致;
(8)當被呼叫輸入、 輸出或雙向埠時, 任務呼叫語句必須包含埠名列表, 且訊號埠順序和型別必須和任務定義結構中的順序和型別一致; 需要說明的是, 任務的輸 出埠必須和暫存器型別的資料變數對應;
(9)綜合任務只能實現組合邏輯, 也就是說, 呼叫可綜合任務的時間為 “ 0"; 而在面向模擬的任務中可以帶有時序控制,如時延,因此面向模擬的任務的呼叫時間不為“ 0”。
下面為任務宣告及呼叫例子
1 task total(input[7:0] width, output[l7:0J area); 2 begin 3 area <= {2'h0, square(width)} + {2'h0, circle(width)}; 4 end 5 endtask 6 7 always @(posedge CLK, negedge RST) 8 begin 9 if (!RST) 10 //Reset 11 begin 12 area <= 17'h0000; 13 end 14 else 15 //Data comes 16 begin 17 total(width, area);//task call(呼叫) 18 end 19 end
為了避免多次呼叫任務造成的地址衝突,新增automatic使任務成為可重入的。這時在呼叫任務時,會自動給任務宣告變數分配動態地址空間,從 而有效避免了地址空間的衝突。
一個模組裡的任務可以在其他模組呼叫。
1 module module_main; 2 task task_1... 3 task task_2... 4 endmodule 5 //other 6 module mainm1; 7 m1.task_1(...) 8 m2.task_1(...) 9 endmodule
上面對於模擬驗證特別管用。
三 總結
今天下午就看了一講,著重是自己看一遍,然後動手敲一下程式碼,之後模擬驗證結果。計算游泳池面積的模擬程式碼:
`timescale 1ns / 1ps module tb_top(); //======================================================== //parameters parameter CLK_FREQ = 50.000;//ddr reference clock frequency, unit: MHz parameter CLK_PERIOD = 1000.0/CLK_FREQ; //unit: ns // parameter FREQ = 100_000_000 ; // parameter BAUDRATE = 115200 ; //======================================================= reg clk; // 50M reg rst_n ; reg [7:0] input_data; wire[16:0] output_data; //======================================================== GSR GSR(.GSRI(1'b1)); //============================================== //rst_n initial begin rst_n = 1'b0; input_data = 0; #200; rst_n = 1'b1; input_data = 8'd1;//1+(pi/4)=1 #200; input_data = 8'd4;//16+12=28 #200; input_data = 8'd6;//36+28=64 #200; #200; input_data = 8'd10;//100+78.5=178.5 #200; rst_n = 1'b0; #200; $stop; end //---------------------------------------------------- //ref clk initial begin clk = 1'b0; end always #(CLK_PERIOD/2.0) clk = ~clk; //================================================== //TX function_total inst_function_total (.CLK(clk), .RST(rst_n), .width(input_data), .area(output_data)); endmodule
模擬結果如下所示:
後面還有很多要看,但以理解原理和動手實踐為主。通過編程式碼和模擬,能夠感覺更真實。