可能是最简单的同步fifo 设计
什么是FIFO
FIFO的英文全称为First In First Out ,是我们在FPGA工程设计开发中经常使用的一种先进先出的数据缓存器。它区别于其他普通存储器(比如rom ,ram)的是没有外部读写地址线。设计开发者使用它的时候不需要对地址进行管理,只需要给写使能.写数据,就能把对应的数据写入存储器保存;如果数据区域不空,给出读使能就能把有效数据读出来。虽然说这样使用对于开发者设计电路结构来说方便简单一些,但也存在很大的局限性:就是在存储或者读取数据时只能顺序进行,而不像使用普通存储器那样可以自由的通过控制地址线来对对应存储区间的数据进行操作。
FIFO的分类
- 同步FIFO:读写时钟为同一个时钟
- 异步FIFO:读写时钟为两个没有关系的独立独立时钟
设计FIFO的关键
不管是设计同步FIFO,还是设计异步FIFO,关键点之一是能产生正确的FIFO空满(empty/full)信号。
FIFO空满判断方法
FIFO空满判断方法有两种:
1.增加拓展位的方法
2.设置数据计数器的方法 (此种方法比较简单)
设置一个数据计数器count。 当写使能有效时每写入一个数据,数据计数器count同时加1;读操作时,每读出一个有效数据,数据计数器count同时减1。当数据计数器count等于0的时刻,fifo为空,empty拉高。当数据计数器count等于fifo深度值时,表示fifo被写满,full拉高。此种方法虽然简单,不易出错,但是由于计数器会占用额外的资源,当fifo的深度较大时,可能会降低fifo的读写速度。
本次同步FIFO设计的基本参数
- FIFO深度:FIFO能存储的数据的个数
- FIFO宽度:写入数据,读出数据的宽度(此处的读写数据位宽保持同样值)。
- 满标志:拉高表示此时FIFO内部已经写满,不能再写入数据。如果仍然进行写数据操作,那么数据会丢失,不会被写入数据存储区
- 空标志:拉高表示此时FIFO内部没有数据,数据已经全部被读走或者没有写入过数据。此时仍然可以进行读数据操作,读出来的数据是无效数据
- 读写时钟:对FIFO进行读写操作的基准时钟
- 读出数据有效标志:此信号为高电平表示此时从FIFO读出的数据是有效的。
具体代码实现过程
写入读出数据位宽,FIFO深度通过参数声明。直接修改参数即可实现其它位宽和深度的FIFO设计。
下面的工程是把上述设计的FIFO作为一个IP核调用,外接一些外设接口在某开发板的测试工程.测试结果表明设计没有问题.
设计源文件部分代码如下:
module syn_fifo
#(
parameter DATA_WIDTH = 3'd4 ,
parameter FIFO_DEPTH = 5'd16 ,
parameter CNT_WIDTH = 3'd5
)
(
input rst_n ,
input clk ,
input [DATA_WIDTH-1:0] data_in ,
input wr_en ,
input rd_en ,
output reg [DATA_WIDTH-1:0] data_out =0 ,
output reg valid =0 ,
output reg [CNT_WIDTH-1 :0] count = 0 ,
output full ,
output empty
);
//------------------------------------------------------------------
// Variable definition
reg [DATA_WIDTH-1:0] ram [FIFO_DEPTH-1:0] ;
reg [CNT_WIDTH-2 :0] read_cnt = 0 ;
reg [CNT_WIDTH-2 :0] write_cnt = 0 ;
//------------------------------------------------------------------
// Read and write pointer,number of data in fifo
always @(posedge clk)
begin
if(rst_n == 1'b0)
begin
read_cnt <= 0 ;
write_cnt <= 0 ;
count <= 0 ;
data_out <= 0 ;
valid <= 0 ;
end
else
begin
case({wr_en,rd_en})
2'b00: begin
count <=count ;
valid <= 0 ;
end
2'b01:
begin
valid <= ( count >0 ) ? rd_en:0 ;
data_out<=ram[read_cnt] ;
read_cnt<= ( count >0 ) ?(read_cnt+1 ):read_cnt ;
if ( count >0 )
count <= count-1 ;
else count <= 0 ;
end
2'b10:
begin
ram[write_cnt] <= ( count<FIFO_DEPTH ) ? data_in:ram[write_cnt] ;
count <= ( count<FIFO_DEPTH ) ? count+1:count ;
write_cnt <= ( count<FIFO_DEPTH ) ? write_cnt+1 :write_cnt ;
valid <= 0 ;
end
2'b11:
begin
ram[write_cnt] <= data_in ;
data_out <= ram[read_cnt] ;
write_cnt <= write_cnt+1 ;
read_cnt <= read_cnt+1 ;
count <= count ;
valid <= (count>0) ? rd_en :0 ;
end
default : ;
endcase
end
end
//------------------------------------------------------------------
// fifo flag output
assign full = (count == FIFO_DEPTH);
assign empty = (count == 0);
endmodule
testbench文件代码如下:
`timescale 1ns/100ps
`define half_cycle 5
module syn_fifo_tb ;
parameter DATA_WIDTH = 3'd4 ;
parameter FIFO_DEPTH = 5'd16 ;
parameter CNT_WIDTH = 3'd5 ;
reg rst_n ;
reg clk ;
reg [DATA_WIDTH-1:0] data_in ;
reg wr_en ;
reg rd_en ;
wire [DATA_WIDTH-1:0] data_out ;
wire valid ;
wire full ;
wire empty ;
syn_fifo
#(
.DATA_WIDTH ( DATA_WIDTH ),
.FIFO_DEPTH ( FIFO_DEPTH ),
.CNT_WIDTH ( CNT_WIDTH )
)
syn_fifo_inst
(
.rst_n ( rst_n ) ,
.clk ( clk ) ,
.data_in ( data_in ) ,
.wr_en ( wr_en ) ,
.rd_en ( rd_en ) ,
.data_out ( data_out ) ,
.valid ( valid ) ,
.full ( full ) ,
.empty ( empty )
);
always #(`half_cycle) clk=~clk;
initial
begin
clk=0;
data_in=0;
rst_n=0;
wr_en =0 ;
rd_en = 0 ;
#50 ;
rst_n=1;
wr_en=1;rd_en=0;
# 500;
wr_en=0;rd_en=1;
#500 ;
wr_en=1;rd_en=0;
#100 ;
wr_en=0;rd_en=1;
#300 ;
wr_en=1;rd_en=0;
#300 ;
wr_en=0;rd_en=1;
#100 ;
wr_en=0;rd_en=0;
#100 ;
wr_en=1;rd_en=0;
#100 ;
wr_en=0;rd_en=0;
#300 ;
wr_en=0;rd_en=1;
#100 ;
wr_en=0;rd_en=0;
#100 ;
wr_en=1;rd_en=0;
#100 ;
wr_en=1;rd_en=1;
#320 ;
wr_en=1;rd_en=1;
#100 ;
wr_en=0;rd_en=0;
#100 ;
wr_en=1;rd_en=0;
#100 ;
wr_en=1;rd_en=0;
#600 ;
$stop;
end
always@(posedge clk)
data_in=data_in+1;
endmodule
仿真结果
-
写入数据测试
仿真测试结果:向深度为16的FIFO中写入16个数据之后,FIFO的full信号被拉高,表明此时已经写满,不能再写入数据。 -
读出数据测试
仿真测试结果: 再连续写入16个数据之后。进行连续读操作,可以把写入的数据正确的读出来(伴随的读数据有效信号为高电平)。注意红框中的信息。 在正确读出最后一个数据的时候,empty拉高,表明目前读出来的数据已经是FIFO里边存储的最后一个数据。之后如果没有新的数据写入,继续进行读操作,那么读出来的数据是无效数据,valid信号应该为低电平。
分享不易,看完点个赞呗。