基於Modelsim的直方圖統計演算法模擬

  • 2020 年 3 月 10 日
  • 筆記

一、前言

    本篇主要針對牟新剛編著《基於FPGA的數字影像處理及應用》第六章第五節中直方圖統計相關類容進行總結,包括程式碼實現及

基於Modelsim的模擬。書讀百遍,其意自現。 2020-03-09 22:16:07

二、基於FPGA的直方圖演算法統計原理

  設計難點:

    (1)統計工作至少要等到當前影像“流過”之後才能完成。此限制決定了我們不可能對統計工作進行流水統計和輸出。

    (2)必須對前期的統計結果進行快取。

    (3)在下一次統計前需要將快取結果清零。

  在直方圖統計中,我們一般選擇片內雙口RAM作為快取存儲器。對於8位的深度圖來說,統計結果的數據量並不大,因此選擇片內

RAM。此外,一方面統計模組需要與其他時序進行配合,因此需提供雙邊讀寫介面;另一方面,統計過程中需要地址資訊,因此選擇

RAM形式的存儲器。接下來的工作就是確定雙口RAM的參數,主要包括數據位寬及地址位寬。假定輸入影像寬度為IW,高度為IH,數據

位寬度為DW。那麼待統計的像素總數為PixelTotal = IW * IH,像素灰度值的理論最大值為PixelMax=2DW-1。

  雙口RAM的統計地址輸入端為輸入像素值,很明顯,這個數據範圍為0~2DW-1,因此RAM的地址位寬最少為DW,即 AWDPRAM ≥ DW。

  雙口RAM的數據輸出端為輸入統計值,很明顯,這個數據範圍為0~PixelTotal,因此RAM的地址位寬最少為log2(PixelTotal)。即DWDPRAM ≥ log2(PixelTotal)。

  例如要對影像解析度為640 x 512位寬為8位的影像進行直方圖統計,則有

                AWDPRAM ≥ 8

           DWDPRAM ≥ log2(PixelTotal) = log2(640×512) ≈19。

  通常情況下會將兩個參數取為2的整次冪,即AWDPRAM = 8 DWDPRAM =32。

  直方圖統計步驟如下:

  (1)將當前統計值讀出,加1後重新寫入RAM。

  (2)重複以上步驟,直至當前影像統計完畢。

  (3)在下一副影像到來之前將結果讀出。

  (4)讀出之後對RAM內容進行清零。

  因此如下圖所示,要完成直方圖統計,需要至少設計三個電路:統計電路,讀出電路,讀出電路和清零電路。

  

  1.統計電路

  在實際的影像中,連續的像素點灰度值為相同值的情況非常常見,如果每來一個像素都對雙口RAM進行一次定址和寫操作,顯然降低了統計效率而提高了功耗。書中給出了一種優化的統計方法:採用一個相同灰度值計數器進行優化,

其中:

  (1)DPRAM:存放統計結果。分為A口和B口,A口負責統計結果寫入,不輸出。B口負責統計結果讀出和清零,無輸入。

  (2)CNT: 相同像素計數器。負責對連續相同灰度值的像素進行計數,複位值為1

  (3)ADD(+):統計值加法器。對當前統計值和新的統計值進行加法運算,重新寫入RAM。

  (4)B_ADDR MUX: B口地址mux,很明顯,B口在統計階段需要完成讀出前一個統計值和清零的分時操作。因此需要一個mux對讀出地址和清零地址進行選通。

  (5)reg:將輸入數據打兩拍以確保讀出的是當前的統計值。

  統計原理如下:

  當前灰度值的統計值由B口讀出,與相同灰度值計數器進行相加後重新寫入RAM。CNT會不斷檢測當前像素和前一個像素是否一致,若不一致,則重置為1,實現統計值加1目的;若一致,則計數器加1,直到不一致之後將一致的總數

寫入RAM,並在每一行影像的最後一個像素統一執行一次寫入操作(沒明白啥意思),這樣大大減少讀寫RAM操作。

  下面幾個關鍵訊號的設計電路來說明統計電路的工作原理。首先將輸入訊號din,輸入有效dvalid打兩拍,分別為din_r,din_r2及dvalid_r,dvalid_r2。

  (1)inc_en

   此訊號負責遞增計數器的遞增使能。當前待統計數據din_r2有效,且與前一個已經統計完成的數據din_r相同時,將遞增計數器加1。否則計數器會複位到1。

  (2)rst_cnt

   此訊號為遞增計數器的複位訊號。除了當前待統計灰度值與上一個統計過的灰度值不同的情況下會複位計數器,第一個有效數據到來時也會複位遞增計數器,為新的一輪統計工作做準備。

  (3)we_a

   此訊號為DPRAM寫入訊號,也是分為兩種情況:若當前待統計灰度值與之前待統計值不同。則直接寫入RAM。否則,就一直累加到數據無效時統一寫入RAM。

  (4)count_en

   此訊號為統計使能,很明顯,在統計階段此訊號需要一直保持有效,統計完成後(也即當前影像遍歷完畢),在讀出和清零階段,需要將此訊號失能,這是由於B口在此階段需要讀出和清零地址。

     此訊號的產生可以通過對影像進行行計數來實現,當到達一副影像的高度時,失能訊號。新的行同步到來時使能訊號。

  2.讀出電路設計

    首先書中統計讀出方法是順序讀出,即灰度值為0的統計值首先讀出,其次是灰度值為1的統計值,最後是灰度值為255的統計值輸出。

    讀出和清零操作並不一定是在統計完成之後立即進行的,可以根據需要由外部時序控制,輸入讀出請求。當然在統計階段,模組是不允許讀出的,此時讀出電路處於複位狀態。在讀出階段,需設計

讀出像素計數器對讀出時序進行控制。

    只有當計數完成,並且外部時序申請讀出時,輸出地址才會進行遞增。否則會被鉗位到0。當一次讀出完成之後此地址發生器複位,也就是count_en會重新使能,直到下一次統計完成。

    3.清零電路設計

   書中給出反相清零的方法,即在讀出時鐘的下一個時鐘進行清零。因此每個像素的統計數據輸出和清零操作均需佔用1個時鐘,奇數時鐘輸出,偶數時鐘清零

三、程式碼實現

  程式碼的部分參照書中的程式碼實現,並與前麵灰度影像生成程式碼進行整合編譯模擬。唯一需要特別注意的是在實例化DPRAM IP核時,輸出埠切記不要快取(添加暫存器輸出)。相應整理後的程式碼如下:

`timescale 1ps/1ps    //==============================================================================//  //FileName: histogram_2d.v  //Date: 2020-02-29  //==============================================================================//    module histogram_2d(      rst_n,      clk,      din_valid,            //輸入有效      din,                //輸入待統計的數據      dout,                //統計輸出      vsync,                //輸入場同步      dout_valid,            //輸出有效      int_flag,            //中斷輸出      rdyOutput,            //數據讀出請求      dout_clk            //數據輸出時鐘      );        //模組入口參數      parameter DW = 14;        //數據位寬      parameter IH = 512;        //影像高度      parameter IW = 640;        //影像寬度      parameter TW = 32;        //直方圖統計數據位寬        localparam TOTAL_CNT = IW * IH;        //像素總數      localparam HALF_WIDTH = (TW >> 1);    //將32位的數據位寬拆分為高低16位          //輸入輸出聲明      input rst_n;      input clk;      input din_valid;      input [DW-1:0] din;      input rdyOutput;        //output wire [HALF_WIDTH:0] dout;        output wire [TW-1:0] dout;        input vsync;      output reg dout_valid;      output reg int_flag;      output dout_clk;          //變數聲明      reg vsync_r;      reg dvalid_r;      reg dvalid_r2;      reg [DW-1:0] din_r;      reg [DW-1:0] din_r2;        wire hsync_fall;      wire hsync_rise;        reg [9:0] hsync_count;      reg count_en;      wire [DW-1:0] mux_addr_b;      wire [DW-1:0] mux_addr_b2;        wire [TW-1:0] q_a;      wire [TW-1:0] q_b;      reg [TW-1:0] counter;        wire [TW-1:0] count_value;      wire rst_cnt;            //統計計數器複位訊號      wire inc_en;            //遞增使能訊號        //DPRAM 訊號      wire we_a;      wire we_b;      wire we_b_l;      reg  we_b_h;        wire [DW-1:0] addr_a;      //中斷暫存器      reg int_r;      wire [DW-1:0] clr_addr;            //清零地址      reg [DW-1:0] clr_addr_r;      reg [DW:0] out_pixel;            //輸出計數        reg count_all;                    //統計完成訊號      //reg count_en_r;      reg count_en_r;        reg [TW-1:0] hist_cnt;            //直方圖統計累加暫存器      wire rstOutput;                    //讀出電路複位訊號        wire [TW-1:0] dataTmp2;      wire clr_flag;                    //全局清零        //將輸入數據打兩拍      always@(posedge clk or negedge rst_n)begin          if(((~(rst_n))) == 1'b1)          begin              vsync_r     <= #1 1'b0;              dvalid_r     <= #1 1'b0;              dvalid_r2     <= #1 1'b0;              din_r        <= #1 {DW{1'b0}};              din_r2        <= #1 {DW{1'b0}};          end          else          begin              vsync_r        <= #1 vsync;              dvalid_r    <= #1 din_valid;              dvalid_r2    <= #1 dvalid_r;              din_r        <= #1 din;              din_r2        <= #1 din_r;          end      end        //輸入行同步計數,確定統計的開始和結束時刻      assign #1 hsync_fall = dvalid_r & (~(din_valid));      assign #1 hsync_rise = (~(dvalid_r)) & din_valid;        always@(posedge clk or negedge rst_n)begin          if(((~(rst_n))) == 1'b1)              hsync_count <= #1 {10{1'b0}};          else          begin              if(vsync_r == 1'b1)                  hsync_count <= #1 {10{1'b0}};              else if(hsync_fall == 1'b1)                  hsync_count <= hsync_count + 10'b1;          end      end        //一幀影像結束後停止統計 下一幀影像到來時開始計數      always@(posedge clk or negedge rst_n)begin          if(((~(rst_n))) == 1'b1)              count_en <= #1 1'b0;          else          begin              if(hsync_count >= IH)                  count_en <= #1 1'b0;              else if(hsync_rise == 1'b1)                  count_en <= #1 1'b1;              else                  count_en <= #1 count_en;          end      end        assign mux_addr_b     = ((count_en == 1'b1)) ? din_r : clr_addr;      assign mux_addr_b2     = ((count_en == 1'b1)) ? din_r : clr_addr_r;        //統計遞增計數器      always@(posedge clk)begin          if(rst_cnt == 1'b1)              counter <= #1 {{TW-1{1'b0}},1'b1}; //複位值為1          else if(inc_en == 1'b1)              counter <= #1 counter + {{TW-1{1'b0}},1'b1};          else              counter <= #1 counter;      end        assign #1 rst_cnt = ((din_r != din_r2) | ((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0))) ? 1'b1 : 1'b0;      assign #1 inc_en = (((din_r == din_r2) & (dvalid_r2 == 1'b1))) ? 1'b1 : 1'b0;      assign #1 we_a = ((((din_r != din_r2) & (dvalid_r2 == 1'b1)) |((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0)))) ? 1'b1 : 1'b0;      assign #1 count_value = ((count_en == 1'b1)) ? counter+q_b : {TW{1'b0}};      assign #1 addr_a = din_r2;          //直方圖存儲器 分高16位和低16位分別存儲      hist_buffer dpram_bin_l(          .address_a(addr_a),                        //輸入地址為像素灰度值          .address_b(mux_addr_b),                    //讀出和清零地址          .clock(clk),                            //同步時鐘          .data_a(count_value[HALF_WIDTH-1:0]),    //當前計數值          .data_b({HALF_WIDTH{1'b0}}),            //清零數據          .wren_a(we_a),          .wren_b(we_b_l),          .q_a(q_a[HALF_WIDTH-1:0]),          .q_b(q_b[HALF_WIDTH-1:0])      );          hist_buffer dpram_bin_h(          .address_a(addr_a),          .address_b(mux_addr_b2),          .clock(clk),          .data_a(count_value[TW-1:HALF_WIDTH]),          .data_b({HALF_WIDTH{1'b0}}),          .wren_a(we_a),          .wren_b(we_b_h),          .q_a(q_a[TW-1:HALF_WIDTH]),          .q_b(q_b[TW-1:HALF_WIDTH])      );        always@(posedge clk or negedge rst_n)begin          if(((~(rst_n))) == 1'b1)              count_en_r <= #1 1'b0;          else              count_en_r <= #1 count_en;      end        //讀出電路邏輯,計數時不能輸出,讀出請求時才輸出      assign rstOutput = count_en_r | (~(rdyOutput));        //輸出像素計數      always@(posedge clk)begin          if(rstOutput == 1'b1)              out_pixel <= {DW+1{1'b0}};          else begin              if((~count_all) == 1'b1)              begin                  if(out_pixel == (((2 ** (DW + 1)) - 1)))                      out_pixel <= #1 {DW+1{1'b0}}; //輸出完畢                  else                      out_pixel <= #1 out_pixel + 1'b1;              end          end      end        //統計結束訊號      always@(posedge clk)begin          //count_all_r <= (~rstOutput);          we_b_h <= we_b_l;          clr_addr_r <= clr_addr;          if(out_pixel == (((2 ** (DW + 1)) - 1)))              count_all <= #1 1'b1;          else if(count_en == 1'b1)              count_all <= #1 1'b0;      end        //全局清零訊號      assign clr_flag = vsync;        //中斷輸出 訊號讀出操作完成      always@(posedge clk or negedge rst_n)begin          if((~(rst_n)) == 1'b1)          begin              int_flag     <= 1'b1;              int_r         <= 1'b1;          end          else          begin              int_flag <= #1 int_r;              if(clr_flag == 1'b1)                  int_r <= #1 1'b1;              else if(out_pixel >= (((2 ** (DW + 1)) - 1)))                  int_r <= #1 1'b0;          end      end        assign we_b_l = (((out_pixel[0] == 1'b1) & (count_all == 1'b0))) ? 1'b1 : 1'b0;        //清零地址,與讀出地址反相      assign clr_addr = out_pixel[DW:1];        wire dout_valid_temp;        wire [HALF_WIDTH-1:0] dout_temp;        always@(posedge clk or negedge rst_n)begin          if((~(rst_n)) == 1'b1)          begin              //dout <= {HALF_WIDTH{1'b0}};              dout_valid <= 1'b0;          end          else          begin              //dout <= #1 dout_temp;              dout_valid <= #1 dout_valid_temp;          end      end        assign dout_temp = (we_b_l == 1'b1) ? q_b[HALF_WIDTH-1:0] : q_b[TW-1:HALF_WIDTH];        assign dout_valid_temp = we_b_h | we_b_l; //輸出使能        assign dout_clk = (dout_valid) ? we_b_h : 1'b0;        assign dout = q_b;    endmodule

 

 頂層程式碼設計如下:

  1 `timescale 1ps/1ps    2    3 //===========================================================================================//    4 //FileName: histogram.v    5 //Date: 2020-03-02    6 //===========================================================================================//    7    8    9 module histogram(   10     RSTn,                //全局複位   11     CLOCK,                //系統時鐘   12   13     IMG_CLK,            //像素時鐘   14     IMG_DVD,            //像素值   15     IMG_DVSYN,            //輸入場訊號   16     IMG_DHSYN,            //輸入數據有效訊號   17     HIST_DAT,            //輸出直方圖統計數據   18     HIST_VALID,            //輸出直方圖統計有效   19     HIST_RDY,            //數據讀出請求   20     HIST_INT,            //數據中斷輸出   21     HIST_CLK            //數據輸出時鐘   22   23 );   24   25     /*image parameter*/   26     parameter iw             = 640;        //image width   27     parameter ih            = 512;        //image height   28     parameter trig_value    = 400;         //250   29     parameter tw            = 32;        //直方圖統計數據位寬   30   31     localparam half_width    = (tw >> 1); //將32位的數據位寬拆分為高低16位   32   33     /*data width*/   34     parameter dvd_dw     = 8;    //image source data width   35     parameter dvd_chn    = 3;    //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr   36     parameter local_dw    = dvd_dw * dvd_chn;    //local algorithem process data width   37     parameter cmd_dw    = dvd_dw * dvd_chn;    //local algorithem process data width   38   39     //Port Declared   40     input RSTn;   41     input CLOCK;   42     input IMG_CLK;   43     input [dvd_dw-1:0] IMG_DVD;   44     input IMG_DVSYN;   45     input IMG_DHSYN;   46     output [tw-1:0] HIST_DAT;   47     output HIST_VALID;   48     input HIST_RDY;   49     output HIST_INT;   50     output HIST_CLK;   51   52     //Variable Declared   53     wire GRAY_CLK;   54     wire GRAY_VSYNC;   55     wire GRAY_DVALID;   56     wire [dvd_dw-1:0] Y_DAT;   57     wire [dvd_dw-1:0] Cb_DAT;   58     wire [dvd_dw-1:0] Cr_DAT;   59   60     wire [local_dw-1:0] RGB_DAT;   61     wire RGB_DVALID;   62     wire RGB_VSYNC;   63   64     video_cap video_cap_inst(   65             .reset_l(RSTn),                //非同步複位訊號   66             .DVD(IMG_DVD),                //輸入影片流   67             .DVSYN(IMG_DVSYN),            //輸入場同步訊號   68             .DHSYN(IMG_DHSYN),            //輸入行同步   69             .DVCLK(IMG_CLK),            //輸入DV時鐘   70             .cap_dat(RGB_DAT),            //輸出RGB通道像素流,24位   71             .cap_dvalid(RGB_DVALID),    //輸出數據有效   72             .cap_vsync(RGB_VSYNC),        //輸出場同步   73             .cap_clk(CLOCK),            //本地邏輯時鐘   74             .img_en(),   75             .cmd_rdy(),                    //命令行準備好,代表可以讀取   76             .cmd_rdat(),                //命令行數據輸出   77             .cmd_rdreq()                //命令行讀取請求   78         );   79   80     defparam video_cap_inst.DW_DVD         = dvd_dw;   81     defparam video_cap_inst.DW_LOCAL     = local_dw;   82     defparam video_cap_inst.DW_CMD         = cmd_dw;   83     defparam video_cap_inst.DVD_CHN     = dvd_chn;   84     defparam video_cap_inst.TRIG_VALUE  = trig_value;   85     defparam video_cap_inst.IW             = iw;   86     defparam video_cap_inst.IH             = ih;   87   88     RGB2YCrCb RGB2YCrCb_Inst(   89             .RESET(RSTn),                //非同步複位訊號   90   91             .RGB_CLK(CLOCK),            //輸入像素時鐘   92             .RGB_VSYNC(RGB_VSYNC),        //輸入場同步訊號   93             .RGB_DVALID(RGB_DVALID),    //輸入數據有訊號   94             .RGB_DAT(RGB_DAT),            //輸入RGB通道像素流,24位   95   96             .YCbCr_CLK(GRAY_CLK),        //輸出像素時鐘   97             .YCbCr_VSYNC(GRAY_VSYNC),    //輸出場同步訊號   98             .YCbCr_DVALID(GRAY_DVALID),    //輸出數據有效訊號   99             .Y_DAT(Y_DAT),                //輸出Y分量  100             .Cb_DAT(Cb_DAT),            //輸出Cb分量  101             .Cr_DAT(Cr_DAT)                //輸出Cr分量  102         );  103  104     defparam RGB2YCrCb_Inst.RGB_DW = local_dw;  105     defparam RGB2YCrCb_Inst.YCbCr_DW = dvd_dw;  106  107     histogram_2d histogram_2d_inst(  108         .rst_n(RSTn),  109         .clk(GRAY_CLK),  110         .din_valid(GRAY_DVALID),        //輸入有效  111         .din(Y_DAT),                    //輸入待統計的數據  112         .dout(HIST_DAT),                //統計輸出  113         .vsync(GRAY_VSYNC),                //輸入場同步  114         .dout_valid(HIST_VALID),        //輸出有效  115         .int_flag(HIST_INT),            //中斷輸出  116         .rdyOutput(HIST_RDY),            //數據讀出請求  117         .dout_clk(HIST_CLK)  118     );  119  120     defparam histogram_2d_inst.DW = dvd_dw;  121     defparam histogram_2d_inst.IH = ih;  122     defparam histogram_2d_inst.IW = iw;  123     defparam histogram_2d_inst.TW = tw;  124  125 endmodule

  用於測試模擬的文件如下:

  1 `timescale 1ps/1ps    2    3 module histogram_tb;    4    5    6     /*image para*/    7     parameter iw             = 640;        //image width    8     parameter ih            = 512;        //image height    9     parameter trig_value    = 400;     //250   10   11     /*video parameter*/   12     parameter h_total        = 2000;   13     parameter v_total        = 600;   14     parameter sync_b        = 5;   15     parameter sync_e        = 55;   16     parameter vld_b            = 65;   17   18     parameter clk_freq         = 72;   19   20     /*data width*/   21     parameter dvd_dw     = 8;    //image source data width   22     parameter dvd_chn    = 3;    //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr   23     parameter local_dw    = dvd_dw * dvd_chn;    //local algorithem process data width   24     parameter cmd_dw    = dvd_dw * dvd_chn;    //local algorithem process data width   25   26   27     parameter hist_dw    = 32 >> 1;   28   29     /*test module enable*/   30     parameter cap_en    = 1;   31   32     /*signal group*/   33     reg pixel_clk = 1'b0;   34     reg reset_l;   35     reg [3:0] src_sel;   36   37   38     /*input dv group*/   39     wire dv_clk;   40     wire dvsyn;   41     wire dhsyn;   42     wire [dvd_dw-1:0] dvd;   43   44     wire [31:0] HIST_DAT;   45     wire HIST_VALID;   46     wire HIST_INT;   47     wire HIST_CLK;   48   49     /*dvd source data generated for simulation*/   50     image_src //#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)   51     u1(   52         .clk(pixel_clk),   53         .reset_l(reset_l),   54         .src_sel(src_sel),   55         .test_data(dvd),   56         .test_dvalid(dhsyn),   57         .test_vsync(dvsyn),   58         .clk_out(dv_clk)   59     );   60   61     defparam u1.iw = iw*dvd_chn;   62     defparam u1.ih = ih + 1;   63     defparam u1.dw = dvd_dw;   64     defparam u1.h_total = h_total;   65     defparam u1.v_total = v_total;   66     defparam u1.sync_b = sync_b;   67     defparam u1.sync_e = sync_e;   68     defparam u1.vld_b = vld_b;   69   70   71     /*local clk: also clk of all local modules*/   72     reg cap_clk = 1'b0;   73   74     /*video capture: capture image src and transfer it into local timing*/   75     histogram histogram_inst(   76         .RSTn(reset_l),                    //全局複位   77         .CLOCK(cap_clk),                //系統時鐘   78   79         .IMG_CLK(pixel_clk),            //像素時鐘   80         .IMG_DVD(dvd),                    //像素值   81         .IMG_DVSYN(dvsyn),                //輸入場訊號   82         .IMG_DHSYN(dhsyn),                //輸入數據有效訊號   83         .HIST_DAT(HIST_DAT),            //輸出直方圖統計數據   84         .HIST_VALID(HIST_VALID),        //輸出直方圖統計有效   85         .HIST_RDY(1'b1),                //數據讀出請求   86         .HIST_INT(HIST_INT),            //數據中斷輸出   87         .HIST_CLK(HIST_CLK)                //數據輸出時鐘   88     );   89   90     initial   91     begin: init   92         reset_l <= 1'b1;   93         src_sel <= 4'b0000;   94         #(100);            //reset the system   95         reset_l <= 1'b0;   96         #(100);   97         reset_l <= 1'b1;   98     end   99  100     //dv_clk generate  101     always@(reset_l or pixel_clk)begin  102         if((~(reset_l)) == 1'b1)  103             pixel_clk <= 1'b0;  104         else  105         begin  106             if(clk_freq == 48)            //48MHz  107                 pixel_clk <= #10417 (~(pixel_clk));  108  109             else if(clk_freq == 51.84)    //51.84MHz  110                 pixel_clk <= #9645 (~(pixel_clk));  111  112             else if(clk_freq == 72)        //72MHz  113                 pixel_clk <= #6944 (~(pixel_clk));  114         end  115     end  116  117     //cap_clk generate: 25MHz  118     always@(reset_l or cap_clk)begin  119         if((~(reset_l)) == 1'b1)  120             cap_clk <= 1'b0;  121         else  122             cap_clk <= #20000 (~(cap_clk));  123     end  124  125     wire clk;  126     assign clk = ~cap_clk;  127  128     generate  129     if(cap_en != 0) begin :capture_operation  130         integer fid, cnt_cap=0;  131  132         always@(posedge clk or negedge reset_l)begin  133             if(((~(reset_l))) == 1'b1)  134                 cnt_cap = 0;  135             else  136                 begin  137                     if(HIST_VALID == 1'b1 && HIST_CLK == 1'b0)  138                     begin  139                         fid = $fopen("E:/Modelsim/histogram_2d/sim/hist_result.txt","r+");  140                         $fseek(fid,cnt_cap,0);  141                         $fdisplay(fid,"%8x",HIST_DAT);  142                         $fclose(fid);  143                         cnt_cap<=cnt_cap+10;  144                     end  145                 end  146         end  147     end  148     endgenerate  149  150 endmodule  151     

 用於modelsim模擬的.do文件如下:

 

  1 #切換至工程目錄    2 cd E:/Modelsim/histogram_2d/sim    3    4 #打開工程    5 project open E:/Modelsim/histogram_2d/sim/histogram_2d    6    7 #添加指定設計文件    8 project addfile E:/Modelsim/histogram_2d/sim/histogram_tb.v    9 project addfile E:/Modelsim/histogram_2d/src/cross_clock_fifo.v   10 project addfile E:/Modelsim/histogram_2d/src/hist_buffer.v   11 project addfile E:/Modelsim/histogram_2d/src/histogram.v   12 project addfile E:/Modelsim/histogram_2d/src/histogram_2d.v   13 project addfile E:/Modelsim/histogram_2d/src/image_src.v   14 project addfile E:/Modelsim/histogram_2d/src/line_buffer_new.v   15 project addfile E:/Modelsim/histogram_2d/src/rgb2gray.v   16 project addfile E:/Modelsim/histogram_2d/src/RGB2YCbCr.v   17 project addfile E:/Modelsim/histogram_2d/src/video_cap.v   18   19 #編譯工程內的所有文件   20 project compileall   21   22 #模擬work庫下面的histogram_tb實例,同時調用altera_lib庫,不進行任何優化   23 vsim -t 1ps -novopt -L altera_lib work.histogram_tb   24   25 #添加輸入灰度影像訊號   26 add wave -divider GRAYImg   27   28 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/clk   29   30 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/vsync   31   32 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din_valid   33   34 add wave -radix hex -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din   35   36 #輸入訊號快取   37 add wave -divider Data_Cache   38   39 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/vsync_r   40   41 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dvalid_r   42   43 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dvalid_r2   44   45 add wave -radix hex -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din_r   46   47 add wave -radix hex -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/din_r2   48   49 #統計開始、結束;統計使能   50 add wave -divider Statistics_Signal   51   52 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_rise   53   54 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_fall   55   56 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_count   57   58 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/hsync_fall   59   60 #添加雙口RAM讀寫控制時序   61 add wave -divider HIST_BUFFER_WR   62   63 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/rst_cnt   64   65 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/inc_en   66   67 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/we_a   68   69 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/count_value   70   71 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/addr_a   72   73 #Buff數據讀出控制時序   74 add wave -divider HIST_BUFFER_RD   75   76 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/rstOutput   77   78 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/out_pixel   79   80 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/count_all   81   82 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/clr_flag   83   84 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/int_flag   85   86 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/int_r   87   88 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/we_b_l   89   90 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/we_b_h   91   92 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/mux_addr_b   93   94 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/mux_addr_b2   95   96 #添加統計輸出訊號   97 add wave -divider Statistics_Out   98   99 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dout_valid  100  101 add wave -radix binary -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dout_clk  102  103 add wave -radix unsigned -position insertpoint sim:/histogram_tb/histogram_inst/histogram_2d_inst/dout  104  105 #複位  106 restart  107  108 #取消警告  109 set StdArithNoWarnings 1  110  111 #開始  112 run 16.1ms

  用於數據處理的Maltab文件如下:

 1 clc;   2 clear;   3   4 fid1 = fopen('hist_result.txt','r'); %FPGA轉換灰度影像   5 data1 = fscanf(fid1,'%8x');   6 fclose(fid1);   7   8 RGBImg = imread('lena_512x512.jpg'); %rgb原始影像   9 RGBImg = imresize(RGBImg,[512 640]);  10  11 GRAYImg = rgb2gray(RGBImg); %Matlab變換灰度影像  12  13 fid2 = fopen('gray_image.txt','r'); %FPGA轉換灰度影像  14 data2 = fscanf(fid2,'%2x');  15 data2 = uint8(data2);  16 gray_data = reshape(data2,640,512);  17 gray_data = gray_data';  18 fclose(fid2);  19  20 figure(1);  21 subplot(2,2,1);  22 imshow(GRAYImg);  23 title('Matlab變換灰度影像');  24 subplot(2,2,2);  25 imhist(GRAYImg);  26 title('Matlab統計直方圖');  27 subplot(2,2,3);  28 imshow(gray_data);  29 title('FPGA變換灰度影像');  30 subplot(2,2,4);  31 bar(data1);  32 title('FPGA統計直方圖');

 

四、模擬結果

(1)輸入訊號快取時序:

 
 (2)統計數據寫入控制時序:

(3)統計數據讀出控制時序:

(4)FPGA直方圖統計結果與Maltab直方圖統計結果對比: