Semaphore, Timer, and Fiber Example

The following example is intended to provide a basic picture of how Zephyr’s semaphores, timers, and fibers work. The actual implementations of the standard hello_world are much simpler, see ZEPHYR_BASE/samples/hello_world

Example Code

#include <nanokernel.h>

#include <nanokernel/cpu.h>

/* specify delay between greetings (in ms); compute equivalent in ticks */

#define SLEEPTIME

#define SLEEPTICKS (SLEEPTIME * CONFIG_TICKFREQ / 1000)

#define STACKSIZE 2000

char fiberStack[STACKSIZE];

struct nano_sem nanoSemTask;

struct nano_sem nanoSemFiber;

void fiberEntry (void)

{
   struct nano_timer timer;
   uint32_t data[2] = {0, 0};

   nano_sem_init (&nanoSemFiber);
   nano_timer_init (&timer, data);

   while (1)
   {
      /* wait for task to let us have a turn */
      nano_fiber_sem_take(&nanoSemFiber, TICKS_UNLIMITED);

      /* say "hello" */
      PRINT ("%s: Hello World!\n", __FUNCTION__);

      /* wait a while, then let task have a turn */
      nano_fiber_timer_start (&timer, SLEEPTICKS);
      nano_fiber_timer_test (&timer, TICKS_UNLIMITED);
      nano_fiber_sem_give (&nanoSemTask);
   }

}

void main (void)

{
   struct nano_timer timer;
   uint32_t data[2] = {0, 0};

   task_fiber_start (&fiberStack[0], STACKSIZE,
                  (nano_fiber_entry_t) fiberEntry, 0, 0, 7, 0);

   nano_sem_init (&nanoSemTask);
   nano_timer_init (&timer, data);

   while (1)
   {
      /* say "hello" */
      PRINT ("%s: Hello World!\n", __FUNCTION__);

      /* wait a while, then let fiber have a turn */
      nano_task_timer_start (&timer, SLEEPTICKS);
      nano_task_timer_test (&timer, TICKS_UNLIMITED);
      nano_task_sem_give (&nanoSemFiber);

      /* now wait for fiber to let us have a turn */
      nano_task_sem_take (&nanoSemTask, TICKS_UNLIMITED);
   }

}

Step-by-Step Description

A quick breakdown of the major objects in use by this sample includes:

  • One fiber, executing in the fiberEntry() routine.

  • The background task, executing in the main() routine.

  • Two semaphores (nanoSemTask, nanoSemFiber),

  • Two timers:

    • One local to the fiber (timer)
    • One local to background task (timer)

First, the background task starts executing main(). The background task calls task_fiber_start initializing and starting the fiber. Since a fiber is available to be run, the background task is pre-empted and the fiber begins running.

Execution jumps to fiberEntry. nanoSemFiber and the fiber-local timer before dropping into the while loop, where it takes and waits on nanoSemFiber. task_fiber_start.

The background task initializes nanoSemTask and the task-local timer.

The following steps repeat endlessly:

  1. The background task execution begins at the top of the main while loop and prints, “main: Hello World!”
  2. The background task then starts a timer for SLEEPTICKS in the future, and waits for that timer to expire.
  3. Once the timer expires, it signals the fiber by giving the nanoSemFiber semaphore, which in turn marks the fiber as runnable.
  4. The fiber, now marked as runnable, pre-empts the background task, allowing execution to jump to the fiber. nano_fiber_sem_take.
  5. The fiber then prints, “fiberEntry: Hello World!” It starts a time for SLEEPTICKS in the future and waits for that timer to expire. The fiber is marked as not runnable, and execution jumps to the background task.
  6. The background task then takes and waits on the nanoSemTask semaphore.
  7. Once the timer expires, the fiber signals the background task by giving the nanoSemFiber semaphore. The background task is marked as runnable, but code execution continues in the fiber, since fibers take priority over the background task. The fiber execution continues to the top of the while loop, where it takes and waits on nanoSemFiber. The fiber is marked as not runnable, and the background task is scheduled.
  8. The background task execution picks up after the call to nano_task_sem_take(). It jumps to the top of the while loop.