Mercurial > ecos
view packages/kernel/current/doc/kernel.sgml @ 208:e0c0827131d1 ecos
Merge from eCos master repository on 2002-05-20-20:11:54-BST
| author | jlarmour |
|---|---|
| date | Mon, 20 May 2002 22:19:26 +0000 |
| parents | |
| children | d2c90368aeef |
line wrap: on
line source
<part ID="kernel"> <title>The eCos kernel</title> <chapter id="ecos-kernel-overview"> <TITLE>eCos kernel overview</TITLE> <PARA>This is an overview of the internal workings of the <EMPHASIS>eCos</EMPHASIS> <!-- <index></index> -->kernel. </PARA> <SECT1 id="kernel-scheduler"> <TITLE><!-- <xref> -->The scheduler</TITLE> <PARA>At the core of the kernel is the <!-- <index></index> -->scheduler. This defines the way in which threads are run, and provides the mechanisms by which they may synchronize. It also controls the means by which interrupts affect thread execution. No single scheduler can cover all possible system configurations. For different purposes we will need to cover several scheduling polices. In this release two schedulers are provided (described in more detail in <xref linkend="sched-subdirectory">):</PARA> <ITEMIZEDLIST> <LISTITEM> <PARA>a <EMPHASIS>bitmap scheduler</EMPHASIS> </PARA> </LISTITEM> <LISTITEM> <PARA>a <EMPHASIS>multi-level queue scheduler</EMPHASIS> </PARA> </LISTITEM> </ITEMIZEDLIST> <PARA>At present the system will only support a single scheduler at any one time. Future systems may allow multiple schedulers to co-exist, but this will be hidden behind the scheduler API in the current release. </PARA> <PARA>To make scheduling safe we need a mechanism to protect the scheduler data structures from concurrent access. The traditional approach to this is to disable interrupts during the critical regions. Unfortunately this increases the maximum interrupt dispatch latency, which is to be avoided in any real-time system. </PARA> <PARA>The mechanisms chosen for <EMPHASIS>eCos</EMPHASIS> is to maintain a counter, <!-- <index></index> --><VARNAME>Scheduler::sched_lock</VARNAME> that, if non-zero, prevents any rescheduling. The current thread can claim the lock by calling <!-- <index></index> --><FUNCTION>Scheduler::lock()</FUNCTION>. This increments the counter and prevents any further scheduling. The function <!-- <index></index> --><FUNCTION>Scheduler::unlock()</FUNCTION> decrements the counter and if it returns to zero, allows scheduling to continue. </PARA> <PARA>For this to work in the presence of interrupts, it is necessary for the <!-- <index></index> -->Interrupt Service Routines (ISR) to defer any scheduler-oriented operations until the lock is about to go zero. We do this by splitting the work of an ISR into two parts, with the second part, the <!-- <index></index> -->Deferred Service Routine (DSR), being queued until the scheduler decides it is safe to run. This is covered in more detail in <xref linkend="interrupts"> and <xref linkend="interrupt-and-exception-handlers">. </PARA> <PARA>On a uni-processor, <FUNCTION>Scheduler::lock()</FUNCTION> is a simple increment of <VARNAME>Scheduler::sched_lock</VARNAME>. It does not need to be a read-modify-write cycle since the lock is strictly nested. The mere fact that the current thread is running implies that the lock has not been claimed by another thread, so it is always claimable. </PARA> <PARA><FUNCTION>Scheduler::unlock()</FUNCTION> is generic to all scheduler implementations. </PARA> </SECT1> <SECT1 id="thread-synchronization"> <TITLE>Thread <!-- <index></index> --> synchronization</TITLE> <PARA>To allow threads to cooperate and compete for resources, it is necessary to provide mechanisms for synchronization and communication. The classic synchronization mechanisms are mutexes/condition variables and semaphores. These are provided in the <EMPHASIS>eCos</EMPHASIS> kernel, together with other synchronization/communication mechanisms that are common in real-time systems, such as event flags and message queues. </PARA> <PARA>One of the problems that must be dealt with in any real-time systems is <!-- <index></index> -->priority inversion. This is where a high priority thread is (wrongly) prevented from continuing by one at lower priority. The normal example is of a high priority thread waiting at a mutex already held by a low priority thread. If the low priority thread is preempted by a medium priority thread then priority inversion has occurred since the high priority thread is prevented from continuing by an unrelated thread of lower priority.</PARA> <PARA>This problem got much attention recently when the Mars Pathfinder mission had to reset the computers on the ground exploration robot repeatedly because a priority inversion problem would cause it to hang. </PARA> <PARA>There are several solutions to this problem. The simplest is to employ a <!-- <index></index> -->priority ceiling protocol where all threads that acquire the mutex have their priority boosted to some predetermined value. This has a number of disadvantages: it requires the maximum priority of the threads using the mutex to be known in advance; if the ceiling priority is too high it acts as a global lock disabling all scheduling and it is pessimistic, taking action to prevent the problem even when it does not arise. </PARA> <PARA>A better solution is to use <!-- <index></index> -->priority inheritance protocol. Here, the priority of the thread that owns the mutex is boosted to equal that of the highest priority thread that is waiting for it. This technique does not require prior knowledge of the priorities of the threads that are going to use the mutex, and the priority of the owning thread is only boosted when a higher priority thread is waiting. This reduces the effect on the scheduling of other threads, and is more optimistic than the priority ceiling protocol. A disadvantage of this mechanism is that the cost of each synchronization call is increased since the inheritance protocol must be obeyed each time. </PARA> <PARA>A third approach to priority inversion is to recognize that relative thread priorities have been poorly chosen and thus the system in which it occurs is faulty. In this case the kernel needs the ability to detect when priority inversion has taken place, and to raise an exception when it occurs to aid debugging. Then this code is removed from the shipping version. </PARA> <PARA>The current <EMPHASIS>eCos</EMPHASIS> release provides a relatively simple implementation of <!-- <index></index> -->mutex priority inheritance. This implementation will only work in the multi-level queue scheduler, and it does not handle the rare case of nested mutexes completely correctly. However it is both fast and deterministic. Mutex priority inheritance can be disabled if the application does not require it. This will reduce both code size and data space. </PARA> <PARA>Future releases will provide alternative implementations of mutex priority inheritance, and application developers will be able to choose the implementation appropriate to their application. </PARA> </SECT1> <SECT1 id="exceptions"> <TITLE>Exceptions</TITLE> <PARA>An exception is a synchronous event caused by the execution of a thread. These include both the machine exceptions raised by hardware (such as divide-by-zero, memory fault and illegal instruction) and machine exceptions raised by software (such as deadline overrun). The standard C++ exception mechanism is too expensive to use for this, and in any case has the wrong semantics for the exception handling in an RTOS. </PARA> <PARA>The simplest, and most flexible, mechanism for <!-- <index></index> -->exception handling is to call a function. This function needs context in which to work, so access to some working data is required. The function may also need to be handed some data about the exception raised: at least the exception number and some optional parameters. </PARA> <PARA>The exception handler receives a data argument which is a value that was registered with the handler and points to context information. It also receives an exception_number which identifies the exception taken, and an error code which contains any additional information (such as a memory fault address) needed to handle the exception. Returning from the function will allow the thread to continue. </PARA> <PARA>Exception handlers may be either global or per-thread, or both, depending on configuration options. If exceptions are per-thread, it is necessary to have an exception handler attached to each thread. </PARA> </SECT1> <SECT1 id="interrupts"> <TITLE><!-- <xref> -->Interrupts</TITLE> <PARA>Interrupts are asynchronous events caused by external devices. They may occur at any time and are not associated in any way with the thread that is currently running. </PARA> <PARA>The <!-- <index></index> -->handling of interrupts is one of the more complex areas in RTOS design, largely because it is the least well defined. The ways in which interrupt vectors are named, how interrupts are delivered to the software and how interrupts are masked are all highly architecture- (and in some cases board-) specific. The approach taken in <emphasis>eCos</emphasis> is to provide a generalized mechanism with sufficient hooks for system-specific code to be inserted where needed. </PARA> <PARA>Let us start by considering the issue of interrupt vectors. Hardware support differs greatly here: from the Intel Architecture and the 680X0 having support for vectoring individual interrupts to their own vectors, to most RISC architectures that only have a single vector. In the first case it is possible to attach an ISR directly to the vector and know that it need only concern itself with the device in question. In the second case it is necessary to determine which device is actually interrupting and then vector to the correct ISR. Where there is an external interrupt controller, it will be possible to query that and provide what is essentially a software implementation of hardware vectoring. Otherwise the actual hardware devices must be tested, by calling the ISRs in turn and letting them make the determination. Since it is possible for two devices to interrupt simultaneously, it is necessary to call all ISRs each time an interrupt occurs. </PARA> <PARA>Interrupt masking has a similar variety of support. Most processors have a simple interrupt mask bit in a status register. The 680X0 has seven levels of masking. Any board with a interrupt controller can be programmed to provide similar multi-level masking. It is necessary to keep the interrupt masking mechanism simple and efficient, and use only architectural support. The cost of manipulating an on-board interrupt controller may be too high. However, individual device drivers may want access to their individual mask bits in the interrupt controller, so support for this must be provided. </PARA> <PARA>Most of the infrastructure necessary for a (somewhat) portable treatment of interrupts is implemented in the <EMPHASIS>eCos</EMPHASIS> Hardware Abstraction Layer (HAL), which is documented in <xref linkend="the-ecos-hardware-abstraction-layer">. </PARA> </SECT1> <SECT1 id="counters-clocks-alarms-and-timers"> <TITLE>Counters, clocks, alarms and timers</TITLE> <PARA>If the hardware provides a periodic clock or timer, it will be used to drive timing-related features of the system. Many CPU architectures now have built in timer registers that can provide a periodic interrupt. This should be used to drive these features where possible. Otherwise an external timer/clock chip must be used. </PARA> <PARA>We draw a distinction between Counters, Clocks, Alarms and Timers. A <!-- <index></index> -->Counter maintains a monotonically increasing counter that is driven by some source of ticks. A <!-- <index></index> -->Clock is a counter driven by a regular source of ticks (i.e. it counts time). Clocks have a <FIRSTTERM>resolution</FIRSTTERM> associated with them. A default system Clock is driven by the periodic interrupt described above, and tracks real-time. Other interrupt sources may drive other Counters that may or may not track real-time at different resolutions. Some Counters may be driven by aperiodic events and thus have no relation to real-time at all. </PARA> <PARA>An <!-- <index></index> -->Alarm is attached to a Counter and provides a mechanism for generating single-shot or periodic events based on the counter's value. A <!-- <index></index> -->Timer is simply an Alarm that is attached to a Clock. </PARA> <PARA>The system (including the kernel) represents time in units of <EMPHASIS>ticks</EMPHASIS>. These are clock-specific time units and are usually the period of the timer interrupt, or a multiple thereof. Conversion of ticks into conventional time and date units should occur only when required via library functions. Equivalence between Clock time and real-time can be made with an <!-- <index></index> -->RTC (real-time clock), <!-- <index></index> -->NTP (network time protocol) or user input. </PARA> <PARA>The representation of the current tick count needs to be 64 bit. This requires either compiler support for 64 bit integers, or assembly code. Even at the extreme of a 1 ns tick (ticks will typically be >1ms), this gives a 584 year rollover period. </PARA> <PARA>The Clock API and configuration options that affect clock, counter and alarm behavior are described in detail in <xref linkend="counters-clocks-and-alarms">. </PARA> </SECT1> </CHAPTER> <CHAPTER id="tour-of-kernel-sources"> <TITLE>A tour of the kernel sources</TITLE> <PARA>This description takes the form of a tour around the sources explaining their structure and describing the functionality of each component. </PARA> <PARA>The kernel is divided into two basic parts, the largely machine independent parts in <filename>packages/kernel/&versiondir;</FILENAME>, and the architecture- and platform-specific parts that comprise the Hardware Abstraction Layer (HAL) in <filename>packages/hal</filename>. These will be described separately. Also note that the HAL is described in great detail in its own chapter (<xref linkend="the-ecos-hardware-abstraction-layer">). </PARA> <SECT1 id="kernel-headers"> <TITLE><!-- <index></index> -->Kernel headers</TITLE> <PARA>Kernel header files (in <FILENAME>packages/kernel/&versiondir;/include</FILENAME>) provide external interfaces and configuration control for the various kernel objects. In general there is an include file for each major kernel class. Those header files having to do with configuration live in the <FILENAME>pkgconf</FILENAME> subdirectory. </PARA> <PARA>The base name of a header file and the source file that implements it are usually the same. So, for example, the member functions defined in <FILENAME>sched.hxx</FILENAME> are implemented in <FILENAME>sched.cxx</FILENAME>. For a number of classes there are also header files that define inline functions, for example <FILENAME>sched.inl</FILENAME>. </PARA> <PARA>There are some kernel objects that are implemented using C++ templates to allow code re-use in future; it is not intended that these template classes be used generally by applications. The appropriate concrete kernel classes should be used instead. </PARA> <PARA>Now we examine the files one by one for reference: </PARA> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>include/bitmap.hxx</FILENAME></TERM> <LISTITEM> <PARA>Bitmap scheduler definition. See source file <FILENAME>sched/bitmap.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/clock.hxx</FILENAME></TERM> <TERM><FILENAME>include/clock.inl</FILENAME> </TERM> <LISTITEM> <PARA>Counter, clock and alarm functions. See source file <FILENAME>common/clock.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/diag.h</FILENAME> </TERM> <LISTITEM> <PARA>Diagnostic routines. See source file <FILENAME>trace/diag.c</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/errors.h</FILENAME> </TERM> <LISTITEM> <PARA>Kernel error codes. See source file <FILENAME>common/except.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/except.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Exception handling.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/flag.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Flag synchronization objects. See source file <FILENAME>sync/flag.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/instrmnt.h</FILENAME> </TERM> <LISTITEM> <PARA>Instrumentation. See source file <FILENAME>instrmnt/meminst.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/intr.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Interrupts. See source file <FILENAME>intr/intr.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/kapi.h</FILENAME></TERM> <TERM><FILENAME>include/kapidata.h</FILENAME> </TERM> <LISTITEM> <PARA>Native 'C' API to the kernel. See source file <FILENAME>common/kapi.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/ktypes.h</FILENAME> </TERM> <LISTITEM> <PARA>Kernel types.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/llistt.hxx</FILENAME> </TERM> <LISTITEM> <PARA>A simple doubly linked-list template class used elsewhere in the kernel.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/lottery.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Lottery scheduler implementation. (Not used). See source file <FILENAME>sched/lottery.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/mbox.hxx</FILENAME></TERM> <TERM><FILENAME>include/mboxt.hxx</FILENAME></TERM> <TERM><FILENAME>include/mboxt2.hxx</FILENAME></TERM> <TERM><FILENAME>include/mboxt.inl</FILENAME></TERM> <TERM><FILENAME>include/mboxt2.inl</FILENAME> </TERM> <LISTITEM> <PARA>Message boxes. See source file <FILENAME>sync/mbox.cxx</FILENAME>; <FILENAME>mboxt.hxx</FILENAME> and <FILENAME>mboxt2.hxx</FILENAME> and <FILENAME>mboxt.inl</FILENAME> and <FILENAME>mboxt2.inl</FILENAME> implement the underlying template function.</PARA> <NOTE> <PARA>The files with a 2 suffix are used by default and provide precise µITRON semantics.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/memfixed.hxx</FILENAME></TERM> <TERM><FILENAME>include/mempoolt.hxx</FILENAME></TERM> <TERM><FILENAME>include/mempolt2.hxx</FILENAME></TERM> <TERM><FILENAME>include/mempoolt.inl</FILENAME></TERM> <TERM><FILENAME>include/mempolt2.inl</FILENAME></TERM> <TERM><FILENAME>include/mfiximpl.hxx</FILENAME></TERM> <TERM><FILENAME>include/mfiximpl.inl</FILENAME> </TERM> <LISTITEM> <PARA>Fixed-block allocation memory pools. See source file <FILENAME>mem/memfixed.cxx</FILENAME>; mempoolt[2] and mfiximpl are a thread-safety template function and underlying memory manager respectively.</PARA> <NOTE> <PARA>The files with a 2 suffix are used by default and provide precise µITRON semantics.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/memvar.hxx</FILENAME></TERM> <TERM><FILENAME>include/mempoolt.hxx</FILENAME></TERM> <TERM><FILENAME>include/mempolt2.hxx</FILENAME></TERM> <TERM><FILENAME>include/mempoolt.inl</FILENAME></TERM> <TERM><FILENAME>include/mempolt2.inl</FILENAME></TERM> <TERM><FILENAME>include/mvarimpl.hxx</FILENAME></TERM> <TERM><FILENAME>include/mvarimpl.inl</FILENAME> </TERM> <LISTITEM> <PARA>Variable-block allocation memory pools. See source file <FILENAME>mem/memvar.cxx</FILENAME>; mempoolt[2] and mvar are a thread-safety template function and underlying memory manager respectively.</PARA> <NOTE> <PARA>The files with a 2 suffix are used by default and provide precise µITRON semantics.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/mlqueue.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Multi-level queue scheduler. See source file <FILENAME>sched/mlqueue.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/mutex.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Mutexes. See source file <FILENAME>sync/mutex.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/sched.hxx</FILENAME></TERM> <TERM><FILENAME>include/sched.inl</FILENAME> </TERM> <LISTITEM> <PARA>General scheduler functions. See source file <FILENAME>sched/sched.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/sema.hxx</FILENAME></TERM> <TERM><FILENAME>include/sema2.hxx</FILENAME> </TERM> <LISTITEM> <PARA>Semaphores. See source files <FILENAME>sync/cnt_sem.cxx</FILENAME> and <FILENAME>sync/bin_sem.cxx</FILENAME> for counting or binary semaphores respectively.</PARA> <NOTE> <PARA>The file with a 2 suffix is used by default and provides precise µITRON semantics.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>include/thread.hxx</FILENAME></TERM> <TERM><FILENAME>include/thread.inl</FILENAME> </TERM> <LISTITEM> <PARA>Threads, regardless of scheduler. See <FILENAME>common/thread.cxx</FILENAME></PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT1> <SECT1 id="kernel-source-files"> <TITLE><!-- <index></index> --> Kernel source files</TITLE> <PARA>The kernel source directory (<FILENAME>packages/kernel/&versiondir;/src</FILENAME>) is divided into a number of sub-directories each containing the source files for a particular kernel subsystem. These sources divide into two classes: those that are generic to all configurations, and those that are specific to a particular configuration. </PARA> <SECT2 id="sched-subdirectory"> <TITLE><!-- <index></index> --><!-- <xref> -->Sched subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>sched/sched.cxx</FILENAME> </TERM> <LISTITEM> <PARA> </PARA> <PARA>This contains the implementation of the base scheduler classes. The most important function here is <FUNCTION>Cyg_Scheduler::unlock_inner()</FUNCTION> which runs DSRs and performs any rescheduling and thread switching.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sched/bitmap.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the bitmap scheduler implementation. It represents each runnable thread with a bit in a bitmap. Each thread must have a unique priority and there is a strict upper limit on the number of threads allowed.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sched/mlqueue.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the multi-level queue scheduler implementation. It implements a number of thread priorities and is capable of timeslicing between threads at the same priority. This scheduler can also support priority inheritance.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sched/lottery.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains a lottery scheduler implementation. This implements a CPU share scheduler based on threads holding a number of lottery tickets. At the start of each time quantum, a random number is generated and the thread holding the matching ticket is scheduled. Compensation tickets and ticket donation allow fair sharing for I/O bound threads and an equivalent mechanism to priority inheritance.</PARA> <NOTE> <PARA>This scheduler is experimental, and is meant to test the behavior of other parts of the kernel with a non-orthodox scheduler. It is not meant to be used for real applications. It is currently under development and is incomplete and unusable.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> --> Common subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>common/thread.cxx</FILENAME> </TERM> <LISTITEM> <PARA> </PARA> <PARA>This implements the basic thread classes. The functions in this file implement the basic thread controls to sleep and wake threads, change priorities and delay and time-out. Also defined here is the idle thread that runs when there is nothing else to do.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>common/clock.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This implements the counter, clock and alarm functions. Also defined here is the system real-time clock that is used to drive timeslicing, delays and time-outs.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>common/kapi.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This implements a C API to the basic kernel functions.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>common/memcpy.c</FILENAME></TERM> <TERM><FILENAME>common/memset.c</FILENAME> </TERM> <LISTITEM> <PARA>Standard ANSI memcpy and memset operations; these are here because the compiler may invoke them for structure operations regardless of the presence of a C library.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> --> Interrupt subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>intr/intr.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This implements the Interrupt class. Most of this code is concerned with posting and calling DSRs. The remainder of the interrupt handling code is machine specific and is in <FILENAME>hal_intr.cxx</FILENAME> in the HAL directory.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Synchronization subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>sync/mutex.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the implementation of mutexes and condition variables. Mutexes can optionally be configured to use a priority inheritance mechanism supplied by the scheduler.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sync/cnt_sem.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the implementation of counting semaphores.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sync/cnt_sem2.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the alternate implementation of counting semaphores which implements precise µITRON semantics.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sync/bin_sem.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the implementation of binary semaphores.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sync/mbox.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains wrapper functions for a message box of (void *) values. The implementation is the template defined in <FILENAME>include/mboxt.hxx</FILENAME> which <FILENAME>include/mboxt.inl</FILENAME> implements in turn. Message boxes exist in the kernel specifically to support µITRON compatibility.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>sync/flag.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the implementation of flag objects. Flag objects exist in the kernel specifically to support µITRON compatibility.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Memory management subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>mem/memfixed.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the wrapper functions for a fixed-block allocation memory manager. The actual implementation is in two parts: <FILENAME>include/mfiximpl.hxx</FILENAME> implements the fixed-block memory management algorithms, and template <FILENAME>include/mempoolt.hxx</FILENAME> implements thread safety and waiting for memory management classes. These are combined in <FILENAME>memfixed.cxx</FILENAME>. Memory pools exist in the kernel specifically to support µITRON compatibility.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>mem/memvar.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains the wrapper functions for a variable-block allocation memory manager. The actual implementation is in two parts: <FILENAME>include/mvarimpl.hxx</FILENAME> implements the variable-block memory management algorithms, and template <FILENAME>include/mempoolt.hxx</FILENAME> implements thread safety and waiting for memory management classes. These are combined in <FILENAME>memvar.cxx</FILENAME>. Memory pools exist in the kernel specifically to support µITRON compatibility.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Instrumentation subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>instrmnt/meminst.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains an implementation of the instrumentation mechanism that stores instrumentation records in a circular buffer in memory. The size of this buffer is configurable. The instrumentation flags mechanism allows the generation of instrumentation records to be controlled on a per-record basis. The header file <FILENAME>cyg/kernel/instrmnt.h</FILENAME> contains macros to generate instrumentation records in various places, and may be configured to only generate instrumentation records where required.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>instrmnt/nullinst.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains an implementation of the instrumentation mechanism that does nothing. By substituting its object file <FILENAME>nullinst.o</FILENAME> for <FILENAME>meminst.o</FILENAME> in a build, the instrumentation mechanism may be disabled without recompiling.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Trace subdirectory</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>trace/simple.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains an implementation of the trace and assert mechanisms that output textual messages via a set of externally defined functions. These are currently supplied by the code in <FILENAME>trace/diag.c</FILENAME> but may be supplied by a device driver in the future.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>trace/fancy.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains a (fancier) implementation of the trace and assert mechanisms that output textual messages via a set of externally defined functions. These are currently supplied by the code in <FILENAME>trace/diag.c</FILENAME> but may be supplied by a device driver in the future.</PARA> <PARA>This more elaborate view was introduced mainly to validate the trace and assertion macros during development. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>trace/null.cxx</FILENAME> </TERM> <LISTITEM> <PARA>This contains an implementation of the trace and assert mechanisms that do nothing. By substituting its object file <FILENAME>null.o</FILENAME> for <FILENAME>simple.o</FILENAME> in a build, the trace mechanisms may be disabled without recompiling.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>trace/diag.c</FILENAME> </TERM> <LISTITEM> <PARA>This contains a number of diagnostic routines that use the HAL supplied diagnostic output mechanism to format and print strings and numbers. There is currently no formatted output.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>trace/tcdiag.c</FILENAME> </TERM> <LISTITEM> <PARA>This contains an implementation of the testing internal API which uses the kernel's diagnostic routines to perform output.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Sload subdirectory</TITLE> <PARA>This contains the sources of a simple S-Record loader that may be used in a ROM for various microprocessor development boards to download code via a serial port.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->HAL source files</TITLE> <PARA>The HAL is divided into architecture- and platform-specific files. For each architecture supported, there is an <FILENAME>arch</FILENAME> directory, containing files generic to that architecture, and a <FILENAME>platform</FILENAME> directory, containing files specific to each platform supported.</PARA> <PARA>Amongst the architectures supported are: the PowerPC, the Tx39 and the MN10300. To find the code corresponding to each architecture, substitute “powerpc”, “mips” and “mn10300”, respectively, for “ARCH” in the following file descriptions. Similarly substitute the appropriate platform name representing your development board for “PLATFORM”.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Architecture files</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/include/basetype.h</FILENAME></TERM> <LISTITEM> <PARA>This file is used to define the base architecture configuration such as endianness and word size.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/include/hal_arch.h</FILENAME></TERM> <LISTITEM> <PARA>This file contains macros that implement various architecture-specific functions. The most important macros here are the thread context initialization and switch macros that are used to implement multithreading.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/include/hal_intr.h</FILENAME> </TERM> <LISTITEM> <PARA>This file contains the HAL support for interrupt management and clock support.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/include/hal_io.h</FILENAME> </TERM> <LISTITEM> <PARA>This file contains the HAL support for accessing hardware registers. It provides a portable API that allows more generic device drivers to be written.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/include/hal_cache.h</FILENAME> </TERM> <LISTITEM> <PARA>This file contains macros to control any caches that may be present.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/include/ARCH_stub.h</FILENAME> </TERM> <LISTITEM> <PARA>This file contains architectural information for a GDB stub, such as the register layout in a GDB packet.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/src/vectors.S</FILENAME> </TERM> <LISTITEM> <PARA>This is an assembly code file that contains the code to handle interrupt and exception vectors. Since system reset can also be considered an exception, this is handled here also. Interrupts are currently handled by placing a stub routine in the hardware vector which calls a Vector Service Routine via an indirection table. There is a API to allow user-defined VSRs to be installed. The default VSR reads the interrupt controller registers and decodes the interrupt source into an offset into a further table of interrupt service routines. It also handles interrupt cleanup, which may result in the execution of deferred service routines (DSRs) and the preemption of the current thread.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/src/context.S</FILENAME> </TERM> <LISTITEM> <PARA>If present, this is an assembly code file that contains the code to support thread contexts. The routines to switch between various contexts, as well as initialize a thread context may be present in this file.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/src/hal_misc.c</FILENAME> </TERM> <LISTITEM> <PARA>This file contains the implementation of various miscellaneous HAL routines that are needed by the kernel or C++ runtime.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/src/ARCH_stub.c</FILENAME> </TERM> <LISTITEM> <PARA>This file contains the architectural part of a GDB stub. This deals with CPU-specific details of the stub, such as the setting of breakpoints and translating exception data into signals that GDB understands.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/arch/&versiondir;/src/ARCH.ld</FILENAME> </TERM> <LISTITEM> <PARA>This file is the linker script. During preprocessing it includes linker script fragments that define the memory layout.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Platform files</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM><FILENAME>ARCH/PLATFORM/&versiondir;/include/hal_diag.h</FILENAME> </TERM> <LISTITEM> <PARA>This file contains the definitions of macros that support the HAL diagnostic output mechanism.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/PLATFORM/&versiondir;/include/plf_stub.h</FILENAME> </TERM> <LISTITEM> <PARA>This file contains a set of macros that allow the common GDB stub code to access the platform-specific minimal serial driver functions.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/PLATFORM/&versiondir;/src/hal_diag.c</FILENAME> </TERM> <LISTITEM> <PARA>This file contain the implementation of the HAL diagnostic output mechanism.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/PLATFORM/&versiondir;/src/plf_stub.c</FILENAME> </TERM> <LISTITEM> <PARA>This file contains a minimal serial driver for the target platform that is used by the GDB stub.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><FILENAME>ARCH/PLATFORM/&versiondir;/src/PLATFORM.S</FILENAME> </TERM> <LISTITEM> <PARA>This is an assembler file that contains any platform-specific code. It often contains platform initialization code called from vectors.S.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> </SECT1> </chapter> <CHAPTER id="requirements-for-programs"> <TITLE>Requirements for programs</TITLE> <PARA><EMPHASIS>eCos</EMPHASIS> <!-- <index></index> -->programs do not have to satisfy any unusual requirements, but there are always some differences between a program written for a real-time operating system as opposed to one written for a time sharing, virtual memory system like UNIX or Windows NT.</PARA> <PARA>This chapter contains checklist of things to remember when writing <EMPHASIS>eCos</EMPHASIS> programs. </PARA> <SECT1 id="cyg-user-start"> <TITLE><!-- <index></index> -->cyg_user_start()</TITLE> <PARA>The entry point for <EMPHASIS>eCos</EMPHASIS> user programs is usually <FUNCTION>cyg_user_start()</FUNCTION> instead of <FUNCTION>main()</FUNCTION>, although <FUNCTION>main()</FUNCTION> <EMPHASIS>can</EMPHASIS> be used if the ISO C library package is selected. Complete detail on the start-up sequence is given in <xref linkend="system-start-up">. </PARA> </SECT1> <SECT1 id="necessary-headers"> <TITLE>Necessary headers</TITLE> <PARA>Any program which uses <EMPHASIS>eCos</EMPHASIS> system calls must have the following line at the top of the file:</PARA> <PROGRAMLISTING>#include <cyg/kernel/kapi.h><!-- <index></index> --></PROGRAMLISTING> <PARA>and the programmer must make sure that <FILENAME>cyg/kernel/kapi.h</FILENAME> is available in the compiler include path. This can be done by setting the <EMPHASIS>C_INCLUDE_PATH</EMPHASIS> environment variable or by including the <EMPHASIS>-I </EMPHASIS>flag on the compiler command line. </PARA> </SECT1> <SECT1 id="necessary-link-instructions"> <TITLE>Necessary link instructions</TITLE> <PARA>The <EMPHASIS>eCos</EMPHASIS> configuration and building process (described in <EMPHASIS>Getting Started with </EMPHASIS><PRODUCTNAME>eCos</PRODUCTNAME> and <PRODUCTNAME>eCos</PRODUCTNAME><EMPHASIS> User's Guide</EMPHASIS>) builds a single library, <FILENAME>libtarget.a</FILENAME>, which contains the selected <EMPHASIS>eCos</EMPHASIS> components. The <FILENAME>libtarget.a</FILENAME> library does <EMPHASIS>not</EMPHASIS> contain any user libraries: If you put some of your source in libraries, you will have to explicitly include those libraries in the linking instruction. </PARA> <PARA>You also need to link to the <EMPHASIS>GNU C Compiler</EMPHASIS> runtime support library (<FILENAME>libgcc.a</FILENAME>).</PARA> <PARA>You should <EMPHASIS>not</EMPHASIS> link to the standard C++ library. This can be achieved with the <EMPHASIS>-nostdlib</EMPHASIS> option.</PARA> <PARA>You should only link to <FILENAME>libtarget.a</FILENAME> and <FILENAME>libgcc.a</FILENAME> using the linker script <FILENAME>target.ld</FILENAME> provided with <EMPHASIS>eCos</EMPHASIS>. The command line for linking should look like </PARA> <screen> gcc [options] [object files] -Ttarget.ld -nostdlib </screen> </SECT1> <SECT1 ID="INTERRUPT-AND-EXCEPTION-HANDLERS"> <TITLE><!-- <xref> -->Interrupt and exception handlers</TITLE> <PARA>In <EMPHASIS>eCos</EMPHASIS> a distinction is made between <EMPHASIS>exceptions</EMPHASIS> and <EMPHASIS>interrupts</EMPHASIS>.</PARA> <VARIABLELIST> <VARLISTENTRY> <TERM><!-- <index></index> -->exceptions </TERM> <LISTITEM> <PARA>are the result of some action by the currently executing code. Examples of exceptions are divide by zero, illegal instruction, bad memory access, etc.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM><!-- <index></index> -->interrupts </TERM> <LISTITEM> <PARA>are the result of a signal source which is conceptually asynchronous with the currently executing code. Examples of interrupts sources are the real-time clock, external and on chip peripherals and so forth. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <PARA>This distinction is made in the <EMPHASIS>eCos</EMPHASIS> hardware abstraction layer (HAL) to provide a cleaner and more portable mechanism for installing interrupt handlers and exception handlers. Individual hardware platforms can have different ways of naming and handling interrupts, which is why this abstraction layer was chosen. </PARA> <PARA>Interrupts and exceptions are both associated with <!-- <index></index> -->vectors, which are labeled by vector numbers (<xref linkend="exception-handling"> and <xref linkend="interrupt-handling">).</PARA> <PARA>There are distinct spaces for exception and interrupt vectors. These are called “exception vector numbers” and “interrupt vector numbers”. System calls which install exception handlers use the exception vector number, and the system calls which install interrupt handlers use the interrupt vector number to specify which interrupt or exception should be handled by the handler.</PARA> <PARA>The details of the vector layout depend on the microprocessor and interrupt controller, and are documented in the relevant API sections.</PARA> <PARA>Interrupt handlers are actually a <EMPHASIS>pair</EMPHASIS> of functions, one of which (the <EMPHASIS>interrupt service routine</EMPHASIS>, or ISR) is executed immediately and runs with that interrupt disabled. Since interrupts are disabled for the duration of the ISR, the ISR should be very brief and should not use any system services.</PARA> <PARA>After the ISR exits, but before the kernel scheduler is invoked again, a <EMPHASIS>delayed service routine</EMPHASIS> (DSR) will be invoked. It executes with scheduling disabled, but with interrupts enabled, so that further invocations of the same DSR can be queued. The DSR can use some producer-side system calls, but it should be carefully crafted to avoid using any call that might put its thread to sleep. One of the few examples of safe calls is <!-- <index></index> --><FUNCTION>cyg_semaphore_post()</FUNCTION>; the non-blocking versions of some system calls are also safe. A call that is unsafe is <!-- <index></index> --><FUNCTION>cyg_mutex_lock()</FUNCTION>, since it will block if the mutex is already locked by another thread.</PARA> <PARA>Finally, <EMPHASIS>eCos</EMPHASIS> has a formalism for installing <EMPHASIS>low level handlers</EMPHASIS> which bypass the kernel mechanisms described above. A program can install a <!-- <index></index> --><FIRSTTERM>vector service routine</firstterm> (VSR) which will be invoked instead of the kernel's usual exception or interrupt handling. The VSR will typically be written in assembly language. </PARA> <PARA>VSRs are associated with vector numbers in the exception space, just like exception handlers (although there are some variations — architectures in which there are no exceptions in the <EMPHASIS>eCos</EMPHASIS> sense). The main difference between VSRs and exception handlers is that VSRs bypass the kernel's usual mechanisms. </PARA> </SECT1> <SECT1 id="memory-allocation"> <TITLE><!-- <index></index> -->Memory allocation</TITLE> <PARA>Most <EMPHASIS>eCos</EMPHASIS> system calls expect you to pass the address of pre-allocated memory for the objects created in that system call. This is frequently the preferred way of doing things for embedded applications, where programmers want to allocate all memory statically and have fine control over that resource.</PARA> <PARA>In contrast, some <EMPHASIS>eCos</EMPHASIS> system calls also allow a NULL pointer to be passed. In such a case the kernel will allocate the memory or select default size. This feature is not supported in the current release, and a warning flag is placed in the documentation for those routines (like <FUNCTION>cyg_thread_create()</FUNCTION>). </PARA> <PARA><EMPHASIS>eCos</EMPHASIS> provides dynamic memory allocation, based on <EMPHASIS>memory pools</EMPHASIS>, a useful and flexible approach to memory management inspired by the µITRON compatibility layer. These are described in <xref linkend="memory-pools">.</PARA> <PARA>If you configure your system to use the Standard C Library you can also use the standard <FUNCTION>malloc()</FUNCTION> library call. </PARA> </SECT1> <SECT1 id="assertions-and-bad-param-handling"> <TITLE>Assertions and bad parameter handling</TITLE> <PARA>This section describes how the <EMPHASIS>eCos</EMPHASIS> kernel and basic packages behave when system calls are invoked with bad parameters.</PARA> <PARA>In <EMPHASIS>eCos</EMPHASIS>, the basic kernel assertion behavior is configuration-dependent.</PARA> <PARA>By default, <!-- <index></index> -->assertions are turned off in the kernel. If the kernel is configured to turn them <EMPHASIS>on</EMPHASIS>, the kernel will make basic assertions, such as checking for invalid parameters when system calls are invoked. If an assertion fails, the kernel will print a message to the diagnostic output channel and stop executing.</PARA> <PARA>If the kernel is configured with assertions disabled (usually when the application has been thoroughly debugged), it will not do any checking. </PARA> <PARA>The configuration sections referenced above also describe the use of preconditions, postconditions and loop invariants. These are no different from ordinary assertions, but they are used in specialized circumstances, and the programmer would wish to select their presence individually. </PARA> </SECT1> </CHAPTER> <CHAPTER id="system-start-up"> <TITLE><!-- <xref> -->System start-up</TITLE> <PARA>We describe here the steps performed by <EMPHASIS>eCos</EMPHASIS> upon start-up, mentioning how a programmer can introduce custom start-up routines. </PARA> <SECT1 id="system-startup-hal"> <TITLE>System start-up — the HAL</TITLE> <PARA>The HAL (Hardware Abstraction Layer, see <xref linkend="the-ecos-hardware-abstraction-layer">) is the <EMPHASIS>eCos</EMPHASIS> package which contains all <!-- <index></index> -->start-up code. Its start-up procedure is outlined in detail in <xref linkend="hal-startup">, but the main steps can be summarized here:</PARA> <ORDEREDLIST> <LISTITEM> <PARA> The HAL initializes the hardware, coordinates with the ROM monitor, and performs diagnostics.</PARA> </LISTITEM> <LISTITEM> <PARA>The HAL invokes all static and global C++ constructors. </PARA> </LISTITEM> <LISTITEM> <PARA>The HAL jumps to <FUNCTION>cyg_start()</FUNCTION>, which has the following prototype:</PARA> <PROGRAMLISTING>void cyg_start( void )</PROGRAMLISTING> </LISTITEM> </ORDEREDLIST> </SECT1> <SECT1 id="system-startup-cyg-start"> <TITLE>System start-up — cyg_start()</TITLE> <PARA><!-- <index></index> --><FUNCTION>cyg_start()</FUNCTION> is the core of the <!-- <index></index> -->start-up mechanism. The default definition is in <FILENAME>infra/current/src/startup.cxx</FILENAME></PARA> <PARA>It calls, in turn, </PARA> <PROGRAMLISTING>cyg_prestart() cyg_package_start() cyg_user_start()</PROGRAMLISTING> <PARA>and then starts the <EMPHASIS>eCos</EMPHASIS> scheduler if the system has been configured to have a kernel and scheduler.</PARA> <PARA>You can override the default <FUNCTION>cyg_start()</FUNCTION> routine by providing your own function by the same name with the following prototype: </PARA> <PROGRAMLISTING>void cyg_start( void )</PROGRAMLISTING> <WARNING> <PARA>Overriding <FUNCTION>cyg_start()</FUNCTION> should rarely, if ever, be done. The functions <FUNCTION>cyg_prestart()</FUNCTION> and <FUNCTION>cyg_user_start()</FUNCTION> described just below allow enough flexibility for installing user initialization code safely for almost all applications.</PARA> </WARNING> <NOTE> <PARA> If you are supplying your own definition of this function from a C++ file, make sure it has “C” linkage. </PARA> </NOTE> </SECT1> <SECT1 id="system-startup-cyg-prestart"> <TITLE>System startup — cyg_prestart()</TITLE> <PARA>The default <!-- <index></index> --><FUNCTION>cyg_prestart()</FUNCTION> function does not do anything; it is meant to be overwritten if the programmer needs to do any initialization <EMPHASIS>before</EMPHASIS> other system level initialization.</PARA> <PARA>You can override the default <FUNCTION>cyg_prestart()</FUNCTION> routine by providing your own function by the same name with the following prototype: </PARA> <PROGRAMLISTING>void cyg_prestart( void )</PROGRAMLISTING> <NOTE> <PARA> If you are supplying your own definition of this function from a C++ file, make sure it has “C” linkage.</PARA> </NOTE> </SECT1> <SECT1 id="system-startup-cyg-package-start"> <TITLE>System startup — cyg_package_start()</TITLE> <PARA>The <!-- <index></index> --> <FUNCTION>cyg_package_start()</FUNCTION> allows individual packages to do their initialization before the main user program is invoked.</PARA> <PARA>Two of the packages shipped with this release of <EMPHASIS>eCos</EMPHASIS> have code in the default <FUNCTION>cyg_package_start()</FUNCTION>; the µITRON and the ISO standard C library compatibility packages (<xref linkend="compat-uitron-microitron-api"> and <xref linkend="c-and-math-library-overview">). </PARA> <PARA>The infrastructure package has configuration options CYGSEM_START_UITRON_COMPATIBILITY and CYGSEM_START_ISO_C_COMPATIBILITY which control specialized initialization.</PARA> <PARA>You can override the default <FUNCTION>cyg_package_start()</FUNCTION> routine by providing your own function by the same name with the following prototype: </PARA> <PROGRAMLISTING>void cyg_package_start( void )</PROGRAMLISTING> <PARA>but you should be careful to initialize the default packages (if you are using them). An example user-supplied function might look like:</PARA> <PROGRAMLISTING> void cyg_package_start(void) { #ifdef CYGSEM_START_UITRON_COMPATABILITY cyg_uitron_start(); /* keep the µITRON initialization */ #endif my_package_start(); /* make sure I initialize my package */ }</PROGRAMLISTING> <NOTE> <PARA>If you are supplying your own definition of this function from a C++ file, make sure it has “C” linkage.</PARA> </NOTE> </SECT1> <SECT1 id="system-startup-cyg-user-start"> <TITLE>System startup — cyg_user_start()</TITLE> <PARA>This is the normal entry point for your code. Although a default empty version is provided by <EMPHASIS>eCos</EMPHASIS>, this is a good place to set up your threads (<xref linkend="thread-operations">).</PARA> <PARA>If you are not including the ISO standard C library package then there will not be a <FUNCTION>main()</FUNCTION> function, so it becomes mandatory to provide this function (<xref linkend="c-library-startup">).</PARA> <PARA>To set up your own <!-- <index></index> --><FUNCTION>cyg_user_start()</FUNCTION> function, create a function by that name with the following prototype:</PARA> <PROGRAMLISTING>void cyg_user_start( void )</PROGRAMLISTING> <PARA>When you return control from <FUNCTION>cyg_user_start()</FUNCTION>, <FUNCTION>cyg_start()</FUNCTION> will then invoke the scheduler, and any threads you created and resumed in <FUNCTION>cyg_user_start()</FUNCTION> will be executed. The preferred approach is to allow the scheduler to be started automatically, rather than to start it explicitly in <FUNCTION>cyg_user_start()</FUNCTION>. </PARA> <CAUTION> <PARA>Remember that <FUNCTION>cyg_user_start()</FUNCTION> is invoked before the scheduler (and frequently the scheduler is invoked as the last step in <FUNCTION>cyg_user_start()</FUNCTION>), so it should not use any kernel services that require the scheduler.</PARA> </CAUTION> <NOTE> <PARA> If you are supplying your own definition of this function from a C++ file, make sure it has “C” linkage.</PARA> </NOTE> </SECT1> </CHAPTER> <CHAPTER id="native-kernel-c-language-api"> <TITLE>Native kernel C language API</TITLE> <PARA>The <EMPHASIS>eCos</EMPHASIS> kernel, like many other real-time kernels, is a library to which the programmer links an application. System calls resemble library API calls, and there is no trap mechanism to switch from user to system mode. </PARA> <PARA>We present here the <EMPHASIS>eCos</EMPHASIS> <!-- <index></index> -->kernel API and the APIs for other kernels provided as compatibility layers on top of eC<EMPHASIS>os</EMPHASIS>. </PARA> <PARA>Since this API sits on top of a configurable system, the semantics are only weakly defined. The exact semantics and even the API itself depend on the configuration. For example if returned error codes were supported this would change the prototype of the functions. The semantics given in this chapter describe the default configuration.</PARA> <PARA>As mentioned above, all source files which use the kernel C API should have the following <computeroutput>#include</computeroutput> statement:</PARA> <PROGRAMLISTING>#include <cyg/kernel/kapi.h></PROGRAMLISTING> <PARA>at the head of the file. </PARA> <SECT1 id="kapi-types"> <TITLE>Types used in programming eCos</TITLE> <PARA>We now describe the types defined for use with <EMPHASIS>eCos</EMPHASIS>. These are available to programs that include <filename>kapi.h</filename>. </PARA> <PARA>Most of these types are meant to be <EMPHASIS>opaque</EMPHASIS> — in other words, programmers do not need to know (and probably should not know) how they are defined. But the types that are numeric are marked, since it can be useful to use comparison operators.</PARA> <PARA>The definitions for these types can be found in the installed tree, in the file <filename>include/cyg/kernel/kapi.h</filename>. </PARA> <PARA>The <EMPHASIS>eCos</EMPHASIS> kernel uses the following naming convention for types: </PARA> <ITEMIZEDLIST> <LISTITEM> <PARA>Types that can be treated as completely opaque usually have _t suffix.</PARA> </LISTITEM> <LISTITEM> <PARA>Types for which it is necessary to know the implementation do not have a _t suffix.</PARA> </LISTITEM> </ITEMIZEDLIST> <SECT2> <TITLE><!-- <index></index> -->cyg_addrword_t</TITLE> <PARA>A type which is large enough to store the larger of an address and a machine word. This is used for convenience when a function is passed data which could be either a pointer to a block of data or a single word. </PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_handle_t</TITLE> <PARA>A <EMPHASIS>handle</EMPHASIS> is a variable used to refer to <EMPHASIS>eCos</EMPHASIS> system objects (such as a thread or an alarm). Most <EMPHASIS>eCos</EMPHASIS> system calls that create system objects will return a handle that is used to access that object from then on.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_priority_t</TITLE> <PARA>A numeric type used to represent the priority of a thread, or the priority of an interrupt level. A lower number means a higher (i.e. more important) priority thread. </PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_code_t</TITLE> <PARA>A numeric type used for various error or status codes, such as exception numbers.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_vector_t</TITLE> <PARA>A numeric type used to identify an interrupt vector. Its value is called the interrupt vector <EMPHASIS>id</EMPHASIS>. This type is used for both ISR vector ids and VSR vector ids.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_tick_count_t</TITLE> <PARA>A numeric type used to count counter ticks. The resolution and other details regarding tick quantities depend on the configuration, but this is a 64 bit type, and no matter what configuration is chosen it should still last for centuries before it overflows.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_bool_t</TITLE> <PARA>A boolean type whose values can be false (0) or true (1).</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_thread_entry_t</TITLE> <PARA>A function type for functions that are entry points for threads. It is used in the thread creation call <FUNCTION>cyg_thread_create()</FUNCTION>.</PARA> <PARA>To help write thread entry point functions, here is how cyg_thread_entry_t is defined:</PARA> <PROGRAMLISTING>typedef void cyg_thread_entry_t(void *);</PROGRAMLISTING> <PARA>Examples of thread functions can be found in the programming tutorial in <EMPHASIS>Getting Started with </EMPHASIS><PRODUCTNAME>eCos</PRODUCTNAME>. </PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_exception_handler_t</TITLE> <PARA>A function type used for installing exception handlers. It is defined as: </PARA> <PROGRAMLISTING>typedef void cyg_exception_handler_t( cyg_addrword_t data, cyg_code_t exception_number, cyg_addrword_t info );</PROGRAMLISTING> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_thread, <!-- <index></index> --> cyg_interrupt, <!-- <index></index> --> cyg_counter, <!-- <index></index> --> cyg_clock, <!-- <index></index> --> cyg_alarm, <!-- <index></index> --> cyg_mbox, <!-- <index></index> --> cyg_mempool_var, and <!-- <index></index> --> cyg_mempool_fix</TITLE> <PARA>These types are of the appropriate size to contain the memory used by the respective kernel objects. These types are only used in the corresponding create call where the programmer allocates the memory for the object and passes the address to the kernel. After creation the provided handle is used to reference the object.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_mempool_info</TITLE> <PARA>Contains information about a memory pool. </PARA> <PROGRAMLISTING>typedef struct { cyg_int32 totalmem; cyg_int32 freemem; void *base; cyg_int32 size; cyg_int32 blocksize; cyg_int32 maxfree; // The largest free block } cyg_mempool_info;</PROGRAMLISTING> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_sem_t, <!-- <index></index> --> cyg_mutex_t, and <!-- <index></index> --> cyg_cond_t</TITLE> <PARA>These types are of the appropriate size to contain the memory used by their respective kernel objects. These objects are always referred to by a pointer to an object of this type.</PARA> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_VSR_t, <!-- <index></index> --> cyg_ISR_t, and <!-- <index></index> --> cyg_DSR_t</TITLE> <PARA>These are function types used when vector, interrupt and delayed service routines are installed. </PARA> <PROGRAMLISTING>typedef void cyg_VSR_t(); typedef cyg_uint32 cyg_ISR_t(cyg_vector_t vector, cyg_addrword_t data); typedef void cyg_DSR_t(cyg_uint32 vector, cyg_ucount32 count, cyg_addrword_t data);</PROGRAMLISTING> </SECT2> <SECT2 id="cyg-resolution-t"> <TITLE><!-- <index></index> --><!-- <xref> -->cyg_resolution_t</TITLE> <PARA>Stores the resolution of a clock. The resolution is defined to be (dividend/divisor) nanoseconds per tick. </PARA> <PROGRAMLISTING> typedef struct { cyg_uint32 dividend; cyg_uint32 divisor; } cyg_resolution_t;</PROGRAMLISTING> </SECT2> <SECT2> <TITLE><!-- <index></index> -->cyg_alarm_t</TITLE> <PARA>The function type used for alarm handlers. </PARA> <PROGRAMLISTING> typedef void cyg_alarm_t(cyg_handle_t alarm, cyg_addrword_t data);</PROGRAMLISTING> </SECT2> </SECT1> <SECT1 id="thread-operations"> <TITLE><!-- <index></index> --><!-- <xref> -->Thread operations</TITLE> <PROGRAMLISTING>void cyg_scheduler_start( void )</PROGRAMLISTING> <PARA>Starts the scheduler with the threads that have been created. It never returns. The scheduler has been chosen at configuration time. <EMPHASIS>eCos</EMPHASIS> currently ships with three schedulers: a bitmap scheduler, a multi-level scheduler (selected by default), and an experimental “lottery” scheduler which is currently incomplete and unusable.</PARA> <PARA>The configuration tool can be used to select between schedulers. The configuration options are CYGSEM_SCHED_BITMAP, CYGSEM_SCHED_MLQUEUE and CYGSEM_SCHED_LOTTERY.</PARA> <NOTE> <PARA> Interrupts are not enabled until the scheduler has been started with <FUNCTION>cyg_scheduler_start()</FUNCTION>.</PARA> </NOTE> <PROGRAMLISTING>void cyg_scheduler_lock( void )</PROGRAMLISTING> <PARA>Locks the scheduler so that a context switch cannot occur. This can be used to protect data shared between a thread and a DSR, or between multiple threads, by surrounding the critical region with <FUNCTION>cyg_scheduler_lock()</FUNCTION> and <FUNCTION>cyg_scheduler_unlock()</FUNCTION>. </PARA> <PROGRAMLISTING>void cyg_scheduler_unlock( void )</PROGRAMLISTING> <PARA>Unlocks the scheduler so that context switching can occur again.</PARA> <PROGRAMLISTING> void <!-- <index></index> -->cyg_thread_create( cyg_addrword_t sched_info, cyg_thread_entry_t *entry, cyg_addrword_t entry_data, char *name, void *stack_base, cyg_ucount32 stack_size, cyg_handle_t *handle, cyg_thread *thread )</PROGRAMLISTING> <PARA>Creates a thread in a suspended state. The thread will not run until it has been resumed with <FUNCTION>cyg_thread_resume()</FUNCTION> and the scheduler has been started with <FUNCTION>cyg_scheduler_start()</FUNCTION>.</PARA> <PARA>Here is a description of the parameters of <FUNCTION>cyg_thread_create()</FUNCTION>:</PARA> <VARIABLELIST> <VARLISTENTRY> <TERM>sched_info</TERM> <LISTITEM> <PARA>Information to be passed to the scheduler. For almost all schedulers this is a simple priority value, and you can simply pass a non-negative integer when you create the thread. Even when this holds, some schedulers may have restrictions on how priorities can be used. For example, the bitmap scheduler can onlyhave one thread at each priority, so if an already-occupied priority slot is quoted, the next free slot of lower priority is chosen.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>entry </TERM> <LISTITEM> <PARA>A user-supplied function: it is a routine that begins execution of the new thread. This function takes a single argument of type cyg_addrword_t, which is usually a pointer to a block of data, which allows <FUNCTION>cyg_scheduler_start()</FUNCTION> to pass data to this particular thread.</PARA> <PARA>Here is a typedef for the entry function: </PARA> <PROGRAMLISTING>typedef void cyg_thread_entry_t(cyg_addrword_t);</PROGRAMLISTING> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>entry_data </TERM> <LISTITEM> <PARA>A data value passed to the entry function. This may be either a machine word datum or the address of a block of data.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>name </TERM> <LISTITEM> <PARA>A C string with the name of this thread.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>stack_base </TERM> <LISTITEM> <PARA>The address of the stack base. If this value is NULL then <FUNCTION>cyg_thread_create()</FUNCTION> will choose a stack base.</PARA> <NOTE> <PARA>Passing a stack base of NULL is not supported in this release. You must pass a real address for the stack base.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>stack_size</TERM> <LISTITEM> <PARA>The size of the stack for this thread. If this is 0, the default stack size will be used for this thread.</PARA> <NOTE> <PARA>Passing a stack size of 0 is not supported in this release. You must pass a real stack size.</PARA> </NOTE> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>handle </TERM> <LISTITEM> <PARA><FUNCTION>cyg_thread_create()</FUNCTION> returns the thread handle in this location.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>thread </TERM> <LISTITEM> <PARA>The thread housekeeping information is placed in the memory pointed to by this parameter. If this pointer is NULL then the memory will be allocated. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <NOTE> <PARA>Passing a NULL value for the thread data structure address is not supported in this release. You must pass a valid address. </PARA> </NOTE> <PROGRAMLISTING>void cyg_thread_exit( void )</PROGRAMLISTING> <PARA>Exits the current thread. At present this simply puts the thread into suspended state.</PARA> <PROGRAMLISTING>void cyg_thread_suspend ( cyg_handle_t thread )</PROGRAMLISTING> <PARA>Suspends the thread. A thread may be suspended multiple times, in which case it will need to be resumed the same number of times before it will run. A thread can suspend itself; the effect will be that the call does not return until some other thread has resumed it. Obviously this can only be done once!</PARA> <PARA>It is OK for a thread to suspend other threads with the scheduler locked; it will continue to run until the scheduler is unlocked. However, a thread suspending itself with the scheduler locked is in danger of causing problems within the system because a suspended thread has been removed from its run-queue, even if it continues to execute! The problematic situation cannot be detected because it is OK for a DSR (ie. alarm function for example) to suspend a thread - possibly the current thread if that is what happened to be running, and DSRs run with the scheduler locked.</PARA> <PARA>If you see an assert referring to something like "Queue map bit not set for pri" it is likely that this means a thread has suspended itself with the scheduler locked. The simplest workaround is: never let a thread suspend itself; look for that behaviour in your code. </PARA> <PROGRAMLISTING> void cyg_thread_resume ( cyg_handle_t thread ) </PROGRAMLISTING> <PARA>Resumes <emphasis>thread</emphasis>. If a thread has been suspended multiple times it will need to be resumed the same number of times before it will run. Threads are created in a suspended state and must be resumed before they will run.</PARA> <PROGRAMLISTING>void cyg_thread_yield ( void )</PROGRAMLISTING> <PARA>Yields control to the next runnable thread of equal priority. If no such thread exists, then this function has no effect.</PARA> <PROGRAMLISTING>void cyg_thread_kill ( cyg_handle_t thread )</PROGRAMLISTING> <PARA>Kills <emphasis>thread</emphasis>.</PARA> <PROGRAMLISTING>cyg_bool_t cyg_thread_delete( cyg_handle_t thread)</PROGRAMLISTING> <PARA>Kills <EMPHASIS>thread</EMPHASIS> and deletes it from the scheduler. If necessary, it will kill <EMPHASIS>thread</EMPHASIS> first using <FUNCTION>cyg_thread_kill( thread )</FUNCTION>. If <EMPHASIS>thread</EMPHASIS> does not terminate in response to the kill message, this function returns false, indicating failure.</PARA> <PARA>This function differs from <FUNCTION>cyg_thread_kill()</FUNCTION> (or calling <FUNCTION>cyg_thread_exit()</FUNCTION> for the current thread) by deregistering the thread from the scheduler. As a result, the thread handle, thread stack and space passed for the thread housekeeping information can then be reused. This is not the case if just <FUNCTION>cyg_thread_kill()</FUNCTION> or <FUNCTION>cyg_thread_exit()</FUNCTION> is invoked for the thread.</PARA> <NOTE> <PARA><FUNCTION>cyg_thread_delete()</FUNCTION> only deregisters the thread from the scheduler, it does not free up any resources that had been allocated by the thread such as dynamic memory, nor does it unlock any synchronization objects owned by the thread. This is the responsibility of the programmer. Additionally, unlike <FUNCTION>cyg_thread_kill()</FUNCTION>, the <FUNCTION>cyg_thread_delete()</FUNCTION> function cannot be self-referencing.</PARA> </NOTE> <NOTE> <PROGRAMLISTING>// Delete another thread. This must be done in a loop, waiting // for the call to return true. If it returns false, go to sleep // for a while, so that the killed thread gets a chance to run // and complete its business. while (!cyg_thread_delete(<thread_handle>) { cyg_thread_delay(1);</PROGRAMLISTING> </NOTE> <PROGRAMLISTING> cyg_handle_t cyg_thread_self( void )</PROGRAMLISTING> <PARA>Returns the handle of the current thread.</PARA> <PROGRAMLISTING>void cyg_thread_release ( cyg_handle_t thread )</PROGRAMLISTING> <PARA>Break the thread out of any wait it is currently in. Exactly how the thread returns from the wait operation, and how, if at all, the break is indicated, depends on the synchronization object it was waiting on.</PARA> <PROGRAMLISTING>cyg_ucount32 cyg_thread_new_data_index ( void )</PROGRAMLISTING> <PARA>Allocates a new per-thread data index from those still available. If no more indexes are available, and assertions are enabled, an assertion will be raised.</PARA> <PROGRAMLISTING>void cyg_thread_free_data_index ( cyg_ucount32 index )</PROGRAMLISTING> <PARA>Return the per-thread data index to the pool.</PARA> <PROGRAMLISTING>CYG_ADDRWORD cyg_thread_get_data( cyg_ucount32 index )</PROGRAMLISTING> <PARA>Retrieve the per-thread data at the given index for the current thread.</PARA> <PROGRAMLISTING>CYG_ADDRWORD *cyg_thread_get_data_ptr( cyg_ucount32 index )</PROGRAMLISTING> <PARA>Return a pointer to the per-thread data at the given index for the current thread. This should be used with some care since in some future implementation the per-thread data may be managed by a dynamic mechanism that might invalidate this pointer at any time. This pointer should only be considered valid until the next call to the per-thread data functions.</PARA> <PROGRAMLISTING>void cyg_thread_set_data( cyg_ucount32 index, CYG_ADDRWORD data )</PROGRAMLISTING> <PARA>Store the data in the per-thread data for the current thread at the given index.</PARA> </SECT1> <SECT1 id="priority-manipulation"> <TITLE><!-- <index></index> -->Priority manipulation</TITLE> <PROGRAMLISTING>void cyg_thread_set_priority( cyg_handle_t thread, cyg_priority_t priority )</PROGRAMLISTING> <PARA>Sets the priority of the given thread to the given value. The smaller the value, the higher the priority of the thread.</PARA> <PARA>Allowed priorities range between 1 and 64. The values of these parameters are configuration-dependent because they depend on which scheduler has been selected, and what value has been configured for the CYGNUM_KERNEL_SCHED_PRIORITIES configuration parameter (<xref linkend="thread-operations">).</PARA> <PARA>There is always an idle thread, owned by the kernel, running at CYG_THREAD_MIN_PRIORITY. Because of this, ordinary threads should never be run at the lowest priority. </PARA> <PROGRAMLISTING>cyg_priority_t cyg_thread_get_priority( cyg_handle_t thread )</PROGRAMLISTING> <PARA>Returns the priority of the given thread.</PARA> <PROGRAMLISTING>void cyg_thread_delay( cyg_tick_count_t delay )</PROGRAMLISTING> <PARA>Puts the current thread to sleep for <emphasis>delay</emphasis> ticks. In a default configuration there are approximately 100 ticks a second. The actual length of the ticks is given by the resolution of the real-time clock. <xref linkend="counters-clocks-and-alarms"> for more information on counter resolution. </PARA> </SECT1> <SECT1 id="exception-handling"> <TITLE><!-- <index></index> --><!-- <xref> -->Exception handling</TITLE> <PARA>Exception handlers can be installed to deal with various system-level exceptions, such as alignment errors, resets, timers and so forth. Exception handling is a configurable feature of <EMPHASIS>eCos</EMPHASIS> and is enabled by default.</PARA> <PARA>The range of values for the exception_number parameter in the functions below is hardware-dependent, as are the individual exceptions. See <filename>hal/ARCH/arch/&versiondir;/include/hal_intr</filename> for the exception vector definitions specific to a given architecture. </PARA> <PARA>The exception handler is a function of the following type: </PARA> <PROGRAMLISTING>typedef void cyg_exception_handler_t( cyg_addrword_t data, cyg_code_t exception_number, cyg_addrword_t info );</PROGRAMLISTING> <PARA>cyg_exception_handler_t is the type used for functions which are called as a result of an exception. It is used in the function <FUNCTION>cyg_exception_set_handler()</FUNCTION>. </PARA> <PROGRAMLISTING>void cyg_exception_set_handler( cyg_code_t exception_number, cyg_exception_handler_t *new_handler, cyg_addrword_t new_data, cyg_exception_handler_t **old_handler, void **old_data )</PROGRAMLISTING> <PARA>Replace current exception handler. This may apply to either the thread, or to a global exception handler, according to how exception handling was configured (global or per-thread). The exception may be ignored, or used to specify a particular handler.</PARA> <PROGRAMLISTING>void cyg_exception_call_handler ( cyg_handle_t thread, cyg_code_t exception_number, cyg_addrword_t exception_info )</PROGRAMLISTING> <PARA>Invoke exception handler for the given exception number. The exception handler will be invoked with <emphasis>exception_info</emphasis> as its third argument.</PARA> </SECT1> <SECT1 id="interrupt-handling"> <TITLE><!-- <index></index> --><!-- <xref> -->Interrupt handling</TITLE> <PARA>Interrupt handling is by nature machine-specific. The <EMPHASIS>eCos</EMPHASIS> kernel aims to provide efficiency and flexibility in this area, while maintaining a very low interrupt latency. To allow the programmer direct access to hardware, the semantics and the interface can vary from one architecture to another.</PARA> <PARA>The interrupt vectors for a given architecture are defined in <filename>hal/ARCH/arch/&versiondir;/include/hal_intr.h</filename> where also special semantics and caveats of the interrupt capabilities would be described.</PARA> <PROGRAMLISTING>typedef void cyg_VSR_t(); typedef cyg_uint32 cyg_ISR_t(cyg_vector_t vector, cyg_addrword_t data); typedef void cyg_DSR_t(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); enum cyg_ISR_results { CYG_ISR_HANDLED = 1, /* Interrupt was handled */ CYG_ISR_CALL_DSR = 2 /* Schedule DSR */ }; </PROGRAMLISTING> <PROGRAMLISTING>void cyg_interrupt_create( cyg_vector_t vector, cyg_priority_t priority, cyg_addrword_t data, cyg_ISR_t *isr, cyg_DSR_t *dsr, cyg_handle_t *handle, cyg_interrupt *intr )</programlisting> <PARA>Creates an interrupt object and returns a handle to it. The object contains information about which interrupt vector to use and the ISR and DSR that will be called after the interrupt object is attached. The interrupt object will be allocated in the memory passed in the <emphasis>intr</emphasis> parameter. The interrupt object is not immediately attached; it must be attached with the <FUNCTION>cyg_interrupt_attach()</FUNCTION> call.</PARA> <PROGRAMLISTING>void cyg_interrupt_delete( cyg_handle_t interrupt )</programlisting> <PARA>Detaches the <emphasis>interrupt</emphasis> from the vector and frees the corresponding memory.</PARA> <PROGRAMLISTING>void cyg_interrupt_attach( cyg_handle_t interrupt )</programlisting> <PARA>Attaches <emphasis>interrupt</emphasis>. </PARA> <PROGRAMLISTING>void cyg_interrupt_detach( cyg_handle_t interrupt )</programlisting> <PARA>Detaches <emphasis>interrupt</emphasis>. </PARA> <PROGRAMLISTING>void cyg_interrupt_get_vsr( cyg_vector_t vector, cyg_VSR_t **vsr )</programlisting> <PARA>Returns a pointer to the VSR currently installed on <emphasis>vector </emphasis>. </PARA> <PROGRAMLISTING>void cyg_interrupt_set_vsr( cyg_vector_t vector, cyg_VSR_t *vsr )</programlisting> <PARA>Sets the current VSR on <emphasis>vector</emphasis>. A VSR directly attaches to the hardware interrupt vector and needs to be written in assembler.</PARA> <PROGRAMLISTING>void cyg_interrupt_disable( void )</programlisting> <PARA>Disables all interrupts.</PARA> <PROGRAMLISTING>void cyg_interrupt_enable( void )</programlisting> <PARA>Enables all interrupts.</PARA> <PROGRAMLISTING>void cyg_interrupt_mask( cyg_vector_t vector )</programlisting> <PARA>Programs the interrupt controller to stop delivery of interrupts on <emphasis>vector</emphasis>. On some architectures this will also disable all lower priority interrupts while on others they remain enabled. </PARA> <PROGRAMLISTING>void cyg_interrupt_mask_intunsafe( cyg_vector_t vector )</programlisting> <PARA>Programs the interrupt controller to stop delivery of interrupts on <emphasis>vector</emphasis>. On some architectures this will also disable all lower priority interrupts while on others they remain enabled. This version differs from cyg_drv_interrupt_unmask in not being interrupt safe.</PARA> <PROGRAMLISTING>void cyg_interrupt_unmask( cyg_vector_t vector )</programlisting> <PARA>Programs the interrupt controller to allow delivery of interrupts on the given interrupt vector. </PARA> <PROGRAMLISTING>void cyg_interrupt_unmask_intunsafe( cyg_vector_t vector )</programlisting> <PARA>Programs the interrupt controller to allow delivery of interrupts on the given interrupt vector. This version differs from cyg_drv_interrupt_unmask in not being interrupt safe. </PARA> <PROGRAMLISTING>void cyg_interrupt_acknowledge( cyg_vector_t vector )</programlisting> <PARA>Should be used from inside an ISR to acknowledge receipt of the interrupt. The interrupt must be acknowledged. If an interrupt is not acknowledged, the interrupt may trigger immediately after the ISR returns, causing the ISR to be called again in a loop. </PARA> <PROGRAMLISTING>void cyg_interrupt_configure( cyg_vector_t vector, cyg_bool_t level, cyg_bool_t up )</programlisting> <PARA>On some interrupt controllers the way an interrupt is detected may be configured. The <emphasis>level</emphasis> parameter chooses between level- or edge-triggered interrupts. The <emphasis>up</emphasis> parameter chooses between high and low level for level triggered interrupts or rising and falling edges for edge triggered interrupts. </PARA> </SECT1> <SECT1 id="counters-clocks-and-alarms"> <TITLE><!-- <xref> -->Counters, clocks and alarms</TITLE> <SECT2> <TITLE><!-- <index></index> -->Counters</TITLE> <PARA>The counter objects provided by the kernel provide an abstraction of the clock facility that is generally provided. Application code can associate alarms with counters, where an alarm is identified by the number of ticks until it triggers, the action to be taken on triggering, and whether or not the alarm should be repeated. </PARA> <PARA>There are two different implementations of the counter objects. The first stores all alarms in a single linked list. The alternative implementation uses a table of linked lists, with the size of the table being a separate configurable option. A single list is more efficient in terms of memory usage and is generally adequate when the application only makes use of a small number of alarms. For more complicated operations it is better to have a table of lists since this reduces the amount of computation whenever the timer goes off. Assuming a table size of 8 (the default value) on average the timer code will only need to check 1/8 of the pending alarms instead of all of them.</PARA> <PARA>The configuration options which select the counter implementation are CYGIMP_KERNEL_COUNTERS_MULTI_LIST (“Option: Implement counters using a table of lists”) and CYGIMP_KERNEL_COUNTERS_SINGLE_LIST (“Option: Implement counters using a single list”). </PARA> <PARA>The following functions can be used to create and manipulate counters:</PARA> <PROGRAMLISTING>void cyg_counter_create( cyg_handle_t *counter, cyg_counter *the_counter )</programlisting> <PARA>Creates a new counter and places it in the space pointed to by <emphasis>counter</emphasis>. A counter stores a value that is incremented by <FUNCTION>cyg_counter_tick()</FUNCTION>. Alarms may be attached to counters, and the alarms will trigger when the counter reaches a specified value.</PARA> <PROGRAMLISTING>void cyg_counter_delete( cyg_handle_t counter )</programlisting> <PARA>Deletes the given counter and frees the corresponding memory.</PARA> <PROGRAMLISTING>cyg_tick_count_t cyg_counter_current_value( cyg_handle_t counter )</programlisting> <PARA>Returns the current value of the given counter.</PARA> <PROGRAMLISTING>void cyg_counter_set_value( cyg_handle_t counter, cyg_tick_count_t new_value )</programlisting> <PARA>Sets the counter's value to new_value.</PARA> <PROGRAMLISTING>void cyg_counter_tick( cyg_handle_t counter )</programlisting> <PARA>Advances the counter by one tick.</PARA> <PROGRAMLISTING>void cyg_counter_multi_tick( cyg_handle_t counter cyg_tick_count_t ticks )</programlisting> <PARA>Advances the counter by multiple ticks.</PARA> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Clocks</TITLE> <PARA>Clocks are counters which are associated with a stream of ticks that represent time periods. Clocks have a resolution associated with them, whereas counters do not. </PARA> <PARA>The most frequently used clock is the <EMPHASIS>real-time clock</EMPHASIS> which serves two special purposes. First, it is necessary to support clock and alarm related functions such as <FUNCTION>cyg_thread_delay()</FUNCTION>. Second, it is needed to implement timeslicing in the mlqueue and lottery schedulers. If the application does not require either of these facilities, then it is possible to disable the real-time clock support completely. It is also possible to disable just timeslicing with the configuration option CYGSEM_KERNEL_SCHED_TIMESLICE, or just the clock and alarm functions, using the option CYGFUN_KERNEL_THREADS_TIMER..</PARA> <PARA>The real-time clock is available if the configuration option CYGVAR_KERNEL_COUNTERS_CLOCK is defined. </PARA> <PARA>Clock resolution is stored in variables of type cyg_resolution_t (<xref linkend="cyg-resolution-t">).</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_clock_create</FUNCTION>( cyg_resolution_t resolution, cyg_handle_t *handle, cyg_clock *clock )</PROGRAMLISTING> <PARA>Creates a clock object with the given <emphasis>resolution</emphasis> and places it in the space pointed to by <emphasis>clock</emphasis>. A clock is a counter driven by a regular source of ticks. For example the system real-time clock is driven by a clock interrupt.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_clock_delete</FUNCTION>( cyg_handle_t clock )</programlisting> <PARA>Deletes a clock object and frees the associated memory.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_clock_to_counter</FUNCTION>( </PROGRAMLISTING> <PROGRAMLISTING>cyg_handle_t clock, cyg_handle_t *counter )</PROGRAMLISTING> <PARA>Converts a clock handle to a counter handle. The counter functions can then be used with the counter handle.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_clock_set_resolution</FUNCTION>( cyg_handle_t clock, cyg_resolution_t resolution )</PROGRAMLISTING> <PARA>Changes the resolution of a given clock object.</PARA> <PROGRAMLISTING>cyg_resolution_t <FUNCTION>cyg_clock_get_resolution</FUNCTION>( cyg_handle_t clock )</PROGRAMLISTING> <PARA>Returns the resolution of clock. </PARA> <PROGRAMLISTING>cyg_handle <FUNCTION>cyg_real_time_clock</FUNCTION>( void )</PROGRAMLISTING> <PARA>Returns a handle to the system-supplied real-time clock.</PARA> <PROGRAMLISTING>cyg_tick_count_t <FUNCTION>cyg_current_time</FUNCTION>( void )</PROGRAMLISTING> <PARA>Returns the real-time clock's counter. This is equivalent to executing the code:</PARA> <PROGRAMLISTING> cyg_clock_to_counter(cyg_real_time_clock(), &h), cyg_counter_current_value(h); </PROGRAMLISTING> </SECT2> <SECT2> <TITLE><!-- <index></index> -->Alarms</TITLE> <PROGRAMLISTING>typedef void cyg_alarm_t(cyg_handle_t alarm, cyg_addrword_t data);</PROGRAMLISTING> <PARA>cyg_alarm_t is the type used for functions which are used to handle alarm events. It is used in the function <FUNCTION>cyg_alarm_create()</FUNCTION>. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_alarm_create</FUNCTION>( cyg_handle_t <EMPHASIS>counter,</EMPHASIS> cyg_alarm_t *<EMPHASIS>alarm_fn,</EMPHASIS> cyg_addrword_t <EMPHASIS>data,</EMPHASIS> cyg_handle_t *<EMPHASIS>handle,</EMPHASIS> cyg_alarm *<EMPHASIS>alarm )</EMPHASIS></PROGRAMLISTING> <PARA>Creates an alarm object. The alarm is attached to the <parameter>counter </parameter> and is created in the memory pointed to by <parameter>alarm </parameter>. When the alarm triggers, the handler function <parameter> alarmfn</parameter> is called and is passed <parameter>data</parameter> as a parameter. The alarm handler executes in the context of the function that incremented the counter and thus triggered the alarm.</PARA> <NOTE> <PARA>If the alarm is associated with the real-time clock, the alarm handler <parameter>alarmfn</parameter> will be invoked by the delayed service routine (DSR) that services the real-time clock. This means that real-time clock alarm handlers (which are possibly the most frequently used) must follow the rules of behavior for DSRs. These rules are outlined in <XREF LINKEND="INTERRUPT-AND-EXCEPTION-HANDLERS">.</PARA> </NOTE> <PROGRAMLISTING>void <FUNCTION>cyg_alarm_delete</FUNCTION>( cyg_handle_t <EMPHASIS>alarm )</EMPHASIS></PROGRAMLISTING> <PARA>Disables the alarm, detaches from the counter, invalidates handles, and frees memory if it was dynamically allocated by <FUNCTION>cyg_alarm_create()</FUNCTION>.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_alarm_initialize</FUNCTION>( cyg_handle_t <EMPHASIS>alarm,</EMPHASIS> cyg_tick_count_t <EMPHASIS>trigger,</EMPHASIS> cyg_tick_count_t <EMPHASIS>interval )</EMPHASIS></PROGRAMLISTING> <PARA>Initialize an alarm. This sets it to trigger at the tick with value <parameter>trigger</parameter>. When an alarm triggers, this event is dealt with by calling the <parameter>alarmfn</parameter> parameter which was passed when the alarm was created using <FUNCTION>cyg_alarm_create()</FUNCTION>. If <parameter>interval</parameter> is non-zero, then after the alarm has triggered it will set itself to trigger again after <parameter>interval </parameter> ticks. Otherwise, if <parameter>interval</parameter> is zero, the alarm will be disabled automatically once it has triggered. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_alarm_get_times</FUNCTION>( cyg_handle_t <EMPHASIS>alarm,</EMPHASIS> cyg_tick_count_t *<EMPHASIS>trigger,</EMPHASIS> cyg_tick_count_t *<EMPHASIS>interval )</EMPHASIS></PROGRAMLISTING> <PARA>Returns the trigger and interval parameters of the alarm.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_alarm_enable</FUNCTION>( cyg_handle_t <EMPHASIS>alarm )</EMPHASIS></PROGRAMLISTING> <PARA>Enables an alarm that has been disabled by calling <FUNCTION>cyg_alarm_disable()</FUNCTION>. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_alarm_disable</FUNCTION>( cyg_handle_t <EMPHASIS>alarm )</EMPHASIS></PROGRAMLISTING> <PARA>Disables an alarm. After an alarm is disabled it will not be triggered unless it is subsequently re-enabled by calling <FUNCTION>cyg_alarm_enable()</FUNCTION> or is reinitialized by calling <FUNCTION>cyg_alarm_initialize()</FUNCTION>.</PARA> <PARA>Note, though, that if a periodic alarm that has been disabled is re-enabled without reinitializing it will be in phase with the <EMPHASIS>original</EMPHASIS> sequence of alarms. If it is <EMPHASIS>reinitialized</EMPHASIS>, the new sequence of alarms will be in phase with the moment in which <FUNCTION>cyg_alarm_initialize()</FUNCTION> was called. </PARA> </SECT2> </SECT1> <SECT1 id="synchronization"> <TITLE>Synchronization</TITLE> <SECT2> <TITLE>Semaphores</TITLE> <PARA>The <!-- <index></index> -->semaphores defined by the type cyg_sem_t are counting semaphores. These objects are not referred to by handles, but rather by the pointer to the variable in which the semaphore is created.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_semaphore_init</FUNCTION>( cyg_sem_t *<EMPHASIS>sem,</EMPHASIS> cyg_ucount32 <EMPHASIS>val )</EMPHASIS></PROGRAMLISTING> <PARA>Initializes a semaphore. The initial semaphore count is set to <parameter>val</parameter>.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_semaphore_destroy</FUNCTION>( cyg_sem_t *<EMPHASIS>sem )</EMPHASIS></PROGRAMLISTING> <PARA>Destroys a semaphore. This must not be done while there are any threads waiting on it. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_semaphore_wait</FUNCTION>( cyg_sem_t *<EMPHASIS>sem )</EMPHASIS></PROGRAMLISTING> <PARA>If the semaphore count is zero, the current thread will wait on the semaphore. If the count is non-zero, it will be decremented and the thread will continue running. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_semaphore_trywait</FUNCTION>( cyg_sem_t *<EMPHASIS>sem )</EMPHASIS></PROGRAMLISTING> <PARA>A non-blocking version of <FUNCTION>cyg_semaphore_wait()</FUNCTION>. This attempts to decrement the semaphore count. If the count is positive, then the semaphore is decremented and <constant>true</constant> is returned. If the count is zero then the semaphore remains unchanged, and <constant>false</constant> is returned, but the current thread continues to run. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_semaphore_timed_wait</FUNCTION>( cyg_sem_t *<EMPHASIS>sem,</EMPHASIS> cyg_tick_count_t <EMPHASIS>abstime )</EMPHASIS></PROGRAMLISTING> <PARA>A time-out version of <FUNCTION>cyg_semaphore_wait()</FUNCTION>. This attempts to decrement the semaphore count. If the count is positive, then the semaphore is decremented and <constant>true</constant> is returned. If the count is zero, it will wait for the semaphore to increment. If however the <parameter>abstime</parameter> time-out is reached first, it will return <constant>false</constant> without changing state, and the current thread will continue to run.</PARA> <PARA>The <parameter>cyg_tick_count_t</parameter> parameter is an absolute time. If a relative time is required, you should use <FUNCTION>cyg_current_time</FUNCTION> with an offset. For example, to time out 200 ticks from the present you would use:</PARA> <PROGRAMLISTING>cyg_semaphore_timed_wait(&sem, cyg_current_time() + 200);</PROGRAMLISTING> <PARA><FUNCTION>cyg_semaphore_timed_wait()</FUNCTION> is only available if the configuration option CYGFUN_KERNEL_THREADS_TIMER is set. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_semaphore_post</FUNCTION>( cyg_sem_t *<EMPHASIS>sem )</EMPHASIS></PROGRAMLISTING> <PARA>If there are threads waiting on this semaphore this will wake exactly one of them. Otherwise it simply increments the semaphore count.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_semaphore_peek</FUNCTION>( cyg_sem_t *<EMPHASIS>sem,</EMPHASIS> cyg_count32 *<EMPHASIS>val )</EMPHASIS></PROGRAMLISTING> <PARA>Returns the current semaphore count in the variable pointed to by <parameter>val</parameter>. </PARA> </SECT2> <SECT2> <TITLE><!-- <xref> -->Mutexes</TITLE> <PARA><!-- <index></index> -->Mutexes (mutual exclusion locks) are used in a similar way to semaphores. A mutex only has two states, locked and unlocked. Mutexes are used to protect accesses to shared data or resources. When a thread locks a mutex it becomes the owner. Only the mutex's owner may unlock it. While a mutex remains locked, the owner should not lock it again, as the behavior is undefined and probably dangerous. </PARA> <PARA>If non-owners try to lock the mutex, they will be suspended until the mutex is available again, at which point they will own the mutex. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mutex_init</FUNCTION>( cyg_mutex_t *<EMPHASIS>mutex )</EMPHASIS></PROGRAMLISTING> <PARA>Initializes a mutex. It is initialized in the unlocked state. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mutex_destroy</FUNCTION>( cyg_mutex_t *<EMPHASIS>mutex )</EMPHASIS></PROGRAMLISTING> <PARA>Destroys a mutex. A mutex should not be destroyed in the locked state, as the behavior is undefined. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mutex_lock</FUNCTION>( cyg_mutex_t *<EMPHASIS>mutex )</EMPHASIS></PROGRAMLISTING> <PARA>Changes the nutex from the unlocked state to the locked state. When this happens the mutex becomes owned by the current thread. If the mutex is locked, the current thread will wait until the mutex becomes unlocked before performing this operation. The result of this function will be TRUE if the mutex has been locked, or FALSE if it has not. A FALSE result can result if the thread has been released from its wait by a call to <FUNCTION>cyg_thread_release()</FUNCTION> or <FUNCTION>cyg_mutex_release()</FUNCTION>.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mutex_unlock</FUNCTION>( cyg_mutex_t *<EMPHASIS>mutex )</EMPHASIS></PROGRAMLISTING> <PARA>Changes the mutex from the locked state to the unlocked state. This function may only be called by the thread which locked the mutex, and should not be called on an unlocked mutex. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mutex_release</FUNCTION>( cyg_mutex_t *<EMPHASIS>mutex )</EMPHASIS></PROGRAMLISTING> <PARA>Release all threads waiting on the mutex pointed to by the mutex argument. These threads will return from cyg_mutex_lock() with a FALSE result and will not have claimed the mutex. This function has no effect on any thread that may have the mutex claimed.</PARA> <PROGRAMLISTING>void cyg_mutex_set_protocol( cyg_mutex_t *<parameter>mutex</parameter>, enum cyg_mutex_protocol <parameter>protocol</parameter>)</PROGRAMLISTING> <PARA>As described in the Thread Synchronization section, eCos's mutex can use a number of schemes when dealing with priority inversion. This function is used to set the protocol for the mutex. <parameter>protocol </parameter> may take one of 3 values: CYG_MUTEX_NONE, CYG_MUTEX_INHERIT, CYG_MUTEX_CEILING. The first indicates no inversion protocol will be used. The second uses the SIMPLE inversion protocol, and the last uses the ceiling protocol.</PARA> <PROGRAMLISTING>void cyg_mutex_set_ceiling( cyg_mutex_t *<parameter>mutex</parameter>, cyg_priority_t <parameter>priority</parameter>)</PROGRAMLISTING> <PARA>When using the ceiling inversion protocol, this function allows the ceiling thread priority of an obtained mutex to be set. </PARA> <PROGRAMLISTING>cyg_priority_t cyg_thread_get_current_priority( cyg_handle_t <parameter>thread</parameter>)</PROGRAMLISTING> <PARA>Returns the current thread priority. If the thread is running at a higher priority than normal due to priority inversion, this functions returns the inverted priority, whereas <function>cyg_thread_get_priority()</function> returns the normal priority.</PARA> </SECT2> <SECT2> <TITLE><!-- <xref> -->Condition Variables</TITLE> <PARA><!-- <index></index> -->Condition variables are a synchronization mechanism which (used with a mutex) grants several threads mutually exclusive access to shared data and to broadcast availability of that data to all the other threads.</PARA> <PARA>A typical example of the use of condition variables is when one thread (the producer) is producing data and several other (consumer) threads are waiting for that data to be ready. The consumers will wait by invoking <FUNCTION>cyg_cond_wait()</FUNCTION>. The producer will lock access to the data with a mutex, and when it has generated enough data for the other processes to consume, it will invoke <FUNCTION>cyg_cond_broadcast()</FUNCTION> to wake up the consumers. The <EMPHASIS>Getting Started with </EMPHASIS><PRODUCTNAME>eCos</PRODUCTNAME> book has example programs which use condition variables to implement a simple message passing system between threads. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_cond_init</FUNCTION>( cyg_cond_t *<EMPHASIS>cond,</EMPHASIS> cyg_mutex_t *<EMPHASIS>mutex )</EMPHASIS></PROGRAMLISTING> <PARA>Initializes the condition variable. A condition variable is attached to a specific mutex. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_cond_destroy</FUNCTION>( cyg_cond_t *<EMPHASIS>cond )</EMPHASIS></PROGRAMLISTING> <PARA>Destroys the condition variable <parameter>cond</parameter>. This must not be done on a condition variable which is in use. After it has been destroyed, it may be subsequently reinitialized. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_cond_wait</FUNCTION>( cyg_cond_t *<EMPHASIS>cond )</EMPHASIS></PROGRAMLISTING> <PARA>Causes the current thread to wait on the condition variable, while simultaneously unlocking the corresponding mutex. <FUNCTION>cyg_cond_wait()</FUNCTION> may be called by a thread which has the corresponding mutex locked.</PARA> <PARA>The thread can only be awakened by a call to <FUNCTION>cyg_cond_signal()</FUNCTION> or <FUNCTION>cyg_cond_broadcast()</FUNCTION> on the same condition variable. When the thread is awakened, the mutex will be reclaimed before this function proceeds. Since it may have to wait for this, <FUNCTION>cyg_cond_wait()</FUNCTION> should only be used in a loop since the condition may become false in the meantime. This is shown in the following example: </PARA> <PROGRAMLISTING>extern cyg_mutex_t mutex; extern cyg_cond_t cond; cyg_mutex_lock( &mutex ); ... while( condition_not_true ) { cyg_cond_wait( &cond ); } ... cyg_mutex_unlock( &mutex ); </programlisting> <programlisting> cyg_bool_t <FUNCTION>cyg_cond_timed_wait</FUNCTION>( cyg_cond_t *<EMPHASIS>cond,</EMPHASIS> cyg_tick_count_t <EMPHASIS>abstime )</EMPHASIS></PROGRAMLISTING> <PARA>A time-out version of <FUNCTION>cyg_cond_wait()</FUNCTION> which waits for a signal or broadcast. If a signal or broadcast is received it returns <constant>true</constant>, but if one is not received by <parameter>abstime</parameter>, it returns <constant>false</constant>.</PARA> <PARA>The <parameter>cyg_tick_count_t</parameter> parameter is an absolute time. If a relative time is required, you should use <FUNCTION>cyg_current_time</FUNCTION> with an offset. For example, to time out 200 ticks from the present you would use:</PARA> <PROGRAMLISTING>cyg_cond_timed_wait(&sem, cyg_current_time() + 200);</PROGRAMLISTING> <PARA><FUNCTION>cyg_cond_timed_wait()</FUNCTION> is only available if the configuration option CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT is set. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_cond_signal</FUNCTION>( cyg_cond_t *<EMPHASIS>cond )</EMPHASIS></PROGRAMLISTING> <PARA>Wakes up at least one thread which is waiting on the condition variable. When a thread is awakened it will become the owner of the mutex. <FUNCTION>cyg_cond_signal()</FUNCTION> may be called by the thread which currently owns the mutex to which the condition variable is attached. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_cond_broadcast</FUNCTION>( cyg_cond_t *<EMPHASIS>cond )</EMPHASIS></PROGRAMLISTING> <PARA>Wakes <EMPHASIS>all</EMPHASIS> the threads waiting on the condition variable. Each time a thread is awakened it will become the current owner of the mutex. </PARA> </SECT2> </SECT1> <SECT1 id="memory-pools"> <TITLE><!-- <xref> -->Memory pools</TITLE> <PARA>There are two sorts of <!-- <index></index> -->memory pools. A variable size memory pool is for allocating blocks of any size. A fixed size memory pool, has the block size specified when the pool is created and only provides blocks of that size.</PARA> <PARA>Blocking, non-blocking and “blocking with time-out” versions of these calls are provided. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_var_create</FUNCTION>( void *<EMPHASIS>base,</EMPHASIS> cyg_int32 <EMPHASIS>size,</EMPHASIS> cyg_handle_t *<EMPHASIS>handle,</EMPHASIS> cyg_mempool_var *<EMPHASIS>var )</EMPHASIS></PROGRAMLISTING> <PARA>Creates a variable size memory pool. The parameters are:</PARA> <VARIABLELIST> <VARLISTENTRY> <TERM>base </TERM> <LISTITEM> <PARA>base of memory to use for pool</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>size </TERM> <LISTITEM> <PARA>size of memory pool in bytes</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>handle </TERM> <LISTITEM> <PARA>returned handle of memory pool</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>var </TERM> <LISTITEM> <PARA>space to put pool structure in</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_var_delete</FUNCTION>( cyg_handle_t <EMPHASIS>varpool )</EMPHASIS></PROGRAMLISTING> <PARA>Deletes the variable size memory pool <parameter>varpool</parameter>. </PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mempool_var_alloc</FUNCTION>( cyg_handle_t <EMPHASIS>varpool,</EMPHASIS> cyg_int32 <EMPHASIS>size )</EMPHASIS></PROGRAMLISTING> <PARA>Allocates a block of length <parameter>size</parameter>. This will block until the memory becomes available.</PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mempool_var_timed_alloc</FUNCTION>( cyg_handle_t <EMPHASIS>varpool,</EMPHASIS> cyg_int32 <EMPHASIS>size,</EMPHASIS> cyg_tick_count_t <EMPHASIS>abstime )</EMPHASIS></PROGRAMLISTING> <PARA>Allocates a block of length <parameter>size</parameter>. If the requested amount of memory is not available, it will wait until <parameter>abstime </parameter> before giving up and returning <constant>NULL</constant>.</PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mempool_var_try_alloc</FUNCTION>( cyg_handle_t <EMPHASIS>varpool,</EMPHASIS> cyg_int32 <EMPHASIS>size )</EMPHASIS></PROGRAMLISTING> <PARA>Allocates a block of length <parameter>size</parameter>. <constant>NULL</constant> is returned if not enough is available.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_var_free</FUNCTION>( cyg_handle_t <EMPHASIS>varpool,</EMPHASIS> void *<EMPHASIS>p )</EMPHASIS></PROGRAMLISTING> <PARA>Frees memory back into variable size pool <parameter>varpool</parameter>. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mempool_var_waiting</FUNCTION>( cyg_handle_t <EMPHASIS>varpool )</EMPHASIS></PROGRAMLISTING> <PARA>Returns true if any threads are waiting for memory in <parameter>varpool</parameter>.</PARA> <PROGRAMLISTING>typedef struct { cyg_int32 totalmem; cyg_int32 freemem; void *base; cyg_int32 size; cyg_int32 blocksize; cyg_int32 maxfree; // The largest free block } cyg_mempool_info; </programlisting><programlisting> void <FUNCTION>cyg_mempool_var_get_info</FUNCTION>( cyg_handle_t <EMPHASIS>varpool,</EMPHASIS> cyg_mempool_info *<EMPHASIS>info )</EMPHASIS></PROGRAMLISTING> <PARA>Puts information about a variable memory pool into the structure provided.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_fix_create</FUNCTION>( void *<EMPHASIS>base,</EMPHASIS> cyg_int32 <EMPHASIS>size,</EMPHASIS> cyg_int32 <EMPHASIS>blocksize,</EMPHASIS> cyg_handle_t *<EMPHASIS>handle,</EMPHASIS> cyg_mempool_fix *<EMPHASIS>fix )</EMPHASIS></PROGRAMLISTING> <PARA>Create a fixed size memory pool. This function takes the following parameters:</PARA> <VARIABLELIST> <VARLISTENTRY> <TERM>base </TERM> <LISTITEM> <PARA>base of memory to use for pool</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>size </TERM> <LISTITEM> <PARA>size of total space requested</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>blocksize </TERM> <LISTITEM> <PARA>size of individual elements</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>handle </TERM> <LISTITEM> <PARA>returned handle of memory pool</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>fix </TERM> <LISTITEM> <PARA>space to put pool structure in</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_fix_delete</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool )</EMPHASIS></PROGRAMLISTING> <PARA>Deletes the given fixed size memory pool.</PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mempool_fix_alloc</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool )</EMPHASIS></PROGRAMLISTING> <PARA>Allocates a block. If the memory is not available immediately, this blocks until the memory becomes available.</PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mempool_fix_timed_alloc</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool,</EMPHASIS> cyg_tick_count_t <EMPHASIS>abstime )</EMPHASIS></PROGRAMLISTING> <PARA>Allocates a block. If the memory is not already available, it will try until <parameter>abstim</parameter>e before giving up and returning a <constant>NULL</constant>.</PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mempool_fix_try_alloc</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool )</EMPHASIS></PROGRAMLISTING> <PARA>Allocates a block. NULL is returned if no memory is available.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_fix_free</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool,</EMPHASIS> void *<EMPHASIS>p )</EMPHASIS></PROGRAMLISTING> <PARA>Frees memory back into fixed size pool.</PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mempool_fix_waiting</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool )</EMPHASIS></PROGRAMLISTING> <PARA>Returns true if there are any threads waiting for memory in the given memory pool.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mempool_fix_get_info</FUNCTION>( cyg_handle_t <EMPHASIS>fixpool,</EMPHASIS> cyg_mempool_info *<EMPHASIS>info )</EMPHASIS></PROGRAMLISTING> <PARA>Puts information about a variable memory pool into the structure provided.</PARA> <PARA>The fixed size memory pool simply returns blocks of memory of exactly the blocksize requested. If the pool is being used to allocate memory for a type that has alignment constraints (such as 4-byte alignment), then it is up to the user to align the memory appropriately for the type in question. Alternatively, choose a blocksize that is an exact multiple of the required alignment.</PARA> <PARA>The memory available from the memory pools will not be the same size as the memory supplied to it. Some of the memory is used for internal data structures of the allocator. <function>cyg_mempool_fix_get_info()</function> and <function>cyg_mempool_var_get_info()</function> may be used to determine the available memory.</PARA> </SECT1> <SECT1 id="message-boxes"> <TITLE><!-- <index></index> -->Message boxes</TITLE> <PARA>Message boxes are a primitive mechanism for exchanging messages between threads, inspired by the µITRON specification. A message box can be created with <FUNCTION>cyg_mbox_create()</FUNCTION> before the scheduler is started, and two threads in a typical producer/consumer relationship can access it. One thread, the producer, will use <FUNCTION>cyg_mbox_put()</FUNCTION> to make data available to the consumer thread which uses <FUNCTION>cyg_mbox_get()</FUNCTION> to access the data. </PARA> <PARA>The size of the internal message queue is configured by the “Message box queue size” ( CYGNUM_KERNEL_SYNCH_MBOX_QUEUE_SIZE ) configuration option. The default value is 10. </PARA> <PARA>Blocking, non-blocking and “blocking with time-out” versions of these calls are provided. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mbox_create</FUNCTION>( cyg_handle_t *<EMPHASIS>handle,</EMPHASIS> cyg_mbox *<EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Creates a message box using the space provided in the mbox parameter, and returns a handle for future access to that message box.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_mbox_delete</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Deletes the given message box. </PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mbox_get</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Waits for a message to be available, then retrieves it and returns the address of the data. </PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mbox_timed_get</FUNCTION>( cyg_handle_t <EMPHASIS>mbox,</EMPHASIS> cyg_tick_count_t <EMPHASIS>timeout )</EMPHASIS></PROGRAMLISTING> <PARA>Waits for a message to be available, but times out if <parameter>timeout </parameter>time passes. This version of the function is only available if the configuration option CYGFUN_KERNEL_THREADS_TIMER is turned on. </PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mbox_tryget</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Checks to see if a message is ready. If no message is available it returns immediately with a return value of <constant>NULL</constant>. If a message is available it retrieves it and returns the address of the data. </PARA> <PROGRAMLISTING>void *<FUNCTION>cyg_mbox_peek_item</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Checks to see if a message is ready, and if one is available returns the address of the data <EMPHASIS>without</EMPHASIS> removing the message from the queue. If no message is available it returns <constant>NULL</constant>. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mbox_put</FUNCTION>( cyg_handle_t <EMPHASIS>mbox,</EMPHASIS> void *<EMPHASIS>item )</EMPHASIS></PROGRAMLISTING> <PARA>Places a message in the given message box. If the queue is full it will block until the message can be sent. It returns <constant>true </constant>if the message was successfully sent, and <constant>false</constant> if the message was not sent and its sleep was awakened by the kernel before the message could be sent. </PARA> <PARA>The <FUNCTION>cyg_mbox_put()</FUNCTION> function is only available if the CYGMTH_MBOXT_PUT_CAN_WAIT configuration has been selected. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mbox_timed_put</FUNCTION>( cyg_handle_t <EMPHASIS>mbox,</EMPHASIS> void *<EMPHASIS>item,</EMPHASIS> cyg_tick_count_t <EMPHASIS>abstime )</EMPHASIS></PROGRAMLISTING> <PARA>A time-out version of <FUNCTION>cyg_mbox_put()</FUNCTION>. This will try to place the message in the given message box. If the queue is full, it will wait until <parameter>abstime</parameter> before giving up and returning <constant>false</constant>. </PARA> <PARA>The <FUNCTION>cyg_mbox_timed_put()</FUNCTION> function is only available if the both the CYGMFN_KERNEL_SYNCH_MBOXT_PUT_CAN_WAIT and CYGFUN_KERNEL_THREADS_TIMER configuration have been selected. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mbox_tryput</FUNCTION>( cyg_handle_t <EMPHASIS>mbox,</EMPHASIS> void *<EMPHASIS>item )</EMPHASIS></PROGRAMLISTING> <PARA>Tries to place a message in the given message box. It returns <constant>true</constant> if the message was successfully sent, and <constant>false</constant> if the message could not be sent immediately, usually because the queue was full. </PARA> <PROGRAMLISTING>cyg_count32 <FUNCTION>cyg_mbox_peek</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Takes a peek at the queue and returns the number of messages waiting in it. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mbox_waiting_to_get</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Queries the kernel to see if other processes are waiting to receive a message in the given message box. Returns <constant>true</constant> if other processes are waiting, <constant>false</constant> otherwise. </PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_mbox_waiting_to_put</FUNCTION>( cyg_handle_t <EMPHASIS>mbox )</EMPHASIS></PROGRAMLISTING> <PARA>Queries the kernel to see if <EMPHASIS>other</EMPHASIS> processes are waiting to send a message in the given message box. Returns <constant>true</constant> if other processes are waiting, <constant>false </constant> otherwise. </PARA> </SECT1> <SECT1 id="flags"> <TITLE>Flags</TITLE> <PARA>Flags are a <!-- <index></index> -->synchronization mechanism which allow a thread to wait for a single condition or a combination of conditions. The conditions are represented by bits in a 32 bit word. Flags are inspired by the <!-- <index></index> -->µITRON specification.</PARA> <PARA>Flags are of type cyg_flag_t, which are 32 bit words, and routines are provided to set or mask some bits in the flag value. </PARA> <PARA>A “consumer side” thread can wait for a “producer side” thread to set the entire collection of bits, or any subset of them.</PARA> <PARA>When a thread sets some bits in a flag, all threads whose requirements are now satisfied are woken up; thus flags have broadcast semantics. A variation on the wait call can specify that the flag value be cleared when the wait call is satisfied, in which case the setting of bits would not be a broadcast. </PARA> <PARA>Blocking, non-blocking, and “blocking with time-out” versions of the wait calls are provided. </PARA> <PROGRAMLISTING>void <FUNCTION>cyg_flag_init</FUNCTION>( cyg_flag_t *<EMPHASIS>flag )</EMPHASIS></PROGRAMLISTING> <PARA>Initializes a flag variable.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_flag_destroy</FUNCTION>( cyg_flag_t *<EMPHASIS>flag )</EMPHASIS></PROGRAMLISTING> <PARA>Destroys a flag variable.</PARA> <PROGRAMLISTING>void <FUNCTION>cyg_flag_setbits</FUNCTION>( cyg_flag_t *<EMPHASIS>flag,</EMPHASIS> cyg_flag_value_t <EMPHASIS>value )</EMPHASIS></PROGRAMLISTING> <PARA>Sets the bits in <parameter>flag</parameter> which are set in <parameter>value</parameter>.</PARA> <PARA>A side effect of <FUNCTION>cyg_flag_setbits()</FUNCTION> is that the kernel wakes up any waiting threads whose requirements are now satisfied. </PARA> <VARIABLELIST> <VARLISTENTRY> <TERM>flag </TERM> <LISTITEM> <PARA>A pointer to the flag whose bits are being set. The new setting of <parameter>flag</parameter> will be <literal>*flag -> (*flag | value)</literal>.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>value </TERM> <LISTITEM> <PARA>A word whose 1 bits will also be set in *<parameter>flag</parameter>. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <PROGRAMLISTING>void <FUNCTION>cyg_flag_maskbits</FUNCTION>( cyg_flag_t *<EMPHASIS>flag,</EMPHASIS> cyg_flag_value_t <EMPHASIS>value</EMPHASIS> ) </PROGRAMLISTING> <PARA>Clear the bits in the given <parameter>flag</parameter> which are zero in the <parameter>value</parameter>. This cannot result in new threads being eligible for awakening.</PARA> <VARIABLELIST> <VARLISTENTRY> <TERM>flag </TERM> <LISTITEM> <PARA>A pointer to the flag whose bits are being cleared. The new setting of <parameter>flag</parameter> will be <literal>*flag -> (*flag & value)</literal>.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>value </TERM> <LISTITEM> <PARA>A word whose 0 bits will also be cleared in *<parameter>flag</parameter>. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <PARA>We now describe the <FUNCTION>cyg_flag_wait()</FUNCTION>, which frequently uses the following macros: </PARA> <PROGRAMLISTING> #define CYG_FLAG_WAITMODE_AND ((cyg_flag_mode_t)0) #define CYG_FLAG_WAITMODE_OR ((cyg_flag_mode_t)2) #define CYG_FLAG_WAITMODE_CLR ((cyg_flag_mode_t)1) </programlisting> <programlisting> cyg_flag_value_t <FUNCTION>cyg_flag_wait</FUNCTION>( cyg_flag_t *<EMPHASIS>flag,</EMPHASIS> cyg_flag_value_t <EMPHASIS>pattern,</EMPHASIS> cyg_flag_mode_t <EMPHASIS>mode )</EMPHASIS></PROGRAMLISTING> <PARA>Wait for all the bits which are one in <parameter>pattern</parameter> to be set in the <parameter>flag</parameter> value (if <parameter>mode</parameter> is <constant>CYG_FLAG_WAITMODE_AND</constant>) or for any of the bits which are one in <parameter>pattern</parameter> to be set in the <parameter>flag</parameter> value (if mode is <constant>CYG_FLAG_WAITMODE_OR</constant>).</PARA> <PARA>When <FUNCTION>cyg_flag_wait()</FUNCTION> returns, meaning that the condition is met, the flag value which succeeded is returned from the call; in other circumstances (such as a bad value for <parameter>mode</parameter> or <parameter>pattern</parameter>), zero is returned to indicate the error.</PARA> <PARA>If the mode is one of those above plus <constant>CYG_FLAG_WAITMODE_CLR </constant>, the whole of the flag value is cleared to zero when the condition is met. </PARA> <PARA><FUNCTION>cyg_flag_wait()</FUNCTION> takes the following parameters: </PARA> <VARIABLELIST> <VARLISTENTRY> <TERM>flag </TERM> <LISTITEM> <PARA>The value of the flag (set by the thread that called <FUNCTION>cyg_flag_setbits()</FUNCTION> or <FUNCTION>cyg_flag_maskbits()</FUNCTION>) is placed in here.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>pattern </TERM> <LISTITEM> <PARA>The set of bits which, if set, will cause the calling thread to be woken up. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>mode </TERM> <LISTITEM> <PARA>A parameter which modifies the conditions for wake-up. It can take the following values: </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>CYG_FLAG_WAITMODE_AND </TERM> <LISTITEM> <PARA>Only wake up if <EMPHASIS>all</EMPHASIS> the bits in <parameter>mask </parameter>are set in the flag.</PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>CYG_FLAG_WAITMODE_OR </TERM> <LISTITEM> <PARA>Wake up if <EMPHASIS>any</EMPHASIS> of the bits in <parameter>mask </parameter> are set in the flag. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>CYG_FLAG_WAITMODE_AND + CYG_FLAG_WAITMODE_CLR </TERM> <TERM>CYG_FLAG_WAITMODE_OR + CYG_FLAG_WAITMODE_CLR </TERM> <LISTITEM> <PARA> Like CYG_FLAG_WAITMODE_AND and CYG_FLAG_WAITMODE_OR, but the entire flag is cleared to zero when the condition is met, whereas normally the value of the flag is unchanged. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> <PARA>Waiting threads are queued depending on the semantics of the underlying scheduler. In release &version;, this means that, if the multi-level queue scheduler is selected, queueing is in FIFO ordering, while the bitmap scheduler supports thread priority ordered queueing. When some flag value bits become signalled by a call to <FUNCTION>cyg_flag_setbits()</FUNCTION>, the queue is scanned in order, and each waiting thread in turn is awoken or re-queued depending on its request. When a thread is awoken, if it made the wait call with CYG_FLAG_WAITMODE_CLR, the flag value is cleared to zero, and the scan of queued threads is terminated. </PARA> <PROGRAMLISTING>cyg_flag_value_t <FUNCTION>cyg_flag_timed_wait</FUNCTION>( cyg_flag_t *<EMPHASIS>flag,</EMPHASIS> cyg_flag_value_t <EMPHASIS>pattern,</EMPHASIS> cyg_flag_mode_t <EMPHASIS>mode,</EMPHASIS> cyg_tick_count_t <EMPHASIS>abstime )</EMPHASIS></PROGRAMLISTING> <PARA>A time-out version of <FUNCTION>cyg_flag_wait()</FUNCTION>. This waits for the condition required by <parameter>pattern</parameter> and <parameter>mode</parameter> to be met, or until the <parameter>abstime </parameter> time-out is reached, whichever is first. If the time-out is reached first, zero is returned. This call is only available if the configuration option <constant>CYGFUN_KERNEL_THREADS_TIMER </constant> is enabled. </PARA> <PROGRAMLISTING>cyg_flag_value_t <FUNCTION>cyg_flag_poll</FUNCTION>( cyg_flag_t *<EMPHASIS>flag,</EMPHASIS> cyg_flag_value_t <EMPHASIS>pattern,</EMPHASIS> cyg_flag_mode_t <EMPHASIS>mode )</EMPHASIS></PROGRAMLISTING> <PARA>A non-blocking version of <FUNCTION>cyg_flag_wait()</FUNCTION>. If the condition required by <parameter>pattern</parameter> and <parameter> mode</parameter> is met, the flag value is returned, otherwise zero is returned. The flag value may be cleared in the event of success by specifying CYG_FLAG_WAITMODE_CLR in the <parameter>mode</parameter>, as usual. </PARA> <PROGRAMLISTING>cyg_flag_value_t <FUNCTION>cyg_flag_peek</FUNCTION>( cyg_flag_t *<EMPHASIS>flag )</EMPHASIS></PROGRAMLISTING> <PARA>Returns the current flag value.</PARA> <PROGRAMLISTING>cyg_bool_t <FUNCTION>cyg_flag_waiting</FUNCTION>( cyg_flag_t *<EMPHASIS>flag )</EMPHASIS></PROGRAMLISTING> <PARA>Returns true if there are threads waiting on this flag.</PARA> </SECT1> </CHAPTER> <CHAPTER id="ecos-interrupt-model"> <TITLE>eCos <!-- <index></index> -->Interrupt Model</TITLE> <PARA>This chapter describes the <EMPHASIS>eCos</EMPHASIS> interrupt model in detail. </PARA> <PARA>Interrupt handling is an important part of most real-time systems. Timely handling of interrupt sources is important. This can be severely impacted by certain activities that must be considered atomic (i.e. uninterruptible). Typically these activities are executed with interrupts disabled. In order to keep such activities to a minimum and allow for the smallest possible interrupt latencies, <EMPHASIS>eCos</EMPHASIS> uses a split interrupt handling scheme. In this scheme, interrupt handling is separated into two parts. The first part is known as the <!-- <index></index> -->Interrupt Service Routine or ISR. The second part is the <!-- <index></index> -->Deferred Service Routine or DSR. This separation explicitly allows for the DSRs to be run with interrupts enabled, thus allowing other potentially higher priority interrupts to occur and be processed while processing a lower priority interrupt. </PARA> <PARA>In order for this model to work, the ISR should run quickly. If the service requirements for the interrupt are small, the interrupt can be completely handled by the ISR and no DSR is required. However, if servicing the interrupt is more complex, a DSR should be used. The DSR will be run at some later time, at the point when thread scheduling is allowed. Postponing the execution of DSRs until this time allows for simple synchronization methods to be used by the kernel. </PARA> <PARA>Further, this controlled calling — when thread scheduling is allowed — means that DSRs can interact with the kernel, for example by signalling that an asynchronous operation has completed. </PARA> <PARA>In order to allow DSRs to run with interrupts enabled, the ISR for a particular interrupt source (or the hardware) must arrange that that interrupt will not recur until the DSR has completed. In some cases, this is how the hardware works. Once an interrupt is delivered another interrupt will not occur until re-enabled. In the general case, however, it is up to the ISR to enforce this behavior. Typically the ISR will "mask" the interrupt source, thus preventing its recurrence. The DSR will then unmask the interrupt when it has been serviced thus allowing new occurrences of the interrupt to be delivered when they happen. </PARA> <PARA>Alternatively, if an ISR is doing very little per interrupt, for example transferring one byte from memory to an IO device, it may only be necessary to interact with the rest of the system when a "transfer" is complete. In such a case an ISR could execute many times and only when it reaches the end of a buffer does it need to request execution of its DSR. </PARA> <PARA>If the interrupt source is "bursty", it may be OK for several interrupts and calls to the ISR to occur before a requested DSR has been executed; the kernel maintains counts for posted DSRs, and in such a case the DSR will eventually be called with a parameter that tells it how many ISRs requested that the DSR be called. Care is needed to get the interrupt code right for such a situation, for one call to the DSR is required to do the work of several. </PARA> <PARA>As mentioned above, the DSR will execute at some later time. Depending on the state of the system, it may be executed at a much later time. There are periods during certain kernel operations where thread scheduling is disabled, and hence DSRs are not allowed to operate. These periods have been purposefully made as limited as possible in the <EMPHASIS>eCos</EMPHASIS> kernel, but they still exist. In addition, user threads have the ability to suspend scheduling as well, thus affecting the possible DSR execution latency. If a DSR cannot be executed sufficiently quickly, the interrupt source may actually overrun. This would be considered a system failure. </PARA> <PARA>One of the problems system designers face is how much stack space to allow each thread in the system. <EMPHASIS>eCos</EMPHASIS> does not dictate the size of thread stacks, it is left to the user when the thread is created. The size of the stack depends on the thread requirements as well as some fixed overhead required by the system. In this case, the overhead is enough stack space to hold a complete thread state (the actual amount depends on the CPU architecture). Guidelines for the minimum stack requirements are provided by the HAL using the symbol <literal>CYGNUM_HAL_STACK_SIZE_MINIMUM</literal>. </PARA> <PARA>A potential problem with this scheme is with nested interrupts. Since interrupts are reenabled during the DSR portion of servicing an interrupt, there is the possibility of a new interrupt (hopefully from a separate source) arriving while this processing takes place. When this new interrupt is serviced some state information about the interrupted processing will be saved on the stack. The amount of this information again depends on the CPU architecture and in some cases it is substantial. This implies that any given stack would need enough space to potentially hold "N" interrupt frames. In a realtime system with many threads this is an untenable situation. To solve this problem, <EMPHASIS>eCos</EMPHASIS> allows for a separate interrupt stack to be used while processing interrupts. This stack needs to be large enough to support "N" nested interrupts, but each individual thread stack only needs the overhead of a single interrupt state. This is because the thread state is kept on the thread's own stack, including information about any interrupt that caused the thread to be scheduled. This is a much better situation in the end, however, since only the interrupt stack need be large enough to handle the potential interrupt servicing needs. </PARA> <PARA><EMPHASIS>eCos</EMPHASIS> allows for the use of the interrupt stack to be totally configurable. The user can elect to not use a separate interrupt stack. This requires making all thread stacks large enough but does reduce the overhead of switching stacks while processing interrupts. On the other hand, if memory is tight, then choosing a separate interrupt stack would be warranted at the cost of a few machine cycles during the processing of each interrupt. </PARA> <PARA>Not all target HALs support this feature from day one anyway; however common configuration features such as this may still be presented in the config tool, and present in include files, even if the actual target selected does not support the feature at this time. </PARA> <PARA>The following problem with the interrupt system has been observed. On the mn10300 simulator, interrupts were occurring immediately after they were re-enabled in the DSR. This should really be considered a case of interrupt overrun since there is no possibility of useful [or any] processing between the time an interrupt has been serviced and an subsequent interrupt occurs, hence the system is totally saturated. The problem came about because the stack was overflowing. It was a user [thread] stack that overflowed because DSR processing was taking place on the thread stack. Analysis of this problem led to a rework of how interrupts are processed, in particular the use of a separate interrupt stack during interrupt processing (both ISR and DSR parts). The overflow can still happen, but now it is restricted to only the interrupt stack. The system designer can make accommodations for this by making a suitably large interrupt stack if it is known that the "overrun" is finite, e.g. in the case of a serial device, this could be the depth of some FIFO. In any case, overrun should be avoided, but having only a single stack that needs to suffer multiple interrupt frames allows for this failure to be detected simply. </PARA> <PARA>Of course, it is only worthwhile having a separate interrupt stack if you are using an <EMPHASIS>eCos</EMPHASIS> configuration that has a scheduler and multiple threads. If there is no kernel, then the C library arranges to call <FUNCTION>main()</FUNCTION>, or your application may be entered from <FUNCTION>cyg_user_start()</FUNCTION>, on the startup stack. It runs on the only stack there is in the system. Depending on the design of the particular HAL for your target platform, it is natural to re-use the startup stack as the interrupt stack as soon as the scheduler is running. Since this is only sensible if there is a kernel, HALs typically only implement the separate interrupt stack if the kernel is present. </PARA> </CHAPTER> <CHAPTER id="real-time-characterization"> <TITLE><!-- <index></index> -->Real-time Characterization</TITLE> <PARA>When building a real-time system, care must be taken to ensure that the system will be able to perform properly within the constraints of that system. One of these constraints may be how fast certain operations can be performed. Another might be how deterministic the overall behavior of the system is. Lastly the memory footprint (size) and unit cost may be important. </PARA> <PARA>One of the major problems encountered while evaluating a system will be how to compare it with possible alternatives. Most manufacturers of real-time systems publish performance numbers, ostensibly so that users can compare the different offerings. However, what these numbers mean and how they were gathered is often not clear. The values are typically measured on a particular piece of hardware, so in order to truly compare, one must obtain measurements for exactly the same set of hardware that were gathered in a similar fashion. </PARA> <PARA>Two major items need to be present in any given set of measurements. First, the raw values for the various operations; these are typically quite easy to measure and will be available for most systems. Second, the determinacy of the numbers; in other words how much the value might change depending on other factors within the system. This value is affected by a number of factors: how long interrupts might be masked, whether or not the function can be interrupted, even very hardware-specific effects such as cache locality and pipeline usage. It is very difficult to measure the determinacy of any given operation, but that determinacy is fundamentally important to proper overall characterization of a system. </PARA> <PARA>In the discussion and numbers that follow, three key measurements are provided. The first measurement is an estimate of the interrupt latency: this is the length of time from when a hardware interrupt occurs until its <!-- <index></index> -->Interrupt Service Routine (ISR) is called. The second measurement is an estimate of overall interrupt overhead: this is the length of time average interrupt processing takes, as measured by the real-time clock interrupt (other interrupt sources will certainly take a different amount of time, but this data cannot be easily gathered). The third measurement consists of the timings for the various kernel primitives. </PARA> <SECT1 id="real-time-char-methodology"><!-- <index></index> --> <TITLE>Methodology</TITLE> <PARA>Key operations in the kernel were measured by using a simple test program which exercises the various kernel primitive operations. A hardware timer, normally the one used to drive the real-time clock, was used for these measurements. In most cases this timer can be read with quite high resolution, typically in the range of a few microseconds. For each measurement, the operation was repeated a number of times. Time stamps were obtained directly before and after the operation was performed. The data gathered for the entire set of operations was then analyzed, generating average (mean), maximum and minimum values. The sample variance (a measure of how close most samples are to the mean) was also calculated. The cost of obtaining the real-time clock timer values was also measured, and was subtracted from all other times. </PARA> <PARA>Most <!-- <index></index> -->kernel functions can be measured separately. In each case, a reasonable number of iterations are performed. Where the test case involves a kernel object, for example creating a task, each iteration is performed on a different object. There is also a set of tests which measures the interactions between multiple tasks and certain kernel primitives. Most functions are tested in such a way as to determine the variations introduced by varying numbers of objects in the system. For example, the mailbox tests measure the cost of a 'peek' operation when the mailbox is empty, has a single item, and has multiple items present. In this way, any effects of the state of the object or how many items it contains can be determined. </PARA> <PARA>There are a few things to consider about these measurements. Firstly, they are quite micro in scale and only measure the operation in question. These measurements do not adequately describe how the timings would be perturbed in a real system with multiple interrupting sources. Secondly, the possible aberration incurred by the real-time clock (system heartbeat tick) is explicitly avoided. Virtually all kernel functions have been designed to be interruptible. Thus the times presented are typical, but best case, since any particular function may be interrupted by the clock tick processing. This number is explicitly calculated so that the value may be included in any deadline calculations required by the end user. Lastly, the reported measurements were obtained from a system built with all options at their default values. Kernel instrumentation and asserts are also disabled for these measurements. Any number of configuration options can change the measured results, sometimes quite dramatically. For example, mutexes are using priority inheritance in these measurements. The numbers will change if the system is built with priority inheritance on mutex variables turned off. </PARA> <PARA>The final value that is measured is an estimate of interrupt latency. This particular value is not explicitly calculated in the test program used, but rather by instrumenting the kernel itself. The raw number of timer ticks that elapse between the time the timer generates an interrupt and the start of the timer ISR is kept in the kernel. These values are printed by the test program after all other operations have been tested. Thus this should be a reasonable estimate of the interrupt latency over time. </PARA> </SECT1> <SECT1 id="real-time-char-using-measurements"> <TITLE>Using these Measurements</TITLE> <PARA>These measurements can be used in a number of ways. The most typical use will be to compare different real-time kernel offerings on similar hardware, another will be to estimate the cost of implementing a task using eCos (applications can be examined to see what effect the kernel operations will have on the total execution time). Another use would be to observe how the tuning of the kernel affects overall operation. </PARA> </SECT1> <SECT1 id="real-time-char-influences-on-performance"> <TITLE>Influences on Performance</TITLE><!-- <index></index> --> <PARA>A number of factors can affect real-time performance in a system. One of the most common factors, yet most difficult to characterize, is the effect of device drivers and interrupts on system timings. Different device drivers will have differing requirements as to how long interrupts are suppressed, for example. The eCos system has been designed with this in mind, by separating the management of interrupts (ISR handlers) and the processing required by the interrupt (<!-- <index></index> -->DSR—Deferred Service Routine— handlers). However, since there is so much variability here, and indeed most device drivers will come from the end users themselves, these effects cannot be reliably measured. Attempts have been made to measure the overhead of the single interrupt that eCos relies on, the real-time clock timer. This should give you a reasonable idea of the cost of executing interrupt handling for devices. </PARA> </SECT1> <SECT1 id="real-time-char-measured-items"> <TITLE>Measured Items</TITLE> <PARA>This section describes the various <!-- <index></index> -->tests and the numbers presented. All tests use the C kernel API (available by way of <FILENAME>cyg/kernel/kapi.h</FILENAME>). There is a single main thread in the system that performs the various tests. Additional threads may be created as part of the testing, but these are short lived and are destroyed between tests unless otherwise noted. The terminology “lower priority” means a priority that is less important, not necessarily lower in numerical value. A higher priority thread will run in preference to a lower priority thread even though the priority value of the higher priority thread may be numerically less than that of the lower priority thread. </PARA> <SECT2><!-- <index></index> --> <TITLE>Thread Primitives</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Create thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_create()</FUNCTION> call. Each call creates a totally new thread. The set of threads created by this test will be reused in the subsequent thread primitive tests. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Yield thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_yield()</FUNCTION> call. For this test, there are no other runnable threads, thus the test should just measure the overhead of trying to give up the CPU. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Suspend [suspended] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_suspend()</FUNCTION> call. A thread may be suspended multiple times; each thread is already suspended from its initial creation, and is suspended again. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Resume thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_resume()</FUNCTION> call. All of the threads have a suspend count of 2, thus this call does not make them runnable. This test just measures the overhead of resuming a thread. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Set priority</TERM> <LISTITEM> <PARA> This test measures the <FUNCTION>cyg_thread_set_priority()</FUNCTION> call. Each thread, currently suspended, has its priority set to a new value. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Get priority</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_get_priority()</FUNCTION> call. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Kill [suspended] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_kill()</FUNCTION> call. Each thread in the set is killed. All threads are known to be suspended before being killed. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Yield [no other] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_yield()</FUNCTION> call again. This is to demonstrate that the <FUNCTION>cyg_thread_yield()</FUNCTION> call has a fixed overhead, regardless of whether there are other threads in the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Resume [suspended low priority] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_resume()</FUNCTION> call again. In this case, the thread being resumed is lower priority than the main thread, thus it will simply become ready to run but not be granted the CPU. This test measures the cost of making a thread ready to run. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Resume [runnable low priority] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_resume()</FUNCTION> call again. In this case, the thread being resumed is lower priority than the main thread and has already been made runnable, so in fact the resume call has no effect. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Suspend [runnable] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_suspend()</FUNCTION> call again. In this case, each thread has already been made runnable (by previous tests). </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Yield [only low priority] thread </TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_yield()</FUNCTION> call. In this case, there are many other runnable threads, but they are all lower priority than the main thread, thus no thread switches will take place. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Suspend [runnable->not runnable] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_suspend()</FUNCTION> call again. The thread being suspended will become non-runnable by this action. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Kill [runnable] thread</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_kill()</FUNCTION> call again. In this case, the thread being killed is currently runnable, but lower priority than the main thread. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Resume [high priority] thread </TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_thread_resume()</FUNCTION> call. The thread being resumed is higher priority than the main thread, thus a thread switch will take place on each call. In fact there will be two thread switches; one to the new higher priority thread and a second back to the test thread. The test thread exits immediately. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Thread switch</TERM> <LISTITEM> <PARA>This test attempts to measure the cost of switching from one thread to another. Two equal priority threads are started and they will each yield to the other for a number of iterations. A time stamp is gathered in one thread before the <FUNCTION>cyg_thread_yield()</FUNCTION> call and after the call in the other thread. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Scheduler Primitives</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Scheduler lock</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_scheduler_lock()</FUNCTION> call. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Scheduler unlock [0 threads] </TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_scheduler_unlock()</FUNCTION> call. There are no other threads in the system and the unlock happens immediately after a lock so there will be no pending DSR’s to run. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Scheduler unlock [1 suspended thread]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_scheduler_unlock()</FUNCTION> call. There is one other thread in the system which is currently suspended. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Scheduler unlock [many suspended threads]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_scheduler_unlock()</FUNCTION> call. There are many other threads in the system which are currently suspended. The purpose of this test is to determine the cost of having additional threads in the system when the scheduler is activated by way of <FUNCTION>cyg_scheduler_unlock()</FUNCTION>. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Scheduler unlock [many low priority threads]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_scheduler_unlock()</FUNCTION> call. There are many other threads in the system which are runnable but are lower priority than the main thread. The purpose of this test is to determine the cost of having additional threads in the system when the scheduler is activated by way of <FUNCTION>cyg_scheduler_unlock()</FUNCTION>.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Mutex Primitives</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Init mutex</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mutex_init()</FUNCTION> call. A number of separate mutex variables are created. The purpose of this test is to measure the cost of creating a new mutex and introducing it to the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Lock [unlocked] mutex</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mutex_lock()</FUNCTION> call. The purpose of this test is to measure the cost of locking a mutex which is currently unlocked. There are no other threads executing in the system while this test runs. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Unlock [locked] mutex</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mutex_unlock()</FUNCTION> call. The purpose of this test is to measure the cost of unlocking a mutex which is currently locked. There are no other threads executing in the system while this test runs. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Trylock [unlocked] mutex</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mutex_trylock()</FUNCTION> call. The purpose of this test is to measure the cost of locking a mutex which is currently unlocked. There are no other threads executing in the system while this test runs. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Trylock [locked] mutex</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mutex_trylock()</FUNCTION> call. The purpose of this test is to measure the cost of locking a mutex which is currently locked. There are no other threads executing in the system while this test runs. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Destroy mutex</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mutex_destroy()</FUNCTION> call. The purpose of this test is to measure the cost of deleting a mutex from the system. There are no other threads executing in the system while this test runs. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Unlock/Lock mutex</TERM> <LISTITEM> <PARA>This test attempts to measure the cost of unlocking a mutex for which there is another higher priority thread waiting. When the mutex is unlocked, the higher priority waiting thread will immediately take the lock. The time from when the unlock is issued until after the lock succeeds in the second thread is measured, thus giving the round-trip or circuit time for this type of synchronizer. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Mailbox Primitives</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Create mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_create()</FUNCTION> call. A number of separate mailboxes is created. The purpose of this test is to measure the cost of creating a new mailbox and introducing it to the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Peek [empty] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_peek()</FUNCTION> call. An attempt is made to peek the value in each mailbox, which is currently empty. The purpose of this test is to measure the cost of checking a mailbox for a value without blocking. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Put [first] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_put()</FUNCTION> call. One item is added to a currently empty mailbox. The purpose of this test is to measure the cost of adding an item to a mailbox. There are no other threads currently waiting for mailbox items to arrive. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Peek [1 msg] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_peek()</FUNCTION> call. An attempt is made to peek the value in each mailbox, which contains a single item. The purpose of this test is to measure the cost of checking a mailbox which has data to deliver. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Put [second] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_put()</FUNCTION> call. A second item is added to a mailbox. The purpose of this test is to measure the cost of adding an additional item to a mailbox. There are no other threads currently waiting for mailbox items to arrive. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Peek [2 msgs] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_peek()</FUNCTION> call. An attempt is made to peek the value in each mailbox, which contains two items. The purpose of this test is to measure the cost of checking a mailbox which has data to deliver. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Get [first] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_get()</FUNCTION> call. The first item is removed from a mailbox that currently contains two items. The purpose of this test is to measure the cost of obtaining an item from a mailbox without blocking. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Get [second] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_get()</FUNCTION> call. The last item is removed from a mailbox that currently contains one item. The purpose of this test is to measure the cost of obtaining an item from a mailbox without blocking. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tryput [first] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_tryput()</FUNCTION> call. A single item is added to a currently empty mailbox. The purpose of this test is to measure the cost of adding an item to a mailbox. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Peek item [non-empty] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_peek_item()</FUNCTION> call. A single item is fetched from a mailbox that contains a single item. The purpose of this test is to measure the cost of obtaining an item without disturbing the mailbox. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tryget [non-empty] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_tryget()</FUNCTION> call. A single item is removed from a mailbox that contains exactly one item. The purpose of this test is to measure the cost of obtaining one item from a non-empty mailbox. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Peek item [empty] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_peek_item()</FUNCTION> call. An attempt is made to fetch an item from a mailbox that is empty. The purpose of this test is to measure the cost of trying to obtain an item when the mailbox is empty. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tryget [empty] mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_tryget()</FUNCTION> call. An attempt is made to fetch an item from a mailbox that is empty. The purpose of this test is to measure the cost of trying to obtain an item when the mailbox is empty. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Waiting to get mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_waiting_to_get()</FUNCTION> call. The purpose of this test is to measure the cost of determining how many threads are waiting to obtain a message from this mailbox. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Waiting to put mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_waiting_to_put()</FUNCTION> call. The purpose of this test is to measure the cost of determining how many threads are waiting to put a message into this mailbox. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Delete mbox</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_mbox_delete()</FUNCTION> call. The purpose of this test is to measure the cost of destroying a mailbox and removing it from the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Put/Get mbox</TERM> <LISTITEM> <PARA>In this round-trip test, one thread is sending data to a mailbox that is being consumed by another thread. The time from when the data is put into the mailbox until it has been delivered to the waiting thread is measured. Note that this time will contain a thread switch. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Semaphore Primitives</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Init semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_init()</FUNCTION> call. A number of separate semaphore objects are created and introduced to the system. The purpose of this test is to measure the cost of creating a new semaphore. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Post [0] semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_post()</FUNCTION> call. Each semaphore currently has a value of 0 and there are no other threads in the system. The purpose of this test is to measure the overhead cost of posting to a semaphore. This cost will differ if there is a thread waiting for the semaphore. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Wait [1] semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_wait()</FUNCTION> call. The semaphore has a current value of 1 so the call is non-blocking. The purpose of the test is to measure the overhead of “taking” a semaphore. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Trywait [0] semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_trywait()</FUNCTION> call. The semaphore has a value of 0 when the call is made. The purpose of this test is to measure the cost of seeing if a semaphore can be “taken” without blocking. In this case, the answer would be no. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Trywait [1] semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_trywait()</FUNCTION> call. The semaphore has a value of 1 when the call is made. The purpose of this test is to measure the cost of seeing if a semaphore can be “taken” without blocking. In this case, the answer would be yes. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Peek semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_peek()</FUNCTION> call. The purpose of this test is to measure the cost of obtaining the current semaphore count value. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Destroy semaphore</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_semaphore_destroy()</FUNCTION> call. The purpose of this test is to measure the cost of deleting a semaphore from the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Post/Wait semaphore</TERM> <LISTITEM> <PARA>In this round-trip test, two threads are passing control back and forth by using a semaphore. The time from when one thread calls <FUNCTION>cyg_semaphore_post()</FUNCTION> until the other thread completes its <FUNCTION>cyg_semaphore_wait()</FUNCTION> is measured. Note that each iteration of this test will involve a thread switch. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Counters</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Create counter</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_create()</FUNCTION> call. A number of separate counters are created. The purpose of this test is to measure the cost of creating a new counter and introducing it to the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Get counter value</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_current_value()</FUNCTION> call. The current value of each counter is obtained. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Set counter value</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_set_value()</FUNCTION> call. Each counter is set to a new value. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tick counter</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_tick()</FUNCTION> call. Each counter is “ticked” once. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Delete counter</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_delete()</FUNCTION> call. Each counter is deleted from the system. The purpose of this test is to measure the cost of deleting a counter object.</PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> <SECT2><!-- <index></index> --> <TITLE>Alarms</TITLE> <VARIABLELIST> <VARLISTENTRY> <TERM>Create alarm</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_alarm_create()</FUNCTION> call. A number of separate alarms are created, all attached to the same counter object. The purpose of this test is to measure the cost of creating a new counter and introducing it to the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Initialize alarm</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_alarm_initialize()</FUNCTION> call. Each alarm is initialized to a small value. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Disable alarm</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_alarm_disable()</FUNCTION> call. Each alarm is explicitly disabled. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Enable alarm</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_alarm_enable()</FUNCTION> call. Each alarm is explicitly enabled. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Delete alarm</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_alarm_delete()</FUNCTION> call. Each alarm is destroyed. The purpose of this test is to measure the cost of deleting an alarm and removing it from the system. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tick counter [1 alarm]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_tick()</FUNCTION> call. A counter is created that has a single alarm attached to it. The purpose of this test is to measure the cost of “ticking” a counter when it has a single attached alarm. In this test, the alarm is not activated (fired). </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tick counter [many alarms]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_tick()</FUNCTION> call. A counter is created that has multiple alarms attached to it. The purpose of this test is to measure the cost of “ticking” a counter when it has many attached alarms. In this test, the alarms are not activated (fired). </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tick & fire counter [1 alarm]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_tick()</FUNCTION> call. A counter is created that has a single alarm attached to it. The purpose of this test is to measure the cost of “ticking” a counter when it has a single attached alarm. In this test, the alarm is activated (fired). Thus the measured time will include the overhead of calling the alarm callback function. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Tick & fire counter [many alarms]</TERM> <LISTITEM> <PARA>This test measures the <FUNCTION>cyg_counter_tick()</FUNCTION> call. A counter is created that has multiple alarms attached to it. The purpose of this test is to measure the cost of “ticking” a counter when it has many attached alarms. In this test, the alarms are activated (fired). Thus the measured time will include the overhead of calling the alarm callback function. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Alarm latency [0 threads]</TERM> <LISTITEM> <PARA>This test attempts to measure the latency in calling an alarm callback function. The time from the clock interrupt until the alarm function is called is measured. In this test, there are no threads that can be run, other than the system idle thread, when the clock interrupt occurs (all threads are suspended). </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Alarm latency [2 threads]</TERM> <LISTITEM> <PARA>This test attempts to measure the latency in calling an alarm callback function. The time from the clock interrupt until the alarm function is called is measured. In this test, there are exactly two threads which are running when the clock interrupt occurs. They are simply passing back and forth by way of the <FUNCTION>cyg_thread_yield()</FUNCTION> call. The purpose of this test is to measure the variations in the latency when there are executing threads. </PARA> </LISTITEM> </VARLISTENTRY> <VARLISTENTRY> <TERM>Alarm latency [many threads]</TERM> <LISTITEM> <PARA>This test attempts to measure the latency in calling an alarm callback function. The time from the clock interrupt until the alarm function is called is measured. In this test, there are a number of threads which are running when the clock interrupt occurs. They are simply passing back and forth by way of the <FUNCTION>cyg_thread_yield()</FUNCTION> call. The purpose of this test is to measure the variations in the latency when there are many executing threads. </PARA> </LISTITEM> </VARLISTENTRY> </VARIABLELIST> </SECT2> </SECT1> <SECT1 id="real-time-char-sample-numbers"><!-- <index></index> --> <TITLE>Sample Numbers</TITLE> <PARA>For sample results, see Appendix 1 of <EMPHASIS>Getting Started with eCos</EMPHASIS></PARA> </SECT1> </CHAPTER> </part>
