Doc: Change the Nanokernel Objects to the Nanokernel Services section.

Moves the Nanokernel Object documentation to a new Nanokernel Services
section within the Kernel Primer. Labels, cross-references, figures,
headings and filenames were updated to reflect the new structure. 

Change-Id: I04f97e712d78cd211a8ed1315f86895a51affa01
Signed-off-by: Rodrigo Caballero <rodrigo.caballero.abraham@intel.com>
This commit is contained in:
Rodrigo Caballero 2015-08-07 13:37:36 -05:00 committed by Anas Nashif
commit f81ee6efdd
8 changed files with 404 additions and 375 deletions

View file

@ -0,0 +1,28 @@
.. _nanokernel:
Nanokernel Services
###################
Section Scope
*************
This section provides an overview of the most important nanokernel
services. The information contained here is an aid to better understand
how the kernel operates at the nanokernel level.
Document Format
***************
Each service is broken off to its own section, containing a definition, a
functional description, the service initialization syntax, and a table
of APIs with the context which may call them. Please refer to the API documentation for further
details regarding the functionality of each service.
.. toctree::
:maxdepth: 1
nanokernel_fibers
nanokernel_timers
nanokernel_signaling
nanokernel_data
nanokernel_interrupts

View file

@ -0,0 +1,100 @@
.. _nanokernel_data:
Data Passing Services
#####################
This section contains the information about all data passing services available in the nanokernel.
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()` |
+--------------------------------+----------------------------------------------------------------+

View file

@ -0,0 +1,147 @@
.. _nanokernel_example:
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.

View file

@ -1,7 +1,7 @@
.. _fibers:
.. _fiber_services:
Fibers
######
Fiber Services
##############
A fiber is an execution thread and a lightweight alternative to a task. It can
use nanokernel objects but not microkernel objects. A runnable fiber will
@ -41,7 +41,7 @@ and starts a fiber from another fiber, while
:c:func:`task_fiber_start()` does so from a task. Both APIs use the
parameters *parameter1* and *parameter2* as *arg1* and *arg2* given to
the fiber . The full documentation on these APIs can be found in the
:ref:`code`.
:ref:`in-code_apis`.
When :c:func:`task_fiber_start()`is called from a task, the new fiber
will be immediately ready to run. The background task immediately stops
@ -91,8 +91,8 @@ unacceptable delay in the scheduling of other fibers, it should yield
by placing a :c:func:`fiber_yield()` call within the loop of a
computational cannot call :c:func:`fiber_yield()`.
Scheduling Fibers
*****************
Fiber Scheduling Model
######################
The fibers in the Zephyr Kernel are priority-scheduled. When several fibers
are ready to run, they run in the order of their priority. When more
@ -115,7 +115,7 @@ for responding to an external event:
Moving Computation Processing to a Task
=======================================
***************************************
Move the processing to a task to minimize the amount of computation that
is performed at the fiber level. This reduces the scheduling delay for
@ -124,14 +124,14 @@ handles the external event runnable.
Moving Code to Handle External Event to ISR
===========================================
*******************************************
Move the code to handle the external event into an ISR. The ISR is
executed immediately after the event is recognized, without waiting for
the other fibers in the queue to be unscheduled.
Adding Yielding Points to Fibers
================================
********************************
Add yielding points to fibers with :c:func:`fiber_yield()`. This service
un-schedules a fiber and places it at the end of the ready fiber list

View file

@ -1,11 +1,10 @@
Interrupt Service Routines
##########################
Interrupt Services
##################
========
Concepts
========
********
Interrupt Service Routines (ISRs) are execution threads
:abbr:`ISRs (Interrupt Service Routines)` are execution threads
that run in response to a hardware or software interrupt.
They are used to preempt the execution of the
task or fiber running at the time of the interrupt,
@ -21,7 +20,7 @@ Each ISR has the following properties:
* The address of the function that is invoked to handle the interrupt.
* The argument value that is passed to that function.
An Interrupt Descriptor Table (IDT) is used to associate a given interrupt
An :abbr:`IDT (Interrupt Descriptor Table)` is used to associate a given interrupt
source with a given ISR.
Only a single ISR can be associated with a specific IRQ at any given time.
An ISR can be incorporated into the IDT when the Zephyr project is built
@ -33,13 +32,13 @@ allowing a single function to service a device that generates
multiple types of interrupts or to service multiple devices
(usually of the same type). The argument value passed to an ISR's function
can be used to allow the function to determine which interrupt has been
signalled.
signaled.
The Zephyr kernel provides a default ISR for all unused IDT entries. This ISR
generates a fatal system error if an unexpected interrupt is signalled.
generates a fatal system error if an unexpected interrupt is signaled.
The kernel supports interrupt nesting. This allows an ISR to be preempted
in mid-execution if a higher priority interrupt is signalled. The lower
in mid-execution if a higher priority interrupt is signaled. The lower
priority ISR resumes execution once the higher priority ISR has completed
its processing.
@ -50,10 +49,8 @@ be applied when it is already in effect. The collective lock must be
unlocked an equal number of times before interrupts are again processed
by the kernel.
=======
Purpose
=======
*******
Use an ISR to perform interrupt processing that requires a very rapid
response, and which can be done quickly and without blocking.
@ -64,13 +61,8 @@ response, and which can be done quickly and without blocking.
should be handed off to a fiber or task. See `Offloading ISR Work`_ for
a description of various techniques that can be used in a Zephyr project.
=====
Usage
=====
Installing an ISR
=================
*****************
Use one of the following procedures to install an ISR:
@ -79,7 +71,7 @@ Use one of the following procedures to install an ISR:
Installing a Static ISR
***********************
=======================
Use a static ISR to register an interrupt handler when the interrupt
parameters are known during the build time and the device is always
@ -127,7 +119,7 @@ For x86 platforms only, you must also create an interrupt stub as follows:
Installing a Dynamic ISR
************************
========================
Use a dynamic ISR to register an interrupt handler when the interrupt
parameters can be found out only at runtime, or when a device is not always
@ -174,7 +166,7 @@ This is an example of a dynamic interrupt for x86:
Working with Interrupts
=======================
***********************
Use the following:
@ -183,7 +175,7 @@ Use the following:
Offloading ISR Work
*******************
===================
Interrupt service routines should generally be kept short
to ensure predictable system operation.
@ -200,17 +192,17 @@ to a fiber or task.
The :c:func:`nano_isr_XXX()` APIs should be used to notify the helper fiber
(or task) that work is available for it.
See :ref:`fibers`.
See :ref:`fiber_services`.
2. An ISR can signal the microkernel server fiber to do interrupt-related
work by sending an event that has an associated event handler.
See :ref:`events`.
See :ref:`microkernel_events`.
3. An ISR can signal a helper task to do interrupt-related work
by sending an event that the helper task detects.
See :ref:`events`.
See :ref:`microkernel_events`.
4. An ISR can signal a helper task to do interrupt-related work.
by giving a semaphore that the helper task takes.
@ -220,7 +212,7 @@ to a fiber or task.
5. A kernel-supplied ISR can signal a helper task to do interrupt-related work
using a task IRQ that the helper task allocates.
See :ref:`task_IRQs`.
See :ref:`microkernel_task_irqs`.
When an ISR offloads work to a fiber there is typically a single
context switch to that fiber when the ISR completes.
@ -239,7 +231,7 @@ that are scheduled to run.
IDT Security
************
============
Ideally, the IDT memory area should be protected against accidental
modification, in the same way that text and read-only data areas
@ -251,9 +243,9 @@ therefore *not* protected. This is true even for systems using
reside in read-only memory (such as flash memory or ROM).
========
ISR APIs
========
APIs
****
This table lists interrupt-related Application Program Interfaces.
@ -272,9 +264,9 @@ This table lists interrupt-related Application Program Interfaces.
| :c:func:`irq_unlock()` | Removes lock on interrupts from all sources. |
+-------------------------+-------------------------------------------------+
==========
ISR Macros
==========
Macros
******
This table lists the macros used to install a static ISR.
@ -285,5 +277,4 @@ This table lists the macros used to install a static ISR.
+----------------------------------+-----------------------------------------+
| :c:macro:`IRQ_CONFIG( )` | Registers a static ISR with the |
| | interrupt controller. |
+----------------------------------+-----------------------------------------+
+----------------------------------+-----------------------------------------+

View file

@ -0,0 +1,51 @@
.. _nanokernel_signaling:
Signaling Services
##################
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()` |
+--------------------------------+----------------------------------------------------------------+

View file

@ -0,0 +1,43 @@
.. _nanokernel_timers:
Timer Services
##############
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.
APIs
****
+--------------------------------+----------------------------------------------------------------+
| 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()` |
+--------------------------------+----------------------------------------------------------------+

View file

@ -1,331 +0,0 @@
.. _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.