基于Modelsim的直方图统计算法仿真

一、前言

    本篇主要针对牟新刚编著《基于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(640x512) ≈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直方图统计结果对比:

 

 

 

    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

 

  

posted @ 2020-03-10 22:29  TheSkyIsMine  阅读(...)  评论(...编辑  收藏