nRF5 SDK v15.0.0
FIFO library

The FIFO library provides a simple circular buffer implementation for storing bytes. The FIFO uses the size and buffer memory provided by the application on initialization.

Initializing FIFO

// Create a FIFO structure
app_fifo_t my_fifo;
// Create a buffer for the FIFO
uint16_t buffer_size = 8;
uint8_t buffer[buffer_size];
// Initialize FIFO structure
uint32_t err_code = app_fifo_init(&my_fifo, buffer, (uint16_t)sizeof(buffer));

The application must provide a FIFO structure and a memory buffer to the initialization function.

The FIFO structure (app_fifo_t) is passed by reference and will be initialized by the FIFO module. It ensures correct indexing when reading and writing data to the FIFO. The library module itself will not store state information, thus the application must ensure that the memory used by app_fifo_t is not reclaimed as long as the FIFO is in use.

The size of the buffer must be a power of two. As seen in the code example, the application must ensure that the buffer has a reserved memory space for the lifetime of the FIFO.

FIFO instance

The app_fifo_t structure that is used to create a FIFO instance contains 4 members:

The FIFO is implemented as a circular buffer of size n. Whenever the nth element is added to the buffer, the index wraps over and counts from index zero. This can be summarized to i = count mod n, where:

To simplify the index handling, the FIFO is restricted to require the buffer size n to be a power of two, because this reduces the modulus operation to become a simple AND operation with n-1. For example, if the buffer size n=8:

n     = 8 = 0b00001000
n - 1 = 7 = 0b00000111

If n is a power of two, ANDing any number with n-1 is identical to the modulus operation of n. For example:

11 mod 8 = 3
11 AND 7 = 0b00001011 & 0b00000111 = 0b00000011 = 3

During initialization of the FIFO, a check is performed to ensure that the size n is a power of two.

The following image shows how the FIFO instance looks like when it is initialized. The two arrows on the top illustrate the read and write indexes. They both start at index 0, and all bytes in the buffer are free to be used.

app_fifo_initialized.svg
Figure 1: FIFO instance read and write indexes after initialization.
Warning
The FIFO implementation is not re-entrant safe. Thus data must be added or fetched from the FIFO from the same context level. However, adding in one context level and fetching from another context is supported.

Adding an element

To add an element to the FIFO, the application must use the app_fifo_put function. This is done by passing the FIFO structure and the data to app_fifo_put.

Example of adding an element to the FIFO:

uint8_t data = 88;
uint32_t err_code;
// Add an element to the FIFO
err_code = app_fifo_put(&my_fifo, data);

When the new element is added to the buffer, the value supplied as parameter to the put function will be copied over to the current index of the write_pos member of the FIFO instance. When an element has been added to the FIFO, the write_pos member of the instance will be incremented by 1 to point to the next free byte. This is illustrated in Figure 2.

app_fifo_put_1_element.svg
Figure 2: FIFO instance with 1 element added.

For each element added, the write_pos counter increments. This is illustrated in Figure 3.

app_fifo_put_4_elements.svg
Figure 3: FIFO instance with 4 elements added.

If the FIFO is full, NRF_ERROR_NO_MEM will be returned.

Fetching an element

To get an element from the FIFO, the application must use the app_fifo_get function. This is done by passing the FIFO structure and a reference, return_val, for returning the data from app_fifo_get.

Example of fetching an element from the FIFO:

// Consume one element from FIFO
uint8_t return_val;
uint32_t err_code;
err_code = app_fifo_get(&my_fifo, &return_val);

When app_fifo_get is called, the memory of the supplied by-reference parameter is filled with the value stored in the memory buffer. Then, the read_pos member of the FIFO instance is incremented to free the byte that was read. Therefore, each byte can be fetched only once. An example of a FIFO where two elements have been consumed is shown in Figure 4.

app_fifo_read_2_elements.svg
Figure 4: FIFO instance with 2 elements consumed.

Empty buffer

The read_pos and write_pos indexes have the same value when the FIFO is empty. This is the case when the FIFO is initialized, because both read_pos and write_pos are initialized to 0, and when all elements in the FIFO have been fetched. This is illustrated in Figure 5.

app_fifo_empty.svg
Figure 5: FIFO instance with no elements.

The FIFO can also be emptied by executing app_fifo_flush.

When the FIFO is empty, all calls to app_fifo_get will return NRF_ERROR_NOT_FOUND.

Full buffer

When write_pos == read_pos + n , the buffer is full. All subsequent calls to app_fifo_put will return NRF_ERROR_NO_MEM.

app_fifo_full.svg
Figure 6: FIFO instance with all available elements used.

Multi-byte operations

It is possible to write and read multiple bytes at the same time. Writing to the FIFO is equivalent to adding multiple bytes, one at a time. Reading from the FIFO is equivalent to fetching multiple bytes, one at a time.

Example of a write operation:

uint32_t err_code;
uint32_t data_len = 5;
uint8_t write_data = {1, 2, 3, 4, 5};
// Write 5 elements to the FIFO
return_val = app_fifo_write(&my_fifo, &write_data, &data_len);
// Check if write was successful
if (return_val == NRF_SUCCESS)
{
// Check if write was partial
if (data_len < 5)
{
// Attempt another write operation using data_len value as offset
}
}
else if (return_val == NRF_ERROR_NO_MEM)
{
// FIFO is full
}
else
{
// API parameters incorrect, should not happen
}

Example of a read operation:

uint32_t return_val;
uint32_t data_len = 5;
uint8_t read_data[5];
// Read 5 elements to the FIFO
return_val = app_fifo_read(&my_fifo, &read_data, &data_len);
// Check if write was successful
if (return_val == NRF_SUCCESS)
{
// Check if read was partial
if (data_len < 5)
{
// Not as many elements in array as requested
}
}
else if (return_val == NRF_ERROR_NOT_FOUND)
{
// FIFO is empty
}
else
{
// API parameters incorrect, should not happen
}

FIFO readable/writeable size operations

To query how many elements are available in the FIFO, the application can use the app_fifo_read function. To query how many elements can be written to the FIFO, the application can use the app_fifo_write function.

Example for querying how many bytes can be written to the FIFO:

uint32_t err_code;
uint32_t data_len;
// Request number of elements that can be written to the FIFO
err_code = app_fifo_write(&my_fifo, NULL, &data_len);
// Check if request was successful
if (err_code == NRF_SUCCESS)
{
// data_len contains the number of bytes that can be written
}
else if (err_code == NRF_ERROR_NO_MEM)
{
// FIFO is full
}
else
{
// API parameters incorrect, should not happen
}

Example for querying how many bytes can be read from the FIFO:

uint32_t err_code;
uint32_t data_len;
// Request number of elements in the FIFO
err_code = app_fifo_read(&my_fifo, NULL, &data_len);
// Check if request was successful
if (err_code == NRF_SUCCESS)
{
// data_len contains the number of elements that can be read
}
else if (err_code == NRF_ERROR_NOT_FOUND)
{
// FIFO is empty
}
else
{
// API parameters incorrect, should not happen
}

Documentation feedback | Developer Zone | Subscribe | Updated