- 什么是FIFO?
FIFO:first in,first out;数据先进先出,后进后出,是一种传统的顺序执行方法,本质上是一种双口数据缓存器,没有外部地址(由内部指针递增来充当数据地址),只能顺序写入,顺序读取,因此不能写入或读取指定地址里的数据。
- 为什么需要FIFO?
在系统设计时,包含很多工作在不同时钟频率下的元件,如处理器、外设等,可能有自己的时钟振荡器,不同模块处理速度有快有慢,但是每个模块各自时钟不间歇,如果模块之间有数据交互,会造成计算紊乱,这也就是跨时钟域要解决的问题,当数据量很大,数据传输速率要求高,需要匹配不同传输速率的系统时,FIFO是处理此类问题的优解。
- FIFO与串口的区别
Uart全称为通用异步收发器(Universal Asynchronous Receiver/Transmitter),是一种通用串行数据总线,先将接收到的并行数据转成串行进行传输,然后再将串行转成并行数据,在两个uart设备之间通信,指定相同传输速率(波特率)、起始位结束位等,遵循同一通信协议。
FIFO本质上只是一个缓冲器,如果CPU处理uart数据绰绰有余,可以不用FIFO的帮助,如果在复杂系统中,CPU工作量太大,那么CPU在处理别的任务时,就会时常被收发数据中断,导致CPU工作效率很低,这时通过FIFO来缓冲,存下若干数据再集中处理就很有必要了。
FIFO和uart可以联合工作,提高收发效率。
- FIFO的分类
- 同步FIFO
读取与写入的时钟相同,可以用计数器或读写指针来判断空满
- 异步FIFO
读取与写入的时钟不同,需要用到同步器、gray码来减少亚稳态的产生,用指针的gray来判断空满,避免使用二进制指针比较空满,这会导致指针值取样错误。
- 异步(双时钟)FIFO主要参数
宽度:FIFO存储的每个数据的位宽
深度:FIFO存储数据的个数
写满标志:满标志拉高,则无法继续写入
若[写域写指针(gray)== 写域读指针(gray)],则full==1
读空标志:空标志拉高,则无法继续读取
若[读域读指针(gray)== 读域写指针(gray)],则null==1
写指针(写域):每存入一个数据,写指针递增1,
读指针(读域):每读取一个数据,读指针递增1
写指针(读域):将写域写指针转化成gray码同步到读域
读指针(写域):将读域读指针转化成gray码同步到写域
指针位数要比FIFO深度多一位用来判断空满!
产生可靠的空满标志是设计FIFO的关键!
- gray码与二进制
格雷码是一种绝对编码方式,它的循环、单步特性消除了随机取数时出现重大误差的可能,是一种可靠的、错误最小化的编码方式,在相邻位间转换时,只有一位产生变化,出错可能大大降低。
红框与绿框里的格雷码体现了极美的对称性,最高位MSB红框为0,绿框为1,剩下的3位LSB相同。
转化公式:
bnary to Gray
g(3) = b(3) ^ 0
g(2) = b(3) ^ b(2)
g(1) = b(2) ^ b(1)
g(0) = b(1) ^ b(0)
g(n) = b(n+1) ^ b(n)
Gray to Binary
b(3) = g(3)
b(2) = g(3) ^ g(2)
b(1) = g(3) ^ g(2) ^ g(1)
b(0) = g(3) ^ g(2) ^ g(1) ^ g(0)
- 空满判断(gray)
FIFO读空条件:write pointer == read pointer
FIFO写满条件:
1、FIFO的深度==2的幂次
假设深度为8的FIFO一开始就只写不读,写满8个后,写指针gray码为4’b1100,此时读指针是4’b0000; 再假设读指针停在4’b0011,写指针写到4‘b1111,
则此时FIFO写满。可以读写指针看到高两位相反,剩下位数相同。因此写满标志条件: (写域写指针高两位==~写域读指针高两位)&&(写域写指针剩余LSB==写域读指针剩余LSB)
2、FIFO的深度!=2的幂次
若FIFO深度为7,则首尾向内收缩一位,即4’b0001与4‘b1001,这样可以保证只有最高位取反,其余相等,深度为6同理,如红色箭头所示,收缩两位。
- 写在最后
仿真时,如果写时钟快于读时钟,仿真图里将写指针同步到读域时,发现漏读了指针,不要惊慌!这个问题肯定会发生,基本无法解决,但是不会对读取数据产生影响,因为同步写指针到读域时,写指针还在按自己的时钟递增,读域采到的写指针数值肯定不会大于此刻写域的写指针。
这个主要影响判断读空的条件,可能会产生假空的状态,而实际上FIFO并没有空,此时读数据就会停止,直到空标志拉低,再继续读即可。
同理,如果读时钟快于写时钟,可能会出现假满状态,也不会影响读数据。
代码
module fifo #(
parameter WSIZE = 8, //the width of fifo
parameter DSIZE = 5 //the depth of fifo
)
(
input sys_rst_n,
//write
input wclk, //clk of writing
input w_en, //en of writing
input wire [WSIZE-1:0]wdata, //data of writing
output w_full, //the signal of writing fully
//read
input rclk, //clk of reading
input r_en, //en of reading
output reg [WSIZE-1:0]rdata, //data of reading
output r_null, //the signal of reading nully
output reg r_valid //Read the data validly
);
//buffer
reg [WSIZE-1:0]buffer[(1<<DSIZE)-1:0]; //Pay attention to this writing
//reg of write
wire [DSIZE:0]gwptr; //gray code of Writing pointer
reg [DSIZE:0]bwptr, //binary code of Writing pointer
bwptr_rclk, //binary code of Writing pointer of reading clk
gwptr_wclk_1, //gray code of Writing pointer for hitting 1 beat
gwptr_rclk_1, //gray code of reading pointer for hitting 1 beat
gwptr_rclk_2; //gray code of reading pointer for hitting 2 beat
//reg of read
wire [DSIZE:0]grptr; //gray code of reading pointer
reg [DSIZE:0]brptr, //binary code of reading pointer
brptr_wclk, //binary code of reading pointer of writing clk
grptr_rclk_1, //gray code of reading pointer for hitting 1 beat
grptr_wclk_1, //gray code of Writing pointer for hitting 1 beat
grptr_wclk_2; //gray code of Writing pointer for hitting 2 beat
//b to g
assign gwptr = (bwptr >> 1) ^ bwptr; //write pointers to convert to Gray code
assign grptr = (brptr >> 1) ^ brptr; //read pointers to convert to Gray code
//write the pointer Gray code in the read field to play two beats
always @(posedge rclk, negedge sys_rst_n) begin
if(~sys_rst_n) begin
gwptr_rclk_1 <= 0;
gwptr_rclk_2 <= 0;
end
else begin
gwptr_rclk_1 <= gwptr;
gwptr_rclk_2 <= gwptr_rclk_1;
end
end
//read the pointer Gray code in the write field to play two beats
always @(posedge wclk, negedge sys_rst_n) begin
if(~sys_rst_n) begin
grptr_wclk_1 <= 0;
grptr_wclk_2 <= 0;
end
else begin
grptr_wclk_1 <= grptr;
grptr_wclk_2 <= grptr_wclk_1;
end
end
//Judge full
assign w_full = ((gwptr[DSIZE:DSIZE-1] == ~grptr_wclk_2[DSIZE:DSIZE-1]) && (gwptr[DSIZE-2:0] == grptr_wclk_2[DSIZE-2:0]));
//Judge null
assign r_null = (grptr == gwptr_rclk_2)? 1'd1 : 1'd0;
//write pointer
always @(posedge wclk, negedge sys_rst_n) begin
if(~sys_rst_n) begin
bwptr <= 0;
end
else if(~w_full && w_en) begin
bwptr <= bwptr + 1;
end
else begin
bwptr <= bwptr;
end
end
//read pointer
always @(posedge rclk, negedge sys_rst_n) begin
if(~sys_rst_n) begin
brptr <= 0;
end
else if(~r_null && r_en) begin
brptr <= brptr + 1;
end
else begin
brptr <= brptr;
end
end
//module memory ();
initial begin
$readmemb("C:/Users/86152/Desktop/FIFO/CODE/doc/memory.txt", buffer);
end
//write data
always @(posedge wclk, negedge sys_rst_n) begin
if(~w_full && w_en) begin
buffer[bwptr[DSIZE-1:0]] <= wdata; //!This expression is ture
end
end
//read data
always @(posedge rclk, negedge sys_rst_n) begin
if(~r_null && r_en) begin
rdata <= buffer[brptr[DSIZE-1:0]];
r_valid <= 1'd1;
end
else begin
r_valid <= 1'd0;
rdata <= rdata;
end
end
endmodule
tb
`timescale 1ns/1ns
module fifo_tb;
//SIZE
parameter WSIZE = 8; //the width of fifo
parameter DSIZE = 5; //the depth of fifo
//system
reg sys_rst_n;
//write
reg wclk; //clk of writing
reg w_en; //en of writing
reg [WSIZE-1:0]wdata; //data of writing
wire w_full; //the signal of writing fully
//read
reg rclk; //clk of reading
reg r_en; //en of reading
wire [WSIZE-1:0]rdata; //data of reading
wire r_null; //the signal of reading nully
wire r_valid; //Read the data validly
initial begin
sys_rst_n <= 1'b0;
//clk
wclk <= 1'b0;
rclk <= 1'b0;
//en
w_en <= 1'b0;
r_en <= 1'b0;
#10
sys_rst_n <= 1'b1;
w_en <= 1'b1; //just write
r_en <= 1'b0;
#64
w_en <= 1'd0; //write fully and then read completely
r_en <= 1'b1;
#192
w_en <= 1'd1; //just write
r_en <= 1'b0;
#40
w_en <= 1'd1; //write and read
r_en <= 1'b1;
#36
w_en <= 1'd0; //just read
r_en <= 1'b1;
#40
w_en <= 1'd1; //just
r_en <= 1'b1;
end
//wclk
always #1 wclk <= ~wclk;
//rclk
always #3 rclk <= ~rclk;
//wdata
always @(posedge wclk, negedge sys_rst_n) begin
if(~sys_rst_n) begin
wdata <= 8'b000_1111;
end
else begin
wdata <= wdata + 8'd1;
end
end
fifo fifo_tb(
.sys_rst_n(sys_rst_n),
.wclk(wclk),
.w_en(w_en),
.wdata(wdata),
.w_full(w_full),
.rclk(rclk),
.r_en(r_en),
.rdata(rdata),
.r_null(r_null),
.r_valid(r_valid)
);
endmodule