FIFO简介:First in First out,先进先出的数据结构。只能顺序的读入数据,顺序的读出数据。
使用RAM进行建模。设计相应的控制模块,控制RAM中的数据写入和读取,先写入的数据先读取。
深度为16的FIFO,将读写指针位宽设置为5位,但是取低四位作为读取RAM的地址。原因如下:
当读写指针相等时,可以得出FIFO为空。
当继续读取数据,读指针达到15之后,如果再继续写入数据,写指针最高位第五位会变为1,由于取其第四位作为RAM地址,会返回到第0位写入数据,直到写入到地址2。
继续读取数据,直到读指针为11100,此时读写指针低四位相等,最高位相反,可以得出FIFO为满。
具体代码:
`timescale 1ns / 1ns
module Syn_FIFO #(
parameter DEPTH = 16,//FIFO深度
parameter WIDTH = 8,//FIFO内数据位宽
parameter P_WIDTH = 5//读写指针位宽
)
(
input rst_n
,input clk
,input [WIDTH-1:0] Data_Write //写入的数据值
,input Write_Sig//写入数据使能
,input Read_Sig//读取数据使能
,output wire [WIDTH-1:0] Data_Read //读取的数据值
,output wire Full_Sig//FIFO为空信号
,output wire Empty_Sig//FIFO为满信号
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
reg [P_WIDTH-1:0] Read_pointer;
reg [P_WIDTH-1:0] Write_pointer;
reg [WIDTH-1:0] rData_Read;
assign Full_Sig = ((Read_pointer[P_WIDTH-2:0]==Write_pointer[P_WIDTH-2:0])
&&(Read_pointer[P_WIDTH-1] != Write_pointer[P_WIDTH-1]))? 1'd1:1'd0;
assign Empty_Sig = (Read_pointer[P_WIDTH-1:0]==Write_pointer[P_WIDTH-1:0])? 1'd1:1'd0;
always @(negedge rst_n or posedge clk) begin
if (!rst_n)
begin
rData_Read <= 0;
Read_pointer <= 0;
Write_pointer <= 0;
end
else begin
if (~Full_Sig && ~Empty_Sig && Read_Sig && Write_Sig ) //非空非满时同时读写
begin
rData_Read <= RAM_MEM[Read_pointer[P_WIDTH-2:0]];
RAM_MEM[Write_pointer[P_WIDTH-2:0]] <= Data_Write;
Read_pointer <= Read_pointer + 1'd1;
Write_pointer <= Write_pointer + 1'd1;
end //
else if (~Empty_Sig && Read_Sig )//非空时读数据
begin
rData_Read <= RAM_MEM[Read_pointer[P_WIDTH-2:0]];
Read_pointer <= Read_pointer + 1'd1;
end
else if (~Full_Sig && Write_Sig)//非满时写数据
begin
RAM_MEM[Write_pointer[P_WIDTH-2:0]] <= Data_Write;
Write_pointer <= Write_pointer + 1'd1;
end
end // else
end // always
assign Data_Read = rData_Read;
endmodule
testbench中的数据读写task如下,完整文件查看源码:
//testbench写数据的task
task Write_Data(input reg Write_en,input reg [7:0] Data_in);
begin
@(posedge clk)
if(Full_Sig) begin //testbench检测到FIFO为满,关闭写使能
Write_Sig <= 1'b0;
Data_Write <= 8'd0;
end
else begin
Write_Sig <= Write_en; //testbench检测到FIFO不满,写入数据
Data_Write <= Data_in;
end
end
endtask
//testbench读数据的task
task Read_Data(input reg Read_en);
begin
@(posedge clk)
if(Empty_Sig)
Read_Sig <= 1'b0;
else Read_Sig <= Read_en;
end
endtask
Testbench通过检测FIFO的Full_Sig和Empty_Sig判断FIFO的空满状态,来判断数据是否写入读取,还是应该关闭读\写的使能。
上图波形图中,考虑连续写入FIFO 16个数据的情形,在1处时钟上升沿,第16个数据192被写入,读指针和写指针相等,在这个时钟上升沿之后,2处Full_Sig被拉高。
但是对于Testbench而言,在1处的时钟上升沿,Testbench检测出Full_Sig还是为0(因为这个时钟上升沿之后,Full_Sig才被拉高),所以Testbench得出的判断是此时FIFO还没满,所以在时钟上升沿之后驱动Write_Sig继续为高,驱动Data_Write为204,计划再写入一个数据进去。
但是在3处时钟上升沿,对于FIFO而言,检测到Full_Sig已经被拉高,所以1处Testbench驱动的数据204并不会被FIFO采集并写入FIFO。
由此错误的判断便导致数据204的丢失,即Testbench以为把数据写入FIFO了,然而其实没有写入,如果Testbench替换为其他设计模块,也会导致这样的问题
对于读FIFO也是如此,从满FIFO中连续读取16个数据,在1处时钟上升沿,第16个数据被读出,即192。在该时钟上升沿之后,Empty_Sig被拉高,2处。
但是对于Testbench而言,在1处时钟上升沿,Testbench检测到Empty_Sig为低,所以驱动Read_Sig继续为高,到3处时钟上升沿之后,Read_Sig才被拉低。
在3处时钟上升沿,由于Read_Sig还是为高,所以对于Testbench而言,他以为自己从FIFO中读取又读取了一个数据192,但实际上是重复读取的FIFO中最后的一个数值。
结论:对于同步FIFO,空满信号时“假的”,并不能给外部读写的其他模块一个准确的判断。