【FPGA图像处理实战】- 图像行缓存设计实现方式一(FIFO)

本文转载自:FPGA入门到精通微信公众号

图像处理中稍复杂点的算法,就需要行缓存,比如实现3*3窗口、6*6窗口的数据计算,行缓存的设计能力也是FPGA图像处理开发的基础技能。

本文将详细介绍使用FIFO来实现图像行缓存的设计,包括关键逻辑分析,verilog源代码实现分享。

一、行缓存功能的设计框架

图像数据一般都是按照从左到右,从上到下,一行行数据的方式发送传输的。

这里以3*3窗口为例,需要同时输出三行(上一行、当前行、下一行),如下图所示。

网上通常看见的行缓存实现,3*3窗口需要3个行缓存,这里我分享更加节省资源的方式,实际只需通过2个行缓存即可实现。

二、行缓存设计的关键思想

每一个行缓存fifo,都是在缓存完一行数据后,如果有新的数据输入,则同步输出一个数据出去。

这样当新的一行数据到来时,第1个行缓存刚好输出上一行数据,第2个行缓存输出当前行数据,刚到来的新的一行数据直接当做下一行数据输出即可。

有些特殊位置数据需要处理:以当前行为图像边界数据时,上一行和下一行数据实际不存在,需要直接给0或者复制当前行来处理。

这里以以3*3窗口为例,边界行处理逻辑如下:

  • 第1个行缓存,是整个画面的第1行时, 上一行数据不存在,复制第1个行缓存或者直接为0输出。
  • 第1个行缓存,是整个画面的最后第1行时, 下一行数据不存在,复制第1个行缓存输出。
  • 三、FPGA实现代码

    1、行缓存子模块实现

    (1)FIFO IP 参数配置

    Native Ports配置页:Read Mode 选择“First Word Fall Through”,设定合理的数据宽度和深度。
    Data Counts配置页:选中“Data Count”


    (2)行缓存子模块

    通过对写入fifo的数据计数或利用fifo自带的data_count计数,在缓存完一行数据后,每输入一个数据,就输出一个数据。

    module line_buffer(
    input wire clk,
    input wire reset,

    input wire [10:0] img_width,

    input wire valid_i,
    input wire [23:0] data_i,

    output wire valid_o,
    output wire [23:0] data_o
    );

    //变量声明
    reg [10:0] wr_data_cnt;
    wire rd_en;
    wire [11:0] fifo_data_count_w;

    //写入数据计数
    always@(posedge clk or posedge reset) begin
    if(reset) begin
    wr_data_cnt <= 0;
    end else begin
    wr_data_cnt <= valid_i&&(wr_data_cnt < img_width) ? (wr_data_cnt + 1'b1) : wr_data_cnt;
    end
    end

    //assign rd_en = valid_i&&(wr_data_cnt == img_width) ? 1'b1 : 1'b0;
    //等价于
    assign rd_en = valid_i&&(fifo_data_count_w == img_width) ? 1'b1 : 1'b0;
    assign valid_o = rd_en;

    fifo_line_buffer u_fifo_line_buffer (
    .clk(clk), // input wire clk
    .srst(reset), // input wire srst
    .din(data_i), // input wire [23 : 0] din
    .wr_en(valid_i), // input wire wr_en
    .rd_en(rd_en), // input wire rd_en
    .dout(data_o), // output wire [23 : 0] dout
    .full(), // output wire full
    .empty(), // output wire empty
    .data_count(fifo_data_count_w) // output wire [11 : 0] data_count
    );

    endmodule

    2、3行缓存同步输出模块

    (1)调用行缓存模块

    连线逻辑与第1节的示意图一致,使用generate语句复制调用模块。

    genvar i;
    integer j;
    wire [0:0] valid [0:N-1];
    wire [23:0] data [0:N-1];

    assign valid[0] = valid_i;
    assign data[0] = img_data_i;

    //行缓存模块, 只需要缓存N-1个fifo即可
    generate for(i=1;i begin:lb

    line_buffer u_line_buffer(
    .clk ( clk ),
    .reset ( reset ),
    .img_width ( img_width ),
    .valid_i ( valid[i-1] ),
    .data_i ( data[i-1] ),
    .valid_o ( valid[i] ),
    .data_o ( data[i] )
    );
    end
    endgenerate

    (2)并行输出3行数据

    直接将第1个缓存的输出有效信号作为实际的输出信号即可。

    reg [10:0] out_data_cnt;
    reg [9:0] out_line_cnt;
    reg ch_valid;
    reg [23:0] ch_data [0:N-1];

    always@(posedge clk or posedge reset) begin
    if(reset) begin
    for(j=0;j<3;j=j+1)
    ch_data[j] <= 0;
    end else if(valid[N-2]) begin
    ch_data[1] <= data[1];
    if(out_line_cnt == 0) begin
    ch_data[2] <= 0;
    ch_data[0] <= data[0];
    end else if(out_line_cnt == img_height -1) begin
    ch_data[2] <= data[2];
    ch_data[0] <= 0;
    end else begin
    ch_data[2] <= data[2];
    ch_data[0] <= data[0];
    end
    end
    end

    always@(posedge clk or posedge reset) begin
    if(reset) begin
    ch_valid <= 0;
    out_data_cnt <= 0;
    out_line_cnt <= 0;
    end else begin
    ch_valid <= valid[N-2];
    out_data_cnt <= valid[N-2] ? ((out_data_cnt == img_width - 1) ? 0 : out_data_cnt + 1) : out_data_cnt;
    out_line_cnt <= valid[N-2]&&(out_data_cnt == img_width - 1) ? ((out_line_cnt == img_height - 1) ? 0 : out_line_cnt + 1) : out_line_cnt;
    end
    end

    3、仿真测试

    如果需要工程源码,想要掌握更多的FPGA图像处理算法,学习FPGA图像算法的实现。

    请阅读下面这篇文章:

    FPGA入门进阶真的难吗?看这里,少走弯路,少踩坑

    FPGA图像处理入门基础系列文章汇总

    最新文章

    最新文章