同步FIFO
先进先出 (FIFO) 是一个非常流行且有用的设计模块,用于模块之间的同步和握手机制。
FIFO的深度:FIFO中的插槽或行数称为FIFO的深度。
FIFO的宽度:每个插槽或行中可以存储的位数称为FIFO的宽度。
FIFO有两种类型
- 同步FIFO
- 异步FIFO
同步FIFO
在同步FIFO中,数据读取和写入操作使用相同的时钟频率。通常,它们与高时钟频率一起使用以支持高速系统。
同步FIFO操作
信号:
wr_en:写使能
wr_data:写入数据
full:FIFO 已满
empty:FIFO 为空
rd_en:读取启用
rd_data:读取数据
w_ptr:写入指针
r_ptr:读取指针
FIFO写入操作
FIFO可以根据wr_en信号在时钟的每个位置存储/写入wr_data,直到它满为止。FIFO 内存中每次写入数据时,写入指针都会递增。
FIFO读取操作
可以根据rd_en信号在时钟的每个位置从FIFO中取出或读取数据,直到它为空。从 FIFO 存储器读取的每个数据都会增加读取指针。
同步FIFO Verilog代码
同步FIFO可以通过多种方式实现。满满和空条件因实现而异。
方法 1
在此方法中,写入和读取指针的宽度 = log2(FIFO 的深度)。FIFO 满载和满载条件可确定为
空条件
w_ptr == r_ptr,即写入和读取指针具有相同的值。
满状态
满条件意味着FIFO中的每个空间都被占用,但随后w_ptr和r_ptr将再次具有相同的值。因此,无法确定它是满的还是空的条件。因此,FIFO 的最后一个空间有意保持为空,完整条件可以写为 (w_ptr+1'b1) == r_ptr)
Verilog代码
module synchronous_fifo #(parameter DEPTH=8, DATA_WIDTH=8) (
input clk, rst_n,
input w_en, r_en,
input [DATA_WIDTH-1:0] data_in,
output reg [DATA_WIDTH-1:0] data_out,
output full, empty
);
reg [$clog2(DEPTH)-1:0] w_ptr, r_ptr;
reg [DATA_WIDTH-1:0] fifo[DEPTH];
// Set Default values on reset.
always@(posedge clk) begin
if(!rst_n) begin
w_ptr <= 0; r_ptr <= 0;
data_out <= 0;
end
end
// To write data to FIFO
always@(posedge clk) begin
if(w_en & !full)begin
fifo[w_ptr] <= data_in;
w_ptr <= w_ptr + 1;
end
end
// To read data from FIFO
always@(posedge clk) begin
if(r_en & !empty) begin
data_out <= fifo[r_ptr];
r_ptr <= r_ptr + 1;
end
end
assign full = ((w_ptr+1'b1) == r_ptr);
assign empty = (w_ptr == r_ptr);
endmodule
测试台代码
module sync_fifo_TB;
parameter DATA_WIDTH = 8;
reg clk, rst_n;
reg w_en, r_en;
reg [DATA_WIDTH-1:0] data_in;
wire [DATA_WIDTH-1:0] data_out;
wire full, empty;
// Queue to push data_in
reg [DATA_WIDTH-1:0] wdata_q[$], wdata;
synchronous_fifo s_fifo(clk, rst_n, w_en, r_en, data_in, data_out, full, empty);
always #5ns clk = ~clk;
initial begin
clk = 1'b0; rst_n = 1'b0;
w_en = 1'b0;
data_in = 0;
repeat(10) @(posedge clk);
rst_n = 1'b1;
repeat(2) begin
for (int i=0; i<30; i++) begin
@(posedge clk);
w_en = (i%2 == 0)? 1'b1 : 1'b0;
if (w_en & !full) begin
data_in = $urandom;
wdata_q.push_back(data_in);
end
end
#50;
end
end
initial begin
clk = 1'b0; rst_n = 1'b0;
r_en = 1'b0;
repeat(20) @(posedge clk);
rst_n = 1'b1;
repeat(2) begin
for (int i=0; i<30; i++) begin
@(posedge clk);
r_en = (i%2 == 0)? 1'b1 : 1'b0;
if (r_en & !empty) begin
#1;
wdata = wdata_q.pop_front();
if(data_out !== wdata) $error("Time = %0t: Comparison Failed: expected wr_data = %h, rd_data = %h", $time, wdata, data_out);
else $display("Time = %0t: Comparison Passed: wr_data = %h and rd_data = %h",$time, wdata, data_out);
end
end
#50;
end
$finish;
end
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule