一、前言
本篇主要针对牟新刚编著《基于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直方图统计结果对比: