FIFOs

Concepts

The microkernel’s FIFO object type is an implementation of a traditional first in, first out queue.

A FIFO allows tasks to asynchronously send and receive fixed-size data items. Each FIFO has an associated ring buffer used to hold data items that have been sent but not yet received.

Any number of FIFOs can be defined in a microkernel system. Each FIFO needs:

  • A name that uniquely identifies it.
  • A maximum quantity of data items that can be queued in its ring buffer.
  • The data item size, measured in bytes, of each data item it can handle.

A task sends a data item by specifying a pointer to an area containing the data to be sent; the size of the data area must equal the FIFO’s data item size. The data is either given directly to a receiving task (if one is waiting), or copied to the FIFO’s ring buffer (if space is available). When a FIFO is full, the sending task can wait for space to become available.

Any number of tasks may wait on a full FIFO simultaneously; when space for a data item becomes available, that space is given to the highest-priority task that has waited the longest.

A task receives a data item by specifying a pointer to an area to receive the data; the size of the receiving area must equal the FIFO’s data item size. The data is copied from the FIFO’s ring buffer (if it contains data items) or taken directly from a sending task (if the FIFO is empty). When a FIFO is empty the task may choose to wait for a data item to become available.

Any number of tasks may wait on an empty FIFO simultaneously; when a data item becomes available it is given to the highest priority task that has waited the longest.

Purpose

Use a FIFO to transfer small data items between tasks in an asynchronous and anonymous manner.

Note

A FIFO can be used to transfer large data items, if desired. However, it is often preferable to send pointers to large data items to avoid copying the data. The microkernel’s memory map and memory pool object types can be helpful for data transfers of this sort.

A synchronous transfer can be achieved by using the microkernel’s mailbox object type.

A non-anonymous transfer can be achieved by having the sending task embed its name in the data it sends, where it can be retrieved by the receiving task. However, there is no straightforward way for the sending task to determine the name of the task that received its data. The microkernel’s mailbox object type does support non-anonymous data transfer.

Usage

Defining a FIFO

The following parameters must be defined:

name
This specifies a unique name for the FIFO.
depth
This specifies the maximum number of data items that can exist at any one time.
width
This specifies the size (in bytes) of each data item.

Public FIFO

Define the FIFO in the application’s .MDEF file with the following syntax:

FIFO name depth width

For example, the file projName.mdef defines a FIFO that holds up to 10 items that are each 12 bytes long as follows:

% FIFO NAME         DEPTH WIDTH
% =============================
  FIFO SIGNAL_FIFO   10    12

A public FIFO can be referenced by name from any source file that includes the file zephyr.h.

Private FIFO

Define the FIFO in a source file using the following syntax:

DEFINE_FIFO(fifo_name, depth, width)

For example, the following code defines a private FIFO named PRIV_FIFO.

DEFINE_FIFO(PRIV_FIFO, 10, 12);

To access this FIFO from a different source file, use the following syntax:

extern const kfifo_t PRIV_FIFO;

Example: Writing to a FIFO

This code uses a FIFO to pass data items from a producing task to one or more consuming tasks. If the FIFO fills up because the consumers can’t keep up, throw away all existing data so newer data can be saved.

void producer_task(void)
{
    struct data_item_t data;

    while (1) {
        /* create data item to send (e.g. measurement, timestamp, ...) */
        data = ...

        /* send data to consumers */
        while (task_fifo_put(SIGNAL_FIFO, &data, TICKS_NONE) != RC_OK) {
            /* FIFO is full */
            task_fifo_purge(SIGNAL_FIFO);
        }

        /* data item was successfully added to FIFO */
    }
}

Example: Reading from a FIFO

This code uses a FIFO to process data items generated by one or more producing tasks.

void consumer_task(void)
{
    struct data_item_t data;

    while (1) {
        /* get a data item */
        task_fifo_get(SIGNAL_FIFO, &data, TICKS_UNLIMITED);

        /* process data item */
        ...
    }
}

APIs

FIFO APIs provided by microkernel.h

task_fifo_put()
Write item to a FIFO, or wait for a specified time period if the FIFO is full.
task_fifo_get()
Read item from a FIFO, or wait for a specified time period if the FIFO is empty.
task_fifo_purge()
Discard all items in a FIFO and unblock any tasks waiting to read or write an item.
task_fifo_size_get()
Read the number of items currently in a FIFO.