Nanokernel FIFOs

Concepts

The nanokernel’s FIFO object type is an implementation of a traditional first in, first out queue. It is mainly intended for use by fibers.

A nanokernel FIFO allows data items of any size tasks to be sent and received asynchronously. The FIFO uses a linked list to hold data items that have been sent but not yet received.

FIFO data items must be aligned on a 4-byte boundary, as the kernel reserves the first 32 bits of each item for use as a pointer to the next data item in the FIFO’s linked list. Consequently, a data item that holds N bytes of application data requires N+4 bytes of memory.

Any number of nanokernel FIFOs can be defined. Each FIFO is a distinct variable of type struct nano_fifo, and is referenced using a pointer to that variable. A FIFO must be initialized before it can be used to send or receive data items.

Items can be added to a nanokernel FIFO in a non-blocking manner by any context type (i.e. ISR, fiber, or task).

Items can be removed from a nanokernel FIFO in a non-blocking manner by any context type; if the FIFO is empty the NULL return code indicates that no item was removed. Items can also be removed from a nanokernel FIFO in a blocking manner by a fiber or task; if the FIFO is empty the thread waits for an item to be added.

Any number of threads may wait on an empty nanokernel FIFO simultaneously. When a data item becomes available it is given to the fiber that has waited longest, or to a waiting task if no fiber is waiting.

Note

A task that waits on an empty nanokernel FIFO does a busy wait. This is not an issue for a nanokernel application’s background task; however, in a microkernel application a task that waits on a nanokernel FIFO remains the current task. In contrast, a microkernel task that waits on a microkernel data passing object ceases to be the current task, allowing other tasks of equal or lower priority to do useful work.

If multiple tasks in a microkernel application wait on the same nanokernel FIFO, higher priority tasks are given data items in preference to lower priority tasks. However, the order in which equal priority tasks are given data items is unpredictible.

Purpose

Use a nanokernel FIFO to asynchronously transfer data items of arbitrary size in a “first in, first out” manner.

Usage

Example: Initializing a Nanokernel FIFO

This code establishes an empty nanokernel FIFO.

struct nano_fifo  signal_fifo;

nano_fifo_init(&signal_fifo);

Example: Writing to a Nanokernel FIFO from a Fiber

This code uses a nanokernel FIFO to send data to one or more consumer fibers.

struct data_item_t {
   void *fifo_reserved;   /* 1st word reserved for use by FIFO */
   ...
};

struct data_item_t  tx_data;

void producer_fiber(int unused1, int unused2)
{
    ARG_UNUSED(unused1);
    ARG_UNUSED(unused2);

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

        /* send data to consumers */
        nano_fiber_fifo_put(&signal_fifo, &tx_data);

        ...
    }
}

Example: Reading from a Nanokernel FIFO

This code uses a nanokernel FIFO to obtain data items from a producer fiber, which are then processed in some manner. This design supports the distribution of data items to multiple consumer fibers, if desired.

void consumer_fiber(int unused1, int unused2)
{
    struct data_item_t  *rx_data;

    ARG_UNUSED(unused1);
    ARG_UNUSED(unused2);

    while (1) {
        rx_data = nano_fiber_fifo_get(&signal_fifo, TICKS_NONE);

        /* process FIFO data */
        ...
    }
}

APIs

The following APIs for a nanokernel FIFO are provided by nanokernel.h:

nano_fifo_init()

Initializes a FIFO.

nano_task_fifo_put(), nano_fiber_fifo_put(), nano_isr_fifo_put(), nano_fifo_put()

Add an item to a FIFO.

nano_task_fifo_get(), nano_fiber_fifo_get(), nano_isr_fifo_get(), nano_fifo_get()

Remove an item from a FIFO, or waits for an item for a specified time period if it is empty.