A FIFO (First-In First-Out) is a storage buffer in which data is read out in the same order it was written in. It is commonly used to decouple producers and consumers that operate at different rates or in different clock domains.
In practice
FIFOs appear throughout embedded hardware and firmware. On the hardware side, many UART, SPI, I2C, USB, and CAN peripherals include shallow hardware FIFOs (often 4 to 64 bytes deep) between the shift register and the data bus, though depth, presence, and features vary significantly by vendor and peripheral instance. Without them, the CPU or DMA would need to service each byte before the next one arrives. On FPGAs, block RAM or distributed LUT RAM is routinely used to implement deeper FIFOs as IP cores or custom HDL, as discussed in posts like "Fit Sixteen (or more) Asynchronous Serial Receivers into the Area of a Standard UART Receiver."
In firmware, software FIFOs (also called ring buffers or circular buffers) are one of the most common data structures in embedded code. They are typically implemented as a fixed-length array with a read pointer and a write pointer. An ISR writes incoming bytes into the FIFO while the main loop or an RTOS task drains it, safely decoupling the two without dynamic memory allocation.
A particularly important variant is the asynchronous FIFO, which has separate write and read clocks. This is the standard mechanism for crossing data between two unrelated clock domains on an FPGA or SoC. Implementing one correctly requires careful handling of the pointer comparison across clock domains, typically using Gray code counters and multi-stage synchronizers to avoid metastability.
Common pitfalls include ignoring overflow conditions (writing to a full FIFO silently corrupts data or drops it), underflow reads (reading from an empty FIFO returns stale data), and miscalculating the required depth. FIFO depth must be sized to absorb the worst-case burst of data that can arrive before the consumer services it; undersizing is a frequent source of subtle data-loss bugs.
Frequently asked
What is the difference between a FIFO and a ring buffer?
They describe the same concept at different levels. 'Ring buffer' (or circular buffer) refers to the common software implementation using an array with head and tail indices that wrap around. 'FIFO' is the abstract behavioral property (first-in first-out ordering). A hardware FIFO built from
flip-flops or
block RAM and a software ring buffer are both FIFOs.
How deep does a FIFO need to be?
Depth depends on the maximum burst size the producer can generate before the consumer can respond. For a UART RX FIFO serviced by a polling loop, you need enough depth to hold all bytes that can arrive during the longest interval the CPU might be busy elsewhere. In
FPGA clock-domain-crossing designs, the required depth is determined by the latency of the synchronizer pipeline and the maximum data rate differential between the two clock domains.
How do you safely implement an asynchronous FIFO across two clock domains?
The standard technique is to maintain the read and write pointers in Gray code. Because adjacent Gray code values differ by only one bit, a pointer that is mid-transition when sampled in the other clock domain can only be misread as the previous or current value, not an arbitrary one. Each pointer is then passed through two or more
flip-flop synchronizer stages in the opposite clock domain before being used for full/empty status calculations.
What happens when a FIFO overflows or underflows?
Behavior depends on the implementation. Hardware FIFOs in MCU peripherals typically set a status flag on overflow or underflow, but the exact response varies by device: overflow may drop new data or overwrite old data, and underflow behavior (such as what value is returned) is implementation-specific and not safe to generalize. Some peripherals assert an interrupt. Software ring buffers do whatever the developer coded: common choices are to drop the newest byte, drop the oldest byte (overwrite), or assert a fault flag. Neither hardware nor software FIFOs guarantee any particular behavior by default, so the status flags must be checked explicitly.
Can a FIFO be used between an ISR and the main loop without a mutex?
On single-core targets, a single-producer single-consumer ring buffer can be made interrupt-safe without a mutex, provided the read and write indices are each updated atomically (i.e., they fit in a single native-width write) and the index update is published only after the data is fully written or read. On multi-core targets or when the FIFO has multiple producers or consumers, additional synchronization is required.
Differentiators vs similar concepts
A FIFO contrasts with a LIFO (Last-In First-Out) structure, commonly implemented as a stack, where the most recently written item is the first one read back. It also differs from a mailbox or message queue in an RTOS context, which typically adds metadata (message size, priority, or sender ID) and blocking semantics on top of FIFO ordering. In hardware, a synchronous FIFO (typically single-clock) is often confused with an asynchronous FIFO (which uses separate clocks for read and write); the two require fundamentally different designs, and using a synchronous FIFO implementation across a clock domain boundary will produce
metastability failures.