Simulation and Synthesis Techniques For Asynchronous FIFO Design
Simulation and Synthesis Techniques For Asynchronous FIFO Design
Simulation and Synthesis Techniques For Asynchronous FIFO Design
net/publication/252160343
CITATIONS READS
73 7,369
2 authors, including:
Clifford E. Cummings
Sunburst Design, Inc.
25 PUBLICATIONS 372 CITATIONS
SEE PROFILE
All content following this page was uploaded by Clifford E. Cummings on 21 January 2015.
ABSTRACT
FIFOs are often used to safely pass data from one clock domain to another asynchronous clock domain. Using a
FIFO to pass data from one clock domain to another clock domain requires multi-asynchronous clock design
techniques. There are many ways to design a FIFO wrong. There are many ways to design a FIFO right but still
make it difficult to properly synthesize and analyze the design.
This paper will detail one method that is used to design, synthesize and analyze a safe FIFO between different clock
domains using Gray code pointers that are synchronized into a different clock domain before testing for "FIFO full"
or "FIFO empty" conditions. The fully coded, synthesized and analyzed RTL Verilog model (FIFO Style #1) is
included.
Using n-bit pointers where (n-1) is the number of address bits required to access the entire FIFO memory buffer, the
FIFO is empty when both pointers, including the MSBs are equal. And the FIFO is full when both pointers, except
the MSBs are equal.
The FIFO design in this paper uses n-bit pointers for a FIFO with 2(n-1) write-able locations to help handle full and
empty conditions. More design details related to the full and empty logic are included in section 5.0.
In the behavioral model of Example 1, it is okay to use binary-count pointers, a Verilog array to represent the FIFO
memory buffer, multi-asynchronous clocks in the same module and non-registered outputs. THIS MODEL IS NOT
INTENDED FOR SYNTHESIS! (Hopefully enough capital letters have been used in this section to discourage
anyone from trying to synthesize this model!)
Two of the always blocks in the module (the always blocks with concatenations) are included to behaviorally
represent the synchronization that will be required in the actual RTL FIFO design. They are not important to the
testing of the data transfer through the FIFO, but they are important to the testing of the correctly timed full and
empty flags in the FIFO model. The exact number of synchronization stages required in the behavioral model is
FIFO-design dependent. This model can be used to help test the FIFO design described in this paper.
Consider the example shown in Figure 6 of an 8-deep FIFO. In this example, a 3-bit Gray code pointer is used to
address memory and an extra bit (the MSB of a 4-bit Gray code) is added to test for full and empty conditions. If the
FIFO is allowed to fill the first seven locations (words 0-6) and then if the FIFO is emptied by reading back the
same seven words, both pointers will be equal and will point to address Gray-7 (the FIFO is empty). On the next
write operation, the write pointer will increment the 4-bit Gray code pointer (remember, only the 3 LSBs are being
used to address memory), making the MSBs different on the 4-bit pointers but the rest of the write pointer bits will
match the read pointer bits, so the FIFO full flag would be asserted. This is wrong! Not only is the FIFO not full,
but the 3 LSBs did not change, which means that the addressed memory location will over-write the last FIFO
memory location that was written. This too is wrong!
This is one reason why the dual n-bit Gray code counter of Figure 4 and Section 4.0 is used.
The correct method to perform the full comparison is accomplished by synchronizing the rptr into the wclk
domain and then there are three conditions that are all necessary for the FIFO to be full:
(1) The wptr and the synchronized rptr MSB's are not equal (because the wptr must have wrapped
one more time than the rptr).
(2) The wptr and the synchronized rptr 2nd MSB's are not equal (because an inverted 2nd MSB from
one pointer must be tested against the un-inverted 2nd MSB from the other pointer, which is required if the
MSB's are also inverses of each other - see Figure 6 above).
(3) All other wptr and synchronized rptr bits must be equal.
In order to efficiently register the wfull output, the synchronized read pointer is actually compared against the
wgnext (the next Gray code that will be registered in the wptr). This is shown below in the sequential always
block that has been extracted from the wptr_full.v code of Example 7:
In the above code, the three necessary conditions to check for FIFO-full are tested and the result is assigned to the
wfull_val signal, which is then registered in the subsequent sequential always block.
The continuous assignment to wfull_val can be further simplified using concatenations as shown below:
Figure 3 by placing a second adder after the Gray-to-binary combinational logic to add four to the binary value and
register the result. This registered value would then be used to do subtraction against the synchronized rptr after it
has been converted to a binary value in the wclk domain, and if the difference is less than four, an almost_full
bit could be set. A less-than operation insures that the almost_full bit is set for the full range when the wptr is
within 0-4 counts of catching up to the synchronized rptr. Similar logic could be used in the rclk-domain to
generate the almost_empty flag.
Almost full and almost empty have not been included in the Verilog RTL code shown in this paper.
`ifdef VENDORRAM
// instantiation of a vendor's dual-port RAM
vendor_ram mem (.dout(rdata), .din(wdata),
.waddr(waddr), .raddr(raddr),
.wclken(wclken),
.wclken_n(wfull), .clk(wclk));
`else
// RTL Verilog memory model
localparam DEPTH = 1<<ADDRSIZE;
reg [DATASIZE-1:0] mem [0:DEPTH-1];
//-------------------
// GRAYSTYLE2 pointer
//-------------------
always @(posedge rclk or negedge rrst_n)
if (!rrst_n) {rbin, rptr} <= 0;
else {rbin, rptr} <= {rbinnext, rgraynext};
//---------------------------------------------------------------
// FIFO empty when the next rptr == synchronized wptr or on reset
//---------------------------------------------------------------
assign rempty_val = (rgraynext == rq2_wptr);
// GRAYSTYLE2 pointer
always @(posedge wclk or negedge wrst_n)
if (!wrst_n) {wbin, wptr} <= 0;
else {wbin, wptr} <= {wbinnext, wgraynext};
//------------------------------------------------------------------
// Simplified version of the three necessary full-tests:
// assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) &&
// (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) &&
// (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));
//------------------------------------------------------------------
assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
wq2_rptr[ADDRSIZE-2:0]});
8.0 Conclusions
Asynchronous FIFO design requires careful attention to details from pointer generation techniques to full and empty
generation. Ignorance of important details will generally result in a design that is easily verified but is also wrong.
Finding FIFO design errors typically requires simulation of a gate-level FIFO design with backannotation of actual
delays and a whole lot of luck!
Synchronization of FIFO pointers into the opposite clock domain is safely accomplished using Gray code pointers.
Generating the FIFO-full status is perhaps the hardest part of a FIFO design. Dual n-bit Gray code counters are
valuable to synchronize and n-bit pointer into the opposite clock domain and to use an (n-1)-bit pointer to do “full”
comparison. Synchronizing binary FIFO pointers using techniques described in section 7.0 is another worthy
technique to use when doing FIFO design.
Generating the FIFO-empty status is easily accomplished by comparing-equal the n-bit read pointer to the
synchronized n-bit write pointer.
The techniques described in this paper should work with asynchronous clocks spanning small to large differences in
speed.
Careful partitioning of the FIFO modules along clock boundaries with all outputs registered can facilitate synthesis
and static timing analysis within the two asynchronous clock domains.
10.0 Acknowledgements
I am grateful to Ben Cohen for his willingness to discuss FIFO design issues with me in preparation for writing this
paper. I would also like to thank Peter Alfke of Xilinx for also discussing with me alternate interesting approaches
to FIFO design.
A special thanks to Steve Golson for doing a great review of the paper on short notice and adding the valuable
information, techniques and advantages related to using binary pointers in FIFO design in place of the Gray code
pointers. Also for finding the original patent information on Frank Gray’s “Pulse Code Communication.”