一、FIFO简介
FIFO( First in First out) 使用在需要产生数据接口的部分,用来存储、缓冲在两个异步
时钟之间的数据传输。在异步电路中,由于时钟之间周期和相位完全独立,因此数据丢失概
率不为零。使用 FIFO 可以在两个不同时钟域系统之间快速而方便地传输实时数据。
在此给自己挖个坑:是否以后会遇到FIFO解决不同时钟域用来同步信号
注:上图为异步FIFO
如果是一个单端口的存储器,还应包含一个仲裁器,保证同一时刻只能进行一项操作(读或写),我们选择双口 RAM(无需真正的双口 RAM,因为我们只是希望有一个简单的相互独立的读写端口)是因为这些实例非常接近实际情况。读、写端口拥有两个计数器( wr_ptr、 rd_ptr)产生的互相独立的读、写地址。计数器的值在读写使能信号来临时传递给“ 读指针” ( rd)和“ 写指针” ( wr)。写指针指向下一个将要写入的位置,读指针指向下一个将要读取的位置。每次写操作使写指针加 1,读操作使读指针加1。左右两侧的模块为读写指针与满空信号产生模块。这两个模块的任务是给 FIFO 提供“ 空”( empty)和“ 满” ( full)信号。这些信号告诉外部电路 FIFO 已经达到了临界条件:如果出现“ 满” 信号,那么 FIFO 为写操作的临界状态,如果出现“ 空” 信号,则 FIFO 为读操作的临界状态。写操作的临界状态表示 FIFO 已经没有空间来存储更多的数据,读操作的临界表示 FIFO 没有更多的数据可以读出。读写指针与满空信号产生模块还可告诉 FIFO 中“ 满”或“ 空” 位置的数值。这是由指针的算术运算来完成了。实际的“ 满” 或“ 空” 位置计算并不是为 FIFO 自身提供的。它是作为一个报告机构给外部电路用的。但是, “ 满” 和“ 空”信号在 FIFO 中却扮演着非常重要的角色,它为了能实现读与写操作各自的独立运行而阻塞性的管理数据的存取。
这种阻塞性管理的重要性不是将数据复写(或重读),而是指针位置。可以控制整个 FIFO,并且使读、写操作改变着指针数值。
FIFO 的工作原理如下所述:复位时,读、写指针均为 0。这是 FIFO 的空状态,空标志(empty)为高电平,此时满标志( full)为低电平。当 FIFO 出现空标志( empty)时,不允许读操作,只能允许写操作。写操作写入到位置 0,并使写指针加 1。此时,空标志( empty)变为低电平。假设没有发生读操作且随后的一段时间中 FIFO 只有写操作,一定时间后,写指针的值等于 7。这就意味着在存储器中,要写入数据的最后一个位置就是下一个位置。在这种情况下,写操作将写指针变为 0,并将输出满标志( full)。
二、同步FIFO框图设计
端口 | 类型 | 作用 |
clk | input | 提供时钟信号 |
reset | input | 提供复位信号 |
data_in | input | 数据输入 |
wr_en | input | 写使能信号 |
rd_en | input | 读使能信号 |
fifo_cnt | output | 还未读出的数据 |
data_out | ouput | 缓存数据输出 |
full | output | “满“信号 |
empty | output | ”空“信号 |
三、波形图绘制
四、程序编写
多仿真多改正
fifo.v
module fifo(
clk,
reset,
data_in,
wr_en,
rd_en,
full,
empty,
data_out,
fifo_cnt);
//---Ports declearation: generated by Robei---
input clk;
input reset;
input [7:0] data_in;
input wr_en;
input rd_en;
output full;
output empty;
output [7:0] data_out;
output [3:0] fifo_cnt;
wire clk;
wire reset;
wire [7:0] data_in;
wire wr_en;
wire rd_en;
wire full;
wire empty;
reg [7:0] data_out;
reg [3:0] fifo_cnt;
//----Code starts here: integrated by Robei-----
//fifo存储空间
reg [7:0]fifo_ram[0:7];
//rd_ptr和wr_ptr指针
reg [3:0]rd_ptr,wr_ptr;
/*空和满信号产生*/
assign empty = (fifo_cnt == 0);//存储0个数据
assign full = (fifo_cnt == 8); //存储8个数据
/*写操作*/
always@(posedge clk or negedge reset)
if(reset == 0)
begin
fifo_ram[0]=8'hff;
fifo_ram[1]=8'hff;
fifo_ram[2]=8'hff;
fifo_ram[3]=8'hff;
fifo_ram[4]=8'hff;
fifo_ram[5]=8'hff;
fifo_ram[6]=8'hff;
fifo_ram[7]=8'hff;
end
else if(wr_en == 1 && (!full)) //写信号并且没有满
fifo_ram[wr_ptr] <= data_in;//数据写入
else if(wr_en == 1&&rd_en == 1) //同时读写,只是指针不移动(后面体现)
fifo_ram[wr_ptr] <= data_in;
/*读操作*/
always@(posedge clk)
if(reset == 0)
data_out <= 0;
else if((rd_en == 1) && (empty == 0))
data_out <= fifo_ram[rd_ptr];
else if(wr_en== 1&&rd_en == 1) //同时读写,只是指针不移动(后面体现)
data_out <= fifo_ram[rd_ptr];
else
data_out <= data_out;
/*指针移动--wr_ptr*/
always@(posedge clk)
if(reset == 0)
wr_ptr <= 0;
else if((wr_en == 1) && (full == 0))
wr_ptr <= wr_ptr + 1'b1;
else if((wr_en == 1)&&(rd_en == 1))
wr_ptr <= wr_ptr + 1'b1;
else if(full)
wr_ptr <= 0;
else
wr_ptr <= wr_ptr;
//wr_ptr <= ((wr == 1 && full == 0)||((wr==1)&&(rd==1)))?wr_ptr+1:wr_ptr;
/*指针移动--rd_ptr*/
always@(posedge clk)
if(reset == 0)
rd_ptr <= 0;
else if((rd_en == 1'b1)&&(wr_en == 1'b1))
rd_ptr <= rd_ptr + 1'b1;
else if((rd_en == 1'b1)&&(empty == 0))
rd_ptr <= rd_ptr + 1'b1;
else if(empty)
rd_ptr <= 0;
else
rd_ptr <= rd_ptr;
//rd_ptr <= ((rd_en == 1 && empty == 0)||((wr_en==1)&&(rd_en==1)))?rd_ptr+1:rd_ptr;
/*fifo_cnt */
always@(posedge clk)
if(reset == 0)
fifo_cnt <= 0;
else
case({wr_en,rd_en})
2'b00:fifo_cnt <= fifo_cnt;
2'b01:fifo_cnt <= (fifo_cnt == 0)?0:fifo_cnt - 1;
2'b10:fifo_cnt <= (fifo_cnt == 8)?8:fifo_cnt + 1;
2'b11:fifo_cnt <= fifo_cnt;
default:fifo_cnt <= fifo_cnt;
endcase
endmodule //fifo
fifo_tb.v
module fifo_tb();
reg clk;
reg reset;
reg [7:0] data_in;
reg wr;
reg rd;
wire full;
wire empty;
wire [7:0] data_out;
wire [3:0] fifo_cnt;
//----Code starts here: integrated by Robei-----
always #2 clk =~clk; //产生时钟
initial begin
clk = 1;
end
initial begin
reset =0;
wr=0;
rd=0;
#1 reset=1;
#3 wr=1;
#5 rd=1;
#5 rd=0;
#5 wr=0;
#5 wr=1;
#10 rd=1;
#10 rd=0;
#3 wr=1;
#5 rd=1;
#5 rd=0;
#5 wr=0;
#5 wr=0;
#10 rd=1;
#10 rd=0;
wr=1;
#4 wr = 0;
#14 $finish;
end
always @(posedge clk or negedge reset )
begin
if (reset == 0)
begin
data_in<=8'hbc;
wr<=0;
rd<=0;
end
else
data_in<=$random;
end
initial begin
$dumpfile ("F:/Robei/Robei_study_fifo/fifo_tb.vcd");
$dumpvars;
end
//---Module instantiation---
fifo fifo1(
.clk(clk),
.reset(reset),
.data_in(data_in),
.wr_en(wr),
.rd_en(rd),
.full(full),
.empty(empty),
.data_out(data_out),
.fifo_cnt(fifo_cnt));
endmodule //fifo_tb
仿真波形
写在最后,有任何不对的希望及时指正我的错误,感激不尽!
五、FIFO求和原理和方法
为什么要用FIFO求和?
要完成3行数据的SUM求和,需要调用2个FIFO。
六、FIFO求和系统框图
串口收发模块哦我们之前写过,现在光编写fifo求和顶层模块
还在编写ing