前言
FIFO(先进先出)队列在图像处理中的应用非常广泛,特别是在需要处理实时数据流和保证数据顺序的场景中。以下是一些具体应用实例:在实时视频流处理中,FIFO队列用于缓存图像帧。这样可以确保图像数据按照捕获顺序被处理,避免因处理延迟导致的图像丢失或混乱。例如,视频监控系统会将捕获到的视频帧存储在FIFO队列中,然后按顺序进行处理和分析。在图像缓冲区管理中,FIFO队列帮助维护图像数据的顺序。这对于处理高分辨率图像或多个图像流的应用非常重要。图像处理系统会将图像数据推送到FIFO队列中,并从中读取数据进行进一步的处理,如滤镜应用或特征提取。在流媒体应用中,例如直播流或视频会议,FIFO队列用于缓存和传输数据包。这样可以保证数据按照传输顺序到达接收端,并且能够有效处理网络抖动或丢包问题。在某些图像处理任务中,尤其是需要批量处理的场景,FIFO队列可以用来管理待处理的图像数据。比如,图像增强或图像拼接任务中,FIFO队列可以保持数据的顺序,并在处理过程中保证一致性。在需要从多个传感器获取数据的系统中,FIFO队列能够处理传感器数据的流入。图像传感器的数据流可以使用FIFO队列缓存,确保数据的顺序和时效性,以便进行实时分析或处理。FIFO队列在图像处理中的主要作用是维护数据的顺序性和实时性,特别是在处理需要高吞吐量和低延迟的数据流时。
正文
一、FIFO求和实验
1.项目需求
电脑模拟产生数据求和矩阵,(5,4)使用RS232将数据发送给FPGA,FPGA通过FIFO进行求和运算,(3行求和),将数据求和结果通过RS232传回电脑。
2.技术介绍
现有一5行4列数据矩阵
FIFO求和,对该矩阵数据,N = 5,M = 4,使X = 3进行列求和运算,得到S(3,4)新矩阵,则S(0,0)=D(0,0)+D(1,0)+D(2,0),S(0,1)= D(0,1)+D(1,1)+D(2,1),S(0,2)=D(0,2)+D(1,2)+D(2,2),S(0,3)=SD(0,3)+D(1,3)+D(2,3),S(1,0)=D(1,0)+D(2,0)+D(3,0)...S(2,0)=D(2,0)+D(3,0)+D(4,0)。(n*m)矩阵转换后变为((n-(x-1)*m))矩阵。
3.顶层架构
4.端口描述
clk | 时钟信号 |
rst_n | 复位信号 |
rx | 传输到FPGA的数据 |
tx | 传输到PC的数据 |
二、代码验证
数据接收
module uart_rx(
input clk ,
input rst_n ,
input rx ,
output reg[7:0]po_data , //接收到的数据
output reg po_flag //数据输出有效
);
parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率
parameter cnt_max =clk_shuj/uart_btl;
reg reg1,reg2,reg3 ;//打排延迟周期,消除亚稳态
reg flag ;//uart工作信号(uart工作时在工作状态切换后产生一个时钟周期高电平)
reg en ;//uart工作使能标志信号(uart工作时在工作状态切换后的下一个时钟周期开始一直拉高,直到检测到停止信号)
reg [15:0] cnt ;//每比特数据持续时钟周期计数器
reg [3 :0] bit_cnt ;//数据个数计数器
reg bit_flag ;//每bit数据接收有效信号
reg [7 :0] rx_data ;//存储输入数据
reg rx_flag ;//输入数据并位结束信号
always@(posedge clk,negedge rst_n)//数据打排1
begin
if(rst_n == 0)
reg1 <= 1'b1;
else
reg1 <= rx;
end
always@(posedge clk,negedge rst_n)//数据打排2
begin
if(rst_n == 0)
reg2 <= 1'b1;
else
reg2 <= reg1;
end
always@(posedge clk,negedge rst_n)//数据打排3
begin
if(rst_n == 0)
reg3 <= 1'b1;
else
reg3 <= reg2;
end
always@(posedge clk,negedge rst_n)//uart工作信号赋值
begin
if(rst_n == 0)
flag <= 1'b0;
else
if((reg2 == 1'b0)&&(reg3 == 1'b1)&&(en == 1'b0))
flag <= 1'b1;
else
flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//uart工作使能标志信号赋值
begin
if(rst_n == 0)
en <= 1'b0;
else
if(flag == 1'b1)
en <= 1'b1;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
en <= 1'b0;
else
en <= en;
end
always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
if(rst_n == 0)
cnt <= 16'd0;
else
if((cnt == cnt_max - 1)||(en == 1'b0))
cnt <= 16'd0;
else
cnt = cnt + 1'b1;
end
always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
if(rst_n == 0)
bit_flag <= 1'b0;
else
if(cnt == cnt_max/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//数据个数计数器驱动逻辑
begin
if(rst_n == 0)
bit_cnt <= 4'd0;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
bit_cnt <= 4'd0;
else
if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always@(posedge clk,negedge rst_n)//接收数据并位
begin
if(rst_n == 0)
rx_data <= 8'd0;
else
if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {reg3,rx_data[7:1]};
end
always@(posedge clk,negedge rst_n)//输入数据并位结束信号
begin
if(rst_n == 0)
rx_flag <= 1'b0;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//输出数据传递
begin
if(rst_n == 0)
po_data <= 8'd0;
else
if(rx_flag == 1'b1)
po_data <= rx_data;
else
po_data <= po_data;
end
always@(posedge clk,negedge rst_n)//输出使能
begin
if(rst_n == 0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
end
endmodule
数据处理
module fifo_add(
input clk ,
input rst_n ,
input [7:0] data ,//rs232传输到FPGA的数据
input flag ,
output reg[7:0]data_out ,//求和结果
output reg out_flag //输出使能
);
parameter l_max = 'd3,//列数
h_max = 'd4;//行数
reg [3:0]l_cnt ;//列计数器
reg [3:0]h_cnt ;//行计数器
reg fifo1_en ;//fifo_1写使能
reg [7:0]fifo1_data;//fifo_1写数据
reg fifo2_en ;//fifo_2写使能
reg [7:0]fifo2_data;//fifo_2写数据
reg rd_en;//读使能信号
wire [7:0]data1; //fifo1读出数据
wire [7:0]data2; //fifo2读出数据
reg add_en;//求和标志信号
reg data_flag;//fifo1的写使能信号
always@(posedge clk,negedge rst_n)//列计数器驱动
begin
if(rst_n == 0)
l_cnt <= 4'd0;
else
if((l_cnt == l_max)&&(flag == 1'b1))
l_cnt <= 4'd0;
else
if(flag == 1'b1)
l_cnt <= l_cnt + 4'd1;
else
l_cnt <= l_cnt;
end
always@(posedge clk,negedge rst_n)//行计数器驱动
begin
if(rst_n == 0)
h_cnt <= 4'd0;
else
if((h_cnt == h_max)&&(l_cnt == l_max)&&(flag == 1'b1))
h_cnt <= 4'd0;
else
if((l_cnt == l_max)&&(flag == 1'b1))
h_cnt <= h_cnt + 4'd1;
else
h_cnt <= h_cnt;
end
always@(posedge clk,negedge rst_n)//fifo_1写使能
begin
if(rst_n == 0)
fifo1_en <= 1'b0;
else
if((h_cnt == 0)&&(flag == 1'b1))
fifo1_en <= 1'b1;
else
fifo1_en <= data_flag;
end
always@(posedge clk,negedge rst_n)//fifo_1写数据
begin
if(rst_n == 0)
fifo1_data <= 8'd0;
else
if((h_cnt == 0)&&(flag == 1'b1))
fifo1_data <= data;
else
if(data_flag == 1'b1)
fifo1_data <= data2;
else
fifo1_data <= fifo1_data;
end
always@(posedge clk,negedge rst_n)//fifo_2写使能
begin
if(rst_n == 0)
fifo2_en <= 1'b0;
else
if((h_cnt >= 4'd1)&&(h_cnt <= h_max - 4'd1)&&(flag == 1'b1))
fifo2_en <= 1'b1;
else
fifo2_en <= 1'b0;
end
always@(posedge clk,negedge rst_n)//fifo_2写数据
begin
if(rst_n == 0)
fifo2_data <= 8'd0;
else
if((h_cnt >= 4'd1)&&(h_cnt <= h_max - 4'd1)&&(flag == 1'b1))
fifo2_data <= data;
else
fifo2_data <= fifo2_data;
end
always@(posedge clk,negedge rst_n)//fifo读写使能(共用)
begin
if(rst_n == 0)
rd_en <= 1'b0;
else
if((h_cnt >= 4'd2)&&(flag <= h_max)&&(flag == 1'b1))
rd_en <= 1'b1;
else
rd_en <= 1'b0;
end
always@(posedge clk,negedge rst_n)//fifo1的写使能信号
begin
if(rst_n == 0)
data_flag <= 1'b0;
else
if((fifo2_en == 1'b1)&&(rd_en == 1'b1))
data_flag <= 1'b1;
else
data_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//求和使能信号
begin
if(rst_n == 0)
add_en <= 1'b0;
else
if(rd_en == 1'b1)
add_en <= 1'b1;
else
add_en <= 1'b0;
end
always@(posedge clk,negedge rst_n)//结果输出
begin
if(rst_n == 0)
data_out <= 8'd0;
else
if(add_en == 1'b1)
data_out <= data + data1 + data2;
else
data_out <= data_out;
end
always@(posedge clk,negedge rst_n)//结果输出使能
begin
if(rst_n == 0)
out_flag <= 1'b0;
else
out_flag <= add_en;
end
fifo1 fifo1_inst (
.clock ( clk ),
.data ( fifo1_data ),
.rdreq ( rd_en ),
.wrreq ( fifo1_en ),
.q ( data1 )
);
fifo1 fifo2_inst (
.clock ( clk ),
.data ( fifo2_data ),
.rdreq ( rd_en ),
.wrreq ( fifo2_en ),
.q ( data2 )
);
endmodule
数据发送
module uart_tx(
input clk ,
input rst_n ,
input [7:0] pi_data ,
input pi_flag ,
output reg tx
);
parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率
parameter cnt_max =clk_shuj/uart_btl;
reg en ;
reg [15:0] cnt ;//每bit数据传输完成计数器
reg flag ;//
reg [3 :0] bit_cnt ;//bit计数器
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
en <= 1'b0;
else
if(pi_flag == 1'b1)
en <= 1'b1;
else
if((bit_cnt == 4'd9)&&(flag == 1'b1))
en <= 1'b0;
else
en <= en;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 16'd0;
else
if((en == 1'b0)||(cnt == cnt_max - 1'd2))
cnt <= 16'd0;
else
if(en == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
flag <= 1'b0;
else
if(cnt == 16'd1)
flag <= 1'b1;
else
flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
bit_cnt <= 4'd0;
else
if((bit_cnt == 4'd9)&&(flag == 1'b1))
bit_cnt <= 4'd0;
else
if((en == 1'b1)&&(flag == 1'b1))
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
tx <= 1'b1;
else
if(flag == 1'b1)
case(bit_cnt)
0: tx <= 1'b0;
1: tx <= pi_data[0];
2: tx <= pi_data[1];
3: tx <= pi_data[2];
4: tx <= pi_data[3];
5: tx <= pi_data[4];
6: tx <= pi_data[5];
7: tx <= pi_data[6];
8: tx <= pi_data[7];
9: tx <= 1'b1;
default :tx <= 1'b1;
endcase
end
endmodule
顶层文件
module fifo_add_top(
input clk ,
input rst_n ,
input rx ,//rs232传输到FPGA的数据
output tx //rs232传输到PC的数据
);
wire [7:0]po_data;
wire [7:0]pi_data;
wire pi_flag;
wire po_flag;
fifo_add fifo_add(
.clk (clk ),
.rst_n (rst_n ),
.data (po_data ),//rs232传输到FPGA的数据
.flag (po_flag ),
.data_out (pi_data),//求和结果
.out_flag (pi_flag) //输出使能
);
uart_tx uart_tx(
.clk (clk ),
.rst_n (rst_n ),
.pi_data (pi_data ),
.pi_flag (pi_flag ),
.tx (tx )
);
uart_rx uart_rx(
.clk (clk ),
.rst_n (rst_n ),
.rx (rx ),
.po_data (po_data), //接收到的数据
.po_flag (po_flag) //数据输出有效
);
endmodule
仿真代码
`timescale 1ns/1ps
module fifo_add_top_tb;
reg clk ;
reg rst_n ;
reg rx ;
reg [7:0] data_a[19:0];
wire tx ;
fifo_add_top fifo_add_top(
.clk (clk ),
.rst_n(rst_n),
.rx (rx ),//rs232浼犺緭鍒癋PGA鐨勬暟鎹
.tx (tx ) //rs232浼犺緭鍒癙C鐨勬暟鎹);
);
initial
$readmemh("E:/FPGA_exer/fifo_add_0904/doc/data.txt",data_a);//灏嗘暟鎹彁鍙栧埌浠跨湡鏂囦欢鐨勫瓨鍌ㄥ櫒涓
initial clk = 1'b1;
always #10 clk = ~clk;
initial begin
rst_n = 1'b0;
rx = 1'b1;
#40
rst_n = 1'b1;
#200
re_byte();
#100000
$stop;
end
//璧嬪€煎嚱鏁
task rx_bit(input [7:0]data);
integer i;
for(i = 0;i < 10; i = i + 1)//寰幆9娆
begin
case(i)
0: rx <= 1'b0;
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7];
9: rx <= 1'b1;
endcase
#(5208*20);//姣忔寤舵椂
end
endtask
//defparam fifo_add_top.uart_rx.clk_shuj = 50_000;
task re_byte();
integer j;
for(j = 0;j < 20;j = j + 1)
rx_bit(data_a[j]);//浼犻€鈥斺€锛涓暟鎹
endtask
endmodule
三、仿真验证
运行仿真,将data,data1,data2数据调到一起,观察data_out
观察几组数据4,1,5,=10,5,2,1=8,求和正确,数据传输到RS232数据发送模块,可以看到,数据正常通过tx输出。
输出正常,实验成功。
参考资料
fifo求和原理