基于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直方图统计结果对比: