这篇文章主要介绍一下同步fifo用verilog实现。首先介绍一下fifo的相关知识。
fifo是 first input first output 的缩写,即先进先出队列,fifo一般用作不同时钟域的缓冲器。fifo根据读和写的时钟是否为同一时钟分为同步fifo和异步fifo。异步fifo相比同步fifo来说,设计更加复杂一点。本文中先讲同步fifo的一种设计方法。下图是同步fifo的结构图:
设计fifo的时候一般需要考虑的有两点:
1.fifo的大小
fifo的大小指就是双端口ram的大小,这个可以根据设计需要来设置。
2.fifo空满状态的判断
fifo空满状态的判断通常有两种方法。
a、fifo中的ram一般是双端口ram,所以有独立的读写地址。因此可以一种是设置读,写指针,写指针指向下一个要写入数据的地址,读指针指向下一个要读的地址,最后通过比较读指针和写指针的大小来确定空满状态。
b、设置一个计数器,当写使能有效的时候计数器加一;当读使能有效的时候,计数器减一,将计数器与ram的size进行比较来判断fifo的空满状态。这种方法设计比较简单,但是需要的额外的计数器,就会产生额外的资源,而且当fifo比较大时,会降低fifo最终可以达到的速度。
下面的设计是将fifo当做一个整体来进行设计,输入输出端口有clk,rst,data_in,data_out,wr_en,rd_en,empty,full,利用verilog进行设计,代码如下:
module fifo(clk,rst,wr_en,rd_en,data_in,data_out,empty,full);
input clk,rst;
input wr_en,rd_en;
input [7:0]data_in;//输入数据
output [7:0]data_out;//输出数据
output empty,full;//空满标志
wire empty,full;
reg [7:0]data_out;
reg [7:0] ram[15:0];//dual port RAM
reg [3:0] wr_ptr,rd_ptr;//写和读指针
reg [3:0] counter;//用来判断空满
always@(posedge clk)
begin
if(!rst)
begin
counter=0;
data_out=0;
wr_ptr=0;
rd_ptr=0;
end
else
begin
case({wr_en,rd_en})
2'b00: counter=counter;
2'b01:
begin
data_out=ram[rd_ptr];//先进先出,因此读的话依旧按照次序来
counter=counter-1;
rd_ptr=(rd_ptr==15)?0:rd_ptr+1;
end
2'b10:
begin
ram[wr_ptr]=data_in;//写操作
counter=counter+1;
wr_ptr=(wr_ptr==15)?0:wr_ptr+1;
end
2'b11:
begin
ram[wr_ptr]=data_in;//读写同时进行,此时counter不增加
data_out=ram[rd_ptr];
wr_ptr=(wr_ptr==15)?0:wr_ptr+1;
rd_ptr=(rd_ptr==15)?0:rd_ptr+1;
end
endcase
end
end
assign empty=(counter==0)?1:0;
assign full =(counter==15)?1:0;
endmodule
在modelsim中进行仿真,测试代码如下:
`timescale 1ns/1ns
`define half_cycle 10
module fifoTB;
wire empty,full;
wire [7:0]data_out;
reg clk,rst;
reg wr_en,rd_en;
reg [7:0] data_in;
always #(`half_cycle) clk=~clk;
initial
begin
clk=0;
data_in=0;
rst=1;
#10 rst=0;
#10 rst=1;
wr_en=1;rd_en=0;
#60
wr_en=0;rd_en=1;
#60
wr_en=1;rd_en=0;
#320 wr_en=0;rd_en=1;
#320 wr_en=1;rd_en=0;
#60 $stop;
end
always@(posedge clk)
begin
data_in=data_in+1;
end
fifo m(clk,rst,wr_en,rd_en,data_in,data_out,empty,full);
endmodule
得到的仿真图如下: