Chapter 143. µITRON API

143.1. Introduction to µITRON

The µITRON specification defines a highly flexible operating system architecture designed specifically for application in embedded systems. The specification addresses features which are common to the majority of processor architectures and deliberately avoids virtualization which would adversely impact real-time performance. The µITRON specification may be implemented on many hardware platforms and provides significant advantages by reducing the effort involved in understanding and porting application software to new processor architectures.

Several revisions of the µITRON specification exist. In this release, eCos supports the µITRON version 3.02 specification, with complete “Standard functionality” (level S), plus many “Extended” (level E) functions. An exception is get_tid() which has µITRON 4 semantics. The definitive reference on µITRON is Dr. Sakamura‚s book: µITRON 3.0, An Open and Portable Real-Time Operating System for Embedded Systems. The book can be purchased from the IEEE Press, and an ASCII version of the standard can be found online at http://www.t-engine.org/specifications#d.

143.2. µITRON and eCos

The eCos kernel implements the functionality used by the µITRON compatibility subsystem. The configuration of the kernel influences the behavior of µITRON programs.

In particular, the default configuration has time slicing (also known as round-robin scheduling) switched on; this means that a task can be moved from RUN state to READY state at any time, in order that one of its peers may run. This is not strictly conformant to the µITRON specification, which states that timeslicing may be implemented by periodically issuing a rot_rdq(0) call from within a periodic task or cyclic handler; otherwise it is expected that a task runs until it is pre-empted in consequence of synchronization or communication calls it makes, or the effects of an interrupt or other external event on a higher priority task cause that task to become READY. To disable timeslicing functionality in the kernel and µITRON compatibility environment, please disable the CYGSEM_KERNEL_SCHED_TIMESLICE configuration option in the kernel package. A description of kernel scheduling is in Kernel Overview.

For another example, the semantics of task queueing when waiting on a synchronization object depend solely on the way the underlying kernel is configured. As discussed above, the multi-level queue scheduler is the only one which is µITRON compliant, and it queues waiting tasks in FIFO order. Future releases of that scheduler might be configurable to support priority ordering of task queues. Other schedulers might be different again: for example the bitmap scheduler can be used with the µITRON compatibility layer, even though it only allows one task at each priority and as such is not µITRON compliant, but it supports only priority ordering of task queues. So which queueing scheme is supported is not really a property of the µITRON compatibility layer; it depends on the kernel.

In this version of the µITRON compatibility layer, the calls to disable and enable scheduling and interrupts (dis_dsp(), ena_dsp(), loc_cpu() and unl_cpu()) call underlying kernel functions; in particular, the xxx_dsp() functions lock the scheduler entirely, which prevents dispatching of DSRs; functions implemented by DSRs include clock counters and alarm timers. Thus time “stops” while dispatching is disabled with dis_dsp().

Like all parts of the eCos system, the detailed semantics of the µITRON layer are dependent on its configuration and the configuration of other components that it uses. The µITRON configuration options are all defined in the file pkgconf/uitron.h, and can be set using the configuration tool or editing the .ecc file in your build directory by hand.

An important configuration option for the µITRON compatibility layer is “Option: Return Error Codes for Bad Params” ( CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS ), which allows a lot of the error checking code in the µITRON compatibility layer to be removed. Of course this leaves a program open to undetected errors, so it should only be used once an application is fully debugged and tested. Its benefits include reduced code size and faster execution. However, it affects the API significantly, in that with this option enabled, bad calls do not return errors, but cause an assert failure (if that is itself enabled) or malfunction internally. There is discussion in more detail about this in each section below.

We now give a brief description of the µITRON functions which are implemented in this release. Note that all C and C++ source files should have the following #include statement:

#include <cyg/compat/uitron/uit_func.h>

143.3. Task Management Functions

The following functions are fully supported in this release:

ER sta_tsk(
  ID tskid,
  INT stacd )
void ext_tsk( void )
void exd_tsk( void )
ER dis_dsp( void )
ER ena_dsp( void )
ER chg_pri(
  ID tskid,
  PRI tskpri )
ER rot_rdq(
  PRI tskpri )
ER get_tid(
  ID *p_tskid )
ER ref_tsk(
  T_RTSK *pk_rtsk,
  ID tskid )
ER ter_tsk(
  ID tskid )
ER rel_wai(
  ID tskid )

The following two functions are supported in this release, when enabled with the configuration option CYGPKG_UITRON_TASKS_CREATE_DELETE with some restrictions:

ER cre_tsk(
  ID tskid,
  T_CTSK *pk_ctsk )
ER del_tsk(
  ID tskid )

These functions are restricted as follows:

Because of the static initialization facilities provided for system objects, a task is allocated stack space statically in the configuration. So while tasks can be created and deleted, the same stack space is used for that task (task ID number) each time. Thus the stack size (pk_ctsk->stksz) requested in cre_tsk() is checked for being less than that which was statically allocated, and otherwise ignored. This ensures that the new task will have enough stack to run. For this reason del_tsk() does not in any sense free the memory that was in use for the task's stack.

The task attributes (pk_ctsk->tskatr) are ignored; current versions of eCos do not need to know whether a task is written in assembler or C/C++ so long as the procedure call standard appropriate to the CPU is followed.

Extended information (pk_ctsk->exinf) is ignored.

143.3.1. Error checking

For all these calls, an invalid task id (tskid) (less than 1 or greater than the number of configured tasks) only returns E_ID when bad params return errors ( CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled, see above).

Similarly, the following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled:

  • pk_crtk in cre_tsk() is a valid pointer, otherwise return E_PAR
  • ter_tsk() or rel_wai() on the calling task returns E_OBJ
  • the CPU is not locked already in dis_dsp() and ena_dsp() ; returns E_CTX
  • priority level in chg_pri() and rot_rdq() is checked for validity, E_PAR
  • return value pointer in get_tid() and ref_tsk() is a valid pointer, or E_PAR

The following conditions are checked for, and return error codes if appropriate, regardless of the setting of CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS :

  • When create and delete functions cre_tsk() and del_tsk() are supported, all calls which use a valid task ID number check that the task exists; if not, E_NOEXS is returned
  • When supported, cre_tsk() : the task must not already exist; otherwise E_OBJ
  • When supported, cre_tsk() : the requested stack size must not be larger than that statically configured for the task; see the configuration options “Static initializers”, and “Default stack size”. Else E_NOMEM
  • When supported, del_tsk() : the underlying eCos thread must not be running - this would imply either a bug or some program bypassing the µITRON compatibility layer and manipulating the thread directly. E_OBJ
  • sta_tsk() : the task must be dormant, else E_OBJ
  • ter_tsk() : the task must not be dormant, else E_OBJ
  • chg_pri() : the task must not be dormant, else E_OBJ
  • rel_wai() : the task must be in WAIT or WAIT-SUSPEND state, else E_OBJ

143.4. Task-Dependent Synchronization Functions

These functions are fully supported in this release:

ER sus_tsk(
    ID tskid )
ER rsm_tsk(
    ID tskid )
ER frsm_tsk(
    ID tskid )
ER slp_tsk( void )
ER tslp_tsk(
    TMO tmout )
ER wup_tsk(
    ID tskid )
ER can_wup(
    INT *p_wupcnt,    ID tskid )

143.4.1. Error checking

The following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled (see the configuration option “Return Error Codes for Bad Params”):

  • invalid tskid; less than 1 or greater than CYGNUM_UITRON_TASKS returns E_ID
  • wup_tsk() , sus_tsk() , rsm_tsk() , frsm_tsk() on the calling task returns E_OBJ
  • dispatching is enabled in tslp_tsk() and slp_tsk() , or E_CTX
  • tmout must be positive, otherwise E_PAR
  • return value pointer in can_wup() is a valid pointer, or E_PAR

The following conditions are checked for, and can return error codes, regardless of the setting of CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS:

  • When create and delete functions cre_tsk() and del_tsk() are supported, all calls which use a valid task ID number check that the task exists; if not, E_NOEXS is returned
  • sus_tsk() : the task must not be dormant, else E_OBJ
  • frsm/rsm_tsk() : the task must be suspended, else E_OBJ
  • tslp/slp_tsk() : return codes E_TMOUT, E_RLWAI and E_DLT are returned depending on the reason for terminating the sleep
  • wup_tsk() and can_wup() : the task must not be dormant, or E_OBJ is returned

143.5.  Synchronization and Communication Functions

These functions are fully supported in this release:

ER sig_sem(
    ID semid )
ER wai_sem(
    ID semid )
ER preq_sem(
    ID semid )
ER twai_sem(
    ID semid,    TMO tmout )
ER ref_sem(
    T_RSEM *pk_rsem ,    ID semid )
ER set_flg(
    ID flgid,    UINT setptn )
ER clr_flg(
    ID flgid,    UINT clrptn )
ER wai_flg(
    UINT *p_flgptn,    ID flgid ,
    UINT waiptn ,    UINT wfmode )
ER pol_flg(
    UINT *p_flgptn,    ID flgid ,
    UINT waiptn ,    UINT wfmode )
ER twai_flg(
    UINT *p_flgptn    ID flgid ,
    UINT waiptn ,    UINT wfmode,    TMO tmout )
ER ref_flg(
    T_RFLG *pk_rflg,    ID flgid )
ER snd_msg(
    ID mbxid,    T_MSG *pk_msg )
ER rcv_msg(
    T_MSG **ppk_msg,    ID mbxid )
ER prcv_msg(
    T_MSG **ppk_msg,    ID mbxid )
ER trcv_msg(
    T_MSG **ppk_msg,    ID mbxid ,    TMO tmout )
ER ref_mbx(
    T_RMBX *pk_rmbx,    ID mbxid )

The following functions are supported in this release (with some restrictions) if enabled with the appropriate configuration option for the object type (for example CYGPKG_UITRON_SEMAS_CREATE_DELETE):

ER cre_sem(
    ID semid,    T_CSEM *pk_csem )
ER del_sem(
    ID semid )
ER cre_flg(
    ID flgid,    T_CFLG *pk_cflg )
ER del_flg(
    ID flgid )
ER cre_mbx(
    ID mbxid,    T_CMBX *pk_cmbx )
ER del_mbx(
    ID mbxid )

In general the queueing order when waiting on a synchronization object depends on the underlying kernel configuration. The multi-level queue scheduler is required for strict µITRON conformance and it queues tasks in FIFO order, so requests to create an object with priority queueing of tasks (pk_cxxx->xxxatr = TA_TPRI) are rejected with E_RSATR. Additional undefined bits in the attributes fields must be zero.

In general, extended information (pk_cxxx->exinf) is ignored.

For semaphores, the initial semaphore count (pk_csem->isemcnt) is supported; the new semaphore's count is set. The maximum count is not supported, and is not in fact defined in type pk_csem.

For flags, multiple tasks are allowed to wait. Because single task waiting is a subset of this, the W bit (TA_WMUL) of the flag attributes is ignored; all other bits must be zero. The initial flag value is supported.

For mailboxes, the buffer count is defined statically by kernel configuration option CYGNUM_KERNEL_SYNCH_MBOX_QUEUE_SIZE; therefore the buffer count field is not supported and is not in fact defined in type pk_cmbx. Queueing of messages is FIFO ordered only, so TA_MPRI (in pk_cmbx->mbxatr) is not supported.

143.5.1. Error checking

The following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled:

  • invalid object id; less than 1 or greater than CYGNUM_UITRON_TASKS/SEMAS/MBOXES as appropriate returns E_ID
  • dispatching is enabled in any call which can sleep, or E_CTX
  • tmout must be positive, otherwise E_PAR
  • pk_cxxx pointers in cre_xxx() must be valid pointers, or E_PAR
  • return value pointer in ref_xxx() is valid pointer, or E_PAR
  • flag wait pattern must be non-zero, and mode must be valid, or E_PAR
  • return value pointer in flag wait calls is a valid pointer, or E_PAR

The following conditions are checked for, and can return error codes, regardless of the setting of CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS :

  • When create and delete functions cre_xxx() and del_xxx() are supported, all calls which use a valid object ID number check that the object exists. If not, E_NOEXS is returned.
  • In create functions cre_xxx() , when supported, if the object already exists, then E_OBJ
  • In any call which can sleep, such as twai_sem() : return codes E_TMOUT, E_RLWAI, E_DLT or of course E_OK are returned depending on the reason for terminating the sleep
  • In polling functions such as preq_sem() return codes E_TMOUT or E_OK are returned depending on the state of the synchronization object
  • In creation functions, the attributes must be compatible with the selected underlying kernel configuration: in cre_sem() pk_csem->sematr must be equal to TA_TFIFO else E_RSATR.
  • In cre_flg() pk_cflg->flgatr must be either TA_WMUL or TA_WSGL else E_RSATR.
  • In cre_mbx() pk_cmbx->mbxatr must be TA_TFIFO + TA_MFIFO else E_RSATR.

143.6. Extended Synchronization and Communication Functions

None of these functions are supported in this release.

143.7. Interrupt management functions

These functions are fully supported in this release:

void   ret_int( void )
ER loc_cpu( void )
ER unl_cpu( void )
ER dis_int(
    UINT eintno )
ER ena_int(
    UINT eintno )
void  ret_wup(
    ID tskid )
ER iwup_tsk(
    ID tskid )
ER isig_sem(
    ID semid )
ER iset_flg(
    ID flgid ,
    UID setptn )
ER isend_msg(
    ID mbxid ,
    T_MSG *pk_msg )

Note that ret_int() and the ret_wup() are implemented as macros, containing a “return” statement.

Also note that ret_wup() and the ixxx_yyy() style functions will only work when called from an ISR whose associated DSR is cyg_uitron_dsr(), as specified in include file <cyg/compat/uitron/uit_ifnc.h>, which defines the ixxx_yyy() style functions also.

If you are writing interrupt handlers more in the eCos style, with separate ISR and DSR routines both of your own devising, do not use these special functions from a DSR: use plain xxx_yyy() style functions (with no ’i‚ prefix) instead, and do not call any µITRON functions from the ISR at all.

The following functions are not supported in this release:

ER def_int(
    UINT dintno,
  T_DINT *pk_dint )
ER chg_iXX(
    UINT iXXXX )
ER ref_iXX(
    UINT * p_iXXXX )

These unsupported functions are all Level C (CPU dependent). Equivalent functionality is available via other eCos-specific APIs.

143.7.1. Error checking

The following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled:

  • loc/unl_cpu() : these must only be called in a µITRON task context, else E_CTX.
  • dis/ena_int() : the interrupt number must be in range as specified by the platform HAL in qustion, else E_PAR.

143.8.  Memory pool Management Functions

These functions are fully supported in this release:

ER get_blf(
    VP *p_blf,    ID mpfid )
ER pget_blf(
    VP *p_blf,    ID mpfid )
ER tget_blf(
    VP *p_blf,    ID mpfid,    TMO tmout )
ER rel_blf(
    ID mpfid,    VP blf )
ER ref_mpf(
    T_RMPF *pk_rmpf,    ID mpfid )
ER get_blk(
    VP *p_blk,    ID mplid,    INT blksz )
ER pget_blk(
    VP *p_blk,    ID mplid,    INT blksz )
ER tget_blk(
    VP *p_blk,    ID mplid,    INT blksz,    TMO tmout )
ER rel_blk(
    ID mplid,    VP blk )
ER ref_mpl(
    T_RMPL *pk_rmpl,    ID mplid )

Note that of the memory provided for a particular pool to manage in the static initialization of the memory pool objects, some memory will be used to manage the pool itself. Therefore the number of blocks * the blocksize will be less than the total memory size.

The following functions are supported in this release, when enabled with CYGPKG_UITRON_MEMPOOLVAR_CREATE_DELETE or CYGPKG_UITRON_MEMPOOLFIXED_CREATE_DELETE as appropriate, with some restrictions:

ER cre_mpl(
    ID mplid,    T_CMPL *pk_cmpl )
ER del_mpl(
    ID mplid )
ER cre_mpf(
    ID mpfid,    T_CMPF *pk_cmpf )
ER del_mpf(
    ID mpfid )

Because of the static initialization facilities provided for system objects, a memory pool is allocated a region of memory to manage statically in the configuration. So while memory pools can be created and deleted, the same area of memory is used for that memory pool (memory pool ID number) each time. The requested variable pool size (pk_cmpl->mplsz) or the number of fixed-size blocks (pk_cmpf->mpfcnt) times the block size (pk_cmpf->blfsz) are checked for fitting within the statically allocated memory area, so if a create call succeeds, the resulting pool will be at least as large as that requested. For this reason del_mpl() and del_mpf() do not in any sense free the memory that was managed by the deleted pool for use by other pools; it may only be managed by a pool of the same object id.

For both fixed and variable memory pools, the queueing order when waiting on a synchronization object depends on the underlying kernel configuration. The multi-level queue scheduler is required for strict µITRON conformance and it queues tasks in FIFO order, so requests to create an object with priority queueing of tasks (pk_cxxx->xxxatr = TA_TPRI) are rejected with E_RSATR. Additional undefined bits in the attributes fields must be zero.

In general, extended information (pk_cxxx->exinf) is ignored.

143.8.1. Error checking

The following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled:

  • invalid object id; less than 1 or greater than CYGNUM_UITRON_MEMPOOLVAR/MEMPOOLFIXED as appropriate returns E_ID
  • dispatching is enabled in any call which can sleep, or E_CTX
  • tmout must be positive, otherwise E_PAR
  • pk_cxxx pointers in cre_xxx() must be valid pointers, or E_PAR
  • return value pointer in ref_xxx() is a valid pointer, or E_PAR
  • return value pointers in get block routines is a valid pointer, or E_PAR
  • blocksize request in get variable block routines is greater than zero, or E_PAR

The following conditions are checked for, and can return error codes, regardless of the setting of CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS:

  • When create and delete functions cre_xxx() and del_xxx() are supported, all calls which use a valid object ID number check that the object exists. If not, E_NOEXS is returned.
  • When create functions cre_xxx() are supported, if the object already exists, then E_OBJ
  • In any call which can sleep, such as get_blk() : return codes E_TMOUT, E_RLWAI, E_DLT or of course E_OK are returned depending on the reason for terminating the sleep
  • In polling functions such as pget_blk() return codes E_TMOUT or E_OK are returned depending on the state of the synchronization object
  • In creation functions, the attributes must be compatible with the selected underlying kernel configuration: in cre_mpl() pk_cmpl->mplatr must be equal to TA_TFIFO else E_RSATR.
  • In cre_mpf() pk_cmpf->mpfatr must be equal to TA_TFIFO else E_RSATR.
  • In creation functions, the requested size of the memory pool must not be larger than that statically configured for the pool else E_RSATR; see the configuration option “Option: Static initializers”. In cre_mpl() pk_cmpl->mplsz is the field of interest
  • In cre_mpf() the product of pk_cmpf->blfsz and pk_cmpf->mpfcnt must be smaller than the memory statically configured for the pool else E_RSATR
  • In functions which return memory to the pool rel_blk() and rel_blf() , if the free fails, for example because the memory did not come from that pool originally, then E_PAR is returned

143.9. Time Management Functions

These functions are fully supported in this release:

ER set_tim(
    SYSTIME *pk_tim )
[Caution]Caution

Setting the time may cause erroneous operation of the kernel, for example a task performing a wait with a time-out may never awaken.

ER get_tim(
    SYSTIME *pk_tim )
ER dly_tsk(
    DLYTIME dlytim )
ER def_cyc(
    HNO cycno,    T_DCYC *pk_dcyc )
ER act_cyc(
    HNO cycno,    UINT cycact )
ER ref_cyc(
    T_RCYC *pk_rcyc,    HNO cycno )
ER def_alm(
    HNO almno,    T_DALM *pk_dalm )
ER ref_alm(
    T_RALM *pk_ralm,    HNO almno )
void ret_tmr( void )

143.9.1. Error checking

The following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled:

  • invalid handler number; less than 1 or greater than CYGNUM_UITRON_CYCLICS/ALARMS as appropriate, or E_PAR
  • dispatching is enabled in dly_tsk() , or E_CTX
  • dlytim must be positive or zero, otherwise E_PAR
  • return value pointer in ref_xxx() is a valid pointer, or E_PAR
  • params within pk_dalm and pk_dcyc must be valid, or E_PAR
  • cycact in act_cyc() must be valid, or E_PAR
  • handler must be defined in ref_xxx() and act_cyc() , or E_NOEXS
  • parameter pointer must be a good pointer in get_tim() and set_tim() , otherwise E_PAR is returned

The following conditions are checked for, and can return error codes, regardless of the setting of CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS :

  • dly_tsk() : return code E_RLWAI is returned depending on the reason for terminating the sleep

143.10.  System Management Functions

These functions are fully supported in this release:

ER get_ver(
    T_VER *pk_ver )
ER ref_sys(
    T_RSYS *pk_rsys )
ER ref_cfg(
    T_RCFG *pk_rcfg )

Note that the information returned by each of these calls may be configured to match the user's own versioning system, and the values supplied by the default configuration may be inappropriate.

These functions are not supported in this release:

ER def_svc(
    FN s_fncd,
    T_DSVC *pk_dsvc )
ER def_exc(
    UINT exckind,
    T_DEXC *pk_dexc )

143.10.1. Error checking

The following conditions are only checked for, and only return errors if CYGSEM_UITRON_BAD_PARAMS_RETURN_ERRORS is enabled:

  • return value pointer in all calls is a valid pointer, or E_PAR

143.11.  Network Support Functions

None of these functions are supported in this release.

143.12. µITRON Configuration FAQ

Q: How are µITRON objects created?

For each type of uITRON object (tasks, semaphores, flags, mboxes, mpf, mpl) these two quantities are controlled by configuration:

  • The maximum number of this type of object.
  • The number of these objects which exist initially.

This is assuming that for the relevant object type, create and delete operations are enabled; enabled is the default. For example, the option CYGPKG_UITRON_MBOXES_CREATE_DELETE controls whether the functions cre_mbx() and del_mbx() exist in the API. If not, then the maximum number of mboxes is the same as the initial number of mboxes, and so on for all µITRON object types.

Mboxes have no initialization, so there are only a few, simple configuration options:

  • CYGNUM_UITRON_MBOXES is the total number of mboxes that you can have in the system. By default this is 4, so you can use mboxes 1,2,3 and 4. You cannot create mboxes outside this range; trying to cre_mbx(5,…) will return an error.
  • CYGNUM_UITRON_MBOXES_INITIALLY is the number of mboxes created automatically for you, during startup. By default this is 4, so all 4 mboxes exist already, and an attempt to create one of these eg. cre_mbx(3,…) will return an error because the mbox in quesion already exists. You can delete a pre-existing mbox, and then re-create it.

If you change CYGNUM_UITRON_MBOXES_INITIALLY, for example to 0, no mboxes are created automatically for you during startup. Any attempt to use an mbox without creating it will return E_NOEXS because the mbox does not exist. You can create an mbox, say cre_mbx(3,…) and then use it, say snd_msg(3,&foo), and all will be well.

Q: How are µITRON objects initialized?

Some object types have optional initialization. Semaphores are an example. You could have CYGNUM_UITRON_SEMAS=10 and CYGNUM_UITRON_SEMAS_INITIALLY=5 which means you can use semaphores 1-5 straight off, but you must create semaphores 6-10 before you can use them. If you decide not to initialize semaphores, semaphores 1-5 will have an initial count of zero. If you decide to initialize them, you must supply a dummy initializer for semaphores 6-10 also. For example, in terms of the configuration output in pkgconf/uitron.h:

   #define CYGDAT_UITRON_SEMA_INITIALIZERS \
        CYG_UIT_SEMA(  1 ),     \
        CYG_UIT_SEMA(  0 ),     \
        CYG_UIT_SEMA(  0 ),     \
        CYG_UIT_SEMA( 99 ),     \
        CYG_UIT_SEMA(  1 ),     \
        CYG_UIT_SEMA_NOEXS,     \
        CYG_UIT_SEMA_NOEXS,     \
        CYG_UIT_SEMA_NOEXS,     \
        CYG_UIT_SEMA_NOEXS,     \
        CYG_UIT_SEMA_NOEXS

Semaphore 1 will have initial count 1, semaphores 2 and 3 will be zero, number 4 will be 99 initially, 5 will be one and numbers 6 though 10 do not exist initially.

Aside: this is how the definition of the symbol would appear in the configuration header file pkgconf/uitron.h — unfortunately editing such a long, multi-line definition is somewhat cumbersome in the GUI config tool in current releases. The macros CYG_UIT_SEMA() — to create a semaphore initializer — and CYG_UIT_SEMA_NOEXS — to invoke a dummy initializer — are provided in in the environment to help with this. Similar macros are provided for other object types. The resulting #define symbol is used in the context of a C++ array initializer, such as:

Cyg_Counting_Semaphore2 cyg_uitron_SEMAS[ CYGNUM_UITRON_SEMAS ] = {
  CYGDAT_UITRON_SEMA_INITIALIZERS
};

which is eventually macro-processed to give

Cyg_Counting_Semaphore2 cyg_uitron_SEMAS[ 10 ] = {
  Cyg_Counting_Semaphore2( ( 1 ) ),
  Cyg_Counting_Semaphore2( ( 0 ) ),
  Cyg_Counting_Semaphore2( ( 0 ) ),
  Cyg_Counting_Semaphore2( ( 99 ) ),
  Cyg_Counting_Semaphore2( ( 1 ) ),
  Cyg_Counting_Semaphore2(0),
  Cyg_Counting_Semaphore2(0),
  Cyg_Counting_Semaphore2(0),
  Cyg_Counting_Semaphore2(0),
  Cyg_Counting_Semaphore2(0),
};

so you can see how it is necessary to include the dummy entries in that definition, otherwise the resulting code will not compile correctly.

If you choose CYGNUM_UITRON_SEMAS_INITIALLY=0 it is meaningless to initialize them, for they must be created and so initialized then, before use.

Q: What about µITRON tasks?

Some object types require initialization. Tasks are an example of this. You must provide a task with a priority, a function to enter when the task starts, a name (for debugging purposes), and some memory to use for the stack. For example (again in terms of the resulting definitions in pkgconf/uitron.h):

#define CYGNUM_UITRON_TASKS 4           // valid task ids are 1,2,3,4
#define CYGNUM_UITRON_TASKS_INITIALLY 4 // they all exist at start

#define CYGDAT_UITRON_TASK_EXTERNS            \
extern "C" void startup( unsigned int );      \
extern "C" void worktask( unsigned int );     \
extern "C" void lowtask( unsigned int );        \
static char stack1[ CYGNUM_UITRON_STACK_SIZE ], \
            stack2[ CYGNUM_UITRON_STACK_SIZE ], \
            stack3[ CYGNUM_UITRON_STACK_SIZE ], \
            stack4[ CYGNUM_UITRON_STACK_SIZE ];

#define CYGDAT_UITRON_TASK_INITIALIZERS \
 CYG_UIT_TASK("main task", 8, startup,  &stack1, sizeof( stack1 )), \
 CYG_UIT_TASK("worker 2" , 9, worktask, &stack2, sizeof( stack2 )), \
 CYG_UIT_TASK("worker 3" , 9, worktask, &stack3, sizeof( stack3 )), \
 CYG_UIT_TASK("low task" ,20, lowtask,  &stack4, sizeof( stack4 )), \

So this example has all four tasks statically configured to exist, ready to run, from the start of time. The “main task” runs a routine called startup() at priority 8. Two “worker” tasks run both a priority 9, and a “low priority” task runs at priority 20 to do useful non-urgent background work.

Task ID | Exists at | Function | Priority | Stack   | Stack
 number |  startup  |  entry   |          | address | size
--------+-----------+----------+----------+---------+----------
   1    |    Yes    |  startup |    8     | &stack1 | CYGNUM…
   2    |    Yes    | worktask |    9     | &stack2 | CYGNUM…
   3    |    Yes    | worktask |    9     | &stack3 | CYGNUM…
   4    |    Yes    |  lowtask |   20     | &stack4 | CYGNUM…
--------+-----------+----------+----------+---------+----------

Q: How can I create µITRON tasks in the program?

You must provide free slots in the task table in which to create new tasks, by configuring the number of tasks existing initially to be smaller than the total. For a task ID which does not initially exist, it will be told what routine to call, and what priority it is, when the task is created. But you must still set aside memory for the task to use for its stack, and give it a name during initialization. For example:

#define CYGNUM_UITRON_TASKS 4           // valid task ids are 1-4
#define CYGNUM_UITRON_TASKS_INITIALLY 1 // only task #1 exists

#define CYGDAT_UITRON_TASK_EXTERNS \
extern "C" void startup( unsigned int ); \
static char stack1[ CYGNUM_UITRON_STACK_SIZE ], \
            stack2[ CYGNUM_UITRON_STACK_SIZE ], \
            stack3[ CYGNUM_UITRON_STACK_SIZE ], \
            stack4[ CYGNUM_UITRON_STACK_SIZE ];

#define CYGDAT_UITRON_TASK_INITIALIZERS \
   CYG_UIT_TASK( "main", 8, startup, &stack1, sizeof( stack1 ) ), \
   CYG_UIT_TASK_NOEXS( "slave",      &stack2, sizeof( stack2 ) ), \
   CYG_UIT_TASK_NOEXS( "slave2",     &stack3, sizeof( stack3 ) ), \
   CYG_UIT_TASK_NOEXS( "slave3",     &stack4, sizeof( stack4 ) ), \

So tasks numbered 2,3 and 4 have been given their stacks during startup, though they do not yet exist in terms of cre_tsk() and del_tsk() so you can create tasks 2–4 at runtime.

Task ID | Exists at | Function | Priority | Stack   | Stack
 number |  startup  |  entry   |          | address | size
--------+-----------+----------+----------+---------+----------
   1    |    Yes    |  startup |    8     | &stack1 | CYGNUM…
   2    |    No     |   N/A    |   N/A    | &stack2 | CYGNUM…
   3    |    No     |   N/A    |   N/A    | &stack3 | CYGNUM…
   4    |    No     |   N/A    |   N/A    | &stack4 | CYGNUM…
--------+-----------+----------+----------+---------+----------

(you must have at least one task at startup in order that the system can actually run; this is not so for other uITRON object types)

Q: Can I have different stack sizes for µITRON tasks?

Simply set aside different amounts of memory for each task to use for its stack. Going back to a typical default setting for the µITRON tasks, the definitions in pkgconf/uitron.h might look like this:

#define CYGDAT_UITRON_TASK_EXTERNS \
extern "C" void task1( unsigned int ); \
extern "C" void task2( unsigned int ); \
extern "C" void task3( unsigned int ); \
extern "C" void task4( unsigned int ); \
static char stack1[ CYGNUM_UITRON_STACK_SIZE ], \
            stack2[ CYGNUM_UITRON_STACK_SIZE ], \
            stack3[ CYGNUM_UITRON_STACK_SIZE ], \
            stack4[ CYGNUM_UITRON_STACK_SIZE ];

#define CYGDAT_UITRON_TASK_INITIALIZERS \
  CYG_UIT_TASK( "t1", 1, task1, &stack1, CYGNUM_UITRON_STACK_SIZE ), \
  CYG_UIT_TASK( "t2", 2, task2, &stack2, CYGNUM_UITRON_STACK_SIZE ), \
  CYG_UIT_TASK( "t3", 3, task3, &stack3, CYGNUM_UITRON_STACK_SIZE ), \
  CYG_UIT_TASK( "t4", 4, task4, &stack4, CYGNUM_UITRON_STACK_SIZE )

Note that CYGNUM_UITRON_STACK_SIZE is used to control the size of the stack objects themselves, and to tell the system what size stack is being provided.

Suppose instead stack sizes of 2000, 1000, 800 and 800 were required: this could be achieved by using the GUI config tool to edit these options, or editting the .ecc file to get these results in pkgconf/uitron.h:

#define CYGDAT_UITRON_TASK_EXTERNS \
extern "C" void task1( unsigned int ); \
extern "C" void task2( unsigned int ); \
extern "C" void task3( unsigned int ); \
extern "C" void task4( unsigned int ); \
static char stack1[ 2000 ], \
            stack2[ 1000 ], \
            stack3[  800 ], \
            stack4[  800 ];

#define CYGDAT_UITRON_TASK_INITIALIZERS \
      CYG_UIT_TASK( "t1", 1, task1, &stack1, sizeof( stack1 ) ), \
      CYG_UIT_TASK( "t2", 2, task2, &stack2, sizeof( stack2 ) ), \
      CYG_UIT_TASK( "t3", 3, task3, &stack3, sizeof( stack3 ) ), \
      CYG_UIT_TASK( "t4", 4, task4, &stack4, sizeof( stack4 ) )

Note that the sizeof() operator has been used to tell the system what size stacks are provided, rather than quoting a number (which is difficult for maintenance) or the symbol CYGNUM_UITRON_STACK_SIZE (which is wrong).

We recommend using (if available in your release) the stacksize symbols provided in the architectural HAL for your target, called CYGNUM_HAL_STACK_SIZE_TYPICAL and CYGNUM_HAL_STACK_SIZE_MINIMUM. So a better (more portable) version of the above might be:

#define CYGDAT_UITRON_TASK_EXTERNS \
extern "C" void task1( unsigned int ); \
extern "C" void task2( unsigned int ); \
extern "C" void task3( unsigned int ); \
extern "C" void task4( unsigned int ); \
static char stack1[ CYGNUM_HAL_STACK_SIZE_TYPICAL + 1200 ], \
            stack2[ CYGNUM_HAL_STACK_SIZE_TYPICAL +  200 ], \
            stack3[ CYGNUM_HAL_STACK_SIZE_TYPICAL        ], \
            stack4[ CYGNUM_HAL_STACK_SIZE_TYPICAL        ];

#define CYGDAT_UITRON_TASK_INITIALIZERS \
      CYG_UIT_TASK( "t1", 1, task1, &stack1, sizeof( stack1 ) ), \
      CYG_UIT_TASK( "t2", 2, task2, &stack2, sizeof( stack2 ) ), \
      CYG_UIT_TASK( "t3", 3, task3, &stack3, sizeof( stack3 ) ), \
      CYG_UIT_TASK( "t4", 4, task4, &stack4, sizeof( stack4 ) )