Asynchronous FIFO is needed whenever we want to transfer data between design blocks that are in different clock domains. The difference in clock domains makes writing and reading the FIFO tricky.
If appropriate precautions are not taken then we could end up in a scenario where write into FIFO has not yet finished and we are attempting to Read it or Vice-versa. This scenario often causes data loss and Metastability issues.
In order to avoid such scenarios, the reading and writing is done via a synchronizer. The synchronizer ensures that read and write pointers calculations are consistent and data in FIFO is not accidentally overwritten or read twice.
However, with the clock crossing we need to ensure that FIFO full and empty conditions are taking into account the clock crossing cycles. In other words, pessimistic full and empty conditions need to be added.
Here’s an example to 8-deep FIFO with Write in aclk domain and read in bclk domain:
logic [3:0] wraddr, wraddr_gray, rdaddr, rdaddr_gray; logic [3:0] wraddr_b1ff,wraddr_bff, wraddr_aff; logic [3:0] rdaddr_a1ff, rdaddr_aff, rdaddr_bff; logic [7:0] wren, wren_qual, rden, rden_qual; logic [31:0] data_ff[7:0]; logic [31:0] data_in, data_out; logic wr_en, rd_en; logic fifo_empty, fifo_full; always_comb begin if(!fifo_full & wr_en) wraddr = wraddr_ff + 1; else wraddr = wraddr_aff; end always_ff @(posedge aclk or negedge reset) begin if(!reset) wraddr_aff <= 0; else if (wr_en) wraddr_aff <= wraddr; else wraddr_aff <= wraddr_aff; end //---- Convert wraddr to Gray code ----// assign wraddr_gray = (wraddr >> 1) ^ wraddr; //----- Clock sync to bclk --------// always_ff @(posedge bclk or negedge reset) begin if(!reset) begin wraddr_bff <= 0; wraddr_b1ff <= 0; end else begin wraddr_bff <= wraddr_gray; wraddr_b1ff <= wraddr_bff; end end always_comb begin if(!fifo_empty & rd_en) rdaddr = rdaddr_bff + 1; else rdaddr = rdaddr_bff; end always_ff @(posedge bclk or negedge reset) begin if(!reset) rdaddr_bff <= 0; else if (rd_en) rdaddr_bff <= rdaddr; else rdaddr_bff <= rdaddr_bff; end //------ Convert rdaddr to Gray code -------// assign rdaddr_gray = (rdaddr >> 1) ^ rdaddr; //------- Clock Sync to aclk ----------// always_ff @(posedge aclk or negedge reset) begin if(!reset) begin rdaddr_aff <= 0; rdaddr_a1ff <= 0; end else begin rdaddr_aff <= rdaddr_gray; rdaddr_a1ff <= rdaddr_aff; end end //------- Data Read and Write ---------// assign wren_qual = (wr_en & !fifo_full) ? (1 << wraddr) : 8'b0; assign rden_qual = (rd_en & !fifo_empty) ? (1 << rdaddr) : 8'b0; genvar i; generate for (i = 0; i < 8; i++) begin always_ff @(posedge aclk or negedge reset) begin if(wren_qual[i]) data_ff[i] <= data_in; else data_ff[i] <= data_ff[i]; end end endgenerate always_comb begin for (int j = 0; j < 8; j++) begin if(rden_qual[j]) data_out = data_ff[j]; else data_out = 8'b0; end //------ Full and Empty Conditions -----------------// assign fifo_full = (wraddr_gray[2:0] == rdaddr_a1ff[2:0]) && (wraddr_gray[3:3] != rdaddr_a1ff[3:3]); assign fifo_empty = (wraddr_b1ff[3:0] == rdaddr_gray[3:0]);
Alternately, similar to Synchronous FIFO, we can also use synchronized write and read rollover signals in calculation of full and empty conditions.
One thought on “Asynchronous FIFO :”
Comments are closed.