FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO。同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的不同步可能会导致亚稳态,导致FIFO工作异常,设计较为复杂;因此,此处我们首先对较简单的同步FIFO进行分析与设计:
目录
一、端口设计
二、功能描述
三、实现代码
四、参考文献:
模块端口:
1、时钟信号clk,作为同步FIFO的驱动信号
2、异步复位信号rst_n
// 写FIFO相关
3、数据输入信号din[DW-1:0],作为FIFO数据写入端,DW数据为位宽
4、写使能we
// 读FIFO相关
5、数据输出信号dout[DW-1:0],作为FIFO数据输出端
6、读使能re
// 标志相关
7、FIFO满标志full,FIFO满时不能再写入数据
8、FIFO空标志empty,FIFO空时不能再读出数据
内部信号:
1、读指针wp,作为读地址(FIFO读写只能顺序进行,不能外部设置,因此为内部信号)
2、写指针rp,作为写地址
3、计数cnt,用于空满状态判断
4、[DW-1:0]ram[0:Depth-1],数据存储空间,Depth为FIFO深度
1、读逻辑:
clk来临,re有效,并且FIFO非空(empty=0)时,将读指针rp对应地址处的数据读出至dout;
2、读指针逻辑:
clk来临,re有效,并且FIFO非空(empty=0)时,进行一次读操作,rp加1(即顺序读出);
3、写逻辑:
clk来临,we有效,并且FIFO不满(full=0)时,将din写入写指针wp对应地址处;
4、读指针逻辑:
clk来临,re有效,并且FIFO不满(full=0)时,进行一次写操作,wp加1(即顺序存储);
5、空满检测逻辑:
用cnt表示FIFO中存储的数据,初始化为0。每进行一次写操作,cnt+1;每进行一次读操作,cnt-1;同时读写一次,cnt不变;
cnt = 0时,FIFO为空,empty = 1;
cnt = depth时,FIFO为满,full = 1;
模块描述如下:
应注意cnt取值及判断逻辑
`timescale 1ns / 1ps
//
// Company:
// Engineer: guoliang CLL
//
// Create Date: 2020/02/27 13:11:04
// Design Name:
// Module Name: sfifo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module sfifo
#(parameter DW = 8,AW = 4)//默认数据宽度8,FIFO深度16
(
input clk,
input rst_n,
input we,
input re,
input [DW-1:0]din,
output reg [DW-1:0]dout,
output empty,
output full
);
// internal signal
parameter Depth = 1 << AW;//depth of FIFO
reg [DW-1:0]ram[0:Depth-1];
reg [AW:0]cnt;
reg [AW-1:0]wp;
reg [AW-1:0]rp;
// FIFO declaration
// 空满检测
assign empty = (cnt==0)?1'b1:1'b0;
assign full = (cnt==Depth)?1'b1:1'b0;
// cnt 计数
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 1'd0;
else if(!empty & re & !full & we)//同时读写
cnt <= cnt;
else if(!full & we)//写
cnt <= cnt+1;
else if(!empty & re)//读
cnt <= cnt-1;
else
cnt <= cnt;
end
// 读指针
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
rp <= 1'b0;
else if(!empty & re)
rp <= rp+1'b1;
else
rp <= rp;
end
//写指针
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
wp <= 1'b0;
else if(!full & we)
wp <= wp+1'b1;
else
wp <= wp;
end
// 读操作
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
dout <= {DW{1'bz}};
else if(!empty & re)
dout <= ram[rp];
else
dout <= dout;
end
//写操作
always@(posedge clk)
begin
if(!full & we)
ram[wp] <= din;
else
ram[wp] <= ram[wp];
end
endmodule
测试文件如下:
先只写不读;din从0递增;
再只读不写;
最后同时读写;
`timescale 1ns / 1ps
//
// Company:
// Engineer: guoliang CLL
//
// Create Date: 2020/02/27 13:49:53
// Design Name:
// Module Name: sfifo_tsb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module sfifo_tsb(
);
// port declaration
reg clk;
reg rst_n;
reg we;
reg re;
reg [7:0]din;
wire [7:0]dout;
wire empty;
wire full;
//clk
initial
begin
clk = 1'b0;
forever #10 clk = ~clk;
end
//
initial
begin
rst_n = 1'b1;
din = 1'b0;
re = 1'b0;
we = 1'b0;
#20 rst_n = 1'b0;
#20 rst_n = 1'b1;
// write
we = 1'b1;
repeat(20) #20 din = din+1'b1;
// read
we = 1'b0;
re = 1'b1;
repeat(20) #20;
// read and write
we = 1'b1;
re = 1'b1;
din = 1'b0;
repeat(20) #20 din = din+1'b1;
end
// inst
sfifo inst(
.clk(clk),
.rst_n(rst_n),
.we(we),
.re(re),
.din(din),
.dout(dout),
.empty(empty),
.full(full)
);
endmodule
仿真结果如下:
先只写不读;din从0递增,每个clk来临进行数据存储,cnt+1,直到存满后full=1,不再存入;
再只读不写;每个clk来临进行数据读出,cnt-1,直到读完后empty=1,不再读出;(注意读出一次数据后,full就为0,即FIFO现在不满)
最后同时读写,接上步操作,只有先写入一次后,FIFO才能非空,才能进行读取;因此输出滞后写入一个时钟周期,cnt恒定;
仿真验证了同步FIFO功能的正确,表明设计正确;
RTL电路如下:
Verilog 同步 FIFO 设计
Verilog同步FIFO