Asynchronous FIFO
Introduction to FIFO
FIFO (First In First Out) is a first-in-first-out data buffer. The difference between it and ordinary memory is that there is no external read and write address line , so it is very simple to use, but the disadvantage is that it can only write data sequentially. When reading data, the data address is automatically incremented by 1 by the internal read/write pointer. It cannot be read or written to a specified address by the address line as in ordinary memory.
Synchronous FIFO means that the read clock and write clock are the same clock;
asynchronous FIFO means that the read and write clocks are inconsistent, and the read and write clocks are independent of each other. Asynchronous FIFO is often used for multi-bit data across clock domains.
Asynchronous FIFO empty or full detection
The empty or full state is judged by using Gray code across clock domains:
(Because Gray code only changes 1 bit each time, double clock can be used to cross clock domains, but this will cause delay in state judgment)
The full flag bit is judged in the write clock domain, because it needs to be fed back to the write data end. The
empty flag bit is judged in the read clock domain, because it needs to be fed back to the read data end
. The bit width of the gray code is ADDR_WIDTH+1: (to judge the full state)
Take the following Gray code as an example. If ADDR_WIDTH is 3 and the read pointer is 0000. When the write pointer is 1100, all the data corresponding to the address has been written (written in a circle), then the FIFO is full. When the read pointer is other The same goes for value.
Asynchronous FIFO minimum depth
Asynchronous FIFO example
RTL design (2)-dual-port RAM is used in the module .
asyncfifo.v
`timescale 1ns / 1ps
// Company:
// Engineer:
//
// Create Date: 2020/12/12
// Author Name: Sniper
// Module Name: asyncfifo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module asyncfifo
#(
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 256
)
(
input rst_n,
//write
input wr_clk,
input wr_en,
input [DATA_WIDTH-1:0] wr_data,
output full,
//read
input rd_clk,
input rd_en,
output reg rd_valid,
output [DATA_WIDTH-1:0] rd_data,
output empty
);
localparam ADDR_WIDTH = $clog2(DATA_DEPTH);
//reg
reg [ADDR_WIDTH:0] wr_addr_ptr;//add one extra MSB for check full or empty
reg [ADDR_WIDTH:0] rd_addr_ptr;
reg [ADDR_WIDTH:0] wr_addr_gray_d1;//use for CDC
reg [ADDR_WIDTH:0] wr_addr_gray_d2;
reg [ADDR_WIDTH:0] rd_addr_gray_d1;
reg [ADDR_WIDTH:0] rd_addr_gray_d2;
reg [DATA_WIDTH-1:0] FIFO_RAM [DATA_DEPTH-1:0];
//wire
wire [ADDR_WIDTH-1:0] wr_addr;//RAM present address
wire [ADDR_WIDTH-1:0] rd_addr;
wire [ADDR_WIDTH:0] wr_addr_gray;//gray code corresponding to the address pointer
wire [ADDR_WIDTH:0] rd_addr_gray;
//assign
assign wr_addr = wr_addr_ptr[ADDR_WIDTH-1-:ADDR_WIDTH];//bits from ADDR_WIDTH-1 counting ADDR_WIDTH number bits
assign rd_addr = rd_addr_ptr[ADDR_WIDTH-1-:ADDR_WIDTH];
assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;//translate gray code in order to reduce Metastable state
assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;
/*full : highest two bits different : wr over number */
/*empty: addr equal **********************************/
//full flag in write clock domain
assign full = (wr_addr_gray == {
~(rd_addr_gray_d2[ADDR_WIDTH-:2]),rd_addr_gray_d2[ADDR_WIDTH-2:0]}) ;
//empty flag in read clock domain
assign empty = ( rd_addr_gray == wr_addr_gray_d2 );
//rd_valid delay 1 cycle
always@(posedge rd_clk or negedge rst_n)
begin
if(!rst_n)
rd_valid <= 1'b0;
else if(rd_en && (~empty))
rd_valid <= 1'b1;
else
rd_valid <= 1'b0;
end
//write addr ptr control
always@(posedge wr_clk or negedge rst_n)
begin
if(!rst_n)
wr_addr_ptr <= 'h0;
else if(wr_en && (~full))
wr_addr_ptr <= wr_addr_ptr + 1;
else
wr_addr_ptr <= wr_addr_ptr;
end
//read addr ptr control
always@(posedge rd_clk or negedge rst_n)
begin
if(!rst_n)
rd_addr_ptr <= 'h0;
else if(rd_en && (~empty))
rd_addr_ptr <= rd_addr_ptr + 1;
else
rd_addr_ptr <= rd_addr_ptr;
end
/*****CLock Domain Crossing*****/
//gray code addr buff
always@(posedge rd_clk )
begin
wr_addr_gray_d1 <= wr_addr_gray;
wr_addr_gray_d2 <= wr_addr_gray_d1;
end
//gray code addr buff
always@(posedge wr_clk )
begin
rd_addr_gray_d1 <= rd_addr_gray;
rd_addr_gray_d2 <= rd_addr_gray_d1;
end
dualram
#(
.WIDTH(DATA_WIDTH),
.DEPTH(DATA_DEPTH)
)
u_dualram
(
.wr_clk(wr_clk),
.wr_addr(wr_addr),
.wr_data(wr_data),
.wr_en(wr_en),
.rd_clk(rd_clk),
.rd_addr(rd_addr),
.rd_data(rd_data),
.rd_en(rd_en)
);
endmodule
tb_asyncfifo.v
`timescale 1ns / 1ps
// Company:
// Engineer:
//
// Create Date: 2020/12/12
// Author Name: Sniper
// Module Name: tb_asyncfifo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
module tb_asyncfifo;
//parameter
parameter DATA_WIDTH = 8;
parameter DATA_DEPTH = 256;
reg rst_n;
reg wr_clk;
reg wr_en;
reg [DATA_WIDTH-1:0] wr_data;
wire full;
reg rd_clk;
reg rd_en;
wire rd_valid;
wire [DATA_WIDTH-1:0] rd_data;
wire empty;
initial
begin
rst_n = 0;
wr_clk = 0;
wr_en = 0;
wr_data = 0;
rd_clk = 0;
rd_en = 0;
#100;
rst_n = 1;
fork
forever @(posedge wr_clk)
begin
if(!full)
wr_en <= 1;
else
wr_en <= 0;
if(wr_en == 1)
wr_data <= wr_data + 1;
end
forever @(posedge rd_clk)
begin
if(!empty)
rd_en <= 1;
else
rd_en <= 0;
end
join
end
//clock
always #8 wr_clk = ~wr_clk;
always #5 rd_clk = ~rd_clk;
//DUT
asyncfifo
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_DEPTH(DATA_DEPTH)
)
DUT
(
.rst_n(rst_n),
.wr_clk(wr_clk),
.wr_en(wr_en),
.wr_data(wr_data),
.full(full),
.rd_clk(rd_clk),
.rd_en(rd_en),
.rd_valid(rd_valid),
.rd_data(rd_data),
.empty(empty)
);
initial
begin
$dumpfile("tb_asyncfifo.vcd");
$dumpvars(0,tb_asyncfifo);
end
initial #100_000 $finish;
endmodule
operation result
vcs -R dualram.v asyncfifo.v tb_asyncfifo.v
时钟周期:
always #8 wr_clk = ~wr_clk;
always #5 rd_clk = ~rd_clk;
时钟周期:
always #5 wr_clk = ~wr_clk;
always #12 rd_clk = ~rd_clk;