文章目录
- 一、前言
- 二、FPGA实现三行缓存的架构
- 三、Verilog代码实现
- 四、仿真验证
- 五、输入图像数据进行仿真验证
一、前言
在 FPGA 做图像处理时,行缓存是一个非常重要的一个步骤,因为图像输入还有输出都是一行一行进行的,即处理完一行后再处理下一行。行缓存可以存储当前行和前一行的数据以及多行的数据,使得在处理当前行时能够方便地访问周围像素。许多图像处理的算法都需要几行的图像数据进行处理,因此行缓存是非常重要的,本文实现三行缓存,多行缓存的思想也是一致的。
二、FPGA实现三行缓存的架构
由于图像数据一般都是从上到下从左到右一个一个输入进来,因此我们优先考虑使用FIFO,先进先出。按照一般想法,我们只需要三个FIFO,每个FIFO存储一行数据即可实现三行缓存,这里可以节省资源只使用两个FIFO实现,具体实现框框架如下:
开始时,图像数据的第0行写入到FIFO1中,图像数据的第1行写入到FIFO2中。
当第2行数据到来时写入到FIFO2中,同时输出写入的数据作为第二行;同时读出FIFO2中的数据写入到FIFO1中,并输出作为第一行,同时读出FIFO1中的数据输出作为第0行。
同理,当第三行数据来临时写入到FIFO2中,同时输出写入数据作为第二行,再同时读出FIFO2中的数据写入到FIFO1中并输出作为第一行,同时读出FIFO2中的数据输出作为第0行。后面的行以此类推。
三、Verilog代码实现
先看输入接口,输入为像素数据和有效信号,输出为三行数据以及有效信号。
input sys_clk ,
input sys_rst ,
input [23:0] i_img_data ,
input i_img_data_valid ,
output [23:0] o_img_data_1line ,
output [23:0] o_img_data_2line ,
output [23:0] o_img_data_3line ,
output o_img_data_valid
再次例化两个FIFO,位宽就为一个像素位宽,深度为一行中最多的像素数量。
img_line_buffer_fifo u0_img_line_buffer_fifo (
.clk (sys_clk ), // input wire clk
.srst (sys_rst ), // input wire srst
.din (fifo1_wr_data ), // input wire [23 : 0] din
.wr_en(fifo1_wr_en ), // input wire wr_en
.rd_en(rd_en ), // input wire rd_en
.dout (fifo1_q ), // output wire [23 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
img_line_buffer_fifo u1_img_line_buffer_fifo (
.clk (sys_clk ), // input wire clk
.srst (sys_rst ), // input wire srst
.din (fifo2_wr_data ), // input wire [23 : 0] din
.wr_en(fifo2_wr_en ), // input wire wr_en
.rd_en(rd_en ), // input wire rd_en
.dout (fifo2_q ), // output wire [23 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
然后根据架构图编写出剩下的代码,编写仿真代码。
四、仿真验证
仿真我们先设置图像宽度为50*50,这样仿真可以跑快一点,然后写入数据流为每次都是0-49循环。就像一幅图像的第0行数据是0-49,第1行的数据也是0-49,每一行的数据都是0-49。按照想法,我们每次输出的数据就是前三行的像素,也就是3行的 0-49数据,仿真代码如下:
`timescale 1ns / 1ps
module tb_img_3line_buffer();
reg sys_clk ;
reg sys_rst ;
reg i_img_data_valid ;
reg [23:0] i_img_data ;
reg [12:0] cnt ;
wire [23:0] o_img_data_1line ;
wire [23:0] o_img_data_2line ;
wire [23:0] o_img_data_3line ;
wire o_img_data_valid ;
initial begin
sys_clk =0;
sys_rst = 1;
i_img_data_valid = 0;
i_img_data = 'd0;
#200;
sys_rst = 0;
end
always #5 sys_clk = ~sys_clk;
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
i_img_data_valid <= 1'b0;
else if(cnt == 49)
i_img_data_valid <= 1'b0;
else
i_img_data_valid <= 1'b1;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
cnt <= 'd0;
else if(cnt == 49)
cnt <= 'd0;
else if(i_img_data_valid == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
i_img_data <= 'd0;
else if(i_img_data == 49)
i_img_data <= 'd0;
else if(i_img_data_valid == 1'b1)
i_img_data <= i_img_data + 1'b1;
else
i_img_data <= i_img_data;
end
img_3line_buffer#(
.IMG_WIDTH ( 50 ),
.IMG_HEIGHT ( 50 )
)u_img_3line_buffer(
.sys_clk ( sys_clk ),
.sys_rst ( sys_rst ),
.i_img_data ( i_img_data ),
.i_img_data_valid ( i_img_data_valid ),
.o_img_data_1line ( o_img_data_1line ),
.o_img_data_2line ( o_img_data_2line ),
.o_img_data_3line ( o_img_data_3line ),
.o_img_data_valid ( o_img_data_valid )
);
endmodule
运行仿真
可以看到写入的每一行数据都是0-49,写入两行后,开始输出数据。
我们可以看到输出的三行数据都是0-49的数据。符合预期。我们修改一下仿真代码,写入2500个数据,对应50*50的图像大小,数据为0-2499,这样第0行的数据就是0-49,第1行的数据就是50-99,第2行的数据就是100-149,第3行的数据就是150-199。输出的数据就应该是(0,50,100),(1,51,101)以此类推,仿真代码如下:
`timescale 1ns / 1ps
module tb_img_3line_buffer();
reg sys_clk ;
reg sys_rst ;
reg i_img_data_valid ;
reg [23:0] i_img_data ;
reg [12:0] cnt ;
wire [23:0] o_img_data_1line ;
wire [23:0] o_img_data_2line ;
wire [23:0] o_img_data_3line ;
wire o_img_data_valid ;
initial begin
sys_clk =0;
sys_rst = 1;
i_img_data_valid = 0;
i_img_data = 'd0;
#200;
sys_rst = 0;
end
always #5 sys_clk = ~sys_clk;
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
i_img_data_valid <= 1'b0;
else if(cnt == 2499)
i_img_data_valid <= 1'b0;
else
i_img_data_valid <= 1'b1;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
cnt <= 'd0;
else if(cnt == 2499)
cnt <= 'd0;
else if(i_img_data_valid == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
i_img_data <= 'd0;
else if(i_img_data == 2499)
i_img_data <= 'd0;
else if(i_img_data_valid == 1'b1)
i_img_data <= i_img_data + 1'b1;
else
i_img_data <= i_img_data;
end
img_3line_buffer#(
.IMG_WIDTH ( 50 ),
.IMG_HEIGHT ( 50 )
)u_img_3line_buffer(
.sys_clk ( sys_clk ),
.sys_rst ( sys_rst ),
.i_img_data ( i_img_data ),
.i_img_data_valid ( i_img_data_valid ),
.o_img_data_1line ( o_img_data_1line ),
.o_img_data_2line ( o_img_data_2line ),
.o_img_data_3line ( o_img_data_3line ),
.o_img_data_valid ( o_img_data_valid )
);
endmodule
运行仿真
验证完成和预期一致,后续一些图像处理算法需要用到这个行缓存。
五、输入图像数据进行仿真验证
现在我们在仿真中输入一张图片,然后通过三行缓存输出,每次只取出第一行的数据写入到新的图片中:
可以看出输出的图像和输入图像一模一样,文件大小也是一模一样,因此三行缓存是没问题的。