概述
FIFO 的英文全称是 First In First Out,即先进先出。 FPGA 使用的 FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互即所谓的跨时钟域信号传递。它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。
根据 FIFO 工作的时钟域,可以将 FIFO 分为同步 FIFO 和异步 FIFO。同步 FIFO 是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。异步 FIFO 是指读写时钟不一致,读写时钟是互相独立的。 Xilinx 的 FIFO IP 核可以被配置为同步 FIFO 或异步 FIFO,其信号框图如下图所示。从图中可以了解到,当被配置为同步 FIFO 时,只使用 wr_clk,所有的输入输出信号都同步于 wr_clk 信号。而当被配置为异步FIFO 时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟 wr_clk,所有与读相关的信号都是同步于读时钟 rd_clk。 异步 FIFO 能够将不同时钟域中的数据同步到所需的时钟域中
FIFO 的宽度: FIFO 一次读写操作的数据位 N;
FIFO 的深度: FIFO 可以存储多少个宽度为 N 位的数据。
空标志: empty。 FIFO 已空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO中读出数据而造成无效数据的读出。
将空标志: almost_ empty。 FIFO 即将被读空。满标志: full。 FIFO 已满时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的写操作继续向 FIFO 中写数据而造成溢出。
将满标志: almost_full。 FIFO 即将被写满。
读时钟:读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
这里请注意,“almost_ empty”和“almost_full”这两个信号分别被看作“empty”和“full”的警告信号,他们距离真正的空(empty)和满(full)都一个时钟的延时。
写时钟:写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
FIFO求和
串口每次输入一个数据,要对多组数据求和,则利用FIFO进行缓存。
要完成 3 行数据的 SUM 求和,需要调用 2 个 FIFO IP 核,当数据开始输入时,将数据的第 0 行数据存储到 fifo1 中,将第 1 行数据存储到 fifo2 中,当数据的第 2 行的第 0 个数据输入的同时,读取写入 fifo1 中的的第 0 个数据和写入 fifo2 中的第 0 个数据,将三个数据求和,求和结果实时输出,在完成求和的同时,将读取的 fifo2 中的第 0 个数据写入 fifo1中, fifo1 读出的数据弃之不用,将输入的第 2 行的数据写入 fifo2 中,当第 2 行的最后一个数据输入,完成前三行的最后一个求和运算后,第 0 行的数据已读取完成,第 1 行的数据重新写入 fifo1,第 2 行的数据写入 fifo2,当第 3 行数据开始传入时,开始进行第 1 行、第2 行和第 3 行的数据求和运算,如此循环,直到最后一个数据输入,完成求和运算。流程示意图具体见图 。
程序框图
程序
fifo_sum_ctrl
`timescale 1ns/1ns
//
// Company:
// Engineer:
//
// Create Date: 2024/01/14 18:36:46
// Design Name:
// Module Name: fifo_sum_ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description: FIFO三列求和实验
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module fifo_sum_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data ,
input wire pi_flag ,
output reg [7:0] po_sum ,
output reg po_flag
);
//parameter define
parameter CNT_ROW_MAX = 7'd49 , //行计数最大值
CNT_COL_MAX = 7'd49 ; //列计数最大值
//wire define
wire [7:0] data_out1 ; //fifo1数据输出
wire [7:0] data_out2 ; //fifo2数据输出
//reg define
reg [6:0] cnt_row ; //行计数,计数一行数据个数
reg [6:0] cnt_col ; //场计数,计数数据行数
reg wr_en1 ; //fifo1写使能
reg wr_en2 ; //fifo2写使能
reg [7:0] data_in1 ; //fifo1写数据输入
reg [7:0] data_in2 ; //fifo2写数据输入
reg rd_en ; //fifo1、fifo2共用的读使能
reg dout_flag ; //控制fifo1,2~84行的写使能
reg po_flag_reg ; //输出标志位缓存,rd_en延后一拍得到,控制计算po_sum
//cnt_row:行计数器,计数一行数据个数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
cnt_row <= 7'd0;
else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1))
cnt_row <= 7'd0;
else if(pi_flag == 1'b1)
cnt_row <= cnt_row + 1'b1;
end
//cnt_col:列计数器,计数数据行数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
cnt_col <= 7'd0;
else if((cnt_col == CNT_COL_MAX) && (pi_flag == 1'b1) && (cnt_row == CNT_ROW_MAX))
cnt_col <= 7'd0;
else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1))
cnt_col <= cnt_col + 1'b1;
end
//wr_en1:fifo1写使能信号,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
wr_en1 <= 1'b0;
else if((cnt_col == 7'd0) && (pi_flag == 1'b1))
wr_en1 <= 1'b1; //第0行写入fifo1
else
wr_en1 <= dout_flag; //2-84行写入fifo1
end
//dout_flag:控制2-CNT_COL_MAX-1行wr_en1信号
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
dout_flag <= 0;
else if((wr_en2 == 1'b1) && (rd_en == 1'b1))
dout_flag <= 1'b1;
else
dout_flag <= 1'b0;
end
//wr_en2:fifo2写使能信号,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
wr_en2 <= 1'b0;
else if((cnt_col >= 7'd1) && (cnt_col <= CNT_COL_MAX - 1'b1) && (pi_flag == 1'b1))
wr_en2 <= 1'b1; //2-CNT_COL_MAX行写入fifo2
else
wr_en2 <= 1'b0;
end
//data_in1:fifo1数据输入
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
data_in1 <= 8'b0;
else if((pi_flag == 1'b1) && (cnt_col == 7'd0))
data_in1 <= pi_data; //第0行数据暂存fifo1中
else if(dout_flag == 1'b1)
data_in1 <= data_out2;//第2-CNT_COL_MAX-1行时,fifo2读出数据存入fifo1
else
data_in1 <= data_in1;
end
//data_in2:fifo2数据输入
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
data_in2 <= 8'b0;
else if((pi_flag == 1'b1)&&(cnt_col >= 7'd1)&&(cnt_col <= (CNT_COL_MAX - 1'b1)))
data_in2 <= pi_data;
else
data_in2 <= data_in2;
end
//rd_en:fifo1和fifo2的共用读使能信号
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if((pi_flag == 1'b1)&&(cnt_col >= 7'd2)&&(cnt_col <= CNT_COL_MAX))
rd_en <= 1'b1;
else
rd_en <= 1'b0;
end
//po_flag_reg:输出标志位缓存,延后rd_en一拍,控制po_sum信号
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
po_flag_reg <= 1'b0;
else if(rd_en == 1'b1)
po_flag_reg <= 1'b1;
else
po_flag_reg <= 1'b0;
end
//po_flag:输出标志信号,延后输出标志位缓存一拍,与po_sum同步输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= po_flag_reg;
end
//po_sum:求和数据输出
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 1'b0)
po_sum <= 8'b0;
else if(po_flag_reg == 1'b1)
po_sum <= data_out1 + data_out2 + pi_data;
else
po_sum <= po_sum;
end
//------------- fifo_data_inst1 --------------
fifo1 fifo_data_inst1
(
.clk (sys_clk ), //input clk
.din (data_in1 ), //input [7:0] din
.wr_en (wr_en1 ), //input wr_en
.rd_en (rd_en ), //input rd_en
.full ( ), // output full
.empty ( ), // output empty
.dout (data_out1 ) //output [7:0] dout
);
//------------- fifo_data_inst2 --------------
fifo2 fifo_data_inst2
(
.clk (sys_clk ), //input clk
.din (data_in2 ), //input [7:0] din
.wr_en (wr_en2 ), //input wr_en
.rd_en (rd_en ), //input rd_en
.full ( ), // output full
.empty ( ), // output empty
.dout (data_out2 ) //output [7:0] dout
);
endmodule
uart_rx
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/09/27 13:27:27
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_rx
#(
parameter UART_BPS = 'd9600, //串口波特率
parameter CLK_FREQ = 'd50_000_000 //时钟频率
)
(
input wire sys_clk,
input wire sys_rst_n,
input wire rx,
output reg [7:0] po_data,
output reg po_flag
);
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
reg rx_reg1 ;
reg rx_reg2 ;
reg rx_reg3 ;
reg start_nedge;
reg work_en;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg [7:0] rx_data;
reg rx_flag;
//将接收的rx信号打两拍,消除亚稳态。再打一拍用消除亚稳态后的rx_reg2、rx_reg3进行下降沿判断
//rx_reg1
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;
end
//rx_reg2
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;
end
//rx_reg2
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;
end
//start_nedge
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
start_nedge <= 1'b0;
else if (rx_reg3 && (~rx_reg2))
start_nedge <= 1'b1;
else
start_nedge <= 1'b0;
end
//work_en
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(start_nedge == 1'b1)
work_en <= 1'b1;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
work_en <= 1'b0;
end
//baud_cnt
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
//
end
//bit_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == BAUD_CNT_MAX/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
end
//bit_cnt
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
bit_cnt <= 4'b0;
else if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
end
//rx_data
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
rx_data <= 8'b0;
else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {rx_reg3, rx_data[7:1]};
end
//rx_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
end
//po_data
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
po_data <= 8'b0;
else if(rx_flag == 1'b1)
po_data <= rx_data;
end
//po_data_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if (sys_rst_n == 1'b0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
end
endmodule
uart_tx
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/09/27 18:45:39
// Design Name:
// Module Name: uart_tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_tx
#(
parameter UART_BPS = 'd9600, //串口波特率
parameter CLK_FREQ = 'd50_000_000 //时钟频率
)
(
input wire sys_clk,
input wire sys_rst_n,
input wire pi_flag,
input wire[7:0] pi_data,
output reg tx
);
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
reg work_en;
reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
//work_en
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(pi_flag == 1'b1)
work_en <= 1'b1;
else if((bit_flag ==1'b1) && (bit_cnt == 4'd9))
work_en <= 1'b0;
end
//baud_cnt
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
end
//bit_flag
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == 13'd1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//bit_cnt
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9))
bit_cnt <= 4'b0;
else if((bit_flag == 1'b1) && (work_en == 1'b1))
bit_cnt <= bit_cnt + 1'b1;
//tx
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tx <= 1'b1; //空闲状态时为高电平
else if(bit_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
endmodule
top_fifo_sum
`timescale 1ns/1ns
//
// Company:
// Engineer:
//
// Create Date: 2024/01/14 18:36:46
// Design Name:
// Module Name: fifo_sum_ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top_fifo_sum
(
input wire sys_clk , //输入系统时钟,50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire rx , //串口数据接收
output wire tx //串口数据发送
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter UART_BPS = 14'd9600 , //比特率
CLK_FREQ = 26'd50_000_000 ; //时钟频率
//wire define
wire [7:0] pi_data ; //输入待求和数据
wire pi_flag ; //输入数据标志信号
wire [7:0] po_sum ; //输出求和后数据
wire po_flag ; //输出数据标志信号
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- uart_rx_inst --------------
uart_rx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_rx_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.rx (rx ), //串口接收数据
.po_data (pi_data ), //串转并后的数据
.po_flag (pi_flag ) //串转并后的数据有效标志信号
);
//------------- fifo_sum_ctrl_inst --------------
fifo_sum_ctrl fifo_sum_ctrl_inst
(
.sys_clk (sys_clk ), //频率为50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低有效
.pi_data (pi_data ), //rx传入的数据信号
.pi_flag (pi_flag ), //rx传入的标志信号
.po_sum (po_sum ), //求和运算后的信号
.po_flag (po_flag ) //输出数据标志信号
);
//------------- uart_tx_inst --------------
uart_tx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_tx_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.pi_data (po_sum ), //并行数据
.pi_flag (po_flag ), //并行数据有效标志信号
.tx (tx ) //串口发送数据
);
endmodule