同步FIFO架构
下图展示了一个同步FIFO的通用架构。DPRAM(双端口RAM)用作FIFO的存储器以使读、写可以独立进行。
通过读、写指针产生各自的读、写地址,送到读、写端口。写指针指向下一个要写入的地址,读指针指向下一个要读取的地址。有效写使能使写地址递增,而有效的读使能使读指针递增。
在上图中的状态模块产生fifo_empty和fifo_full信号。如果fifo_full有效,说明FIFO内的空间已满不能再写入数据。如果fifo_empty有效说明FIFO内没有可供读取的下一个有效数据。通过对两个指针进行相同的逻辑,该模块也指示出任意时刻FIFO中空或满区域的个数。
双端口存储器(DPRAM)可以同步读取或异步读取。对于同步读操作,应该在数据在FIFO输出端有效前提供明确的读信号。对于异步读操作,DPRAM没有寄存的数据输出;有效数据在写入后即可用(先读数据然后递增指针值)。
空满信号判断方法
第一种方法:通过多设置一位的读写指针信号判断空满,当读写指针相等时则为空,当读写指针最高位相反,其余为相等时则为满(相当于写指针比读指针多绕了一圈)。
第二种方法:设置一个额外的计数器来判断,计数器在复位时初始化为0。随后的任何写操作会使其递增1,任何读操作会使其递减1。当计数器的值为0时则为空,当计数器的值等于FIFO深度时,则为满。
但是该方法比第一种方法相比效率要低一些。因为这种方法要求增加额外的硬件来产生FIFO空和满的条件。随着FIFO深度的增加,比较器的宽度也会增加。
设计代码
代码设计要点主要为读写指针的控制,且根据读写指针的大小关系判断空满,
且代码设计为满后即使写使能拉高也不能往里写数据,为空后即使读使能拉高后也读取不了数据,
module syn_fifo#(
parameter DATA_WIDTH = 16,
parameter DEPTH = 8)
(
input clk,
input rst,
input wr_en,
input [DATA_WIDTH-1:0]wdata,
input rd_en,
output reg [DATA_WIDTH-1:0]rdata,
output reg full,
output reg empty);
reg [DATA_WIDTH-1:0]ram[DEPTH-1:0];
reg [$clog2(DEPTH):0]wpoint,rpoint;
wire full_r,empty_r;
assign empty_r = (wpoint == rpoint);
assign full_r = ((wpoint[$clog2(DEPTH)]!=rpoint[$clog2(DEPTH)])&&(wpoint[$clog2(DEPTH)-1:0]==rpoint[$clog2(DEPTH)-1:0]));
always@(posedge clk or negedge rst)begin
if(rst)begin
full <= 1'b0;
empty <= 1'b0;
end
else if(full_r)
full <= 1'b1;
else if(empty_r)
empty <= 1'b1;
else begin
full <= 1'b0;
empty <= 1'b0;
end
end
always@(posedge clk or negedge rst)begin
if(rst)
wpoint <= 0;
else if(wr_en&&(!full_r))
wpoint <= wpoint + 1;
end
always@(posedge clk or negedge rst)begin
if(rst)
rpoint <= 0;
else if(rd_en&&(!empty_r))
rpoint <= rpoint + 1;
end
always@(posedge clk or negedge rst)begin
if(rst)
ram[wpoint[$clog2(DEPTH)-1:0]] <= 0;
else if(wr_en&&(!full_r))
ram[wpoint[$clog2(DEPTH)-1:0]] <= wdata;
end
always@(posedge clk or negedge rst)begin
if(rst)
rdata <= 0;
else if(rd_en&&(!empty_r))
rdata <=ram[rpoint[$clog2(DEPTH)-1:0]];
end
endmodule
仿真文件
module tb();
parameter DATA_WIDTH = 8;
parameter DEPTH = 8;
reg clk,rst;
reg wr_en;
reg [DATA_WIDTH-1:0]wdata;
reg rd_en;
wire [DATA_WIDTH-1:0]rdata;
wire full, empty;
initial begin
clk = 1;
rst = 1;
wr_en = 0;
rd_en = 0;
wdata = 0;
end
always #10 clk = ~clk;
initial begin
#25;
rst = 1'b0;
#3;
wr_en = 1;
repeat(9)begin
@(negedge clk)
wdata = $random;
end
@(negedge clk)
wr_en = 0;
rd_en = 1;
repeat(8)begin
@(negedge clk);
end
end
syn_fifo #(
.DATA_WIDTH(DATA_WIDTH),
.DEPTH(DEPTH))
inst (
.clk(clk),
.rst(rst),
.wr_en(wr_en),
.wdata(wdata),
.rd_en(rd_en),
.rdata(rdata),
.full(full),
.empty(empty));
endmodule
这里设计深度为8,但设置了九个输入数据。可以看到01并没有写入到ram中
读数据