EmbeddedRelated.com

FIFO

Category: Memory | Also known as: first-in first-out queue

A FIFO (first-in first-out queue) is a data structure or hardware peripheral in which data is read out in exactly the order it was written: the first item enqueued is the first item dequeued. It is the standard mechanism for buffering a stream of data between two points that produce or consume at different rates or in different execution contexts.

In practice

FIFOs appear in embedded systems in two distinct forms. Software FIFOs, typically implemented as circular (ring) buffers in RAM, are used to pass data between ISRs and main-loop code, between RTOS tasks, or between protocol layers. Hardware FIFOs are integrated directly into peripherals: UARTs on many MCUs (for example, the STM32 USART has a configurable FIFO and the LPC55S6x has a shared system FIFO) buffer incoming or outgoing bytes so the CPU does not have to respond to every single byte interrupt. Similarly, most CAN controllers include multi-message receive FIFOs that buffer whole frames rather than individual bytes.

The primary problem a FIFO solves is rate decoupling. A UART receiving at 115200 baud produces bytes at a regular rate on the wire, but the intervals at which the main loop runs and services those bytes are irregular. A FIFO absorbs incoming data and lets the application drain it at a convenient time. Similarly, in an RTOS environment, a FIFO (often called a message queue or ring buffer) lets a producer task and a consumer task run at independent rates without sharing global variables directly. This pattern of using queues to pass events between communicating state machines is common in event-driven embedded designs.

The most common pitfall in software FIFO implementations is the overflow case: when the producer is faster than the consumer and the buffer fills, the implementation must decide whether to drop the new item (drop-on-full), overwrite the oldest item (overwrite mode), or block. Each policy is correct in different applications, and the wrong choice leads to silent data loss or deadlock. A second pitfall involves concurrency: a ring buffer accessed from both an ISR and a main loop requires careful attention to atomicity. On many 32-bit MCUs, updating the head or tail index is a single-instruction store and is often safe if only one side writes each index, but this is a rule of thumb rather than a guarantee — atomicity depends on the specific architecture, alignment, and memory model. On 8-bit architectures a 16-bit index update is not atomic, and explicit critical sections are needed.

Memory-constrained targets force careful sizing. A FIFO that is too small causes frequent overflows; one that is too large wastes scarce RAM. Working within tight memory budgets requires deliberate design. When double-buffering is a viable alternative to a ring buffer (swapping two fixed buffers rather than maintaining head/tail pointers), the tradeoff between the two approaches deserves explicit consideration.

Frequently asked

What is the difference between a FIFO and a circular buffer?
They are usually the same thing implemented differently. A circular (ring) buffer is the standard software technique for implementing a FIFO in a fixed block of RAM: two indices (head and tail) chase each other around an array. The term 'FIFO' describes the ordering policy; 'circular buffer' or 'ring buffer' describes the data structure. Hardware FIFOs inside peripherals achieve the same ordering policy in dedicated silicon rather than RAM.
How do I safely share a FIFO between an ISR and main-loop code?
The common safe pattern for a single-producer/single-consumer ring buffer is to have the ISR own one index (say, the write index) and the main loop own the other (the read index). Each side reads the other's index only once per operation and treats it as volatile. Because each side writes only its own index, no lock is needed on most architectures provided the index read and write are each individually atomic. On 8-bit MCUs where a multi-byte index cannot be written atomically, or in multi-producer/multi-consumer arrangements, a critical section (disabling interrupts briefly) or an RTOS-provided queue primitive is required.
How large should a FIFO be?
Size the FIFO to cover the worst-case burst the producer can emit before the consumer next runs. For a UART receiving at 115200 baud feeding a main loop that runs every 10 ms, the worst-case burst is roughly 115200/8 * 0.01 = ~144 bytes, so a 256-byte buffer provides comfortable margin. For RTOS message queues, depth is typically expressed in message counts rather than bytes; a depth of 8 to 32 messages is common for inter-task communication. Always add margin beyond the calculated minimum and instrument for high-water-mark violations during testing.
What happens when a FIFO overflows?
The hardware or software FIFO is full and a new item arrives before an old one has been read. Hardware FIFOs typically set an overflow/overrun status flag and discard the new byte; the STM32 USART ORE (overrun error) flag is a common example. Software FIFOs can be written to either drop the new item, overwrite the oldest item, or assert/log a fault. Silently dropping data without any notification is the most dangerous policy. At minimum, increment a diagnostic counter so overflow events are visible during testing.
Are FreeRTOS queues the same as FIFOs?
FreeRTOS queues are FIFO-ordered by default: items are sent to the back and received from the front. FreeRTOS also provides xQueueSendToFront() to prepend an item, giving LIFO (stack) behavior for that one call. The underlying mechanism uses a circular buffer internally. FreeRTOS queues add thread safety, blocking, and timeout semantics on top of the basic FIFO ordering, making them the standard choice for inter-task data passing in FreeRTOS-based projects rather than a hand-rolled ring buffer.

Differentiators vs similar concepts

A FIFO is often contrasted with a LIFO (last-in first-out) stack, where the most recently added item is the first removed. In embedded contexts, the CPU call stack is a LIFO structure, while data streaming between peripherals and application code almost always uses FIFO ordering to preserve the sequence of arrival. FIFOs are also distinct from mailboxes or single-element message slots: a mailbox holds exactly one item and is overwritten on each write, with no ordering history.