一 簡述算法
最近惡補基礎知識,借了<<Verilog傳奇>>,《基於FPGA的嵌入式圖像處理系統設計》和<<基千FPGA的數字圖像處理原理及應用>>這三本書。express
<<Verilog傳奇>>是關於Verilog基礎知識的,總共九章。因爲書籍內容太多沒時間看,故通常都是瞭解整本書的大體內容,遇到問題時能回憶起在那看過,再返回來仔細研究。後面兩本書是圖像處理的FPGA應用,有不少基本的圖像處理操做,如常見的直方圖技術,各類濾波,圖像分割等。重點是圖像處理算法的FPGA映射,還有圖像仿真驗證平臺。編程
(一)<<Verilog傳奇>>總結:編程語言
第一章用來介紹Verilog語言的基本知識, 包括髮展歷史、設計流芯片結構和可綜合性等。 這裏的重點是幫你們創建Yerilog語言與其餘學科之間的聯繫。幫助你們掌握設計數字邏輯系統須要考量的有關內容。ide
第二章,除了介紹IEEE有關Verilog語言的標準體系及非RTL級的設計以外,還重 點說明了常量、 變量及結構化模塊的內容, 是學習Verilog語言的基礎。函數
第3、 四章, 分別介紹了用assign和always定義的組合邏輯電路的描述方法。 其中包含各類運算符操做的寫法, 以及條件、 多選的描述。 最後, 以多路選擇器爲例, 說明 瞭如何分析一個組合邏輯電路系統。學習
第五章, 介紹了時序邏輯電路系統的Verilog語言描述方法, 包括D觸發器及D觸 發器鏈的寫法。 本章還介紹瞭如何拆分組合電路以適應系統操做時間要求的概念。 在此基礎上, 介紹了三種系統速度與面積平衡的方法: 並行化設計、流水線設計與時分複用 設計。測試
第六章, 結合工程實踐, 介紹了常常遇到的若干問題, 其中包括復位系統設計、 可變移位操做、 有限狀態機設計、 多時鐘系統處理及循環操做的處理。spa
第七章, 介紹了與[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
仿真結果以下所示:
後面還有不少要看,但以理解原理和動手實踐爲主。經過編代碼和仿真,可以感受更真實。