FIFO表示先进先出,是一种存储器,要求第一个写入的数据第一个从队列中读出,而同步FIFO表示使用单一时钟来控制写操作和读操作。
这是一个(功能缺失的)FIFO的示意图,写使能时将写数据传入FIFO,在读使能时将内部存储数据传出FIFO。
为满足先进先出的功能,内部需要定义读指针与写指针。代码如下:
module fifo_mem
#(parameter
FIFO_WIDTH = 8,//宽度,是一次读写操作的数据为宽
FIFO_DEPTH = 32,//深度,可以存贮的数据个数
FIFO_PTR = 5//指针位宽,例如深度为32的FIFO需要的指针宽度为5,因为2的5次方是32
)
(clk,rst_n,wr_en,rd_en,wr_data,rd_data);
input clk ;
input rst_n ;
input wr_en ;//写操作使能信号,高有效
input rd_en ;//读操作使能信号,高有效
input [FIFO_WIDTH-1:0] wr_data ;//输入数据
output [FIFO_WIDTH-1:0] rd_data ;//输出数据
reg [FIFO_WIDTH-1:0] raml[FIFO_DEPTH-1:0] ;// 寄存器
reg [FIFO_WIDTH-1:0] rd_data ;
reg [FIFO_PTR-1:0] wr_ptr,rd_ptr ;//读写指针,每次读,读指针+1,每次写,写指针+1
always@(posedge clk or negedge rst_n)
if(!rst_n)//复位信号,对读写指针归零,然后将FIFO内存储数据清零
begin
wr_ptr = 0;
rd_ptr = 0;
repeat(FIFO_DEPTH)
begin
raml[wr_ptr] = 0;
wr_ptr = wr_ptr+1;
end
end
else if(wr_en&&!rd_en)
begin
raml[wr_ptr] <= wr_data;
wr_ptr <= wr_ptr+1;
end
else if(!wr_en&rd_en)
begin
rd_data <= raml[rd_ptr];
rd_ptr <= rd_ptr+1;
end
else if(wr_en&rd_en)
begin
rd_data <= raml[rd_ptr];
raml[wr_ptr] <= wr_data;
rd_ptr <= rd_ptr+1;
wr_ptr <= wr_ptr+1;
end
endmodule
编写测试模块
`timescale 1ns/1ns
module tb #(parameter
FIFO_WIDTH = 8,//宽度,是一次读写操作的数据为宽
FIFO_DEPTH = 32//深度,可以存贮的数据个数
)();
reg clk ;
reg rst_n ;
reg wr_en ;
reg rd_en ;
reg [FIFO_WIDTH-1:0] wr_data ,wr_data_next ;
wire[FIFO_WIDTH-1:0] rd_data ;
initial
begin
clk = 1'b1;
rst_n = 1'b0;
wr_en = 1'b0;
rd_en = 1'b0;
wr_data= 8'b0;
wr_data_next=8'b0;
#20
rst_n = 1'b1;
#10
wr_en = 1'b1;
#5
repeat(40)
begin
wr_data = wr_data_next;
#20 wr_data_next = wr_data+1;
end
wr_en = 1'b0;
#30
rd_en = 1'b1;
end
always#10 clk = ~clk;
fifo_mem fifo_men_inst
(
.clk (clk ) ,
.rst_n (rst_n ) ,
.wr_en (wr_en ) ,
.rd_en (rd_en ) ,
.wr_data (wr_data ) ,
.rd_data (rd_data )
);
endmodule
运行测试模块
但是此FIFO存在一下问题:
1:当写空间用完后可以继续写入数据;对应上面第一张图右下角。
2:当数据读取完后可以继续读取数据;对应上面第二张图后半部分。
这两个问题可以在FIFO中加入一个控制模块来解决,此控制模块中有一个计数器,
由于加入了计数模块,FIFO可以输出自己的可写余量、已用容量以及可读可写标志。
此FIFO的示意图带有control模块,内部有一个计数器,用来计FIFO的可用余量,当计数器为0时,将read_en拉低,stack_empty拉高,当计数器为FIFO的深度时,将write_en拉低和stack_full拉高,此外,FIFO可读可写,将read_end 、write_en、拉高,stack_full、stack_empty拉低 。
编写fifo_control模块
module fifo_control
#(parameter
FIFO_PTR = 5 ,
FIFO_DEEPTH= 32
)
(clk,rst_n,wr_en_in,rd_en_in,wr_en,rd_en,data_value,stack_full,stack_empty);
input clk ;
input rst_n ;
input wr_en_in ;
input rd_en_in ;
output wr_en ;
output rd_en ;
output [FIFO_PTR-1:0] data_value ;//已写数据量
output stack_full ;
output stack_empty ;
wire wr_en ;
wire rd_en ;
reg [FIFO_PTR-1:0] data_value ;
wire stack_full,stack_empty ;
assign stack_full = (data_value == FIFO_DEEPTH-1)? 1:0;
assign stack_empty = (data_value == 0)? 1:0;
assign wr_en = (!stack_full&&wr_en_in) ? 1:0 ;
assign rd_en = (!stack_empty&&rd_en_in)? 1:0 ;
always@(posedge clk or negedge rst_n)
if(!rst_n)
data_value <= 0;
else if((wr_en_in&&!rd_en_in&&!stack_full)
||(wr_en_in&&rd_en_in&&stack_empty&&!stack_full))//写,不读,数据空间加一
data_value <= data_value+1;
else if((!wr_en_in&&rd_en_in&&!stack_empty)
||(wr_en_in&&rd_en_in&&!stack_empty&&stack_full)) //不写,读,数据空间减一
data_value <= data_value-1;
else if(wr_en_in&&rd_en_in&&!stack_empty&&!stack_full) //写,读,数据空间不变
data_value <= data_value;
else
data_value <= data_value;
endmodule
编写fifo模块
module fifo#(parameter
FIFO_WIDTH = 8,//宽度,是一次读写操作的数据为宽
FIFO_DEPTH = 32,//深度,可以存贮的数据个数
FIFO_PTR = 5//指针位宽,例如深度为32的FIFO需要的指针宽度为5,因为2的5次方是32
)
(clk,rst_n,wr_en_in,rd_en_in,wr_data,rd_data,data_value,stack_full,stack_empty);
input clk ;
input rst_n ;
input [FIFO_WIDTH-1:0]wr_data ;
input wr_en_in ;
input rd_en_in ;
output [FIFO_WIDTH-1:0]rd_data ;
output [FIFO_PTR-1:0] data_value ;
output stack_full ;
output stack_empty ;
wire wr_en,rd_en ;
fifo_mem fifo_mem_inst
(
.clk (clk ) ,
.rst_n (rst_n ) ,
.wr_en (wr_en ) ,
.rd_en (rd_en ) ,
.wr_data (wr_data ) ,
.rd_data (rd_data )
);
fifo_control fifo_control_inst
(
.clk (clk ),
.rst_n (rst_n ),
.wr_en_in (wr_en_in ),
.rd_en_in (rd_en_in ),
.wr_en (wr_en ),
.rd_en (rd_en ),
.data_value (data_value ),
.stack_full (stack_full ),
.stack_empty (stack_empty )
);
endmodule
重新编写测试模块
`timescale 1ns/1ns
module tb #(parameter
FIFO_WIDTH = 8,//宽度,是一次读写操作的数据为宽
FIFO_DEPTH = 32,//深度,可以存贮的数据个数
FIFO_PTR = 5//指针位宽,例如深度为32的FIFO需要的指针宽度为5,因为2的5次方是32
)();
reg clk ;
reg rst_n ;
reg wr_en_in ;
reg rd_en_in ;
reg [FIFO_WIDTH-1:0] wr_data ,wr_data_next ;
wire[FIFO_WIDTH-1:0] rd_data ;
wire [FIFO_PTR-1:0] data_value ;
wire stack_full,stack_empty ;
initial
begin
clk = 1'b1;
rst_n = 1'b0;
wr_en_in = 1'b0;
rd_en_in = 1'b0;
wr_data= 8'b0;
wr_data_next=8'b0;
#20
rst_n = 1'b1;
#10
wr_en_in = 1'b1;
#5
repeat(40)
begin
wr_data = wr_data_next;
#20 wr_data_next = wr_data+1;
end
wr_en_in = 1'b0;
#30
rd_en_in = 1'b1;
end
always#10 clk = ~clk;
fifo fifo_inst
(
.clk (clk ),
.rst_n (rst_n ),
.wr_data (wr_data ),
.wr_en_in (wr_en_in ),
.rd_en_in (rd_en_in ),
.rd_data (rd_data ),
.data_value (data_value ),
.stack_full (stack_full ),
.stack_empty (stack_empty )
);
endmodule
测试结果:stack_full成功拉高,数据无法写入
stack_empty成功拉低,数据无法读出
由此实现了同步FIFO