FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。



FIFO的一些重要参数



 1、FIFO的宽度:也就是英文资料里常看到的THE WIDTH,它指的是FIFO一次读写操作的数据位,就像MCU有8位和16位,ARM 32位等等。



 2、FIFO的深度:THE DEEPTH,它指的是FIFO可以存储多少个N位的数据(如果宽度为N)。如一个8位的FIFO,若深度为8,它可以存储8个8位的数据,深度为12 ,就可以存储12个8位的数据。



    3、满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。



    4、空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。



 5、读指针:指向下一个读出地址。读完后自动加1。



 6、写指针:指向下一个要写入的地址的,写完自动加1。




     对于FIFO,读写指针都指向一个内存的初始位置,每进行一次读写操作,相应的指针就递增一次,指向下一个内存位置。当指针移动到了内存的最后一个位置时,它又重新跳回初始位置。在FIFO非满或非空的情况下,这个过程将随着读写控制信号的变化一直进行下去。如果FIFO处于空的状态,下一个读动作将会导致向下溢(underflow),一个无效的数据被读人;同样,对于一个满了的FIFO,进行写动作将会导致向上溢出(overflow),一个有用的数据被新写入的数据覆盖。这两种情况都属于误动作,因此需要设置满和空两个信号,对满信号置位表示FIFO处于满状态,对满信号复位表示FIFO非满,还有空间可以写入数据;对空信号置位表示FIFO处于空状态,对空信号复位表示FIFO非空,还有有效的数据可以读出。



/**************************************
* Module: fifo
* Date:2014-08-10
*
* Description: FIFO存储器设计
***************************************/
module fifo(clk,rstp,din,writep,readp,dout,emptyp,fullp
);
input clk;
input rstp; // 复位信号
input[15:0] din;
input readp;
input writep;
output[15:0] dout;
output emptyp;
output fullp;
parameter DEPTH = 2,
MAX_COUNT=2'b11; //定义地址最大值

reg emptyp;
reg fullp;
reg[15:0] dout;
reg[(DEPTH-1):0] tail;
reg[(DEPTH-1):0] head;
reg[(DEPTH-1):0] count;
reg[15:0] fifomem[0:MAX_COUNT]; // 定义fifo存储器,4个16位的存储器

// dout被赋给tail指针指向的数值
always @(posedge clk) begin
if(rstp==1) begin
dout <= 16'h0000; // 复位信号有效置0
end
else begin
dout <= fifomem[tail]; //将fifomem中第tail个单元给dout
end
end

// 写入数据
always @(posedge clk) begin
if(rstp==1'b0 && writep == 1'b1 && fullp == 1'b0) begin
fifomem[head]<=din; // 写入
end
end

// head指针递增
always @(posedge clk) begin
if(rstp==1'b1) begin
head<=2'b00;
end
else begin
if(writep==1'b1 && fullp==1'b0) begin
head<=head+1;
end
end
end

//tail指针递增
always @(posedge clk) begin
if(rstp==1'b1) begin
tail<=2'b00;
end
else begin
if(readp==1'b1 && emptyp==1'b0) begin
tail<=tail+1;
end
end
end

// 计数器
always @(posedge clk) begin
if (rstp == 1'b1) begin
count <= 2'b00;
end
else begin
case ({readp, writep})
2'b00: count <= count;
2'b01:
if (count != MAX_COUNT)
count <= count + 1; //为写状态时计数器进行加法计数
2'b10:
if (count != 2'b00)
count <= count - 1; //为读状态计数器进行减法计数
2'b11:
count <= count;
endcase
end
end

// empty指针
always @(count) begin
if (count == 2'b00)
emptyp <= 1'b1; //count为0时emptyp赋为1
else
emptyp <= 1'b0;
end

// fullp指针
always @(count) begin
if (count == MAX_COUNT)
fullp <= 1'b1; //计数到最大时fullp赋为1
else
fullp <= 1'b0;
end

endmodule





/**************************************
* Module: test_fifo
* Date:2014-08-10

*
* Description: FIFO测试程序
***************************************/
module test_fifo;

reg clk;
reg rstp;
reg [15:0] din;
reg readp;
reg writep;
wire [15:0] dout;
wire emptyp;
wire fullp;

reg [15:0] value;
fifo U1 (.clk(clk),.rstp(rstp),.din(din),.readp(readp),.writep(writep),.dout(dout),
.emptyp(emptyp),.fullp(fullp));

// 读任务
task read_word;
begin
@(negedge clk);
readp = 1;
@(posedge clk) #5;
readp = 0;
end
endtask

// 写任务
task write_word;
input [15:0] value;
begin
@(negedge clk);
din = value;
writep = 1;
@(posedge clk);
#5;
din = 16'hzzzz;
writep = 0;
end
endtask


initial begin
clk = 0;
forever begin
#10 clk = 1;
#10 clk = 0;
end
end


initial begin
//test1;
test2; //调用测试模块2
end


task test1;
begin
din = 16'hzzzz;
writep = 0;
readp = 0;
rstp = 1;
#50 rstp = 0;
#50;
write_word (16'h1111);
write_word (16'h2222);
write_word (16‘h3333); //写入3个数据
read_word;
read_word; //读两个
write_word (16‘h4444); //在写一个数据
repeat (6) begin
read_word;
end
write_word (16'h0001);
write_word (16'h0002);
write_word (16'h0003);
write_word (16'h0004);
write_word (16'h0005);
write_word (16'h0006);
write_word (16'h0007);
write_word (16'h0008);
repeat (6) begin
read_word;
end
end
endtask


task test2;
reg [15:0] writer_counter;
begin
writer_counter = 16'h0001;
din = 16'hzzzz;
writep = 0;
readp = 0;
rstp = 1;
#50 rstp = 0;
#50;
fork
//写数据
begin
repeat (500) begin
@(negedge clk);
if (fullp == 1'b0) begin
write_word (writer_counter);
#5;
writer_counter = writer_counter + 1;
end
#50;
end
end
//读数据
begin
forever begin
@(negedge clk);
if (emptyp == 1'b0) begin
read_word;
end
#50;
end
end
join
end
endtask


endmodule