zephyr/doc/object/nanokernel.rst

332 lines
15 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

.. _nanokernelObjects:
Nanokernel Objects
##################
Section Scope
*************
This section provides an overview of the most important nanokernel
objects. The information contained here is an aid to better understand
how the Zephyr Kernel operates at the nanokernel level.
Document Format
***************
Each object is broken off to its own section, containing a definition, a
functional description, the object initialization syntax, and a table
of Application Program Interfaces (APIs) with the context which may
call them. Please refer to the API documentation for further details
regarding each objects functionality.
Nanokernel FIFO
***************
Definition
==========
The FIFO object is defined in :file:`kernel/nanokernel/nano_fifo.c`.
This is a linked list of memory that allows the caller to store data of
any size. The data is stored in first-in-first-out order.
Function
========
Multiple processes can wait on the same FIFO object. Data is passed to
the first fiber that waited on the FIFO, and then to the background
task if no fibers are waiting. Through this mechanism the FIFO object
can synchronize or communicate between more than two contexts through
its API. Any ISR, fiber, or task can attempt to get data from a FIFO
without waiting on the data to be stored.
.. note::
The FIFO object reserves the first WORD in each stored memory
block as a link pointer to the next item. The size of the WORD
depends on the platform and can be 16 bit, 32 bit, etc.
Application Program Interfaces
==============================
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Context** | **Interfaces** |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Initialization** | :c:func:`nano_fifo_init()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Interrupt Service Routines** | :c:func:`nano_isr_fifo_get()`, :c:func:`nano_isr_fifo_put()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Fibers** | :c:func:`nano_fiber_fifo_get()`, :c:func:`nano_fiber_fifo_get_wait()`, :c:func:`nano_fiber_fifo_put()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Tasks** | :c:func:`nano_task_fifo_get()`, :c:func:`nano_task_fifo_get_wait()`, :c:func:`nano_task_fifo_put()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
Nanokernel LIFO Object
**********************
Definition
==========
The LIFO is defined in :file:`kernel/nanokernel/nano_lifo.c`. It
consists of a linked list of memory blocks that uses the first word in
each block as a next pointer. The data is stored in last-in-first-out
order.
Function
========
When a message is added to the LIFO, the data is stored at the head of
the list. Messages taken off the LIFO object are taken from the head.
The LIFO object requires the first 32-bit word to be empty in order to
maintain the linked list.
The LIFO object does not store information about the size of the
messages.
The LIFO object remembers one waiting context. When a second context
starts waiting for data from the same LIFO object, the first context
remains waiting and never reaches the runnable state.
Application Program Interfaces
==============================
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Context** | **Interfaces** |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Initialization** | :c:func:`nano_lifo_init()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Interrupt Service Routines** | :c:func:`nano_isr_lifo_get()`, :c:func:`nano_isr_lifo_put()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Fibers** | :c:func:`nano_fiber_lifo_get()`, :c:func:`nano_fiber_lifo_get_wait()`, :c:func:`nano_fiber_lifo_put()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Tasks** | :c:func:`nano_task_lifo_get()`, :c:func:`nano_task_lifo_get_wait()`, :c:func:`nano_task_lifo_put()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
Nanokernel Semaphore
********************
Definition
==========
The nanokernel semaphore is defined in
:file:`kernel/nanokernel/nano_sema.c` and implements a counting
semaphore that sends signals from one fiber to another.
Function
========
Nanokernel semaphore objects can be used from an ISR, a fiber, or the
background task. Interrupt handlers can use the nanokernels semaphore
object to reschedule a fiber waiting for the interrupt.
Only one context can wait on a semaphore at a time. The semaphore starts
with a count of 0 and remains that way if no context is pending on it.
Each 'give' operation increments the count by 1. Following multiple
'give' operations, the same number of 'take' operations can be
performed without the calling context having to wait on the semaphore.
Thus after n 'give' operations a semaphore can 'take' n times without
pending. If a second context waits for the same semaphore object, the
first context is lost and never wakes up.
Application Program Interfaces
==============================
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| Context | Interfaces |
+================================+========================================================================================================+
| **Initialization** | :c:func:`nano_sem_init()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Interrupt Service Routines** | :c:func:`nano_isr_sem_give()`, :c:func:`nano_isr_sem_take()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Fibers** | :c:func:`nano_fiber_sem_give()`, :c:func:`nano_fiber_sem_take()`, :c:func:`nano_fiber_sem_take_wait()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
| **Tasks** | :c:func:`nano_task_sem_give()`, :c:func:`nano_task_sem_take()`, :c:func:`nano_task_sem_take_wait()` |
+--------------------------------+--------------------------------------------------------------------------------------------------------+
Timer Objects
*************
Definition
==========
The timer objects is defined in :file:`kernel/nanokernel/nano_timer.c`
and implements digital counters that either increment or decrement at a
fixed frequency. Timers can be called from a task or fiber context.
Function
========
Only a fiber or task context can call timers. Timers can only be used in
a nanokernel if it is not part of a microkernel. Timers are optional in
nanokernel-only systems. The nanokernel timers are simple. The
:c:func:`nano_node_tick_delta()` routine is not reentrant and should
only be called from a single context, unless it is certain other
contexts are not using the elapsed timer.
Application Program Interfaces
==============================
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| **Context** | **Interface** |
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| **Initialization** | :c:func:`nano_timer_init()` |
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| **Interrupt Service Routines** | |
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| **Fibers** | :c:func:`nano_fiber_timer_test()`, :c:func:`nano_fiber_timer_wait()`, :c:func:`nano_fiber_timer_start()`, :c:func:`nano_fiber_timer_stop()` |
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
| **Tasks** | :c:func:`nano_task_timer_test()`, :c:func:`nano_task_timer_wait()`, :c:func:`nano_task_timer_start()`, :c:func:`nano_task_timer_stop()` |
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------+
Semaphore, Timer, and Fiber Example
***********************************
The following example is pulled from the file:
:file:`samples/microkernel/apps/hello_world/src/hello.c`.
Example Code
============
.. code-block:: c
#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_wait (&nanoSemFiber);
/* 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_wait (&timer);
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_wait (&timer);
nano_task_sem_give (&nanoSemFiber);
/* now wait for fiber to let us have a turn */
nano_task_sem_take_wait (&nanoSemTask);
}
}
Step-by-Step Description
========================
A quick breakdown of the major objects in use by this sample includes:
- One fiber, executing in the :c:func:`fiberEntry()` routine.
- The background task, executing in the :c:func:`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:
#. The background task execution begins at the top of the main while
loop and prints, “main: Hello World!”
#. The background task then starts a timer for SLEEPTICKS in the
future, and waits for that timer to expire.
#. Once the timer expires, it signals the fiber by giving the
nanoSemFiber semaphore, which in turn marks the fiber as runnable.
#. The fiber, now marked as runnable, pre-empts the background
process, allowing execution to jump to the fiber.
nano_fiber_sem_take_wait.
#. 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.
#. The background task then takes and waits on the nanoSemTask
semaphore.
#. 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.
#. The background task execution picks up after the call to
:c:func:`nano_task_sem_take_wait()`. It jumps to the top of the
while loop.