有关异步FIFO的使用中存在的问题以及解决方法

分类: RAM/ROM/FIFO |
//------2016年10月12日13:46:40-----fifo模块,模块中的读写fifo操作是异步的--------------//
module fifo_module(
clk, rst_n, Write_Req, Read_Req, Fifo_Empty_Sig,
Fifo_Full_Sig,
Fifo_Write_Data, Fifo_Read_Data,
Sq_rS0, Sq_rS1, Sq_rS2, Sq_rS3
);
input clk;
input rst_n;
input Write_Req; //fifo读使能
input Read_Req; //fifo写使能
input [7:0] Fifo_Write_Data; //fifo写入的数据
output [7:0] Fifo_Read_Data; //fifo读出的数据
output Fifo_Full_Sig; //fifo满标志
output Fifo_Empty_Sig; //fifo空标志
parameter Deep = 3'd3; //fifo深度--4
reg [7:0] rShift [Deep:0]; //4深度的fifo
reg [2:0] Count; //fifo中已用深度计数
reg [7:0] Data; //寄存fifo读出的数据
output [7:0] Sq_rS0;
output [7:0] Sq_rS1;
output [7:0] Sq_rS2;
output [7:0] Sq_rS3;
assign Sq_rS0 = rShift[0];
assign Sq_rS1 = rShift[1];
assign Sq_rS2 = rShift[2];
assign Sq_rS3 = rShift[3];
always @ ( posedge clk or negedge rst_n )
if ( !rst_n )
begin //复位操作
rShift[0] <= 8'd0;
rShift[1] <= 8'd0;
rShift[2] <= 8'd0;
rShift[3] <= 8'd0;
Data <= 8'd0; //初始化时,fifo占用深度0
Count <= 3'd0;
end
else if ( Write_Req && !Fifo_Full_Sig )
//当写允许和fifo不满时,向fifo写入数据操作
begin
rShift[0] <= Fifo_Write_Data;
rShift[1] <= rShift[0];
rShift[2] <= rShift[1];
rShift[3] <= rShift[2];
Count <= Count + 1'b1;
end
else if ( Read_Req && !Fifo_Empty_Sig ) //读写异步
begin
Data <= rShift[Count]; //从fifo读出一组数据
Count <= Count - 1'b1;
end
assign Fifo_Full_Sig = ( Count == Deep + 1'b1 ) ? 1'b1 : 1'b0;
//fifo判满操作,满--1,不满--0
assign Fifo_Empty_Sig = ( Count == 1'b0 ) ? 1'b1 : 1'b0;
//fifo判空操作,空--1,不空--0
//不论此处是Deep还是Deep+1,仿真结果都会丢失一组数据!读出的数据都会少一组数据
assign Fifo_Read_Data = Data;
endmodule
//-------------------------2016年10月12日13:47:37---测试模块,向fifo写入4组数据--//
`timescale 1 ns/ 1 ps
module fifo_module_vlg_tst();
reg [7:0] Fifo_Write_Data;
reg Read_Req;
reg Write_Req;
reg clk;
reg rst_n;
// wires
wire Fifo_Empty_Sig;
wire Fifo_Full_Sig;
wire [7:0] Fifo_Read_Data;
wire [7:0] Sq_rS0;
wire [7:0] Sq_rS1;
wire [7:0] Sq_rS2;
wire [7:0] Sq_rS3;
// assign statements (if any)
fifo_module i1 (
// port map - connection between master ports and
signals/registers
.Fifo_Empty_Sig(Fifo_Empty_Sig),
.Fifo_Full_Sig(Fifo_Full_Sig),
.Fifo_Read_Data(Fifo_Read_Data),
.Fifo_Write_Data(Fifo_Write_Data),
.Read_Req(Read_Req),
.Sq_rS0(Sq_rS0),
.Sq_rS1(Sq_rS1),
.Sq_rS2(Sq_rS2),
.Sq_rS3(Sq_rS3),
.Write_Req(Write_Req),
.clk(clk),
.rst_n(rst_n)
);
reg [3:0] i;
initial //50MHz时钟
begin
rst_n = 0;
#10 rst_n = 1;
clk = 0;
forever
#10 clk = ~clk;
end
always @ ( posedge clk or negedge rst_n )
if ( !rst_n )
begin
Fifo_Write_Data <= 8'd0;
Write_Req <= 1'b0;
i <= 4'd0;
end
else
begin
case ( i )
0:
if ( !Fifo_Full_Sig )
begin
Write_Req <= 1'b1; //写允许信号和数据同时生效,所以第一个写入的数据是1,而不是0
Fifo_Write_Data <= Fifo_Write_Data + 1'b1;
end
else
begin
Write_Req <= 1'b0;
i <= i + 1'b1;
end
1:
if ( !Fifo_Empty_Sig )
begin
Read_Req <= 1'b1;
end
else
begin
Read_Req <= 1'b0;
i <= i + 1'b1;
end
2:
i <= i;
endcase
end
endmodule
//--------------------------------------------下面是modelsim的仿真结果------------------------//
存在的问题:
(1)由于.v文件中非阻塞赋值的关系,仿真结果会丢失最后的一组数据(本例中的5)。在100ns时刻,Fifo_Full_Sig还为0,所以测试文件读到.v文件中的fifo还未满。所以会再向fifo输入一组数据,但是在100ns时刻之后,fifo已经满,Fifo_Full_Sig拉高生效是即时的。之后.v模块中fifo不再接受数据,导致最后的一组数据丢失!!
(2)由于220ns处的Count变为0是阻塞赋值,即时生效。所以导致在220ns时刻,测试文件读到Fifo_Empty_Sig拉高,fifo空,决定拉低Read_Req。在下一个时钟下降沿处Read_Req拉低生效。但是此时fifo还有一个数据没有读取,导致丢失。
(3)解决方法待续
2016年10月12日13:58:36
//---------------------------------------2016年10月13日11:27:42
更新---------------------------------------//
为解决上述问题,引入了Left_Sig=Deep-Count;信号
module fifo_module(
clk, rst_n, Write_Req, Read_Req, Fifo_Empty_Sig,
Fifo_Full_Sig,
Fifo_Write_Data, Fifo_Read_Data,
Sq_rS0, Sq_rS1, Sq_rS2, Sq_rS3,
Left_Sig
);
input clk;
input rst_n;
input Write_Req; //fifo读使能
input Read_Req; //fifo写使能
input [7:0] Fifo_Write_Data; //fifo写入的数据
output [7:0] Fifo_Read_Data; //fifo读出的数据
output Fifo_Full_Sig; //fifo满标志
output Fifo_Empty_Sig; //fifo空标志
parameter Deep = 3'd3; //fifo深度--4
reg [7:0] rShift [Deep:0]; //4深度的fifo
reg [2:0] Count; //fifo中已用深度计数
reg [7:0] Data; //寄存fifo读出的数据
output [2:0] Left_Sig;
//改进版
assign Left_Sig = Deep - Count;
//避免读空和写入溢出
output [7:0] Sq_rS0;
output [7:0] Sq_rS1;
output [7:0] Sq_rS2;
output [7:0] Sq_rS3;
assign Sq_rS0 = rShift[0];
assign Sq_rS1 = rShift[1];
assign Sq_rS2 = rShift[2];
assign Sq_rS3 = rShift[3];
always @ ( posedge clk or negedge rst_n )
if ( !rst_n )
begin //复位操作
rShift[0] <= 8'd0;
rShift[1] <= 8'd0;
rShift[2] <= 8'd0;
rShift[3] <= 8'd0;
Data <= 8'd0; //初始化时,fifo占用深度0
Count <= 3'd0;
end
else if ( Write_Req && Read_Req &&
!Fifo_Empty_Sig && !Fifo_Full_Sig )
begin
//if...else默认自带优先级,同时读写的优先级最高--读写同步
rShift[0] <= Fifo_Write_Data; //先写入
rShift[1] <= rShift[0];
rShift[2] <= rShift[1];
rShift[3] <= rShift[2];
Data <= rShift[Count]; //再读出,写入和读出发生在同一个时钟,同时生效
end
else if ( Write_Req )
//当写允许和fifo不满时,向fifo写入数据操作--读写异步
begin
rShift[0] <= Fifo_Write_Data;
rShift[1] <= rShift[0];
rShift[2] <= rShift[1];
rShift[3] <= rShift[2];
Count <= Count + 1'b1;
end
else if ( Read_Req ) //读写异步
begin
Data <= rShift[Count]; //从fifo读出一组数据
if ( !Fifo_Empty_Sig )
//最后一次,只读数据,计数器不减
Count <= Count - 1'b1; //读count和Count-1同时生效,在最后一组数据时候,由于
//Fifo_Empty_Sig是即时的结果、
//导致最后一组数据
end
assign Fifo_Full_Sig = ( Count == Deep + 1'b1 ) ? 1'b1 : 1'b0;
//fifo判满操作,满--1,不满--0
assign Fifo_Empty_Sig = ( Count == 1'b0 ) ? 1'b1 : 1'b0;
//fifo判空操作,空--1,不空--0
//不论此处是Deep还是Deep+1,仿真结果都会丢失一组数据!读出的数据都会少一组数据
assign Fifo_Read_Data = Data;
endmodule
//----------------测试文件-------------------//
`timescale 1 ns/ 1 ps
module fifo_module_vlg_tst();
reg [7:0] Fifo_Write_Data;
reg Read_Req;
reg Write_Req;
reg clk;
reg rst_n;
// wires
wire Fifo_Empty_Sig;
wire Fifo_Full_Sig;
wire [7:0] Fifo_Read_Data;
wire [7:0] Sq_rS0;
wire [7:0] Sq_rS1;
wire [7:0] Sq_rS2;
wire [7:0] Sq_rS3;
wire [2:0] Left_Sig;
// assign statements (if any)
fifo_module i1 (
// port map - connection between master ports and
signals/registers
.Fifo_Empty_Sig(Fifo_Empty_Sig),
.Fifo_Full_Sig(Fifo_Full_Sig),
.Fifo_Read_Data(Fifo_Read_Data),
.Fifo_Write_Data(Fifo_Write_Data),
.Read_Req(Read_Req),
.Sq_rS0(Sq_rS0),
.Sq_rS1(Sq_rS1),
.Sq_rS2(Sq_rS2),
.Sq_rS3(Sq_rS3),
.Write_Req(Write_Req),
.clk(clk),
.rst_n(rst_n),
.Left_Sig(Left_Sig)
);
reg [3:0] i;
reg [3:0] j;
initial //50MHz时钟
begin
rst_n = 0;
#10 rst_n = 1;
clk = 0;
forever
#10 clk = ~clk;
end
always @ ( posedge clk or negedge rst_n )
if ( !rst_n )
begin
Fifo_Write_Data <= 8'd0;
Write_Req <= 1'b0;
i <= 4'd0;
j <= 4'd0;
end
else
begin
case ( i )
0:
if ( Left_Sig <= 3'd1 )
//读写异步
begin
Write_Req <= 1'b0;
i <= i + 1'b1;
end
else
begin
Write_Req <= 1'b1; //写允许信号和数据同时生效,
//所以第一个写入的数据是1,而不是0
Fifo_Write_Data <= Fifo_Write_Data + 1'b1;
end
1:
if ( Left_Sig <= 3'd2 )
begin
Read_Req <= 1'b1;
end
else
begin
Read_Req <= 1'b0;
i <= i + 1'b1;
end
2:
i <= i;
endcase
end
endmodule
//------------下面是读写数据的仿真结果-------------------------------------//