RTL Design (5)-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
Insert picture description here
Insert picture description here
. The bit width of the gray code is ADDR_WIDTH+1: (to judge the full state)
Insert picture description here
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.
Insert picture description here

Asynchronous FIFO minimum depth

Insert picture description here

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;
Insert picture description here

时钟周期:
always #5 wr_clk = ~wr_clk;
always #12 rd_clk = ~rd_clk;
Insert picture description here
Insert picture description here

Guess you like

Origin blog.csdn.net/meng1506789/article/details/111075932