目录
一、设计原理
FIFO是一种先进先出的数据缓存器,与普通存储器的区别在于没有外部读写地址线,只能顺序写入和读取数据。同步FIFO(First In First Out,先进先出)指的是读时钟和写时钟为同一个时钟,即读写操作在同一时钟沿发生。
时钟同步:
- 同步FIFO的核心在于读写操作都在同一个时钟信号的控制下进行。这意味着读使能、写使能、数据输入、数据输出以及满/空状态信号等都在时钟的同一沿(如上升沿或下降沿)发生变化。
内部指针:
- 同步FIFO内部通常包含两个指针:读指针(Read Pointer)和写指针(Write Pointer)。读指针指向下一个要读取的数据位置,写指针指向下一个要写入的数据位置。
- 读写指针在时钟的驱动下自动递增,以追踪数据的读写位置。当写指针追上读指针时(考虑到FIFO的循环特性),表示FIFO已满;当读指针追上写指针时,表示FIFO为空。
数据储存:
- FIFO通常使用双口RAM(Random Access Memory)或类似的存储结构来存储数据。双口RAM允许在同一时间内进行读写操作,适合FIFO的应用场景。
空满状态检测:
- 设计同步FIFO时,需要解决的一个重要问题是如何准确检测FIFO的空和满状态。这通常通过比较读写指针的位置来实现。为了避免读写指针在FIFO满或空时重叠导致的混淆,有时会在指针设计中加入额外的位(如格雷码中的MSB位)来区分这种情况。
读写控制:
- 写操作:当写使能有效且FIFO未满时,数据被写入由写指针指示的位置,随后写指针递增。
- 读操作:当读使能有效且FIFO非空时,数据从由读指针指示的位置读出,随后读指针递增。
二、架构框图
简单同步fifo,仅供参考
三、参考代码
top
// -----------------------------------------------------------------------------
// Item:
// -----------------------------------------------------------------------------
// Author : xiaohui_fpga
// File : syn_fifo.v
// Create : 2024-07-15 15:30:59
// Revise : 2024-07-15 19:40:00
// Instruction :
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps
module syn_fifo#(
parameter DATA_WIDTH = 8 ,
parameter DATA_DEPTH = 4
)(
input clk ,
input reset ,
input wr_en ,
input [DATA_WIDTH-1:0] din ,
input rd_en ,
output reg [DATA_WIDTH-1:0] dout ,
output [$clog2(DATA_DEPTH)-1:0] dcount ,
output full ,
output empty
);
reg [DATA_WIDTH-1:0] fifo_ram [DATA_DEPTH-1:0];
reg [DATA_WIDTH-1:0] wr_ptr;
reg [DATA_WIDTH-1:0] rd_ptr;
reg [$clog2(DATA_DEPTH)-1:0] count;
assign full = count == DATA_DEPTH -1;
assign empty = count == 0 ;
assign dcount = DATA_DEPTH - 1 - count;//还剩空间数量
//写操作
always @(posedge clk) begin
if (wr_en && ~full) begin
fifo_ram[wr_ptr] <= din;
end
else begin
fifo_ram[wr_ptr] <= fifo_ram[wr_ptr];
end
end
//更新写地址
always@(posedge clk)begin
if(reset)begin
wr_ptr <= 0;
end
else if(wr_en && ~full)begin
wr_ptr <= wr_ptr + 1;
end
else begin
wr_ptr <= 0;
end
end
//读操作
always @(posedge clk) begin
if (rd_en && ~empty) begin
dout <= fifo_ram[rd_ptr];
end
else begin
dout <= dout;
end
end
//更新读地址
always@(posedge clk)begin
if(reset)begin
rd_ptr <= 0;
end
else if(rd_en && ~empty)begin
rd_ptr <= rd_ptr + 1;
end
else begin
rd_ptr <= rd_ptr;
end
end
//空间计数
always @(posedge clk) begin
if (reset) begin
count <= 0;
end
else begin
case({wr_en,rd_en})
2'b00:begin
count <= count;
end
2'b01:begin
if (count > 0) begin
count <= count -1;
end
end
2'b10:begin
if (count < DATA_DEPTH-1) begin
count <= count +1;
end
end
default:count <= count;
endcase
end
end
endmodule
tb_top
// -----------------------------------------------------------------------------
// Item:
// -----------------------------------------------------------------------------
// Author : xiaohui_fpga
// File : tb_top.v
// Create : 2024-07-15 16:54:03
// Revise : 2024-07-15 19:49:06
// Instruction :
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps
module tb_top();
parameter DATA_WIDTH = 8;
parameter DATA_DEPTH = 21;
reg clk;
reg reset;
reg wr_en;
reg [DATA_WIDTH-1:0] din;
reg rd_en;
wire [DATA_WIDTH-1:0] dout;
wire [$clog2(DATA_DEPTH)-1:0] dcount;
wire full;
wire empty;
//clock
initial begin
clk = 0;
forever #(10) clk = ~clk;
end
//
initial begin
reset <= 1'b1;
wr_en <= 0;
rd_en <= 0;
din <= 0;
#40
reset <= 1'b0;
#35
wr_en <= 1'b1;
#215
rd_en <= 1'b1;
/*观察empty时,注释47、48行,反注释50、51行*/
//#200
//rd_en <= 0;
/*观察full时,注释50、51行,反注释47、48行*/
//#200
//wr_en <= 0;
end
always @(posedge clk) begin
if (wr_en) begin
din <= din + 1;
end
end
syn_fifo #(
.DATA_WIDTH(DATA_WIDTH),
.DATA_DEPTH(DATA_DEPTH)
) inst_syn_fifo (
.clk (clk),
.reset (reset),
.wr_en (wr_en),
.din (din),
.rd_en (rd_en),
.dout (dout),
.dcount (dcount),
.full (full),
.empty (empty)
);
endmodule
四、仿真结果
例如,检测empty时
五、总结
同步fifo参数化设计,以上为简化版同步fifo架构实现,仅供参考。特别注意:若一次将要写入的数据数量大于该简化版同步fifo设定的深度,但读使能设计合理,避免了fifo的爆满,empty不会与读出最后一位相对应。