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

     

     

        

        

      

    初入江湖,天下无敌;再学三年,寸步难行。
  • 相关阅读:
    Ansible 日常使用技巧
    Linux下科学计数法(e)转化为数字的方法 [shell中几种数字计算说明]
    业务日志清理脚本
    Kubernetes容器集群
    Kubernetes 之Pod学习
    数据结构之数组
    Java Class 文件中Method的存储
    理解Flink Transformation
    理解Java BlockingQueue
    理解Java FutureTask
  • 原文地址:https://www.cnblogs.com/huangwei0521/p/12459289.html
Copyright © 2020-2023  润新知