IC学习笔记2——同步FIFO
前言
FIFO(First in First out)是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取和写入某个指定的地址,FIFO分为同步FIFO和异步FIFO,接下来会首先介绍同步FIFO,再深入介绍异步FIFO相关问题。
一、同步FIFO
同步FIFO和异步FIFO最大的区别是,同步FIFO的读时钟和写时钟都是同一时钟。
1.1 实现原理
1.2 模块端口
一个简单的同步FIFO端口设计如下图所示:
上图列出了同步FIFO需要的外部端口信号,在这里简单的解释一下,其中clk为时钟信号。
rst_n为复位信号,后缀_n表示低电平有效。
din[7:0] 为输入数据,这里为8位数据。
we为写使能信号,当we为高电平的时候表示有效。
re为读使能信号,当re为高电平的时候表示有效。
dout[7:0]为输出数据,这里也是8位数据。
full为满信号,当full为高电平时,表明FIFO已经写满,此时即使we为高电平也不能写入数据。
empty为空信号,当empty为高电平时,表示FIFO里面数据已经读完,即使re为高电平也不能读出数据。
上面所列是一些外部信号,当然在同步FIFO中也存在一些内部信号。
wptr[3:0]为写指针信号,总是指向下一个时钟要写的地址,在这里写指针信号是4位,表示FIFO的大小为16。
rptr[3:0]为读指针信号,总是指向下一个时钟要读的地址。
[7:0] ram[0:15] ram表示同步FIFO存储数据的存储体,[7:0]表示一个数据的位数,[0:15]存储的个数,0~15表示可
以存储16个数据。
1.3 实现代码
module sfifo(
input clk,
input rst_n,
input we,
input re,
input [7:0] din,
output empty,
output full,
output reg [7:0] dout
);
reg[7:0] ram[0:15];
reg[4:0] cnt;
reg[3:0] wptr;
reg[3:0] rptr;
always@(posedge clk or negedge rst_n) //下面是对读指针的描述
begin
if(rst_n==1'b0)
rptr<=4'b0;
else if(empty==1‘b0&re==1’b1) //这段代码的意思是非空读使能,读指针加1
rptr<= rptr+1’b1;
else
rptr<= rptr;
end
always@(posedge clk or negedge rst_n) //下面是对读操作的描述
begin
if(rst_n==1'b0)
dout<=8’bzzzz_zzzz; //同步FIFO初始输出为高阻态
else if(empty==1‘b0&re==1’b1) //这段代码的意思是非空读使能,将读指针对应的数据取出,送往输出
//假如此时读指针是3,同时发生 rptr<= rptr+1’b1和dout<= ram[rptr],应注意此时后者读取是读指针为3的数据
dout<= ram[rptr];
else
dout<= dout;
end
always@(posedge clk or negedge rst_n) //下面是对写指针的描述
begin
if(rst_n==1'b0)
wptr<=4'b0;
else if(full==1‘b0&we==1’b1) //这段代码的意思是非满写使能,写指针加1
wptr<= wptr+1’b1;
else
wptr<= wptr;
end
always@(posedge clk ) //下面是对写操作的描述
begin
if(full==1‘b0&we==1’b1) //这段代码的意思是非满写使能,将输入数据写到写指针对应的ram中
ram[wptr]<= din;
else
ram[wptr]<= ram[wptr];
end
always@(posedge clk or negedge rst_n) //下面是对计数器的描述
begin
if(rst_n==1'b0) //初始计数器为0,表示FIFO此时写入的数据个数为0.可以被读出的个数也为0
cnt<=5'b0;
else if((empty==1‘b0&re==1’b1) && (full==1‘b0&we==1’b1)) //同时读写,计数器不变
cnt<=cnt;
else if (full==1‘b0&we==1’b1) //非满写使能,表示一次写行为,计数器加1
cnt<=cnt+1'b1;
else if(empty==1‘b0&re==1’b1) //非空读使能,表示一次读行为,计数器减1
cnt<=cnt-1'b1;
else
cnt<=cnt;
end
assign empty =(cnt==5‘b0)? 1’b1:1‘b0; //当计数器为0时,表示同步FIFO中没有数据可读,所以empty为1
assign full =(cnt==5‘d16)?1’b1:1‘b0; //当计数器为16时,表示同步FIFO中存储的数据已满,所以full为1
endmodule
1.4 仿真测试
module sfifo_tb();
reg clk;
reg rst_n;
reg we;
reg re;
reg [7:0] din;
wire [7:0] dout;
wire empty;
wire full;
sfifo u1( //例化
.clk (clk),
.rst_n (rst_n),
.we (we),
.re (re),
.din (din),
.empty (empty),
.full (full),
.dout (dout)
);
initial begin //设置时钟,周期是20ns,50Mhz
clk=0;
forever #10 clk=~clk;
end
initial begin
rst_n=1'b0; //设置初始状态,复位,读无效,写无效,输入数据为0
we =1'b0;
re =1'b0;
din =8'b0;
#28 rst_n=1'b1; //松开复位
we=1’b1; //写使能
din=8‘d1; //输入数据为1
@(posedge clk); //写入数据
repeat(15) //接着写入2,3,4,5,6,7,8,9,10,11,12,
//13,14,15,16这些数据
begin
#18;
din=din+1'b1;
@(posedge clk);
end
#18 din=din+1'b1; //此时同步FIFO已经写满了,在往同步FIFO中写数据17,17这个数据不会被写进
@(posedge clk);
#18 re=1’b1; //读使能,写无效
we=1'b0;
@(posedge clk); //第一个读出的数为1
repeat(15) //读取剩余的数
begin
@(posedge clk);
end
#2;
re=1‘b0; //结束读操作
end
endmodule
其仿真结果波形图如下所示,在we为高时,clk上升沿时写入第一个数据1. 数据16为最后写入的数据,这个时候同步FIFO的满信号full为高。在往其中写数据17,是写不进去的。读操作和写操作类似,如下图所示。