Difference between revisions of "OCEOS Directives Reference"

From wiki
Jump to navigation Jump to search
 
(4 intermediate revisions by the same user not shown)
Line 54: Line 54:
<p>The values if the structure members are overwritten by the application developer #defines for NUMBER_OF_TASKS, NUMBER_OF_MUTEXES, ...etc.</p>
<p>The values if the structure members are overwritten by the application developer #defines for NUMBER_OF_TASKS, NUMBER_OF_MUTEXES, ...etc.</p>


  <syntaxhighlight lang="C" line>
  <syntaxhighlight lang="C">
  /*
  /*
  * Application Configuration Structure
  * Application Configuration Structure
Line 732: Line 732:


=== oceos_mutex_create ===
=== oceos_mutex_create ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  enum DIRECTIVE_STATUS  oceos_mutex_create(
  /**
     unsigned int,      // mutex ID, used as index, must be in range 0 to 62
  * Creates a mutex with priority ceiling
     U8_t               // mutex priority ceiling
  *
);
  * This function should only be called after OCEOS is initialised with
  *  oceos_init() and before initialisation ends with oceos_init_finish()
  *  and scheduling starts with oceos_start()
  *
  * @param id      Mutex ID, must be in range 0 to 62
  * @param priority Mutex priority ceiling from 1 to 254 (high to low task priority)
  * @return enum DIRECTIVE_STATUS
  *        INCORRECT_STATE  System Meta pointer is NULL or corrupt
  *        INVALID_ID      Failed mutex id check
  *        INVALID_NAME    Mutex ID is not available
  *        INVALID_NUMBER  Mutex priority is not valid
  *        TOO_MANY        Mutex configuration max value reached
  *        SUCCESSFUL      All is OK
  */
enum DIRECTIVE_STATUS  oceos_mutex_create(
     const unsigned int id,      // mutex ID, must be in range 0 to 62
     const U8_t priority        // mutex priority ceiling
);
</syntaxhighlight>
</syntaxhighlight>


Line 755: Line 772:


=== oceos_mutex_wait ===
=== oceos_mutex_wait ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  enum DIRECTIVE_STATUS  oceos_mutex_wait(
  /**
     unsigned int       // mutex ID, must be in range 0 to 62
  * Wait on mutex with ID.
);
  * If task succeeds in grabbing mutex, system level priority rises to
  * priority of this mutex, so tasks with lower or equal priority will
  * not be able to start till mutex is released.
  *
  * This should only be called after OCEOS scheduling has started.
  *
  * @param id  Mutex ID, must be in range 0 to 62
  * @return enum DIRECTIVE_STATUS
  *        NOT_CONFIGURED  System Meta pointer is NULL or corrupt
  *        INTERNAL_ERROR  Mutex Dynamic pointer is NULL or
  *                          Scheduling is not started or
  *                          Inconsistent Mutex state (check log)
  *        INVALID_ID      Mutex ID is out of range
  *        SUCCESSFUL      All is OK
  */
enum DIRECTIVE_STATUS  oceos_mutex_wait(
     const unsigned int id      // mutex ID, must be in range 0 to 62
);
</syntaxhighlight>
</syntaxhighlight>


Line 773: Line 807:


=== oceos_mutex_signal ===
=== oceos_mutex_signal ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  enum DIRECTIVE_STATUS  oceos_mutex_signal(
  /**
     unsigned int       // mutex ID, must be in range 0 to 62
* Signal a mutex with ID
);
*
* This should only be called after OCEOS scheduling has started.
*
* @param id  Mutex ID, must be in range 0 to 62
* @return enum DIRECTIVE_STATUS
*        NOT_CONFIGURED  System Meta pointer is NULL or corrupt
*        INTERNAL_ERROR  Mutex Dynamic pointer is NULL or
*                          Scheduling is not started or
*                          Inconsistent Mutex state (check log)
*        INVALID_ID      Mutex ID is out of range
*        SUCCESSFUL      All is OK
*/
enum DIRECTIVE_STATUS  oceos_mutex_signal(
     const unsigned int id      // mutex ID, must be in range 0 to 62
);
</syntaxhighlight>
</syntaxhighlight>


Line 789: Line 837:


=== oceos_mutex_get_value ===
=== oceos_mutex_get_value ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  unsigned int            oceos_mutex_get_value(
  /**
     unsigned int       // mutex ID, must be in range 0 to 62
* Get current value of mutex with ID
);
*
* @param id Mutex ID, must be in range 0 to 62
* @return unsigned int
*          0              => Unavailable
*          1              => Available
*          STATUS_INVALID  => (== 0xffffffff) problem
*/
unsigned int            oceos_mutex_get_value(
     const unsigned int id      // mutex ID, must be in range 0 to 62
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,340: Line 1,397:
   RMW_ACTION,              // Read-modify-write Action
   RMW_ACTION,              // Read-modify-write Action
  };
  };
</syntaxhighlight>
<syntaxhighlight lang="C" line>
/*
  * Add job to timed action Q
  * return -1 if fail
  */
</syntaxhighlight>
</syntaxhighlight>


=== oceos_timed_jobs_number ===
=== oceos_timed_jobs_number ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  int   oceos_timed_jobs_number();         // get number of jobs on timed actions queue
  /**
* Get number of Jobs (request to start the task) waiting on timed action queue.
* These jobs are added by oceos_task_timed_start() or
* oceos_dataq_read_restart_timeout when data is not available (NULL) or
* oceos_sem_wait_restart_timeout when counting semaphore is 0
*
* @return int
*      -1      Timed actions manager is not initialised
*      number  If all ok
*/
int oceos_timed_jobs_number(void);       // number of jobs on timed actions queue
 
</syntaxhighlight>
</syntaxhighlight>


Line 1,360: Line 1,422:


=== oceos_timed_jobs_remove ===
=== oceos_timed_jobs_remove ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  int oceos_timed_jobs_remove(       // remove all jobs of this task from timed actions queue
  /**
                             U32_t);                        // task ID
* Remove job from timed action queue
  * When task started using oceos_task_timed_start(),
* this directive requires pointer to store ID
* of job that put on the timed action queue.
* This job can be removed later using this directive and stored job ID.
*
* @param U32_t job_id  Job ID
* @return enum DIRECTIVE_STATUS
*          NOT_CONFIGURED  Timed actions manager is not initialized
*          INVALID_ID      Job ID is not valid
*          UNSATISFIED    Failed to stop timer
*          INTERNAL_ERROR  Failed to restart timed actions
*          SUCCESSFUL      All OK
*/
enum DIRECTIVE_STATUS  oceos_timed_jobs_remove(
    U32_t job_id                             // Job ID
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,375: Line 1,453:


=== oceos_timed_jobs_reset ===
=== oceos_timed_jobs_reset ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  int oceos_timed_jobs_reset();          // clear all timed jobs from timed actions queue
  /**
  * Remove all jobs (requests to start a task) from timed actions queue
* These jobs were added by oceos_task_timed_start() or
* oceos_dataq_read_restart_timeout when data is not available (NULL) or
* oceos_sem_wait_restart_timeout when counting semaphore is 0
*
* @return enum DIRECTIVE_STATUS
*      NOT_CONFIGURED  Timed actions manager is not initialized
*      UNSATISFIED    Failed to stop timer
*      INCORRECT_STATE Timed queue is empty
*      SUCCESSFUL      All OK
*
*/
enum DIRECTIVE_STATUS  oceos_timed_jobs_reset(void);
 
</syntaxhighlight>
</syntaxhighlight>


Line 1,389: Line 1,481:


=== oceos_timed_output_add ===
=== oceos_timed_output_add ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  int oceos_timed_output_add(            // add output to timed action queue, return action ID
  /**
                            const enum ACTION_TYPE,         // Action type
* Add output action to timed actions queue :
                            const U64_t,                   // system time at which action should be done
* 1. Create timed output action
                            const U32_t,                   // forward tolerance for time
*  2. Stop timer
                            const U32_t,                   // backward tolerance for time
*  3. Add action to action_array
                            const adrs_t,                   // output address
*  4. Start timer or execute if time is right
                            const U32_t,                   // output value
*
                            const U32_t,                    // output mask
* Returns timed action ID that can be used later to remove
);
* if need by oceos_timed_output_remove
*
* @param enum ACTION_TYPE action_type  Action type
* @param U64_t act_time                System time at which action should be done
* @param U32_t before_time            Forward tolerance for time
* @param U32_t after_time              Backward tolerance for time
* @param adrs_t address                Output address
* @param U32_t value                  Output value
* @param U32_t mask                    Output mask
* @return int
*      -1          Fail
*      action ID   All OK
*/
int oceos_timed_output_add(
    const enum ACTION_TYPE action_type,// Action type
    const U64_t act_time,             // system time at which action should be done
    const U32_t before_time,           // forward tolerance for time
    const U32_t after_time,           // backward tolerance for time
    const adrs_t address,             // output address
    const U32_t value,                 // output value
    const U32_t mask                  // output mask
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,415: Line 1,528:


=== oceos_timed_output_number ===
=== oceos_timed_output_number ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  int oceos_timed_output_number();        // get number of timed outputs on timed actions queue
  /**
  * Get number of timed output actions waiting on timed action queue.
* These timed output actions are created by oceos_timed_output_add
*
* @return int
*      -1      Timed actions manager is not initialised
*      number  All ok
*/
int oceos_timed_output_number(void);
</syntaxhighlight>
</syntaxhighlight>


Line 1,426: Line 1,547:


=== oceos_timed_output_remove ===
=== oceos_timed_output_remove ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  enum DIRECTIVE_STATUS   oceos_timed_output_remove(         // remove this output from action queue
  /**
                            const U8_t                     // Index in action array, returned when action was created
* Remove timed output action from timed actions queue.
                            );
* When action is created, action index should be stored
* to remove if need
*
* @param U8_t action_index Action index when created
* @return enum DIRECTIVE_STATUS
*      NOT_CONFIGURED      Timed actions manger is not initialized
*      INVALID_ID          Action index is not valid
*      OBJECT_WAS_DELETED  Action with action_index is not available
*      SUCCESSFUL          All OK
*/
enum DIRECTIVE_STATUS oceos_timed_output_remove(
    const U8_t action_index    // Index in action array, returned when action was created
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,446: Line 1,579:


=== oceos_timed_output_reset ===
=== oceos_timed_output_reset ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  enum DIRECTIVE_STATUS oceos_timed_output_reset();         // clear all timed outputs from action queue
  /**
* Remove all timed output actions from timed actions queue
* These timed output actions were created by oceos_timed_output_add
*
* @return enum DIRECTIVE_STATUS
*      NOT_CONFIGURED  Timed actions manager is not initialized
*      UNSATISFIED      Failed to stop timer
*      INCORRECT_STATE  Timed q is empty
*      SUCCESSFUL      All OK
*/
enum DIRECTIVE_STATUS oceos_timed_output_reset(void);
</syntaxhighlight>
</syntaxhighlight>


Line 1,642: Line 1,785:


=== oceos_log_add_entry ===
=== oceos_log_add_entry ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  // add a log entry (32-bit time is included automatically)
  /**
  enum DIRECTIVE_STATUS  oceos_log_add_entry(
* Add a log entry (32-bit time is included automatically)
     enum LOG_ENTRY_TYPE,// 8 bits
*
     const unsigned int // information (24 bits)
* Overwrites the oldest unread entry if the log is full.
);
*
* @param type  Log type (8 bits)
* @param info  Extra information (24 bits)
* @return enum DIRECTIVE_STATUS
*          NOT_CONFIGURED  Log check state failed or
*                          Logging manager is not initialised
*          INVALID_NUMBER  Parameter info is more than 24 bits
*          INCORRECT_STATE Problem with read/write indexes
*          SUCCESSFUL      All OK
*/
enum DIRECTIVE_STATUS  oceos_log_add_entry(
     enum LOG_ENTRY_TYPE type,// 8 bits
     const unsigned int info // information (24 bits)
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,666: Line 1,822:


=== oceos_log_remove_entry ===
=== oceos_log_remove_entry ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  // read and remove the oldest unread log entry
  /**
  enum DIRECTIVE_STATUS  oceos_log_remove_entry(
* Read and remove the oldest unread log entry
     struct log_entry * const
  *
);
* If the log is not empty use the entry at the read index to update the value
* at the output pointer, returning SUCCESSFUL.
*
* If the log is empty, set the value at the output pointer to LOG_LOG_NOT_VALID_ENTRY
* and return UNSATISFIED
*
* The read index is incremented in a circular fashion.
*
* @param outputPtr Pointer to Log entry to return log information
* @return enum DIRECTIVE_STATUS
*          NOT_CONFIGURED  Log check state failed or
*                          Logging is not setup
*          INCORRECT_STATE Problem with read/write indexes
*          UNSATISFIED    Log is empty
*          SUCCESSFUL      All OK
*/
enum DIRECTIVE_STATUS  oceos_log_remove_entry(
     struct log_entry * const outputPtr
);
</syntaxhighlight>
</syntaxhighlight>


Line 1,685: Line 1,859:


=== oceos_log_get_indexed_entry ===
=== oceos_log_get_indexed_entry ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  // read the log entry at the given index
  /**
  enum DIRECTIVE_STATUS  oceos_log_get_indexed_entry(
* Read the log entry at the given index
     const unsigned int,
  * Returns the entry at the specified position in the log.
     struct log_entry * const
* The entry is not removed and the log and log indices are not changed.
);
* (intended to allow the log be examined for example after reset)
*
* @param index  Log entry index
* @param struct log_entry * const outputPtr log entry ptr
* @return enum DIRECTIVE_STATUS
*          NOT_CONFIGURED  Logging is not set up
*          INVALID_NUMBER  Index is out of range
*          INVALID_ADDRESS  OutputPtr is NULL
*          SUCCESSFUL      All OK
*/
enum DIRECTIVE_STATUS  oceos_log_get_indexed_entry(
     const unsigned int index,
     struct log_entry * const outputPtr
);
</syntaxhighlight>
</syntaxhighlight>
<p>This directive returns the entry at the specified position in the log. The entry is not removed and the log and log indices are not changed. </p><p>It is intended to allow the log be examined for example after reset. If the index is out of range, set the value at the output pointer to NOT_VALID_ENTRY and return UNSATISFIED.</p>
<p>This directive returns the entry at the specified position in the log. The entry is not removed and the log and log indices are not changed. </p><p>It is intended to allow the log be examined for example after reset. If the index is out of range, set the value at the output pointer to NOT_VALID_ENTRY and return UNSATISFIED.</p>
Line 1,705: Line 1,892:


=== oceos_log_reset ===
=== oceos_log_reset ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  // clear all log entries and reset to empty
/**
  enum DIRECTIVE_STATUS oceos_log_reset();
  * Clear all log entries and reset to empty
  *
* Set all log entries to LOG_LOG_NOT_VALID_ENTRY and the log to empty, with the
* read and write indices set to 0. and function called flag 0
*
* Does not affect the system status variable
* Does not affect the context switch log
*
* @return enum DIRECTIVE_STATUS
*          NOT_CONFIGURED  System Meta pointer is NULL or
*                          Fixed data start sentinel is corrupt
*                          System Log pointer is NULL or
*                          Logging manager is not initialised
*          UNSATISFIED    Log area end sentinel is corrupt
*          SUCCESSFUL      All OK
*/
enum DIRECTIVE_STATUS   oceos_log_reset(void);
</syntaxhighlight>
</syntaxhighlight>


Line 1,721: Line 1,924:


=== oceos_log_get_size ===
=== oceos_log_get_size ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  // get the number of log entries
  /**
  unsigned int oceos_log_get_size();
* Get the number of log entries
  *
* Return the number of entries in the log
* according to the current values of the read and write indices.
*
* The value will be zero if the current values of the read and write indices
* are the same.
*
* @return unsigned int
*              LOG_INVALID_SIZE    Log state is invalid or
*                                  Logging manager is not initialised or
*                                  Problem with read/write indexes
*              log size            All OK
*/
unsigned int           oceos_log_get_size(void);
</syntaxhighlight>
</syntaxhighlight>


Line 1,731: Line 1,948:


Status returns are:
Status returns are:
Intiger (number of entries in the log)
Int (number of entries in the log)
* LOG_INVALID_SIZE (log data area corrupt)
* LOG_INVALID_SIZE (log data area corrupt)
* NOT_CONFIGURED (log data area not initialised)
* NOT_CONFIGURED (log data area not initialised)
Line 2,085: Line 2,302:


=== oceos_system_state_get ===
=== oceos_system_state_get ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  // returns the value of the system state variable
  /**
  U32_t  oceos_system_state_get();
* Returns the value of the system state variable
  *
* @return U32_t
*          system state or
*          STATUS_INVALID Detected a problem
  */
U32_t                  oceos_system_state_get(void);
 
</syntaxhighlight>
</syntaxhighlight>


Line 2,097: Line 2,321:


=== oceos_system_state_set ===
=== oceos_system_state_set ===
<syntaxhighlight lang="C" line>
<syntaxhighlight lang="C">
  enum DIRECTIVE_STATUS  oceos_system_state_set(
  /**
     U32_t               // new value of system state variable
* Sets system state variable and
);
* remember previous setting
*
* @param U32_t new_state New value of system state variable
* @return enum DIRECTIVE_STATUS
*          INCORRECT_STATE  System Meta pointer is NULL or
*                          Log pointer is NULL
*          SUCCESSFUL      All OK
*
*/
enum DIRECTIVE_STATUS  oceos_system_state_set(
     U32_t new_state              // new value of system state variable
);
</syntaxhighlight>
</syntaxhighlight>



Latest revision as of 17:12, 11 March 2022

Introduction

Directives Reference

OCEOS provides a set of directives to the application developer. These directives are detailed below.

Initialisation Manager

oceos_init

/**
 * Initialises OCEOS and  must be the first directive used.
 *
 * Initialises the OCEOS system meta data using the information supplied in
 * the application configuration and prepares for creation of tasks etc.
 *
 * The values provided in the application configuration are checked as much
 * as possible before being used.
 *
 * The system log also is created here.
 *
 * This function sets up sysMetaPtr and init_meta_ptr and must have completed
 * successfully before tasks etc. can be created.
 *
 * It disables interrupts and leaves them disabled, they will be re-enabled to
 * the previous level after all tasks etc. have been created and
 * oceos_init_finish() has completed successfully.
 *
 * @param app_config Pointer to configuration struct
 * @return enum DIRECTIVE_STATUS
 *          INVALID_ADDRESS         If Failed on memory initialisation (write to memory)
 *          UNSATISFIED             If Failed on Log area initialisation
 *          SUCCESSFUL              If All OK
 *    FOR ARM :
 *          NOT_CONFIGURED          If System frequency is not set or
 *                                  oceos_SysTick_Handler is not in trap table or
 *                                  Timer start called without initialisation (check log)
 *    FOR SPARC:
 *          NOT_CONFIGURED          If System timer handler is NULL or
 *                                  System frequency is not set
 *          INVALID_ID              If System sub timer ID is not valid
 *          FAILED_TO_SET_REGISTER  If Failed on register write (check log)
 *
 */
enum DIRECTIVE_STATUS   oceos_init(
    struct application_configuration
);

This directive initialises the OCEOS fixed data structures. It is called from application_init() in config.c.

Parameters in: application_configuration structure defined below (oceos.h).

The values if the structure members are overwritten by the application developer #defines for NUMBER_OF_TASKS, NUMBER_OF_MUTEXES, ...etc.

 /*
 * Application Configuration Structure
 *
 * The application must create a structure of this type, initialise its fields
 * according to the application then pass it to oceos_init().
 *
 * When initialising the configuration structure fields It is recommended that
 * the values be predefined (#define) as constants as indicated below.
 *
 * If these are defined before oceos_areas.h is included then oceos_areas.h
 * will provide the sizes and field offsets for the log area and the fixed
 * data area. See oceos_areas.h for more details.
 *
 * It is recommended that fields that are not used are set to 0 or to NULL.
 *
 * The configuration structure is not used subsequently by OCEOS,
 * after oceos_init() completes the space used for it can be freed.
 *
 */
 struct application_configuration {

  /* addresses to be used used for internal OCEOS data */
  U32_t  log_address;
  U32_t  fixed_data_address;
  U32_t  dynamic_data_address;

  /* used in relation to the system stack */
  U32_t  stack_start_address;
  U32_t  stack_lower_bound_address;

  /* user defined functions to handle invalid system state and log 3/4 full */
  void   (*system_error_function)();
  void   (*log_full_function)();

  /* basic application component numbers */
  unsigned int  number_of_tasks               :8;  // NUMBER_OF_TASKS
  unsigned int  number_of_readyQ_entries      :8;  // NUMBER_OF_READYQ_ENTRIES
  unsigned int  log_number_of_entries         :10; // NUMBER_OF_LOG_ENTRIES
  unsigned int  number_of_mutexes             :6;  // NUMBER_OF_MUTEXES

/* 32-bit word */

  // very wasteful, but this structure is transient
  unsigned int  CS_log_entries_base2          :4;
  // In SPARC system time is taken from two chained timers that initialised and running without interrupting;
  // In ARM SysTick is used to get system time, every time timer overflows the counter is incremented, using interrupts.
  // For ARM, for example, user can implement own system time and use chained timers or interrupt at larger intervals
  // and SysTick will not be used at all to save power;
  unsigned int  use_oceos_system_time         :1;  // If true system timer will be initialized
  // For SPARC only: system timer is chained with next timer, microseconds, no interrupts
  unsigned int  sys_time_timer_index          :4;
  // whether interrupt nesting is enabled
  unsigned int  interrupt_nesting_enabled     :1;
  unsigned int  number_of_counting_semaphores :6;  // NUMBER_OF_SEMAPHORES
  unsigned int  number_of_data_queues         :6;  // NUMBER_OF_DATAQS
  unsigned int  timed_actions_queue_size      :8;  // NUMBER_OF_ACTION_ENTRIES
  unsigned int                                :2;  // SPARE

};

Status returns:

  • SUCCESSFUL
  • INVALID_ADDRESS (incorrect address)
  • INVALID_SIZE (incorrect number of entries)
  • UNSATISFIED (initialisation structure or log area initialised incorrectly)
  • NOT_CONFIGURED (timer not initialised)

oceos_init_finish

/**
 * Finalizes the fixed data array after all tasks etc. have been created
 *
 * All information needed to start OCEOS can be found in the fixed data area
 * when oceos_init_finish() has completed.
 *
 * Once all tasks etc. have been created oceos_init_finish() checks the
 * accumulated information against the application configuration data
 * stored in the fixed meta structure.
 *
 * oceos_init_finish also calculates the size of the dynamic data area and
 * checks it does not overlap with the system stack, the system log, or the
 * fixed data, and if all ok sets up pointers to the various dynamic area
 * components in the fixed data.
 *
 * It then adds an XOR checksum to the fixed data area, and checks that this
 * is followed by FILLER, which it replaces with the end sentinel.
 *
 * On successful completion it restores the interrupt level to its value
 * before oceos_init() was called, otherwise interrupts remain disabled.
 *
 * @return enum DIRECTIVE_STATUS
 *          INCORRECT_STATE     If system Meta pointer is NULL or
 *                              Init pointer is NULL or
 *                              Start up phase check fail or
 *                              Fixed data checksum failed (check log)
 *          INVALID_NAME        If Start Sentinel for fixed area is corrupt or
 *                              Start Sentinel for dynamic area is corrupt
 *          INVALID_NUMBER      If Configuration and actual parameters do not match
 *                              (In configuration file was defined 5 tasks, but created less)
 *          SUCCESSFUL          If All OK
 */
 enum DIRECTIVE_STATUS   oceos_init_finish();

This directive finishes intitialisation for the fixed data area. It is called after tasks, metexes and semaphores have been created.

Status returns:

  • SUCCESSFUL
  • INVALID_NAME (data is corrupt)
  • INCORRECT_STATE (sysMeta is null or initialisation incorrect)
  • INVALID_NUMBER (fixed data invalid)

oceos_start

 /**
 * Starts OCEOS scheduling
 *
 * Create dynamic OCEOS structures and start scheduling
 *
 * This function should only be called once
 * and only after oceos_init()
 * and oceos_init_finish() have
 *  been called successfully.
 * Normally this function does not return.
 * If a problem is detected the function
 * terminates and returns an appropriate
 * DIRECTIVE_STATUS code, with interrupts disabled.
 *
 * @param fixed_array_ptr   Pointer to fixed data array
 * @param start_task        Task ID
 * @param data_ptr          Pointer to data to be passed to the task
 * @return enum DIRECTIVE_STATUS
 *          INTERNAL_ERROR  If fixed_array_ptr is NULL or
 *                          fixed_array_ptr start sentinel is corrupt or
 *                          Size of fixed area is not correct or
 *                          fixed_array_ptr checksum failed
 *          INCORRECT_STATE If Dynamic area setup failed
 */
 enum DIRECTIVE_STATUS   oceos_start(
    const U32_t * const,// pointer to fixed data array
    const unsigned int, // taskID. If invalid CPU enters sleep mode
    void * const        // pointer to data to be passed to task
 );

This directive starts OCEOS scheduling and is called after oceis_init_finish(). Parameters passed are:

  • Pointer to the fixed data area (usually fixed_data declared in config.c)
  • The task ID of the first task to be executed. Usually this task will start other tasks necessary for the application.
  • Pointer to be passed to the task above. A null pointer can be passed if the task does not use the pointer.

Status returns:

  • SUCCESSFUL
  • INTERNAL_ERROR (fixed data null or corrupt)
  • INCORRECT_STATE (dynamic structure initialisation failure)

oceos_exit

 /**
 * Ends scheduling and exit from oceos_start.
 * No task can be started after this.
 * OCEOS will exit when the current job ends.
 * An idle task in an endless loop should check that scheduling is enabled.
 *
 * @return DIRECTIVE_STATUS
 *          INCORRECT_STATE  If System Meta pointer is NULL or
 *                           Initialisation is not finished or
 *                           Scheduling was not started
 *          INTERNAL_ERROR   If Dynamic Meta pointer is NULL
 *          SUCCESSFUL       If All OK
 */
 enum DIRECTIVE_STATUS   oceos_exit();

This directive exits OCEOS terminating all jobs. Status returns:

  • SUCCESSFUL
  • INTERNAL_ERROR (dynamic meta pointer is null)
  • INCORRECT_STATE (initialisation is not finished yet or scheduling was not started)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)

oceos_CPU_sleep

 /**
 * Puts CPU in sleep mode.
 * User must to be sure that interrupts are enabled.
 *
 * @return enum DIRECTIVE_STATUS
 *          SUCCESSFUL  When CPU returns from sleep
 */
 enum DIRECTIVE_STATUS   oceos_CPU_sleep();

This directive puts the CPU in sleep mode which may be required to save power. The watchdog will be disabled and only an interrupt will wake the CPU. If the application is required to wake the processor in the future then a timed action can be used.

Status returns:

  • SUCCESSFUL

Task Manager

Task Status

 /* A task's status is returned as below if queried */
 enum TASK_STATUS {
    TASK_DISABLED,      // cannot be scheduled, and no current jobs
    TASK_ENABLED,       // can be scheduled, but no current jobs
    TASK_INVALID,       // task does not exist
  };

Valid task states are listed above.

oceos_task_create

 /**
 * Create a task.
 * Can only be use before OCEOS starts.
 * Stores the task information in the OCEOS fixed data area.
 *
 * @param taskID                Unique task ID from 0 to TASKS_MAX_ID
 * @param priority              TASK_MAX_PRIORITY to TASK_MIN_PRIORITY
 * @param threshold             TASK_MAX_PRIORITY to priority
 * @param jobs_max              Number of 1 to JOBS_MAX
 * @param FP_used               whether uses floating point hardware
 * @param initially_enabled     whether task enabled initially
 * @param start_function        task start address
 * @param end_function          start of task end function
 * @param time_deadline         maximum time to complete (ignore if 0)
 * @param time_mininterstart    minimum time between start requests
 * @return enum DIRECTIVE_STATUS
 *          INCORRECT_STATE  Init Meta Pointer is NULL or
 *                           Pointer to Task fixed data is NULL
 *          TOO_MANY         Exceeds number of MAX Tasks or
 *                           Exceeds number of MAX Jobs
 *          INTERNAL_ERROR   System Meta pointer is NULL or
 *                           System Meta pointer start sentinel is corrupt
 *          INVALID_ID       Failed on task ID check
 *          INVALID_NAME     Task ID is already in use
 *          INVALID_PRIORITY Failed on task priority check
 *          INVALID_NUMBER   Failed on task threshold check
 *          INVALID_SIZE     Failed on task max job check
 *          SUCCESSFUL       All is OK
 */
 enum DIRECTIVE_STATUS   oceos_task_create(
    unsigned int,             // unique, 0 to TASKS_MAX_ID
    U32_t,           // TASK_MAX_PRIORITY to TASK_MIN_PRIORITY
    U32_t,          // TASK_MAX_PRIORITY to priority
    U32_t,           // 1 to JOBS_MAX
    BOOLE_t,            // whether uses floating point hardware
    BOOLE_t,  // whether task enabled initially
    void (void *),  // task start address
    void (void *),    // start of task end function
    U32_t,       // maximum time to complete (ignore if 0)
    U32_t   // minimum time between start requests
 );

This directive populates the data structures for a task. It should be called after application_init() and before oceos_init_finish()

It must be called for each task otherwise oceos_start(..) will return an error.

Parameters in:

  • integer specifying task ID (0 to 254 and specified a enumerated type in config.h)
  • integer specifying task priority (1 to 254 where 1 is the highest priority and 254 the lowest)
  • integer specifying pre-emption threshold (1 to 254). Only higher priority tasks can pre-empt.
  • integer specifying the maximum number of jobs for this task (1 to 15)
  • boolean specifying if floating point is used (false=not used). Enabling floating point causes the floating point registers to be saved when the task in pre-empted.
  • boolean specifying if the task is initially enabled (false is disabled)
  • pointer to function to be executed on task start
  • pointer to function to be executed when task ends
  • integer specifying the maximum time (in us) that the task can run (0 = no limit)
  • integer specifying the minimum time between task finishing and being restarted (0 = no minimum time)

Status returns:

  • SUCCESSFUL
  • TOO_MANY (NUMBER_OF_TASKS in config.h have already been created)
  • INVALID_NAME (task ID already used)
  • INVALID_ID (invalid task ID)
  • INVALID_PRIORITY (incorrect priority)
  • INVALID_SIZE (incorrect number of jobs)
  • INVALID_NUMBER (incorrect pre-emption threshold)
  • INCORRECT_STATE (not in initialisation state)
  • INTERNAL_ERROR (data area not setup)

oceos_task_start

 /**
 *  Starting a task involves three main stages
 *      1 setting up a job
 *          finding a free job entry if available
 *          setting up this entry including passing it the data void *
 *
 *      2 placing the job on readyQ
 *
 *      3 doing a context switch
 * This function should only be called after OCEOS is initialised
 *       and the dynamic task and job structures set up
 *
 *  When used from an interrupt handler,
 *  the context switch is only done
 *  after all nested interrupts have unwound
 *
 * @param taskID Task ID must be in range 0 to 254
 * @param ptr    Pointer to task data
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED              System Meta pointer is NULL or
 *                                      Fixed data start sentinel is corrupt
 *          INCORRECT_STATE             One of the System Meta pointers is NULL
 *          INVALID_ID                  Failed on task ID check
 *          INCORRECT_STATE             Tried to start disabled task
 *          TOO_MANY                    Max number of jobs(requests to start) for task is exceeded
 *          FAILED_TO_PUT_JOB_ON_REDYQ  Failed to put job for task on ready Q
 *          SUCCESSFUL                  All is OK
 */
 enum DIRECTIVE_STATUS   oceos_task_start(
    const unsigned int, // task ID, must be in range 0 to 254
    void * const        // pointer to data
 );

This directive starts a job for the specified task. Starting a task involves three main stages

  • setting up a job
    • finding a free job entry if available
    • setting up this entry including passing it the data void
  • placing the job on readyQ
  • doing a context switch

The context switch must be dealt with differently when a task is started from an interrupt handler.

The function should only be called after oceos_start(..) has been called.

IMPORTANT NOTE: Tasks can be started in interrupt services routines but CANNOT be started in trap handlers.

Parameters in:

  • integer specifying ID of task, must be in range 0 to NUMBER_OF_TASKS - 1
  • pointer: data pointer passed to job when task starts

Status returns:

  • SUCCESSFUL
  • INVALID_ID (incorrect task ID)
  • TOO_MANY (job queue full)
  • FAILED_TO_PUT_JOB_ON_REDYQ (problem placing job on the ready queue)
  • INCORRECT_STATE (problem with data area, attempt to start disabled task)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)

oceos_task_timed_start

 /**
 *  Schedule a task to start execution at a given system time
 *
 *  The before and after offsets define a window around the selected time
 *  in which the task may be started.
 *
 *  If the current system time is before the start window a job is created
 *  and a corresponding action put on the timed action queue.
 *
 *  If the current system time is within the time window for starting the task,
 *  it is started immediately.
 *
 *  If the current system time is after the window, an error is reported.
 *
 *  Times are in microseconds.
 *
 *  This function should only be called after OCEOS is initialised
 *  and the dynamic task and job structures set up
 *
 * @param taskID        Task ID must be in range 0 to 254
 * @param ptr           Pointer to data
 * @param start_time    System time at which job should start
 * @param before        Forward tolerance for time
 * @param after         Backward tolerance for time
 * @param return_job_id Return job_id to the user, to remove this job from timed action if needed
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   System Meta pointer is NULL or
 *                           Fixed data start sentinel is corrupt
 *          INCORRECT_STATE  One of the System Meta pointers is NULL or
 *                           System time is not initialised or
 *                           Tried to start disabled task
 *          INVALID_ID       Failed on task ID check
 *          UNSATISFIED      System time is greater when start time plus after time
 *          TOO_MANY         Max number of jobs(requests to start) for task is exceeded
 *          INTERNAL_ERROR   Failed to put job for task on ready Q
 *          SUCCESSFUL       All is OK
 */
enum DIRECTIVE_STATUS oceos_task_timed_start(
    const unsigned int,
    void * const,         // data to be used by task
    U64_t,               // system time at which task should start
    U32_t,               // offset before time in which start o.k.
    U32_t,              // offset after time in which start o.k.
    U32_t *            // in case need to cancel later
);

This directive schedules a task to start execution at a given system time. It should only be called after oceos_start(..)

Parameters in:

  • integer specifying ID of task, must be in range 0 to NUMBER_OF_TASKS - 1
  • pointer passed to job when task starts
  • integer (64-bit) specifying system time at which task should start
  • integer specifying the forward time tolerance in usecs.

    The task can only be started if the current time is within this tolerance before the specified start time.

  • integer specifying the backward time tolerance in usecs.

    The task can only be started if the current time is within this tolerance after the specified start time.

  • Pointer to integer to store the ID of the job to be started.

Status returns:

  • SUCCESSFUL
  • INCORRECT_STATE (data area not initialised)
  • INVALID_ID (invalid task ID)
  • UNSATISFIED (invalid start time)
  • TOO_MANY (too many jobs already started)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (error adding job to timed actions queue)

oceos_task_disable

 /**
 *  Disable a task and remove any current pending jobs on readyQ.
 *
 *  A disabled task's jobs  will not be put into execution nor put on readyQ.
 *
 *  Note that where execution of a task has already commenced,
 *  disabling the task will not prevent it from completing.
 *
 *  This function should only be called after OCEOS is initialised
 *  and the dynamic task and job structures set up
 *
 * @param taskID  Task ID must be in range 0 to 254
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED      System Meta pointer is NULL or
 *                              Fixed data start sentinel is corrupt
 *          INTERNAL_ERROR      One of the System Meta pointers is NULL or
 *                              Dynamic Meta pointer is NULL
 *          INVALID_ID          Failed on task ID check
 *          ALREADY_SUSPENDED   Task is already disabled
 *          SUCCESSFUL          All is OK
 */
 enum DIRECTIVE_STATUS   oceos_task_disable(
    const unsigned int  // task ID, must be in range 0 to 254
 );

This directive disables a task and removes any current or pending jobs.

Parameters in:

  • integer specifying task ID

Status returns:

  • SUCCESSFUL
  • INTERNAL_ERROR (data area not initilised)
  • INVALID_ID (incorrect task ID)
  • ALREADY_SUSPENDED (task is disabled already)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)

oceos_task_enable

 /**
 *  Enable a task - only enabled tasks are placed on readyQ or started.
 *
 *  This function should only be called after OCEOS is initialised
 *  and the dynamic task and job structures set up
 *
 * @param taskID   Task ID must be in range 0 to 254
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED      System Meta pointer is NULL or
 *                              Fixed data start sentinel is corrupt
 *          INCORRECT_STATE     One of the System Meta pointers is NULL
 *          INTERNAL_ERROR      Dynamic Meta pointer is NULL
 *          INVALID_ID          Failed on task ID check
 *          ALREADY_SUSPENDED   Task is already disabled
 *          SUCCESSFUL          All is OK
 */
 enum DIRECTIVE_STATUS   oceos_task_enable(
    const unsigned int  // task ID, must be in range 0 to 254
 );

This directive enables a task. It can only be called after oceos_start(..);

Parameters in:

  • integer specifying task ID

Status returns:

  • SUCCESSFUL
  • INVALID_ID (incorrect task ID)
  • INCORRECT_STATE (data area not initialised)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (scheduling not started)

oceos_task_self

 /**
 * Get the taskID of the current job
 *
 * @return unsigned int
 *          ID                Task ID of currently executing job
 *          TASK_ID_INVALID   ERROR
 */
 unsigned int  oceos_task_self();

This directive returns the task ID of the currently executing job or TASK_ID_INVALID if a problem.

oceos_task_get_priority

 /**
 * Get priority of a task
 *
 * @param taskID  Task ID must be in range 0 to 254
 * @return U8_t
 *          priority               Priority of the given task
 *          TASK_INVALID_PRIORITY  ERROR
 */
 U8_t  oceos_task_get_priority(
    const unsigned int  // task ID, must be in range 0 to 254
 );

This directive returns the priority of the specified task ID.

Parameters in:

  • integer specifying task ID

Return values:

  • Integer with the priority if the task ID.
  • TASK_INVALID_PRIORITY (invalid task ID, data area not initialised)

oceos_task_get_status

 /**
 * Get status of a task
 *
 * @param taskID  Task ID must be in range 0 to 254
 * @return enum TASK_STATUS
 *              TASK_DISABLED  Cannot be scheduled, and no current jobs
 *              TASK_ENABLED   Can be scheduled, but no current jobs
 *              TASK_INVALID   Task does not exist
 */
 enum TASK_STATUS  oceos_task_get_status(
    const unsigned int  // task ID, must be in range 0 to 254
 );

This directive returns the status of the specified task ID as a value of enum TASK_STATUS type below.

 enum TASK_STATUS {
    TASK_DISABLED,      // cannot be scheduled, and no current jobs
    TASK_ENABLED,       // can be scheduled, but no current jobs
    TASK_INVALID,       // task does not exist
 };

Parameters in:

  • integer specifying task ID

Return values:

  • TASK_ENABLED (task is enabled, can be scheduled, but no current jobs)
  • TASK_DISABLED (task is disabled, cannot be scheduled, and no current jobs)
  • TASK_INVALID (task ID passed is incorrect)

oceos_task_kill

  /**
 * Directive to terminate current task when
 * undesired behavior was detected.
 * Current task is terminated and back to scheduler.
 * This task can be restarted later.
 *
 * If oceos_task_kill is called from IRQ handler,
 * the task is terminated when IRQ nesting is unraveled.
 *
 * The user provided END function for this task is called.
 *
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED      System Meta pointer is NULL or
 *                              Fixed data start sentinel is corrupt
 *          INCORRECT_STATE     Called while initialisation is ongoing or
 *                              Scheduling is not started
 *          INTERNAL_ERROR      Dynamic Meta pointer is NULL
 *          SUCCESSFUL          All is OK (only return this if was called from IRQ handler)
 */
  enum DIRECTIVE_STATUS        oceos_task_kill();

This directive kills the currently executing job. It can be called from

  1. within the task
  2. the error handler
  3. from an interrupt service routine

OCEOS can only kill the current job due to its single stack design. Application scenarios where this directive could be used include:

  1. Application regular interrupt checking job status

    If such an application interrupt detected an error in the current job it can call oceos_task_kill() as part of its recovery actions

  2. oceos_on_error(..)

    OCEOS calls oceos_on_error(..) in the case of a system_error. This is an application function contained in config.c where application recovery code is located.

    oceos_task_kill is a possible directive to be used as part of error recovery.

Parameters in: None Status returns:

  • SUCCESSFUL
  • INCORRECT_STATE (data area not initialised or inconsistent, scheduling not started)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (dynamic data area not initialised or inconsistent)

oceos_task_get_info

 /**
 * Get task information for task with ID.
 *
 * User must pass pointer to struct task_info in order to retrieve data.
 *
 * @param task_id  Task ID
 * @param info     Pointer to struct task_info
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED      System Meta pointer is NULL or
 *                              Fixed data start sentinel is corrupt
 *          INCORRECT_STATE     Called while initialisation is ongoing
 *          INTERNAL_ERROR      Dynamic Meta pointer is NULL
 *          INVALID_ID          Failed on task ID check
 *          NOT_DEFINED         Pointer to task_info is NULL
 *  
 enum DIRECTIVE_STATUS oceos_task_get_info (
    const unsigned int ,  // Task ID
    struct task_info *   // Pointer to struct task_info
 );
 1  /*
 2   * task info to be used with oceos_task_get_info
 3   */
 4  struct task_info {
 5 
 6     unsigned int    jobs_current_max  :16;        // maximum number of current jobs for this task, no roll over, can be reset
 7     unsigned int    jobs_total        :16;        // number of times this task was started, no roll-over, can be reset
 8     unsigned int    jobs_current;                 // Number of task instances currently in use by oceos
 9     U32_t           time_max_delay;               // maximum time job is waiting on ready queue before becoming active
10     U32_t           time_max_finish;              // maximum time to finish job execution after starting
11     U32_t           time_max_exec;                // maximum time spent executing
12     U32_t           time_total_exec_low;          // total CPU time used by this task low bits
13     U32_t           time_total_exec_high;         // total CPU time used by this task high bits
14     U32_t           time_min_gap_starts;          // minimum time between job creations, can be reset
15     U32_t           time_min_gap_finish_start;    // minimum time between job ending and new job creation, can be reset
16     U32_t           time_last_start;              // time of most recent job creation
17     U32_t           time_last_finish;             // time of most recent job finish
18     U32_t           preempt_max;                  // maximum times any job was pre-empted, no roll-over, can be reset
19  };

This directive updates the task_info structure for the specified task ID. The elements of the structure are as follows:

  • jobs_current_max (maximum number of concurrent jobs for task since start)
  • jobs_total (number of times the task was started)
  • jobs_current_max (current number of jobs for the specified task)
  • time_max_delay (maximum time on ready queue before becoming active)
  • time_max_finish (maximum time to finish job execution after starting)
  • time_max_exec (maximum time spent executing (see note below))
  • time_total_exec_low (total CPU time used by this task low 32-bits)
  • time_total_exec_high (total CPU time used by this task high 32-bits)
  • time_min_gap_starts (minimum time between job creations)
  • time_min_gap_finish_start (minimum time between job ending and new job creation)
  • time_last_start (time of most recent job creation)
  • time_last_finish (time of most recent job finish)
  • preempt_max (maximum times any job was pre-empted)

Parameters in:

  • integer with task ID
  • pointer to task_info structure

Status returns:

  • SUCCESSFUL
  • INVALID_ID (invalid task ID)
  • INCORRECT_STATE (data area inconsistent or not initialised)
  • NOT_DEFINED (pointer to task_info structure is NULL)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (dynamic data area inconsistent)

Note:

The task execution times above include time spent in interrupt service routines (ISRs) while the task was executing.

Time spent in ISRs is expected to be a small proportion of the overall task execution time.

In the design of OCEOS (ref [SDD]) it was deemed too much of an overhead to accumulate time spent in ISRs.

Mutex Manager

Mutexes can only be created before scheduling begins and are only used after scheduling has started.

In OCEOS each mutex has a priority ceiling, the priority of the highest priority task that uses the mutex. OCEOS uses this to ensure that a task that needs a mutex only starts when the mutex is available. As a result problems such as unbounded priority inversion and deadlocks cannot occur in OCEOS.

A mutex has a unique mutex ID, an unsigned integer usually defined in the application header file as an enum MUTEX_NAME{} thus allowing a user-friendly name be associated with each mutex ID.

Mutex IDs are used as indices into both fixed and dynamic data arrays and must cover the range 0 to ((number of mutexes) -1). This is facilitated by using enum MUTEX_NAME{} to automatically assign names to IDs 0,1,....

The priority ceiling of a mutex is specified when the mutex is created and does not change subsequently. Priority ceilings are stored in the fixed data area as an array U8_t mutexCeilings[] indexed by the mutex ID.

Mutex dynamic data such as its current state and the job holding it are stored in the dynamic data area in an array of U32_t mutexDynamic[] also indexed by the mutex ID.

If a job holds more than one mutex, these should be acquired and released in a nested manner. To help ensure this is the case, a record is kept of the number of mutexes a job already holds when it acquires a mutex, and this is compared with the number of mutexes the job holds after the mutex is released. A warning is given if these are not the same.

oceos_mutex_create

 /**
  * Creates a mutex with priority ceiling
  *
  * This function should only be called after OCEOS is initialised with
  *  oceos_init() and before initialisation ends with oceos_init_finish()
  *  and scheduling starts with oceos_start()
  *
  * @param id       Mutex ID, must be in range 0 to 62
  * @param priority Mutex priority ceiling from 1 to 254 (high to low task priority)
  * @return enum DIRECTIVE_STATUS
  *         INCORRECT_STATE  System Meta pointer is NULL or corrupt
  *         INVALID_ID       Failed mutex id check
  *         INVALID_NAME     Mutex ID is not available
  *         INVALID_NUMBER   Mutex priority is not valid
  *         TOO_MANY         Mutex configuration max value reached
  *         SUCCESSFUL       All is OK
  */
enum DIRECTIVE_STATUS   oceos_mutex_create(
    const unsigned int id,       // mutex ID, must be in range 0 to 62
    const U8_t  priority         // mutex priority ceiling
);

This directive creates a mutex with a specified ID and priority ceiling. It should be called for each mutex up to NUMBER_OF_MUTEXES after application_init() and before oceos_init_finish() Note that an error will be returned by oceos_init_finish() if not all mutexes have been created.

Parameters in:

  • integer specifying mutex ID (0 to 62)
  • byte specifying priority ceiling (0 to 254)

Status returns:

  • SUCCESSFUL
  • INVALID_ID (invalid mutex ID)
  • INVALID_NAME (mute ID already created)
  • INVALID_NUMBER (invalid priority ceiling)
  • TOO_MANY (all mutexes already created)
  • INCORRECT_STATE (data area not initialised)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)

oceos_mutex_wait

 /**
  * Wait on mutex with ID.
  * If task succeeds in grabbing mutex, system level priority rises to
  * priority of this mutex, so tasks with lower or equal priority will
  * not be able to start till mutex is released.
  *
  * This should only be called after OCEOS scheduling has started.
  *
  * @param id  Mutex ID, must be in range 0 to 62
  * @return enum DIRECTIVE_STATUS
  *         NOT_CONFIGURED   System Meta pointer is NULL or corrupt
  *         INTERNAL_ERROR   Mutex Dynamic pointer is NULL or
  *                          Scheduling is not started or
  *                          Inconsistent Mutex state (check log)
  *         INVALID_ID       Mutex ID is out of range
  *         SUCCESSFUL       All is OK
  */
enum DIRECTIVE_STATUS   oceos_mutex_wait(
    const unsigned int  id      // mutex ID, must be in range 0 to 62
);

This directive, if successful, assigns a mutex to a job and replaces the current system priority ceiling with the ceiling specified when the mutex was created. If the mutex is already held by another task then an error status is returned.

Parameters in:

  • mutex ID (0 to 62)

Status returns:

  • SUCCESSFUL
  • INCORRECT_STATE (data area not initialised)
  • INTERNAL_ERROR (problem with data area, inconsistent priority ceiling, mutex already held)
  • INVALID_ID (invalid mutex ID)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (inconsistent tasks state)

oceos_mutex_signal

 /**
 * Signal a mutex with ID
 *
 * This should only be called after OCEOS scheduling has started.
 *
 * @param id  Mutex ID, must be in range 0 to 62
 * @return enum DIRECTIVE_STATUS
 *         NOT_CONFIGURED   System Meta pointer is NULL or corrupt
 *         INTERNAL_ERROR   Mutex Dynamic pointer is NULL or
 *                          Scheduling is not started or
 *                          Inconsistent Mutex state (check log)
 *         INVALID_ID       Mutex ID is out of range
 *         SUCCESSFUL       All is OK
 */
enum DIRECTIVE_STATUS   oceos_mutex_signal(
    const unsigned int  id      // mutex ID, must be in range 0 to 62
);

This directive releases a mutex from a job, restores the previous system priority ceiling, and causes a reschedule. If the mutex is not currently held by the job then nothing is done and an appropriate status returned.

Parameters in:

  • mutex ID (0 to 62)

Status returns:

  • SUCCESSFUL
  • INVALID_ID (invalid mutex ID)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (problem with data area, inconsistent priority ceiling, inconsistent tasks state, mutex held by a different job)

oceos_mutex_get_value

 /**
 * Get current value of mutex with ID
 *
 * @param id Mutex ID, must be in range 0 to 62
 * @return unsigned int
 *          0               => Unavailable
 *          1               => Available
 *          STATUS_INVALID  => (== 0xffffffff) problem
 */
unsigned int            oceos_mutex_get_value(
    const unsigned int  id      // mutex ID, must be in range 0 to 62
);

This directive returns the current value of a mutex. 0 indicates mutex held by a job, 1 indicates that mutex is not held by any job, any other value indicates an error.

Parameters in:

  • mutex ID (0 to 62)

Return values:

  • 0 (mutex held by a job)
  • 1 (mutex not held by a job)
  • STATUS_INVALID

Semaphore Manager

A counting semaphore has an integer value that is always greater than or equal to zero. This value is set initially when the semaphore is created and modified by atomic wait() and signal() operations.

Each counting semaphore has a pending jobs queue of pending jobs waiting for the semaphore value to become greater than zero.

When a wait operation is performed the semaphore is decremented and the new value is returned, unless the semaphore is already zero in which case the value remains unchanged.

When the semaphore is signalled its value is incremented, and any jobs on its pending jobs queue are transferred to the ready queue and also removed from the timed jobs queue if present.

oceos_sem_create

 typedef U8_t  sem_t;
 /**
 * Create a counting semaphore
 *
 * @param sem_t sem_id         Counting semaphore ID (0 to 62)
 * @param U16_t max_permits    Max number of permits (1 to 4094)
 * @param U16_t count_permits  Initial number of permits (1 to 4094)
 * @param U16_t pending_q_size Max number of pending queue jobs (1 to 254)
 * @param BOOLE_t use_timeout  Specify if timeout are required
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   If System Meta pointer is NULL or corrupt
 *          INCORRECT_STATE  If Init Meta pointer is NULL
 *          INVALID_ID       If Semaphore id is not OK
 *          INVALID_NUMBER   If max_permits or count_permits or pending_q_size is not OK
 *          TOO_MANY         If max_permits > count_permits
 *          INVALID_SIZE     If pending_q_size is zero
 *          SUCCESSFUL       if OK
 */
enum DIRECTIVE_STATUS   oceos_sem_create(
    sem_t sem_id,              // counting semaphore ID (0 to 62)
    U16_t max_permits,         // max number of permits (1 to 4094)
    U16_t count_permits,       // initial number of permits (1 to 4094)
    U16_t  pending_q_size,      // max number of pending queue jobs (1 to 255)
    BOOLE_t use_timeout        // whether timeout is used oceos_dataq_read_restart_timeout
);

This directive creates a counting semaphore. It should be called after application_init() and before oceos_init_data_finish()

The number of semaphores created must equal the NUMBER_OF_SEMAPHORES definition in config.h.

Parameters in:

  • integer specifying Semaphore ID (from 0 to 62)
  • integer specifying the maximum number of permits allowed for this semaphore (1 to 4094)
  • integer specifying the starting number of permits available (0 to 4094)
  • integer specifying the maximum number of jobs on the q for this semaphore (1 to 254)
  • boolean specifying if semaphore is using timeout

Status returns:

  • SUCCESSFUL
  • INVALID_ID (invalid semaphore ID)
  • INVALID_NUMBER (invalid no of permits of max jobs)
  • TOO_MANY (permits exceeds max)
  • INVALID_SIZE (invalid max no of jobs)
  • INCORRECT_STATE (data area not initialised or inconsistent)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)

oceos_sem_signal

 /**
 * Signals a semaphore, increments and transfers pending jobs to ready queue
 *
 * @param sem_id counting semaphore ID
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   If System Meta pointer is NULL or corrupt
 *          INCORRECT_STATE  If Init Meta pointer set in init state or scheduling not started
 *          INTERNAL_ERROR   If Semaphore fixed data pointer is NULL or (check log for more information)
 *          INVALID_ID       If Semaphore id is not OK
 *          TOO_MANY         If Semaphore max_permits value reached or (check log for more information)
 *          SUCCESSFUL       if OK
 */
enum DIRECTIVE_STATUS   oceos_sem_signal(
    sem_t sem_id              // counting semaphore ID
);
 /*
  * Job decrements the number of available permits for semaphore and return SUCCESSFUL
  * If number of available permits is zero, return UNSATISFIED and job should handle unsuccessful result
  */

This directive increments the permits value of the specified semaphore. Any jobs on its pending jobs queue are transferred to the ready queue and also removed from the timed jobs queue if present.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)

Status values returned:

  • SUCCESSFUL
  • INVALID_ID (invalid semaphore ID)
  • TOO_MANY (maximum permits already reached, ready queue full)
  • INCORRECT_STATE (data area not initialised, system state invalid for this operation)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (data area inconsistent)

oceos_sem_wait_continue

 /**
 * Wait on semaphore,
 * decrement the number of available permits for semaphore,
 * if no permits continue and task should handle unsuccessful result
 *
 * @param sem_id Semaphore ID
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   If System Meta pointer is NULL or corrupt
 *          INCORRECT_STATE  If Init Meta pointer set in init state or scheduling not started
 *          INTERNAL_ERROR   If Semaphore fixed data pointer is NULL or (check log for more information)
 *          INVALID_ID       If Semaphore id is not OK
 *          UNSATISFIED      If Semaphore has no permits
 *          SUCCESSFUL       if OK
 */
enum DIRECTIVE_STATUS   oceos_sem_wait_continue(
    sem_t sem_id              // counting semaphore ID
);

This directive decrements the number of available permits for the specified semaphore. If the number of available permits is zero, then status UNSATISFIED is returned and the job should handle the unsuccessful result.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)

Status values returned:

  • SUCCESSFUL
  • UNSATISFIED (permits value is already zero)
  • INTERNAL_ERROR (data area inconsistent, job data inconsistent)
  • INVALID_ID (invalid semaphore ID)
  • NOT_CONFIGURED (sysMetaPtr is null or ata is corrupt)
  • INCORRECT_STATE (data area not initialised, system state invalid for this operation)

oceos_sem_wait_restart

 /**
 * Waits on semaphore,
 * if fail, put job on pending queue and exit task
 *
 * @param sem_id Semaphore ID
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   If System Meta pointer is NULL or corrupt
 *          INCORRECT_STATE  If Init Meta pointer set in init state or scheduling not started
 *          INTERNAL_ERROR   If Semaphore fixed data pointer is NULL or (check log for more information)
 *          INVALID_ID       If Semaphore id is not OK
 *          UNSATISFIED      If job was started as a result of a timeout on this semaphore
 *          TOO_MANY         If Semaphore pending Q is full
 *          SUCCESSFUL       if OK
 */
enum DIRECTIVE_STATUS   oceos_sem_wait_restart(
    sem_t sem_id              // counting semaphore ID
);

This directive decrements the number of available permits for the specified semaphore. If the number of available permits is zero the job is terminated and restarted when the number of permits has been incremented.

Jobs restarted will lose any data stored in variables on the stack so it may be necessary for the application to store such data elsewhere so that it is available when the job is restarted.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)

Status values returned:

  • SUCCESSFUL
  • INVALID_ID (invalid semaphore ID)
  • TOO_MANY (job pending queue full)
  • UNSATISFIED (timeout expired before permits were incremented)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INCORRECT_STATE (data area not initialised, system state invalid for this operation)
  • INTERNAL_ERROR (data area inconsistent, job data inconsistent)

oceos_sem_wait_restart_timeout

 /**
 * Waits on semaphore,
 * if fail, put job on pending queue with optional timeout.
 * In order to use this directive, semaphore must be created with
 * flag use_timeout is set to TRUE
 *
 * @param sem_id         Semaphore ID
 * @param U64_t timeout  Restart time if no signal, 0 => ignore
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   If System Meta pointer is NULL or corrupt
 *          INCORRECT_STATE  If Init Meta pointer set in init state or
 *                           scheduling not started or
 *                           system time is not initialized (check log for more information)
 *          INTERNAL_ERROR   If Semaphore fixed data pointer is NULL or (check log for more information)
 *          INVALID_ID       If Semaphore id is not OK
 *          UNSATISFIED      If Called without timeout initialisation, semaphore was created with use_timeout = FALSE or
 *                           job was started as a result of a timeout on this semaphore (check log for more information)
 *          TOO_MANY         If Semaphore pending Q is full
 *          SUCCESSFUL       if OK
 */
enum DIRECTIVE_STATUS   oceos_sem_wait_restart_timeout(
    sem_t sem_id,              // counting semaphore ID
    U64_t timeout              // timeout, restart time if no signal, 0 => ignore
);

This directive decrements the number of available permits for the specified semaphore. If the number of available permits is zero the job is terminated and restarted when the number of permits has been incremented. If a timeout is specified and subsequently expires, the job is restarted and status UNSATISFIED is returned.

Jobs restarted will lose any data stored in variables on the stack so it may be necessary for the application to store such data elsewhere so that it is available when the job is restarted.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)
  • integer (64-bit) specifying the timeout in microseconds

Status values returned:

  • SUCCESSFUL
  • INVALID_ID (invalid semaphore ID)
  • TOO_MANY (job pending queue full)
  • UNSATISFIED (timeout expired before permits were incremented)
  • INCORRECT_STATE (data area not initialised, system state invalid for this operation)
  • NOT_CONFIGURED (sysMetaptr is null or data is corrupt)
  • INTERNAL_ERROR (data area inconsistent, job data inconsistent)

oceos_sem_get_value

 /**
 * Returns number of available permits
 * for semaphore ID
 *
 * @param sem_id Semaphore ID
 * @return int
 *          -1    If problem
 *          >= 0  Number of available permits
 */
int                     oceos_sem_get_value(
    sem_t sem_id               // counting semaphore ID
);

This directive returns the permits value of the specified semaphore.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)

Values returned:

  • integer with the current number of available permits
  • -1 (data area not initialised, OCEOS not started, invalid semaphore ID)

oceos_sem_penq_get_size

 /**
 * Returns number of jobs on semaphore pending queue
 * for semaphore ID
 *
 * @param sem_id Semaphore ID
 * @return int
 *          -1    If problem
 *          >= 0  Number of jobs on semaphore pending Q
 */
int                     oceos_sem_penq_get_size(
    sem_t sem_id              // counting semaphore ID
);

This directive returns the number of jobs pending for this semaphore.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)

Values returned:

  • integer with the current number of jobs on the pending queue
  • -1 (data area not initialised, OCEOS not started, invalid semaphore ID)

oceos_sem_reset

 /**
 * Restore permits to original value and
 * remove jobs from pending queue
 *
 * @param sem_id Semaphore ID
 * @return enum DIRECTIVE_STATUS
 *          INCORRECT_STATE  If Init Meta pointer set in init state or scheduling not started
 *          INTERNAL_ERROR   If Semaphore fixed data pointer is NULL or (check log for more information)
 *          INVALID_ID       If Semaphore id is not OK
 *          SUCCESSFUL       if OK
 */
enum DIRECTIVE_STATUS   oceos_sem_reset(
    sem_t sem_id             // counting semaphore ID
);

This directive returns the queue to its initial state. It removes all jobs from the pending queue and resets the number of permits to the initial value specified when the semaphore was created.

Parameters in:

  • integer specifying the semaphore ID (0 to 62)

Values returned:

  • SUCCESSFUL
  • INVALID_ID (invalid semaphore ID)
  • INCORRECT_STATE (data area not initialised, system state invalid for this operation)
  • INTERNAL_ERROR (data area inconsistent, job data inconsistent)

Data Queue Manager

Data queues are FIFO (first-in-first-out) queues of non-null void pointers ordered by arrival time. Each queue has an associated pending jobs queue, and two associated atomic operations, read() and write(). A size() operation gives the number of pointers on the queue, and the create() operation sets the maximum size of the queue.

oceos_dataq_create

 typedef U8_t dataq_t
 /**
 * Create data queue
 * If use_timeout = TRUE, timed actions must be initialised
 *
 * @param dataq_t dataq_id   data queue ID
 * @param U16_t dataq_size   data queue size
 * @param U16_t pen_q_size   pending jobs queue size
 * @param BOOLE_t roll_over  whether to roll over when full
 * @param BOOLE_t            use_timeout whether timeout is used oceos_dataq_read_restart_timeout
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  System meta pointer is NULL
 *          INCORRECT_STATE Init meta pointer is NULL
 *          INVALID_ID      Dataq ID is out of range
 *          TOO_MANY        Dataq ID exceeds dataq number in configuration file
 *          INVALID_NAME    Dataq ID is not available (already in use)
 *          INVALID_NUMBER  Dataq_size or pen_q_size is out of range
 *          INVALID_SIZE    Dataq pen_q_size <= 0
 *          SUCCESSFUL      All is OK
 */
enum DIRECTIVE_STATUS   oceos_dataq_create(
    dataq_t dataq_id,            // data queue ID
    U16_t dataq_size,            // data queue size
    U16_t pen_q_size,            // pending jobs queue size
    BOOLE_t roll_over,           // whether to roll over when full
    BOOLE_t use_timeout          // whether timeout is used oceos_dataq_read_restart_timeout
);

This directive creates the specified data queue with specified queue size and pending jobs queue size. Roll-over on the queue can also be enabled/disabled. Roll-over means that if the queue is full new entries will overwrite the oldest ones on the queue.

Parameters in:

  • integer specifying the data queue to be created (0 to 62)
  • integer specifying the maximum number of entries of the data queue (1 to 255)
  • integer specifying the maximum number of pending jobs (1 to 255)
  • boolean to enable/disable roll-over (0 to disable, 1 to enable roll-over)
  • boolean specifying if dataq is using a timeout

Status returned:

  • SUCCESSFUL
  • INVALID_ID (invalid data queue ID)
  • TOO_MANY (data queue ID too large)
  • INVALID_NAME (data queue already created)
  • INVALID_NUMBER (invalid data queue size)
  • INVALID_SIZE (invalid pending queue size)
  • INCORRECT_STATE (data area not initialised or inconsistent)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)

oceos_dataq_write

 /**
 * Puts pointer to the data on data queue.
 * Pointer to the data must not be NULL
 *
 * @param dataq_t dataq_id Data queue ID
 * @param void* data       Non-null pointer to data
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  System meta pointer is NULL
 *          INCORRECT_STATE Init meta pointer is NULL
 *          INVALID_ID      Dataq ID is out of range
 *          INVALID_ADDRESS Data pointer is NULL
 *          TOO_MANY        Dataq is full or
 *                          Ready Q is full (check system log)
 *          INTERNAL_ERROR  Wrong task index on pending Q or
 *                          Failed to remove task from timed action Q if timeout is used
 *          SUCCESSFUL      All is OK
 */
enum DIRECTIVE_STATUS   oceos_dataq_write(
    dataq_t dataq_id,       // data queue ID
    void* data              // non-null pointer to data
);

Directive to add a pointer to the end of the data queue.

Parameters in:

  • integer specifying data queue ID
  • pointer to data

Status returned:

  • SUCCESSFUL
  • INVALID_ID (invalid data queue ID)
  • TOO_MANY (data queue full, pending queue full)
  • INVALID_ADDRESS (data is null)
  • INCORRECT_STATE (data area not initialised or inconsistent)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (invalid job state)

oceos_dataq_read_continue

 /**
 * Read data queue,
 * if empty return NULL
 *
 * @param dataq_t dataq_id Data queue ID
 * @return void*
 *          NULL   System check ERROR (check system log)
 *          Data   Pointer to the data
 */
void*                   oceos_dataq_read_continue(
    dataq_t dataq_id            // data queue ID
);

This directive returns the oldest pointer from the data queue. If the queue is empty NULL is returned.

Parameters in:

  • integer specifying data queue (0 to 62)

Status returns:

  • data pointer to oldest entry on the queue
  • NULL (sysMetaPtr is null, data is corrupt, queue is empty, data area not initialised, invalid data queue ID)

oceos_dataq_read_restart

 /**
 * Read data queue next item.
 * If dataq is empty, task is put on pending Q for dataq and
 * restarted by oceos_dataq_write
 *
 * @param dataq_t dataq_id Data queue ID
 * @return void*
 *          NULL   System check ERROR or
 *                 Task was originally started from Dataq pending q
 *                 Dataq pending Q is full (check system log)
 *          Data   Pointer to the data
 */
void*                   oceos_dataq_read_restart(
    dataq_t dataq_id            // data queue ID
);

This directive returns the oldest pointer from the data queue. If the queue is empty the task is put on the pending queue to be restarted when new data is available on the queue.

Jobs restarted will lose any data stored in variables on the stack so it may be necessary for the application to store such data elsewhere so that it is available when the job is restarted.

Parameters in:

  • integer specifying data queue ID (0 to 62)

Return values:

  • data pointer to oldest entry on the queue
  • NULL (sysMetaPtr is null, data is corrupt, queue is empty, data area not initialised, invalid data queue ID)

oceos_dataq_read_restart_timeout

 /**
 * Read data queue next item.
 * If dataq is empty, task is put on pending Q for dataq and
 * restarted by oceos_dataq_write or by timeout if timeout != 0
 * In order to use this feature timed actions must be configured and
 * dataq created with use timeout set to true
 *
 * @param dataq_id dataq_id Data queue ID
 * @param U64_t timeout     Timeout, restart time if no entry, 0 => ignore
 * @return void*
 *          NULL   System check ERROR or
 *                 Task was originally started from Dataq pending q or timeout
 *                 Dataq pending Q is full (check system log)
 *          Data   Pointer to the data
 */
void*                   oceos_dataq_read_restart_timeout(
    dataq_t dataq_id,            // data queue ID
    U64_t timeout                // timeout, restart time if no entry, 0 => ignore
);

This directive returns the oldest pointer from the data queue. If the queue is empty the task is put on the pending queue to be restarted when new data is available on the queue. A 64-bit timeout in microseconds can be set, after which the task will be removed from the pending queue and NULL will be returned. If a timeout value of zero is passed then the job will remain on the pending queue until a data queue pointer is available.

Jobs restarted will lose any data stored in variables on the stack so it may be necessary for the application to store such data elsewhere so that it is available when the job is restarted.

Parameters in:

  • integer specifying data queue ID (0 to 62)

Return values:

  • data pointer to oldest entry on the queue
  • NULL (sysMetaPtr is null, data is corrupt, queue is empty, data area not initialised, invalid data queue ID)

oceos_dataq_get_size

 /**
 * Returns number of items on the data queue with ID
 *
 * @param dataq_t dataq_id data queue ID
 * @return int
 *          -1   ERROR
 *          >= 0 Number of Items on Dataq with ID
 */
int                     oceos_dataq_get_size(
    dataq_t dataq_id            // data queue ID
);

This directive returns the current number of pointers on the data queue. A negative return value indicates an error.

Parameters in:

  • integer specifying data queue ID (0 to 62)

Return values:

  • integer whose value is the current number of pointers on the data queue
  • -1 sysMetaPtr is null, data is corrupt, data area not initialised or inconsistent, invalid data queue ID

oceos_dataq_penq_get_size

 /**
 * Returns number of jobs on the dataq pending job's queue
 *
 * @param dataq_t dataq_id Data queue ID
 * @return int
 *          -1   ERROR
 *          >= 0 Number of Items on Dataq with ID
 */
int                     oceos_dataq_penq_get_size(
    dataq_t dataq_id            // data queue ID
);

This directive returns the current number of jobs on the pending queue for the specified data queue ID. A negative value indicates an error.

Parameters in:

  • integer specifying data queue ID (0 to 62)

Return values:

  • integer whose value is the current number of pointers on the data queue
  • -1 sysMetaPtr is null, data is corrupt, data area not initialised or inconsistent, invalid data queue ID

oceos_dataq_clear

 /**
 * Clears data queue and
 * frees tasks on pending queue
 *
 * @param dataq_t dataq_id Data queue ID
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  System meta pointer is NULL
 *          INCORRECT_STATE Init meta pointer is NULL
 *          INVALID_ID      Dataq ID is out of range
 *          INTERNAL_ERROR  Wrong task index on pending Q or
 *                          Failed to remove task from timed action Q if timeout is used
 *          SUCCESSFUL      All is OK
 */
enum DIRECTIVE_STATUS  oceos_dataq_clear(
    dataq_t dataq_id            // data queue ID
);

This directive resets the specified data queue deleting all entries and clearing jobs from pending queue.

Parameters in:

  • integer specifying data queue ID (0 to 62)

Status returns:

  • SUCCESSFUL
  • INVALID_ID (invalid data queue ID)
  • INCORRECT_STATE (memory area not initialised or inconsistent)
  • NOT_CONFIGURED (sysMetaPtr is null or data is corrupt)
  • INTERNAL_ERROR (inconsistent pending job data)

Timer Action Manager

This timed action manager provides directives to perform actions at a precise time in the future. The currently supported action types are:

  • Timed output to memory address or register
  • Start job which allows a job to be started at a future time (oceos_task_timed_start)
  • Timeout for data queue pending job (internal)
  • Timeout for semaphore queue pending job (internal)

Action Types

The action types implemented in OCEOS are listed in the enumerated type below. The action types required by the user for oceos_timed_output_add(..) are WRITE_ACTION and RMW_ACTION. Other action types are provided for information but are not available to the user.

1  enum ACTION_TYPE {
2   INVALID_ACION,
3   START_JOB_ACTION,         // The job was created with start in future. Put job on ready Q
4   DATA_Q_WAIT_ACTION,       // The job is on data_Q pending Q with timeout
5   SEM_Q_WAIT_ACTION,        // The job is on semaphore_Q pending Q with timeout
6   WRITE_ACTION,             // Write value to address
7   RMW_ACTION,               // Read-modify-write Action
8  };

oceos_timed_jobs_number

 /**
 * Get number of Jobs (request to start the task) waiting on timed action queue.
 * These jobs are added by oceos_task_timed_start() or
 * oceos_dataq_read_restart_timeout when data is not available (NULL) or
 * oceos_sem_wait_restart_timeout when counting semaphore is 0
 *
 * @return int
 *      -1      Timed actions manager is not initialised
 *      number  If all ok
 */
int oceos_timed_jobs_number(void);        // number of jobs on timed actions queue

This directive returns the number of queued jobs setup with oceos_task_timed_start()

Parameters in: None Values returned:

  • integer with the number of jobs in the queue
  • -1 (timed actions not initialised)

oceos_timed_jobs_remove

 /**
 * Remove job from timed action queue
 * When task started using oceos_task_timed_start(),
 * this directive requires pointer to store ID
 * of job that put on the timed action queue.
 * This job can be removed later using this directive and stored job ID.
 *
 * @param U32_t job_id  Job ID
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  Timed actions manager is not initialized
 *          INVALID_ID      Job ID is not valid
 *          UNSATISFIED     Failed to stop timer
 *          INTERNAL_ERROR  Failed to restart timed actions
 *          SUCCESSFUL      All OK
 */
enum DIRECTIVE_STATUS   oceos_timed_jobs_remove(
    U32_t job_id                             // Job ID
);

This directive removes the specified job from the timed jobs queue. The specified job ID is usually one returned by a successful call of oceos_task_timed_start()

Parameters in:

  • integer specifying timed job ID

Status returns:

  • NOT_CONFIGURED (timed actions not initialised)
  • INVALID_ID (invalid timed job ID, invalid action ID, not found on readyQ)
  • UNSATISFIED (problem stopping timed timer)
  • INTERNAL_ERROR (timed action do fail)

oceos_timed_jobs_reset

 /**
 * Remove all jobs (requests to start a task) from timed actions queue
 * These jobs were added by oceos_task_timed_start() or
 * oceos_dataq_read_restart_timeout when data is not available (NULL) or
 * oceos_sem_wait_restart_timeout when counting semaphore is 0
 *
 * @return enum DIRECTIVE_STATUS
 *      NOT_CONFIGURED  Timed actions manager is not initialized
 *      UNSATISFIED     Failed to stop timer
 *      INCORRECT_STATE Timed queue is empty
 *      SUCCESSFUL      All OK
 *
 */
enum DIRECTIVE_STATUS   oceos_timed_jobs_reset(void);

This directive resets the timer jobs queue and removes all timed job actions.

Parameters in: None Status returns:

  • SUCCESSFUL
  • INVALID_ID (invalid id)
  • NOT_CONFIGURED (timed jobs system not initialised)
  • UNSATISFIED (failed to stop timer)
  • INCORRECT_STATE (timed q is empty)

oceos_timed_output_add

 /**
 * Add output action to timed actions queue :
 *  1. Create timed output action
 *  2. Stop timer
 *  3. Add action to action_array
 *  4. Start timer or execute if time is right
 *
 * Returns timed action ID that can be used later to remove
 * if need by oceos_timed_output_remove
 *
 * @param enum ACTION_TYPE action_type  Action type
 * @param U64_t act_time                System time at which action should be done
 * @param U32_t before_time             Forward tolerance for time
 * @param U32_t after_time              Backward tolerance for time
 * @param adrs_t address                Output address
 * @param U32_t value                   Output value
 * @param U32_t mask                    Output mask
 * @return int
 *      -1          Fail
 *      action ID   All OK
 */
int oceos_timed_output_add(
    const enum ACTION_TYPE action_type,// Action type
    const U64_t act_time,              // system time at which action should be done
    const U32_t before_time,           // forward tolerance for time
    const U32_t after_time,            // backward tolerance for time
    const adrs_t address,              // output address
    const U32_t value,                 // output value
    const U32_t mask                   // output mask
);

This directive places a specified action type (write or rmw) on the timed output queue to be executed at the specified time in the future. Forward and backward tolerances are specified, and the action will be carried out within these tolerances.

The target address to be written is supplied along with an output value. The mask is supplied for rmw (read, modify, write) operations on specified bits in the target address. Only the bits in value specified by the mask will be written to the address.

The only output type currently supported is WORD32.>Other types may be supported in the future. The action ID is returned, if successful.

Parameters in:

  • integer action types WRITE_ACTION and RMW_ACTION are supported. WRITE_ACTION causes output value to be written to output address. RMW action causes only the bits in value specified by the mask to be written to the output address.
  • 64-bit integer with the scheduled future time at which the action is to be performed.
  • Integer with forward tolerance in microseconds on the scheduled action time. The action will never be performed in advance of the forward tolerance of the scheduled time.
  • Integer with backward tolerance in microseconds after the scheduled action time. The action will never be performed later than the backward tolerance after the scheduled time.
  • Address (32-bit) of the output location. This is the location to which the value will be written.
  • Integer (32-bit) with the value to be written to the output address.
  • Integer (32-bit) with the output mask used in the read-modify-write operation.

Status returns:

  • Integer with action ID if successful
  • -1 (unsuccessful, timed outputs not initialised, action queue full, failed to stop timer during add operation, failed to execute output action if the time was already within tolerance)

oceos_timed_output_number

 /**
 * Get number of timed output actions waiting on timed action queue.
 * These timed output actions are created by oceos_timed_output_add
 *
 * @return int
 *      -1      Timed actions manager is not initialised
 *      number  All ok
 */
int oceos_timed_output_number(void);

This directive returns the number of timed outputs on the timed actions queue.

Parameters in: None Values returned:

  • integer with number of timed outputs on the action queue
  • -1 (timed actions not initialised)

oceos_timed_output_remove

 /**
 * Remove timed output action from timed actions queue.
 * When action is created, action index should be stored
 * to remove if need
 *
 * @param U8_t action_index Action index when created
 * @return enum DIRECTIVE_STATUS
 *      NOT_CONFIGURED      Timed actions manger is not initialized
 *      INVALID_ID          Action index is not valid
 *      OBJECT_WAS_DELETED  Action with action_index is not available
 *      SUCCESSFUL          All OK
 */
enum DIRECTIVE_STATUS oceos_timed_output_remove(
    const U8_t  action_index    // Index in action array, returned when action was created
);

This directive removes the specified action ID from the timed outputs queue.

Parameters in:

  • integer with action ID

Status returns:

  • SUCCESSFUL
  • NOT_CONFIGURED (timed action system not initialised)
  • INVALID_ID (action ID invalid)
  • OBJECT_WAS_DELETED (action ID was already deleted)
  • UNSATISFIED (failed to stop timer)
  • INCORRECT_STATE (issue removing from timed_q)
  • INTERNAL_ERROR (timed action do fail)
  • INVALID_NUMBER (log entry type or log entry info incorrect)

oceos_timed_output_reset

 /**
 * Remove all timed output actions from timed actions queue
 * These timed output actions were created by oceos_timed_output_add
 *
 * @return enum DIRECTIVE_STATUS
 *      NOT_CONFIGURED   Timed actions manager is not initialized
 *      UNSATISFIED      Failed to stop timer
 *      INCORRECT_STATE  Timed q is empty
 *      SUCCESSFUL       All OK
 */
enum DIRECTIVE_STATUS oceos_timed_output_reset(void);

This directive clears all timed outputs from the action queue. Parameters in: None Status returns:

  • SUCCESSFUL
  • INVALID_ID (incorrect id)
  • NOT_CONFIGURED (timed actions system not initialised)
  • UNSATISFIED (failed to stop timer)
  • INCORRECT_STATE (timed q is empty)

Interrupt Manager Directives

Interrupt management is done using BCC calls.

OCEOS includes two directives that wrap the corresponding BCC calls so that OCEOS can ensure the application does not use interrupts used by OCEOS

oceos_interrupt_handle_register (SPARC)

This function is for SPARC architecture only.

1  /*
2   * OCEOS Wrapper around bcc_isr_register_node(struct bcc_isr_node *isr_node)
3   * in order to check if user not interfering with OCEOS internal configurations
4   * set the function to be used for the given interrupt
5   * @param struct bcc_isr_node
6   */
7  enum DIRECTIVE_STATUS   oceos_interrupt_handle_register(
8     struct bcc_isr_node *isr_node
9  );

Directive to register an interrupt using the standard BCC interrupt functions (see section 5.9 of BCC manual). Extracts from bcc.h and OCEOS example below.

Parameters passed are:

  • Pointer to bcc_isr_node structure with the following elements initialised:
    • Source (interrupt source number)
    • Handler (interrupt handler address)
    • Pointer passed to handler

Return Values are:

  • SUCCESSFUL (Interrupt handler successfully registered)
  • NOT_CONFIGURED (Interrupt Manager not initialised)
  • INVALID_NUMBER (Interrupt source number is used by OCEOS)
  • INTERNAL_ERROR (BCC failed to register isr_node to source number)

struct bcc_isr_node:

 1  /*
 2   * Interrupt node for registering interrupt handler
 3   *
 4   * This structure is used for registering interrupt handlers with
 5   * bcc_isr_register_node() and bcc_isr_unregister_node(). An application shall
 6   * not use it if bcc_isr_register() and bcc_isr_unregister() is used.
 7   */
 8  struct bcc_isr_node {
 9         /* BCC private data (do not touch) */
10         void *__private;
11         /* Interrupt source number */
12         int source;
13         /* User ISR handler */
14         void (*handler)(
15                 void *arg,
16                 int source
17         );
18         /* Passed as parameter to handler */
19         void *arg;
20  };

OCEOS example :

 1  /*
 2   * Register handler
 3   */
 4  void fun0(void * ptr){
 5     node.source = 12;
 6     node.handler = start_task;
 7     node.arg = ptr;
 8     ret = oceos_interrupt_handle_register(&node);
 9     if (SUCCESSFUL != ret) {
10         printf("ERROR :: Failed to add ISR handler\n");
11     }
12     bcc_int_unmask(12);
13     exit(1);
14  }
1  /*
2   * Handler for interrupt
3   */
4  void start_task(void *arg, int source){
5   oceos_task_start(t_harry,arg);
6   oceos_task_start(t_dick,arg);
7 }


bcc_isr_register_node:

 1  /*
 2   * Register interrupt handler, non-allocating
 3   *
 4   * This function is similar to bcc_isr_register() with the difference that the
 5   * user is responsible for memory management. It will never call malloc().
 6   * Instead the caller has to provide a pointer to a preallocated and
 7   * initialized ISR node of type struct bcc_isr node.
 8   *
 9   * The memory pointed to by isr_node shall be considered owned exclusively by
10   * the run-time between the call to bcc_isr_register_node() and a future
11   * bcc_isr_unregister_node(). It means that the memory must be available for
12   * this time and must not be modified by the application. The memory pointed to
13   * by isr_node must be writable.
14   *
15   * This function should be used to install interrupt handlers in applications
16   * which want full control over memory allocation.
17   *
18   * isr_node: Pointer to user initialized ISR node. The fields source, handler
19   *           and optionally the arg shall be initialized by the caller.
20   *
21   * return:
22   * - BCC_OK: Handler installed successfully.
23   * - BCC_FAIL: Failed to install handler.
24   */
25  int bcc_isr_register_node(
26         struct bcc_isr_node *isr_node
27  );

oceos_interrupt_handle_unregister (SPARC)

This function is for SPARC architecture only.

1  /*
2   * OCEOS Wrapper around bcc_isr_unregister_node(struct bcc_isr_node *isr_node)
3   * in order to check if user not interfering with OCEOS internal configurations
4   * set the function to be used for the given interrupt
5   * @param struct bcc_isr_node
6   */
7  enum DIRECTIVE_STATUS   oceos_interrupt_handle_unregister(
8     struct bcc_isr_node *isr_node
9  );

Directive to unregister an interrupt using the standard BCC interrupt function (see section 5.9 of BCC manual). Extracts from bcc.h and OCEOS example below.

Parameters passed are:

  • Pointer to bcc_isr_node structure with the following elements initialised:
    • Source (interrupt source number)
    • Handler (interrupt handler address)
    • Pointer passed to handler

Return Values are:

  • SUCCESSFUL (Interrupt handler successfully registered)
  • NOT_CONFIGURED (Interrupt Manager not initialised)
  • INVALID_NUMBER (Interrupt source number is used by OCEOS)
  • INTERNAL_ERROR (BCC failed to unregister isr_node to source number)

bcc_isr_unregister_node :

 1  /*
 2   * Unregister interrupt handler, non-allocating
 3   *
 4   * This function is similar to bcc_isr_unregister() with the difference that
 5   * the user is responsible for memory management.  It is only allowed to
 6   * unregister an interrupt handler which has previously been registered with
 7   * bcc_isr_register_node().
 8   *
 9   * isr_node: Same as input parameter to bcc_isr_register_node().
10   *
11   * return:
12   * - BCC_OK: Handler successfully unregistered.
13   * - BCC_FAIL: Failed to unregister handler.
14   */
15  int bcc_isr_unregister_node(
16         const struct bcc_isr_node *isr_node
17  );

Timing Manager directives

oceos_time_sys_get64

1  // get the current system time (64-bit)
2  U64_t  oceos_time_sys_get64();

Directive to return zero if timer is not initialised or the OCEOS system time as a 64-bit number. The OCEOS system time is the number of microseconds since OCEOS was started.

oceos_time_sys_get32

1  // get the current system time (low 32 bits of 64 bit value)
2  U32_t oceos_time_sys_get32();

Directive to return zero if timer is not initialised or the low 32-bits of the OCEOS system time. The lower 32 bits of OCEOS system time (microseconds) rolls over every 71 minutes.

Logging Manager Directives

oceos_log_add_entry

 /**
 * Add a log entry (32-bit time is included automatically)
 *
 * Overwrites the oldest unread entry if the log is full.
 *
 * @param type  Log type (8 bits)
 * @param info  Extra information (24 bits)
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  Log check state failed or
 *                          Logging manager is not initialised
 *          INVALID_NUMBER  Parameter info is more than 24 bits
 *          INCORRECT_STATE Problem with read/write indexes
 *          SUCCESSFUL      All OK
 */
enum DIRECTIVE_STATUS   oceos_log_add_entry(
    enum LOG_ENTRY_TYPE type,// 8 bits
    const unsigned int info // information (24 bits)
);

Directive to add an entry to the log and add a 32-bit time tamp. Each log entry consists of a type (8-bit), and information (24-bits).

OCEOS log types are:

  • NOT_VALID_ENTRY (value 0 not allowed)
  • ALL_OK (System OK, information value provides more information)
  • NO_JOB_FREE (TBD)
  • READYQ_FULL (The ready queue is full)
  • MUTEX_WAIT_REPEAT (TBD)
  • MUTEX_SIGNAL_REPEAT (TBD)

Status returns are:

  • SUCCESSFUL (entry successfully added to log)
  • NOT_CONFIGURED (log data area not initialised or corrupt)
  • INVALID_NUMBER (type or information is invalid)
  • INCORRECT_STATE (log read or write pointer is inconsistent)

oceos_log_remove_entry

 /**
 * Read and remove the oldest unread log entry
 *
 * If the log is not empty use the entry at the read index to update the value
 * at the output pointer, returning SUCCESSFUL.
 *
 * If the log is empty, set the value at the output pointer to LOG_LOG_NOT_VALID_ENTRY
 * and return UNSATISFIED
 *
 * The read index is incremented in a circular fashion.
 *
 * @param outputPtr Pointer to Log entry to return log information
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  Log check state failed or
 *                          Logging is not setup
 *          INCORRECT_STATE Problem with read/write indexes
 *          UNSATISFIED     Log is empty
 *          SUCCESSFUL      All OK
 */
enum DIRECTIVE_STATUS   oceos_log_remove_entry(
    struct log_entry * const outputPtr
);

Directive to remove the oldest unread log entry and copy its contents to the pointer passed. If the log is not empty use the entry at the read index to update the value at the output pointer, returning SUCCESSFUL.

If the log is empty, set the value at the output pointer to NOT_VALID_ENTRY and return UNSATISFIED.

Parameters passed are:

  • Pointer to struct log_entry here removed entry will be returned.

Status returns are:

  • SUCCESSFUL (entry successfully removed and returned in pointer)
  • NOT_CONFIGURED (log data area not initialised or corrupt)
  • INCORRECT_STATE (log read or write pointer is inconsistent)
  • UNSATISFIED (log is empty)

oceos_log_get_indexed_entry

 /**
 * Read the log entry at the given index
 * Returns the entry at the specified position in the log.
 * The entry is not removed and the log and log indices are not changed.
 * (intended to allow the log be examined for example after reset)
 *
 * @param index  Log entry index
 * @param struct log_entry * const outputPtr log entry ptr
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED   Logging is not set up
 *          INVALID_NUMBER   Index is out of range
 *          INVALID_ADDRESS  OutputPtr is NULL
 *          SUCCESSFUL       All OK
 */
enum DIRECTIVE_STATUS   oceos_log_get_indexed_entry(
    const unsigned int index,
    struct log_entry * const outputPtr
);

This directive returns the entry at the specified position in the log. The entry is not removed and the log and log indices are not changed.

It is intended to allow the log be examined for example after reset. If the index is out of range, set the value at the output pointer to NOT_VALID_ENTRY and return UNSATISFIED.

Parameters passed are:

  • Integer containing the log entry to be read
  • Pointer (struct log_entry type) where entry address will be copied

Status returns are:

  • SUCCESSFUL (entry successfully retrieved and pointer returned)
  • NOT_CONFIGURED (log data area not initialised or corrupt)
  • INVALID_NUMBER (invalid entry number passed)
  • INVALID_ADDRESS (invalid pointer address passed)

oceos_log_reset

/**
 * Clear all log entries and reset to empty
 *
 * Set all log entries to LOG_LOG_NOT_VALID_ENTRY and the log to empty, with the
 * read and write indices set to 0. and function called flag 0
 *
 * Does not affect the system status variable
 * Does not affect the context switch log
 *
 * @return enum DIRECTIVE_STATUS
 *          NOT_CONFIGURED  System Meta pointer is NULL or
 *                          Fixed data start sentinel is corrupt
 *                          System Log pointer is NULL or
 *                          Logging manager is not initialised
 *          UNSATISFIED     Log area end sentinel is corrupt
 *          SUCCESSFUL      All OK
 */
enum DIRECTIVE_STATUS   oceos_log_reset(void);

Set all log entries to NOT_VALID_ENTRY and the log to empty, with the read and write indices set to 0 and function called flag 0.

The log is empty if the index of the next write is the same as the index of the next read.

It does not affect the system status variable

Note that Traps should be disabled before calling.

No parameters.

Status returns are:

  • SUCCESSFUL (log successfully reset)
  • NOT_CONFIGURED (sysMetaPtr is null, log data area not initialised or corrupt)
  • UNSATISFIED (unexpected data corruption)

oceos_log_get_size

 /**
 * Get the number of log entries
 *
 * Return the number of entries in the log
 * according to the current values of the read and write indices.
 *
 * The value will be zero if the current values of the read and write indices
 * are the same.
 *
 * @return unsigned int
 *              LOG_INVALID_SIZE    Log state is invalid or
 *                                  Logging manager is not initialised or
 *                                  Problem with read/write indexes
 *              log size            All OK
 */
unsigned int            oceos_log_get_size(void);

This directive returns the number of entries in the log according to the current values of the read and write indices. The value will be zero if the current values of the read and write indices are the same.

No parameters.

Status returns are:

  • Int (number of entries in the log)
  • LOG_INVALID_SIZE (log data area corrupt)
  • NOT_CONFIGURED (log data area not initialised)

Protection Manager

The two memory protection units in the GR716 can be used to restrict write access to memory segments by selected system bus masters (MEMPROT0) and by selected DMA bus masters (MEMPROT1).

Each unit allows four memory segments be defined for write protection. In addition MEMPROT0 can restrict access via the system bus AHB to APB bridges by selected system bus masters and the four DMA units to selected APB bus devices.

MEMPROT0 can protect four segments in the range 0x40000000 to 0x4fffffff or the range 0x80000000 to 0x8041ffff, as well as specific APB units.

MEMPROT1 can protect four segments in the range 0x30000000 to 0x31000000 and restrict the access of any master on the DMA bus.

An attempt to write to a protected area causes an AMBA ERROR response which can cause an interrupt from MEMSCRUB or from one of the AHBSTAT units.

The memory protection manager's directives relate to the EDAC and memory protection features of the GR716.

Support for the external memory scrubber (MEMSCRUB) and the two memory protection units (MEMPROT0/1) is available also from the GR716 BSP. Cobham Gaisler documentation on this support can be found in the GR716 board support package documentation in BCC.pdf, Section 7.25 for MEMSCRUB, Section 7.24 for MEMPROT0/1. Support for the internal memory EDAC and scrubbing and for external memory EDAC is not provided by the GR716 BSP.

The directives here access hardware registers and do not use the BSP.

The internal instruction RAM and data RAM of the GR716 each has its own EDAC and scrubber support internal to the GR716. The internal scrubbers use one 32-bit word per burst and an appropriate number of cycles between bursts should be specified for each scrubber. The scrubbers can be individually configured to cause an interrupt when an uncorrectable error occurs and if this is done an appropriate handler for Interrupt Line 63, default Int 19, should be set up in advance.

The GR716 internal RAM can also be protected from external write accesses by the memory protection unit on the DMA bus (MEMPROT1). The external RAM memory controllers (FTMCTRL0/1) provide EDAC support, with scrubbing provided by the MEMSCRUB unit on the main system bus.

As the main external memory (FTMCTRL0) uses an 8-bit data bus the high 20% of this RAM is used for the EDAC check bits and cannot be used for data when EDAC is enabled.

(The GR716-MINI board has 2 MiB of main external RAM accessed via FTMCTRL0 and with EDAC enabled only addresses 0x40000000 to 0x40199997 can be used for data, the check bits are stored at addresses 0x4019999a to 0x401fffff, and addresses 0x40199998 and 0x40199999 cannot be used).

When using MEMSCRUB this restriction must be taken into account in setting the high address of the scrubbing range. (The GR716-MINI external RAM scrubber range is 0x40000000 to 0x40199997.)

On the GR716 the MEMSCRUB unit works in bursts of two 32-bit words, and an appropriate number of cycles between bursts should be specified.

MEMSCRUB can be configured to cause an interrupt due to correctable or uncorrectable error counts exceeding a pre-set threshold, and if this is done an appropriate handler for Interrupt Line 63, default Int 19, should be set up in advance (the same interrupt as for the internal scrubbers).

Two memory protection (MEMPROT) units, one for the main system bus, one for the DMA bus, each allow up to four memory segments be defined and can be configured to prevent writes by a specific bus master to a segment.

MEMPROT0 controls memory writes via the main memory controller FTMCTRL0 by selected bus masters for addresses 0x40000000 to 0x4fffffff.

N.B. If EDAC is in use with FTMCTRL0 the memory area to which access is allowed must include the EDAC check bits as well as the data.

MEMPROT0 can also restrict writes to APB bus addresses in the range 0x80000000 to 0x8041ffff and to specific devices on the APB bus.

MEMPROT1 controls memory writes to the GR716 internal RAM from the DMA bus for addresses in the range 0x30000000 to 0x31ffffff.

Write access permission can be specified on an individual basis for each bus master on the DMA bus.

MEMPROT0/1 do not themselves cause a trap/interrupt when an attempt is made to write to protected memory, but as well as preventing the write return an AMBA ERROR response which can give rise to an interrupt from AHBSTAT1/0 respectively. If this used an appropriate error handler for Interrupt Line 63, default Int 19, should be set up in advance (the same interrupt as for the scrubbers).

N.B. The appropriate clock gating must be set for it to be possible to use FTMCTRL, MEMSCRUB, or the MEMPROT units, and the appropriate GPIO pin selection also configured. This can be done using the GR716 BSP.

The MEMPROT units themselves must be configured to allow access to the registers of the other devices.

A handler for Interrupt Line 63, default Int 19, should be set up in advance if required. The directives here assume this has already been done.

N.B. Where a register contains read-only bits the values set for those bits in the input data used with the directives below must be 0, if not the input will be treated as invalid.

oceos_protect_internal_EDAC_set (SPARC)

This function is for SPARC architecture only.

1  /*
2   * Internal EDAC control and scrubbing
3   */
4  //  sets the internal EDAC control registers
5  enum DIRECTIVE_STATUS   oceos_protect_internal_EDAC_set(
6     const unsigned int, // internal data memory configuration
7     const unsigned int  // internal instruction memory configuration
8  );

This directive sets the configuration registers for the GR716 internal data RAM and internal instruction RAM. These registers determine various characteristics, including whether EDAC is enabled. See GR716.h for further information. As well as setting the registers this routine reads them back to check that they have been set as requested.

Parameters passed are:

  • data_config (32-bit integer with configuration for internal data memory)
  • inst_config (32-bit integer with configuration for internal instruction memory)

(Note that Input values for read-only bit fields in the registers must be 0)

Status returns are:

  • SUCCESSFUL (registers successfully loaded with configuration values)
  • INVALID_NUMBER (if problem with input parameter)
  • INVALID_ID (problem loading registers)

oceos_protect_internal_scrubber_set (SPARC)

This function is for SPARC architecture only.

1  // sets the internal memory scrubber control and interrupt
2  enum DIRECTIVE_STATUS   oceos_protect_internal_scrubber_set(
3     const unsigned int, // internal data scrubber wash data
4     const unsigned int, // internal data scrubber control
5     const unsigned int, // internal data scrubber configuration
6     const unsigned int, // internal instruction scrubber wash data
7     const unsigned int, // internal instruction  scrubber control
8     const unsigned int  // internal instruction  scrubber configuration
9  );

This directive sets the configuration registers for the GR716 internal data RAM and internal instruction RAM scrubbers. These registers determine various scrubbing characteristics, including enabling the scrubbers. See GR716.h for further information. As well as setting the registers this routine reads them back to check that they have been set as requested.

Parameters in:

  • Data scrubber wash data (integer)
  • Data scrubber control (integer)
  • Data scrubber configuration (integer)
  • Instruction scrubber wash data (integer)
  • Instruction scrubber control (integer)
  • Instruction scrubber configuration (integer)

Note that input bits for read-only fields in the registers should be 0.

Status returns are:

  • SUCCESSFUL (all O.K.)
  • INVALID_NUMBER (problem with input parameter)
  • INVALID_ID (other problem)

oceos_protect_internal_status_get (SPARC)

This function is for SPARC architecture only.

1  // returns the internal EDAC and scrubber status
2  unsigned int            oceos_protect_internal_status_get(
3     const unsigned int  // 0 => data memory, 1 => instruction memory
4  );

This directive returns the current value of either the data or the instruction configuration register.

Parameters in:

  • Memory type (integer, 0 for data memory, 1 for instruction memory)

Values returned

  • unsigned int (value of configuration register, 0 if a problem)

oceos_protect_external_EDAC_set (SPARC)

This function is for SPARC architecture only.

1  /*
2   * External RAM EDAC control and scrubbing
3   */
4  // sets the main external RAM EDAC control register.
5  enum DIRECTIVE_STATUS   oceos_protect_external_EDAC_set(
6     const unsigned int  // EDAC control (FTMCTRL.MCFG3)
7  );

This directive sets the EDAC control register (MCFG3) in the external memory controller. The value input is checked to ensure all read-only bits are 0. The value is not read back for checking as some fields can change. The external EDAC present bit (ME) is checked to make sure it is set.

Parameters in:

  • Value to be set (integer, only low order 12 bits are used)
1  /*
2   *  Return:
3   *      DIRECTIVE_STATUS    SUCCESSFUL      if no problem detected
4   *                          INVALID_NUMBER  if high order 20 bits not 0
5   *                          INCORRECT_STATE if EDAC not present
6   */

oceos_protect_external_status_get (SPARC)

This function is for SPARC architecture only.

1  // returns the main external RAM EDAC status register (FTMCTRL0.)
2  unsigned int oceos_protect_external_status_get();

This directive returns the contents of the EDAC control register (MCFG3) in the external memory controller (FTMCTRL).

Parameters in: None

Return: unsigned int with value of FTMCTRL.MCFG3 (0 if clock gated off)

oceos_protect_external_scrubber_set (SPARC)

This function is for SPARC architecture only.

 1  // set the parameters for the external scrubber unit MEMSCRUB
 2  enum DIRECTIVE_STATUS   oceos_protect_external_scrubber_set(
 3     const unsigned int, // configuration
 4     const unsigned int, // error threshold
 5     const unsigned int, // wash data (used twice)
 6     const unsigned int, // first range low address
 7     const unsigned int, // first range high address
 8     const unsigned int, // second range start address
 9     const unsigned int  // second range end address
10  );

This directive sets the parameters for the external memory scrubber unit MEMSCRUB. BCC provides more extensive driver support for MEMSCRUB than is given here. This may be used as an alternative (cf BCC.pdf Section 25).

Notes

  • On the GR716 the burst length used by MEMSCRUB is 2
  • The same wash data is written to MEMSCRUB twice to fill its buffer
  • Values input are checked to ensure all read-only bits are 0
  • The memory controller should have EDAC enabled (this is not checked)

Parameters in:

  • config MEMSCRUB.CONFIG - configuration
  • error_thresh MEMSCRUB.ERROR - interrupt conditions
  • wash_data MEMSCRUB.INIT - wash data (used twice)
  • range1_low MEMSCRUB.RANGEL - range 1 start address
  • range1_high MEMSCRUB.RANGEH - range 2 end address
  • range2_low MEMSCRUB.RANGEL2 - range 2 start address
  • range2_high MEMSCRUB.RANGEH2 - range 2 end address

Status returns:

  • SUCCESSFUL if no problem detected
  • INVALID_NUMBER if any read-only bit is set

oceos_protect_external_scrubber_status_get (SPARC)

This function is for SPARC architecture only.

1  // get the status of the external scrubber MEMSCRUB
2  unsigned int oceos_protect_external_scrubber_status_get();

This directive returns the external memory scrubber status register MEMSCRUB.STAT as required to re-enable interrupts on task completion.

Parameters in: None

Returns: unsigned int with MEMSCRUB.STAT value

oceos_protect_external_scrubber_position_get (SPARC)

This function is for SPARC architecture only.

1  // returns the current value of the MEMSCRUB position register
2  unsigned int oceos_protect_external_scrubber_position_get();

This directive returns the current value of the position register MEMSCRUB.POS.

Parameters in: None

Returns: unsigned int with MEMSCRUB.POS value

oceos_protect_unit_control (SPARC)

This function is for SPARC architecture only.

1  // Enable or disable protection by a MEMPROT unit
2  enum DIRECTIVE_STATUS   oceos_protect_unit_control(
3     const unsigned int, // MEMPROT select, 0 => main bus, 1 => DMA bus
4     const unsigned int  // 1 => enable protection, 0 => disable
5  );

This directive allows enable or disable protection by a MEMPROT unit.

Parameters in:

  • integer indicating which MEMPROT unit (0 for main bus, 1 for DMA bus)
  • integer indicating enable or disable (0 is disable, 1 is enable)

Status returned:

  • SUCCESSFUL
  • INCORRECT_STATE (problem enabling clock gating for device)
  • UNSATISFIED (problem updating MEMPROT registers)

oceos_protect_segment_define (SPARC)

This function is for SPARC architecture only.

1  // Define the range of addresses for a segment of a MEMPROT unit
2  enum DIRECTIVE_STATUS   oceos_protect_segment_define(
3     const unsigned int, // MEMPROT select, 0 => main bus, 1 => DMA bus
4     const unsigned int, // segment number 0 to 3
5     const unsigned int, // start address
6     const unsigned int  // end address
7  );

This directive defines the range of addresses for a segment of a MEMPROT unit.

Parameters in:

  • integer to select MEMPROT, 0 => main bus, 1 => DMA bus
  • integer with segment number (0-3)
  • integer with start address
  • integer with end address

Status returns:

  • SUCCESSFUL
  • INVALID_NUMBER (error in input parameters)
  • INCORRECT_STATE (error enabling clock gating)
  • UNSATISFIED (values red back did not match values written)

oceos_protect_memprot0_segment_control (SPARC)

This function is for SPARC architecture only.

1  // Control access to MEMPROT0 segments from masters on main bus
2  enum DIRECTIVE_STATUS   oceos_protect_memprot0_segment_control(
3     const unsigned int, // segment number 0 to 3
4     const unsigned int, // 1 => allow LEON3 access to segment, 0 => deny
5     const unsigned int, // 1 => allow MEMSCRUB access to segment, 0 => deny
6     const unsigned int, // 1 => allow DMA bus access to segment, 0 => deny
7     const unsigned int  // 1 => enable segment protection, 0 => disable
8  );

This directive allows control of access to MEMPROT0 segments from masters on main bus.

Parameters in:

  • integer specifying segment (0 to 3)
  • integer specifying leon access (1 allows LEON3 access, 0 denies access
  • integer specifying MEMSCRUB access to segment (1=allow, 0=deny)
  • integer specifying DMA bus access to segment (1=allow, 0=deny)
  • integer specifying segment protection enabled or disabled (1=enable, 0=disable)

Status returns:

  • SUCCESSFUL
  • INVALID_NUMBER (error in input parameters)
  • INCORRECT_STATE (error enabling clock gating for device)
  • UNSATISFIED (did not read back correctly)

oceos_protect_memprot1_segment_control (SPARC)

This function is for SPARC architecture only.

1  // Control access to MEMPROT1 segments from masters on DMA bus
2  enum DIRECTIVE_STATUS   oceos_protect_memprot1_segment_control(
3     const unsigned int, // segment number 0 to 3
4     const unsigned int, // DMA bus masters (low 16 bits)
5     const unsigned int  // 1  => enable segment protection, 0 => disable
6  );

This directive allows control of access to MEMPROT1 segments from masters on main bus.

Parameters in:

  • integer specifying segment (0 to 3)
  • integer specifying DMA bus masters (low 16 bits)
  • integer specifying segment protection enabled or disabled (1=enable, 0=disable)

Status returns:

  • SUCCESSFUL
  • INVALID_NUMBER (error in input parameters)
  • INCORRECT_STATE (error enabling clock gating for device)
  • UNSATISFIED (did not read back correctly)

oceos_protect_APB_devices (SPARC)

This function is for SPARC architecture only.

1  // Control access to APB devices by main bus masters and DMA units
2  enum DIRECTIVE_STATUS   oceos_protect_APB_devices(
3     const unsigned int, // APB bus  (0,1,2,3)
4     const unsigned int, // CPU (top 16) and Bridge (low 16) accessible devices
5     const unsigned int, // MEMSCRUB (low 16) accessible devices
6     const unsigned int, // DMA0 (top 16) and DMA1 (low 16) accessible devices
7     const unsigned int  // DMA2 (top 16) and DMA3 (low 16) accessible devices
8  );

This directive control access to APB devices by main bus masters and DMA units

Parameters in:

  • integer specifying APB (0,1,2,3)
  • integer specifying CPU and Bridge access
  • integer specifying MEMSCRUB access (low 16)
  • integer specifying DMA0 and DMA1 access
  • integer specifying DMA2 and DMA3 access

Status returned:

  • SUCCESSFUL
  • INVALID_NUMBER (error in input parameters)
  • INCORRECT_STATE (error enabling clock gating for device)
  • UNSATISFIED (did not read back correctly)

FDIR Manager directives

oceos_system_state_get

 /**
 * Returns the value of the system state variable
 *
 * @return U32_t
 *          system state or
 *          STATUS_INVALID Detected a problem
 */
U32_t                   oceos_system_state_get(void);

This directive returns the value of the system state variable.

Parameters in: None Return values:

  • Integer with value of system state variable
  • STATUS_INVALID (data area corrupt or not initialised)

oceos_system_state_set

 /**
 * Sets system state variable and
 * remember previous setting
 *
 * @param U32_t new_state New value of system state variable
 * @return enum DIRECTIVE_STATUS
 *          INCORRECT_STATE  System Meta pointer is NULL or
 *                           Log pointer is NULL
 *          SUCCESSFUL       All OK
 *
 */
enum DIRECTIVE_STATUS   oceos_system_state_set(
    U32_t new_state              // new value of system state variable
);

This directive sets the system state variable and stores the previous value.

Parameters in:

  • integer with value of written

Status returns are:

  • SUCCESSFUL
  • INCORRECT_STATE (sysMetaPtr is null or log area pointer is null)

oceos_system_watchdog_init (SPARC)

This function is for SPARC architecture only.

1  enum DIRECTIVE_STATUS   oceos_system_watchdog_init(
2     BOOLE_t,            // whether enabled initially
3     U32_t,              // watchdog timeout
4     U8_t                // watchdog window
5  );

This directive initialises the watchdog. The watchdog window reload value is in the control register. Large window sizes make resets more likely.

Parameters in:

  • Boolean specifying whether watchdog is initially enabled (1 for enabled, 0 for disabled)
  • Integer specifying watchdog timeout value
  • Byte specifying window size

Status returns:

  • SUCCESSFUL
  • INVALID_NUMBER (error in input parameters)

oceos_system_watchdog_enable (SPARC)

This function is for SPARC architecture only.

1  // enable the system watchdog
2  enum DIRECTIVE_STATUS  oceos_system_watchdog_enable();

This directive enables the watchdog which will use timeout and window values setup previously. Parameters in: None Status return: SUCCESSFUL

oceos_system_watchdog_disable (SPARC)

This function is for SPARC architecture only.

1  // disable the system watchdog
2  enum DIRECTIVE_STATUS  oceos_system_watchdog_disable();

This directive disables the system watchdog. Parameters in: None Status return: SUCCESSFUL

oceos_system_watchdog_ticks_remaining (SPARC)

This function is for SPARC architecture only.

1  // number of ticks remaining until watchdog timeout
2  unsigned int  oceos_system_watchdog_ticks_remaining();

This directive returns the number of ticks to watchdog timeout.

oceos_system_watchdog_reset (SPARC)

This function is for SPARC architecture only.

1  // reset the watchdog
2  enum DIRECTIVE_STATUS  oceos_system_watchdog_reset(
3     BOOLE_t             // whether to enable or disable after reset
4  );

This directive resets the watchdog allowing it to be enabled or disabled after reset. Status return: SUCCESSFUL

OCEOS Data Areas

All operating systems require data space to hold various settings and the current states of tasks and other components. Most operating systems also require a separate stack for each task.

OCEOS has been designed to minimise these data memory requirements and to provide clear definitions of data memory use.

OCEOS uses a single system stack shared with the application instead of one stack per task, a major space saving.

The other data space used by OCEOS can be divided into three areas

  1. the fixed data area
  2. the dynamic data area
  3. the log area

The sizes of these areas depend on the number of tasks etc. but are constants for a given application.

OCEOS does not use dynamic memory allocation.

Each area is 32-bit aligned, starts with a constant 32-bit sentinel (OCEOS_VERSION) and ends with a constant 32-bit sentinel (END_SENTINEL). The size of an area, in 32-bit words, is calculated by OCEOS and stored by OCEOS immediately after the initial OCEOS_VERSION constant.

OCEOS does not allow tasks or other components be created after scheduling begins, so much of its configuration data is fixed once all tasks etc. have been created and can be stored in an area that does not change. This fixed data area is initialised by oceos_init(), updated as tasks etc. are created, and finalised with the addition of a checksum by oceos_init_finish(). The general layout of this area is always the same, as described below, its actual size depends on the number of tasks etc.

As OCEOS runs information about tasks and their associated jobs and other components is stored in the dynamic data area. The general layout of this area is always the same, as described below, its actual size depends on the number of tasks, maximum jobs for each task, etc.

As OCEOS runs a system state variable and system log are maintained and updated. These are stored in the log area, which may be placed in external non-volatile memory if this is available. The general layout of this area is always the same, as described below, its actual size depends on the system log size.

The start address of each area is 32-bit aligned and is placed by the application in the configuration structure (.fixed_data_address, .dynamic_data_address, .log_address). OCEOS sets up each area, placing the sentinels at the start and end of each area and the size of the area (in 32-bit words) immediately after the initial sentinel.

In many applications it will be convenient to allocate space for the three areas as arrays of 32-bit words. The start addresses of these arrays are then placed in the configuration structure as above.

To define these arrays their sizes (number of 32-bit words) are needed.

Although OCEOS provides those sizes, as indicated above, it can only do so after all tasks etc. have been created. The array sizes however are required when the arrays are defined, which is before any call to OCEOS is made.

The simplest approach is to initially use overestimates of the array sizes, start using OCEOS, read the second word in each area to get the actual sizes, and restart after re-defining the arrays with these sizes. The sizes can be read and output by application code or using the DMON monitor “oceos info” command.

To find the precise sizes of the three OCEOS data areas the application developer should define values for the number of tasks, mutexes, etc. Next, build a skeleton OCEOS application with the number of tasks, mutexes, semaphores, etc specified. Make sure the initial estimates for the three data areas are overestimates e.g. 0x100 for each. To run the application tasks, semaphores, mutexes,…etc must be created. Below are examples of DMON output, oceos_config.h, and application code.

Data area.jpg


 1 // 1: Includes OCEOS main header file
 2 #include "oceos.h"
 3 //----------------------------------------------------------------------------
 4 // 2: Number of Log entries.
 5 #define NUMBER_OF_LOG_ENTRIES                   64 // 16 to 1024, default 64
 6 //----------------------------------------------------------------------------
 7 // 3: Number of tasks
 8 #define NUMBER_OF_TASKS                         1  // 1 to 255
 9 //----------------------------------------------------------------------------
10 // 4: Number of ready queue entries, 0 to 254, see manual( MUST BE UPDATED BY USER);
11 //    Defaults to number of tasks, as each task should have aa least one job;
12 //    If Number of ready Q entries larger that number of total jobs => defaults to number of total jobs
13 #define NUMBER_OF_READYQ_ENTRIES                20
14 //----------------------------------------------------------------------------
15 // 5: Number of Mutexes
16 #define NUMBER_OF_MUTEXES                       0  // 0 to 63
17 //----------------------------------------------------------------------------
18 // 6: Number of Semaphores
19 #define NUMBER_OF_SEMAPHORES                    0  // 0 to 63
20 //----------------------------------------------------------------------------
21 // 7: Number of Data Qs
22 #define NUMBER_OF_DATAQS                        0  // 0 to 63
23 //----------------------------------------------------------------------------
24 // 8: LOG data array size
25 #define LOG_DATA_ARRAY_SIZE_U32S                0x100// not calculated value, just for sample (calculated value should be 0x88)
26 //----------------------------------------------------------------------------
27 // 9: FIXED data array size
28 #define FIXED_DATA_ARRAY_SIZE_U32S              0x100// not calculated value, just for sample (calculated value should be 0x2c)
29 //----------------------------------------------------------------------------
30 // 10: DYNAMIC data array size
31 #define DYN_DATA_ARRAY_SIZE_U32S                0x100// not calculated value, just for sample (calculated value should be 0x2c)
32 //----------------------------------------------------------------------------
33 // 11: Timed Actions
34 #define NUMBER_OF_ACTION_ENTRIES                0  // User-modify field
35 //----------------------------------------------------------------------------
36 // 12: Log entries base 2
37 #define CS_LOG_DEF_ENTRIES_BASE2                0
38 //----------------------------------------------------------------------------
39 // 13: OCEOS stack start Address
40 #define OCEOS_STACK_START_ADDRESS               0
41 //----------------------------------------------------------------------------
42 // 14: OCEOS stack end address
43 #define OCEOS_STACK_LOW_BOUND_ADDRESS           0
44 //----------------------------------------------------------------------------
45 
46 // For arm
47 #else
48  /*
49  * ARM specific configurations
50  */
51 #ifdef __SAM3X8E__
52 
53 #ifndef BSP_SYSFREQ
54 #define BSP_SYSFREQ 84                                      // ARM target frequency in Mhz
55 #endif
56 #endif
57 #ifdef __SAMV71Q21__
58 #ifndef BSP_SYSFREQ
59 #define BSP_SYSFREQ 300                                     // ARM target frequency in Mhz
60 #endif
61 #endif
62 #ifdef __VA41620__
63 #ifndef BSP_SYSFREQ
64 #define BSP_SYSFREQ 100                                      // ARM target frequency in Mhz
65 #endif
66 #endif
67 
68 #endif
69 
70 // For Sparc
71 #ifdef __sparc__
72 // 15: System Timer Unit ADDRESS and configurations for system time and timed actions
73 #define SYS_TIME_TIMER_INDEX                    2  // System time is using two chained timers.This index is lower timer index
74 #define OCEOS_TA_TIMER_INDEX                    6  // Must be a timer with highest priority
75 #define OCEOS_TA_INTERRUPT_NUMBER               14 // Interrupt number for timed action timer
76 #define OCEOS_SYS_TIME_GPTIMER_ADDRESS          0x80003000      // Default GR716 GPTIMER 0
77 #define OCEOS_TA_TIMER_ADDRESS                  0x80003000      // Default GR716 GPTIMER 0
78 
79 #define OCEOS_SYS_TIME_GPTIMER_ADDRESS          0
80 
81 #define OCEOS_TA_TIMER_ADDRESS                  0
82 #define BSP_SYSFREQ 20
83 
84 //--------------------------------------------------------------------
85 // User defined function to deal with system errors
86 void oceos_on_error(void);
87 //---------------------------------------------------------------------
88 // User defined function to deal with log 2/3 full
89 void oceos_on_full_log(void);
90 //---------------------------------------------------------------------
91 extern U32_t log_data[];
92 extern U32_t fixed_data[];
93 extern U32_t dynamic_data[];
94 extern const unsigned int __oceos_bsp_sysfreq;
95 /**
96  * Initialize OCEOS with user configurations
97  */
98 int application_init(void);


This example application to display data area sizes is provided with OCEOS under Exaple/AreaDisplayARM folder.

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 /* N.B.  Application header is included first */
 5 #include "asw.h"                        // application header
 6 #include "oceos_config.h"                              // OCEOS header
 7 #include "mutex.h"
 8 
 9 // application data
10 static char *message1Ptr = "here we go again";
11 
12 int main(void) {
13   /*
14    * Initialise the application configuration and OCEOS
15    *
16    * This application function creates the application configuration
17    * and passes it to oceos_init(), which initialises the fixed data
18    * and enables the system log
19    */
20   if (!application_init()) {
21     //LOG
22     printf("\nProblem\n");
23     return -1;
24   }
25 
26   /*
27    * Create tasks, mutexes, etc.
28    */
29   enum DIRECTIVE_STATUS status;
30 
31   // create tasks (names are defined in the application header)
32 
33   status = oceos_task_create(t_tom, 254, 254, 5, 0, 1, fun0, NULL, 0xfffc, 0);
34 
35   printf("\n Tasks created\n");
36 
37   if (SUCCESSFUL != status) {
38     // LOG
39     printf(
40            "\nAbandoning, problem creating task or mutex, resulting return code: %u\n",
41            status);
42     return -1;
43   }   // if
44   /*
45    * Finish initialising OCEOS and setting up the fixed data
46    */
47   status |= oceos_init_finish();
48   if (SUCCESSFUL != status) {
49     // LOG
50     printf(
51            "\nAbandoning, problem ending OCEOS initialisation, resulting return code: %u\n",
52            status);
53     return -1;
54   }   // if
55   /*
56    * Start OCEOS scheduling
57    *
58    * The OCEOS fixed data provides all the information required.
59    *
60    * If a valid task is specified, it is started and passed the pointer.
61    * Otherwise the system is put in sleep mode
62    */
63   status |= oceos_start(fixed_data, t_tom, (void*) message1Ptr);
64 
65   // should not get here
66   return -1;                          // returned as an integer
67 
68 }   // main
69 
70 /*
71  * Set up timer and register handler
72  */
73 void fun0(void *ptr) {
74 	
75   printf("Log data area U32s = %p\n",log_data[WORDS_USED_OFFSET]);
76   printf("Fixed data area U32s = %p\n",fixed_data[WORDS_USED_OFFSET]);
77   printf("Dynamic data area U32s = %p\n",dynamic_data[WORDS_USED_OFFSET]);
78   
79   oceos_exit();
80   return;
81 }   // fun0()


DATA AREA LAYOUTS

The design of OCEOS makes the contents of its data areas readily visible to an application. It is straightforward to determine the details of state of the system should it be necessary to do so.

Each data area is made up of a number of fields whose sizes depend on the particular application but whose general layout is invariant.

The layout of each area is described in oceos_areas.h which also provides the offset at which each field in an area is located if the above labels are defined before oceos_areas.h is included.

Each field starts at a 32-bit boundary, so treating an area as an array of 32-bit words any field can be accessed by simply use the offset as an index into the array.

Data areas layout in memory

Memory layout2.jpg

Data Areas Layout

Data areas layout2.jpg

Error messages

Applications written to use OCEOS are a combination of tasks calling OCEOS directives. All directives return a status value from the enumerated type below (which is based on RTEMS codes). The meaning of the directive status returned depends on context and the called directive.

 1  /* Status codes returned by OCEOS directives (RTEMS based) */
 2 enum DIRECTIVE_STATUS {
 3         SUCCESSFUL,
 4         INVALID_NAME,
 5         INVALID_ID,
 6         TOO_MANY,
 7         TIMEOUT,
 8         OBJECT_WAS_DELETED,
 9         INVALID_SIZE,
10         INVALID_ADDRESS,
11         INVALID_NUMBER,
12         NOT_DEFINED,
13         FAILED_TO_PUT_JOB_ON_REDYQ,
14         UNSATISFIED,
15         INCORRECT_STATE,
16         ALREADY_SUSPENDED,
17         INVALID_PRIORITY,
18         INVALID_NODE,
19         NOT_CONFIGURED,
20         INTERNAL_ERROR,
21         FAILED_TO_SET_REGISTER
22 };

OCEOS Application Notes

This section contains some tips for application developers.

Directives that restart tasks

Some OCEOS directives access a resource which, if not available, cause the task to be restarted when the resource has changed state e.g. oceos_dataq_read_restart, oceos_sem_wait_restart. Tasks restarted will lose any data stored in local variables so it may be necessary for the application to store such data elsewhere so that it is available when the task is restarted.

Mutex priority ceilings

The priority ceiling of a mutex ensures that a task holding a mutex cannot be pre-empted by a higher priority task looking for the same mutex. Note that it is the application developer’s responsibility to ensure the correct priority ceiling is setup when the mutex is created. The priority ceiling should be set to the value of the highest priority task using the mutex.

Task local/global storage

In a RTOS tasks usually start, run for a finite period, then exit. A task involving an endless loop must suspend waiting for a resource at least once around the loop, otherwise it will lock out lower priority tasks. Unless of course it itself is the lowest priority ‘idle’ task, when this lock out doesn’t matter.

As a task runs it stores data in local variables, in most RTOS on its own stack. When a task suspends this locally stored data remains available and can be used by the task when it continues. On exit this local data is lost. Hence if data acquired by a task is to be used by it the next time it starts this data must be stored globally.

A typical example might be to keep track of the total number of things the task has processed.

This would require:

  1. A global integer for the total count, set up and initialised to 0 before the task first starts

    e.g. unsigned int total = 0U;

  2. Each time the task runs it keeps a count and before it exits uses this to update total

    e.g total += count;

    if (total < count) total = UINTMAX_C; // assuming predefined

This is all that is needed if all tasks that update total have the same priority and there is only one CPU. (It is assumed that an RTOS does not do time slicing between tasks with the same priority.)

If tasks with different priorities can update total or there are multiple CPUs then precautions must be taken to avoid a lost update

e.g. disable interrupts on all CPUs remembering previous states

update total

restore all interrupts to their previous states

Another scenario in which a task will need to store data globally is when it has acquired data from a sensor or other source and has to exit before it can complete processing the data. The data needs to be stored globally so that the task can acquire it again when next it starts.

A flag can be used by the task to check if the data has already been acquired, in which case it uses the globally stored data rather than acquiring new data. Once use of the global data is completed the task must reset this flag.

This would require

  1. A global flag for the state of this globally stored data, set to ‘false’ before the task first starts

    e.g. Boole_t my_data_already_obtained = FALSE;

  2. A global structure for the data (this could incorporate the flag) set up before any task starts

    e.g. struct my_data data;

  3. Logic in the task to use the already stored data if any

    e.g. if(!my_data_already_obtained) {

    1. my_data_already_obtained = (data = obtain new data)} // atomic
  4. if (my_data_already_obtained) process the data
  5. Once data processing is complete the task resets the flag

    e.g. my_data_already_obtained = FALSE;

If different tasks can acquire the data, tasks are basically in competition to acquire it and each task should have its own ‘xx_data_already_obtained’ flag and its own structure for holding the data. With only one CPU (and no time slicing between tasks of the same priority) this can be relaxed slightly to one flag and one structure per priority rather than per task.

Another example might be where a task reads data from a queue and has to exit before it can use the data, but the data needs to be used by the task when it restarts rather than being discarded.

Using e.g. an OCEOS data queue (which contains void pointers), no flag is needed as the pointer itself can fulfil that role as well as holding the last item read from the queue by the task:

  1. A global void pointer for use by the task is initialized to ‘NULL’ before any task starts

    e.g. void * my_last_read = NULL;

  2. Logic in the task to use the previously read data (if any) or read fresh data

    e.g. if(NULL == my_last_read) my_last_read = (read the OCEOS data queue)

  3. Data processing

    e.g. if(NULL != my_last_read) { process the data at my_last_read}

  4. Once data processing is complete the task resets my_last_read

    e.g. my_last_read = NULL;

If different tasks can read the data queue, tasks are basically in competition to acquire the data and each task will require its own ‘xx_last_read’. As above this can be relaxed slightly for one CPU to one per priority assuming there is no time slicing between tasks of the same priority.

N.B. If there are multiple CPUs the above will need to be modified.

All of the above apply generally and except for the OCEOS data queue are not specific to OCEOS.

Specific to OCEOS

In the context of the need for global storage OCEOS differs from many other RTOS in two ways

  1. An OCEOS task may be forced to exit rather than suspending when a counting semaphore or data queue item it needs is not available. It then restarts automatically when the item becomes available, rather than continuing from where it was as with other OS.
  2. OCEOS allows the same task be used to process different devices of the same type, rather than needing a different task for each device

OCEOS forced to exit:

In most RTOS, a wait operation on a counting semaphore can cause the task to suspend at that point and wait until the counting semaphore is signalled, it then resumes from where it was suspended

Similarly for an attempt to read a data queue when the data queue is empty.

In most RTOS this behaviour is made possible by each task having its own stack on which the local data remains stored while the task is suspended waiting. Each counting semaphore and data queue also has a pending queue of tasks waiting to continue.

OCEOS has a single system stack shared by all tasks, a significant space saving. This is used by tasks for their local data as they execute, but this space cannot be retained while a task stops and waits for something to become available as a lower priority task can then resume.

As a result In OCEOS tasks cannot be suspended waiting for something to be available. Instead an OCEOS application can choose either to ‘continue’ or to ‘restart’.

If the ‘continue’ option is used an appropriate status code is returned and the application continues taking into account the unavailability of the sought item.

The ‘restart’ option causes the task to exit and be placed on the pending queue of the counting semaphore or data queue. This is similar to what happens when a task waits in other RTOS, but now the task will restart rather than continuing and its previous local data has been lost.

Hence if the ‘restart’ option is chosen and it is desired to reuse some already acquired data when the task restarts, any already acquired data should be globally stored before ‘restart’ is used, as described in the earlier general examples above. (A timeout can be specified with the restart.)

Using the same task with various devices

In many system there are multiple instances of the same unit. Rather than having different tasks to handle each unit, OCEOS allows the same task be used as the processing is similar.

The distinction between the units is made by passing the task a pointer to a specific structure associated with the specific unit.

These structures are global, usually one instance of a particular structure for each unit of a particular type. They are set up by the application, which also may initialise some of their fields. They are usually set up and initialised before OCEOS starts, their contents are not restricted by OCEOS.

Each unit structure will typically give register addresses for the unit and provide space for global storage of information specific to that unit such as the time it last caused an interrupt or the total number of interrupts it has caused.

Typically an OCEOS task is started as a result of an interrupt from a unit. The interrupt handler identifies which task should be started to handle this type of unit, and identifies the specific unit structure associated with the unit that caused the interrupt.

The interrupt handler may store further information in the unit’s structure, such as the time at which the unit last interrupted, the minimum time between interrupts, or other information as determined by the application in setting up the interrupt handler.

The interrupt handler then passes the task ID and a pointer to the specific unit structure to OCEOS with a request to start the task.

If subsequently the task has to exit prematurely due to e.g. a counting semaphore being unavailable, both the task and the unit structure are put on the pending queue, and when the task is restarted by e.g. signalling the counting semaphore it can complete the processing for the unit.

Before e.g. waiting on a counting semaphore the task can if appropriate update the information in the unit structure, perhaps setting a flag in the structure to indicate to the interrupt handler when the next interrupt occurs that processing of the previous interrupt from the unit was incomplete. (The unit structures are in global storage.)

Typically a the task will only update a unit structure with information the task has gathered specific to the unit associated with the structure. More general information the task has gathered, e.g. by reading from an OCEOS data queue, will be stored typically in a global variable or structure associated with the task (or with the task priority) as described in the earlier examples above.

The application developer is free to choose what approaches are taken in order to make the previous information available to the task when next it starts.

Example:

A simple example involves a low priority task putting an item pointer on a data queue and then starting a higher priority task which uses the item.

The high priority task reads the data queue but then waits on a semaphore and as this is 0 has to exit and will restart when the low priority task signals the counting semaphore.

In order to ensure the data from the queue is available when it restarts, the high priority task saves it in global memory before doing the wait on semaphore, and on starting checks to see if data is available in the global storage before reading from the data queue.

(Many other permutations of these activities are possible)

Two tasks, t_tom and t_dick, one counting semaphore s_do_now, one data queue d_items, and in global memory data storage item_store_ptr.

t_tom is the low priority initial task passed to OCEOS when starting:

t_tom loops forever

Output a message

Ensure the global storage item_store_ptr has been cleared

Place an item pointer on the data queue d_items

Start the high priority task t_dick.

Output a message Signal the semaphore s_do_now

t_dick is the higher priority task

output a message

If item_store_ptr is NULL

update with value read from data queue d_items using restart option

output a message

wait on counting semaphore s_do_now using restart option

output a message involving data read from d_items

clear item_store

exit

Expected behaviour: (illustrated by various messages at various stages)

t_tom adds item to data queue d_items

t_tom starts t_dick causing itself to be immediately pre-empted by t_dick

t_dick starts and as d_items is not empty updates item_store_ptr with pointer read from d_items

t_dick waits on counting semaphore s_do_now and exits as this is 0, going on pending queue

t_tom continues and signals the counting semaphore s_do_now

t_dick restarts, processes the data pointed to by item_store_ptr and exits

t_tom goes back to start of loop

Using OCEOS the basic application structure would be

Two tasks:

t_tom (ID=0)

t_dick (ID=1)

(names are an enumerated type, IDs are allocated automatically)

Priorities:

t_tom: 9

t_dick: 8 (highest in this case)

Max jobs: 1 in each case (in general recommended this is >=2)
ReadyQ max entries: 2 ( total of ‘Max jobs’, no point having more than this)
Counting Semaphore:

s_do_now (ID=0)

(names are an enumerated type, IDs are allocated automatically)

Initial values: 0
PendingQ size: 1

(at most one instance of the high priority task, this is only restarted by the

low priority task after the previous start has finished)

Data Queue:

d_items (ID=0)

(names are an enumerated type, IDs are allocated automatically)

Size:

1 (the high priority task removes the item before the low priority task can add a new item

PendingQ size:

1 (only one instance of the high priority task can be waiting for an item)

Global data storage void * item_store_ptr
Application functions

tom(),

dick() These carry out the task actions described above

Header

app_config.h should be updated to correspond to the above

In the above, the minimum sizes consistent with the application have been used.

In general it is recommended that the ‘Max jobs’ for each task be at least two, to cater for a new instance of a task being started before a previous instance has finished. In this simple case that cannot happen, but in general it may happen for some unanticipated reason, particularly when tasks are started by interrupt handlers.

The number of ready queue entries need never be more than the maximum number of task instances that can be in existence at any one time. This can never exceed the total of the ‘Max jobs’, and in most cases will be significantly less than this (in OCEOS it cannot exceed 254). In this case it must cater for two task instances having been started and not being finished at the same time.

The initial value of the counting semaphore is 0, as a result the high priority task does not progress the first time it reads the semaphore but exits and goes on the semaphore pending queue.

The high priority task will also be unable to progress if the data queue is empty when it starts but in this case something is always placed on the queue before the high priority task is started.

DMON debug support for OCEOS

DMON commands list:

  • oceos info
  • oceos log <last | all>
  • oceos state
  • oceos itask <taskID | all>
  • oceos status <rq | dpq | spq | tapq>
  • oceos cpuload <status|enable|disable>
  • traptable [svt | mvt]
  • load <fill> [filename]
  • stack <check><lowerBound><cpuID>

oceos info

Display oceos configuration information. Should be called after oceos_start() directive.

  • checks weather log, fixed and dynamic data areas were not overwritten by application at initialisation phase
  • displays the size of log, fixed and dynamic data section that application used, can be used by user to adjust these data area sizes for building application
  • displays oceos configurations
  • displays pointers to data structures of oceos

See oceos info command example below:

Oceos info printout.jpg

oceos log <last | all>

Display last log message or all log messages. Default is last log message. DMON finds log area start symbol, number of entries, read and write indexes and displays log entries in latest entries first order.

Each entry contains:

  • time the log entry was made
  • entry type
  • entry comment

See oceos log last command example below:

Oceos log last printout.jpg

See oceos log all command example below:

Oceos log all printout.jpg

oceos state

Display system state variables. OCEOS has two system state variables. One variable can be reset by oceos directive, but second is preserved to track any issues occurred while oceos was running. DMON translates system state flags and displays to the user.

See oceos state command example below:

Oceos state printout.jpg

oceos itask <taskID | all>

Display task information for task with ID or for all tasks in oceos. Default is all task information.

Available information:

location in memory pointer to task dynamic and pointer to task fixed blocks
taskId
task priority
task threshold
jobsMax
timeDeadline
timeMinBetweenStarts
jobsCurrent current number of jobs created, <= jobsMax
status Enum TASK_STATUS
jobsCurrentMax maximum number of current jobs for this task, no roll over, can be reset
jobsTotal number of times this task was started, no roll-over, can be reset
timeCurrentExec execution time so far of current job
timeMaxDelay maximum time job is waiting on ready queue before becoming active
timeMaxFinish maximum time to finish job execution after starting
timeMaxExec maximum time spent executing
timeTotalExecLow total CPU time used by this task low bits
timeTotalExecHigh total CPU time used by this task high bits
timeMinGapStarts minimum time between job creations, can be reset
timeMinGapFinishStart minimum time between job ending and new job creation, can be reset
timeLastStart time of most recent job creation
timeLastFinish time of most recent job finish
preemptMax maximum times any job was pre-empted, no roll-over, can be reset

See oceos itask 0 command example below:

Oceos itask 0 printout.jpg

See oceos itask all command example below:

Oceos itask all printout.jpg

oceos status <rq | dpq | spq | tapq>

Display oceos status i.e. number of tasks on ready queue (rq), number of tasks on pending data queue (dpq), number of tasks on pending semaphore queue (spq) and number of tasks on timed action queue (tapq). Default “oceos status” is number of tasks on each queue. And then user can get more details about tasks on particular queue by passing switch to “oceos status” command.

See oceos status rq command example below:

Oceos status rq printout.jpg

See oceos status dpq command example below:

Oceos status dpq printout.jpg

See oceos status spq command example below:

Oceos status spq printout.jpg

See oceos status tapq command example below:

Oceos status tapq printout.jpg

oceos cpuload <status|enable|disable>

Enable/Disable display of CPU Load Graph at interval 1000 ms or check status. Updated while OCEOS is running.

See the CPU load enabled graphs below:

Oceos cpu load graph 1.jpg


Oceos cpu load graph 4.jpg


See the Task Statistics graph for task 0 to 9 below:

Oceos task statistic graph 1.jpg

Oceos task statistic graph 2.jpg

Oceos task statistic graph 3.jpg

Oceos task statistic graph 4.jpg

Oceos task statistic graph 5.jpg

traptable [svt | mvt]

DMON displays content of trap table (interrupt number => trap handler) and translates address to symbols if available.

load <fill> [filename]

It is extension to load command to fill memory from _bss_end to begging of stack with 0xCAFEBABE. When program stops user can use DMON command “stack check” to see how far stack was used.

stack <check><lowerBound><cpuID>

Extension to stack command to check how far stack was grown, can be used when program stops after “load fill” command. User can pass lower bound as parameter. If lower bound parameter is not passed _bss_end symbol is used.

OCEOS Device Support

In OCEOS a device is typically handled by a task, but it may not be necessary to have a different task for each device. If multiple devices are processed in a similar way one task with its maximum number of jobs at least equal to the number of devices may be enough. When the task is started, typically by an interrupt handler, it is passed a data structure specific to one device and the job then created processes that specific device. For example, the six GR716 UARTs might all be handled by one task.

Separate tasks will be needed however (even for devices of the same type) if different priorities are required for processing different devices as all devices processed by one task are processed with the same priority.

In a GR716 application that uses OCEOS, device support will typically have three main parts:

  1. Initialisation routines. These are typically called from the main application to
    1. set up the device’s clock gating control so that it is disabled initially
    2. associate a specific interrupt with the specific device and disable this
    3. associate a specific interrupt handler with this interrupt
    4. usually create a data structure associated with each specific device instance
    5. set the clock gating control so that the device can be configured and function
    6. set the GPIO control to map shared GPIO pins to the device as appropriate
    7. set the parameters of the device itself
    8. use memory protection to protect the device settings
  2. Interrupt handler code to deal with immediate processing requirements. If this starts a task it typically updates the device specific data structure and passes a pointer to this to the task.
  3. Tasks:
    1. A device handler task typically started by the interrupt handler. The job thus created is passed a pointer to the device specific data structure.
    2. An idle task, which clears any pending device interrupt, enables the device interrupt, and then loops indefinitely, perhaps putting the CPU in sleep mode at the end of each loop.

BCC provides support for many of the initialisation routines, with in some cases OCEOS providing wrappers primarily for checking purposes.

It should be noted that the initialisation routines, data structure, interrupt handler and tasks are part of the application software and are not part of OCEOS itself.

General Design Considerations

Typically a device will cause an interrupt, the handler will determine the data structure associated with the device, update this, and if appropriate start a task, passing it a pointer to the data structure. The interrupt handler then exits, the job created will execute at a later time dependent on priority.

In general, the interrupt handler should be kept as short as possible, and any significant processing of data be relegated to a task, or sequence of tasks (in OCEOS tasks can be chained).

In general, an interrupt handler will only start a task in certain conditions, for example when a buffer into which incoming data is placed is becoming full.

When data is being buffered, using a circular buffer, or using double buffering may be advisable.

Careful analysis will be needed to determine the appropriate interrupt priority and the appropriate task priority. These will vary from application to application and are likely to depend on the relative loads on different devices on the systems. A worst-case analysis approach seems advisable.

Example - Using a UART on the GR716

This example uses a UART application to illustrate the main steps involved in GR716 device support.

There are six APBUARTs on the GR716, and APBUART2 is used here.

Application

APBUART2 will receive an incoming stream of characters that are to be buffered with a buffer size of 255 characters and passed to a task for processing. If the buffer becomes full older entries are not to be overwritten, newer entries are to be dropped and an error indicated.

Commands requiring action may be included in the data stream and may consist of one or more characters. Commands should be acted upon as soon as possible after being received. A delay of no more than eight character times is the maximum acceptable, and the ordering of commands and data must be preserved.

Even parity is to be used, with one stop bit and a baud rate of 38,400. Flow control is not used.

Parity errors, a break condition on the RXD line, and a RX FIFO overrun on APBUART2 are to be detected and appropriate error handling and reporting of these conditions is required.

The memory protection features of the GR716 are to be used to protect the APBUART2 configuration.

Design Outline

A circular buffer will be used for the internal buffer to allow flexibility in adding and removing characters.

A data structure associated with APBUART2 will be used to hold this buffer and status information.

Processing will be structured as an interrupt handler I_HANDLER and a task T_PROCESS. An idle task T_IDLE will enable the device interrupt after scheduling begins.

I_HANDLER will add characters to the buffer and start T_PROCESS when this becomes ¾ full, when a command is detected in the input stream, or when there is a parity error, break condition, or RX FIFO overrun. Status flags in the device data structure will indicate the reason for starting T_PROCESS. The number of interrupts is to be minimised while ensuring a timely response to a single character command and to anomalies.

T_PROCESS will remove data from the buffer, reset the status flags, and handle any commands or error conditions. Once started it will loop checking the status and will exit when there is nothing for it to do. Entries may be added to the internal buffer by I_HANDLER while T_PROCESS is running.

One single character command ‘esc’ and two other commands “#a” and “#b” are to be recognised, and all other character sequences treated as data text.

(For this example what T_PROCESS does with the data, or what it does once it recognises a command, are not developed further.)

A low priority idle task T_IDLE will also be created. Initially this will enable the device interrupt and then loop indefinitely, putting the CPU in sleep mode waiting for interrupts at the end of each loop.

Before developing the design further it is necessary to consider the settings needed for APBUART2 and the initialisation required for clock gating, GPIO pin mapping, interrupts, and memory protection.

APBUART Configuration

Each APBUART has two internal 16 byte FIFO buffers for transmit (TX) and receive (RX). These are accessed via the 32-bit data register (DATA) with only the low 8-bits of this register actually used. A read of DATA provides the oldest byte in the RX FIFO as the low byte in the word returned and removes it from the RX FIFO. Writing to DATA adds the low byte in the word to the TX FIFO from which it is removed by the transmitter. For testing purposes a loop back mode can be set so that the TX output is internally connected to the RX input and actual outputs are disabled. A FIFO debug mode can also be set that disables the transmitter and allows the FIFO debug register (FIFO) be used to read from the RX FIFO and write to the TX FIFO.

In each APBUART the baud rate used is the same for receive and transmit and is determined by the value set in its scaler register (SCALER). This value should be the number of system clock cycles that corresponds to 1/8 of the time for one serial bit, or as close as possible to this.

The data format used is always 8 bits per character, with an optional even or odd parity bit, and one stop bit. The transmitter can be set to use two stop bits.

Hardware flow control can be used, with the input CTSN signal controlling transmission from the APBUART, and the output RTSN signal connected to the source of the incoming data.

For this example, the APBUART2 control register (CTRL) and scaler register (SCALER) are as follows:

CTRL 0x80003025

Even parity (PE, PS=0)

Receiver enable (RE)

Receiver interrupt enable (RI)

delayed interrupt enable (DI)

break interrupt enable (BI)

Receiver FIFO interrupt enable (RF)

Other control bits all 0

SCALLER 0xAB (Baud rate 38400 at 50 MHz system clock)

Note that while interrupts are enabled here, APBUART2 interrupts are disabled initially in the interrupt controller IRQAMP and only enabled by T_IDLE after scheduling begins.

These control settings ensure that an interrupt is generated after every character is received, but only after a gap of just over four character times without a character, so that a block of characters received in quick succession will cause only one interrupt. In case such a block succeeds in filling the RX FIFO an interrupt is also generated when the RX FIFO is half full. An interrupt is also caused by a parity error, a break on the RX line, i.e. line held low for longer than 11 bits, or a RX FIFO overrun.

With the above settings each incoming character corresponds to about 15,000 system clock cycles, so if a FIFO half full interrupt occurs it will take at least 120,000 system clock cycles before a further 8 bytes can arrive potentially causing a FIFO overrun and loss of an incoming byte. The interrupt latency and time to remove the first byte from the RX FIFO needs to be guaranteed to be less than this by appropriate choice of the interrupt priority.

These control settings might be assigned in many other ways. It might for example be desirable to cause an interrupt immediately on arrival of a character so as to avoid the four-character latency above.

Before the APBUART2 control bits are set the clock gating, GPIO pin allocations, interrupt controller, and memory protection need to be configured, and these are considered next.

Clock gating

To use a GR716 device, or to modify its registers, the clock gating for the device must be enabled. In the initialisation sequence clock gating can be used to ensure a device is off. To save power when a device is not in use clock gating may also be used at various points in an application.

In the GR716 the APBUART clocks are controlled by the primary clock gating unit. This unit’s unlock register UNLOCK0, its clock enable register CLKEN0 and its core reset register RESTET0 all use Bit 18 to correspond to APBUART2. To modify a bit in CLKEN0 or RESET0 the corresponding bit in UNLOCK0 must first be set, and should be reset to 0 once the desired changes have been made to protect against unintended changes.

(Note: The GR716 documentation uses UNLOCK1 etc. to refer to these registers, but this appears to be a misprint, as they are also referred to as ‘Unlock register 0’ etc. and UNLOCK1 etc. is also used in referring to the secondary clock gating unit, so ‘0’ is used here rather than ‘1’.)

Disable APBUART2

  1. Ensure no software is using the APBUART2 and that it cannot cause an interrupt
  2. Set UNLOCK0 bit 18 to 1
  3. Reset CLKEN0 bit 18 to 0
  4. Reset UNLOCK0 bit 18 to 0

Enable APBUART2

  1. Set UNLOCK0 bit 18 to 1
  2. Set RESET0 bit 18 to 1
  3. Set CLKEN0 bit 18 to 1
  4. Reset CLKEN0 bit 18 to 0
  5. Reset RESET0 bit 18 to 0
  6. Set CLKEN0 bit 18 to 1
  7. Reset UNLOCK0 bit 18 to 0

GPIO Mapping

In the GR716 most input-output pins can be allocated to any one of a number of devices as determined by an I/O switch matrix that must be configured appropriately by the application.

APBUART2 uses GPIO[10] and GPIO[11] for its external TXD and RXD data lines respectively. These GPIO pins can be used for 8 different purposes, and to allocate them for use by APBUART2 the appropriate value must be written to the appropriate system configuration register.

For pins GPIO[10] and GPIO[11] the appropriate configuration register is SYS.CFG.GP1. In SYS.CFG.GP1 bits 8:11 determine the allocation of GPIO[10] and bits 12:15 of GPIO[11], and in both cases the pin is allocated to APBUART2 (TXD and RXD respectively) by setting these 4 bits to 0b0001.

Care is needed in writing to SYS.CFG.GP1 as other bit positions determine the allocation of other pins.

N.B. GPIO mapping consistency should be checked, perhaps using OCE’s DMON or Gaisler’s grmon.

It is worth noting also that when a device is disabled its associated pins are returned to GPIO input.

Interrupts

Each device on the GR716 has a fixed hardware Interrupt Line that is mapped to an Interrupt ID by the Interrupt Controller (IRQAMP). This mapping can be changed by writing to a register on IRQAMP.

The Interrupt ID in conjunction with its associated 0 or 1 interrupt level determines the priority of an interrupt. The highest priority interrupt is passed to the CPU and determines the interrupt handler used. Interrupts can be masked so that a specific interrupt is ignored until the interrupt is unmasked. After reset all interrupts are masked, i.e. disabled (IRQAMP.PIMASK = 0x00000000).

The default interrupt ID assignments for the six APBUARTs are:

APBUART Interrupt Line Interrupt ID
0 24 24
1 25 25
2 42 3
3 44 5
4 45 6
5 46 7

The GR716 Interrupt Controller IRQAMP can be used to change these assignments.

For this example the default assignment is used for APBUART2, i.e. interrupt ID = 3

Initially Interrupt 3 is configured:

Ensure Masked OFF IRQAMP.PIMASK X&= 0xfffffff7
Interrupt CLEAR IRQAMP.ICLEAR |= 0x8
Set level HIGH IRQAMP.ILEVEL |= 0x8
Check no timestamp used IRQAMP.TSISEL != 3 for IRQAMP.ITSTMPC0,1,2,3

After APBUART2 has been configured, Interrupt 3 will be cleared and then enabled

Interrupt CLEAR IRQAMP.ICLEAR |= 0x8
Enable interrupt IRQAMP.PIMASK |= 0x8

The task will periodically disable and enable the interrupt

Disable interrupt IRQAMP.PIMASK &= 0xfffffff7
Enable interrupt IRQAMP.PIMASK |= 0x8

Memory Protection

The GR716 contains two memory protection (MEMPROT) units that can be used to enable/disable writing to certain memory segments and to the configuration registers of various devices.

The MEMPROT units can be gated on or off by the primary clock gating unit, which must enable them in order for them to be configured or used. The MEMPROT units can also protect the clock gating units.

Protecting the APBUART2 configuration involves the MEMPROT registers APB3PROT0, APBPROT1, APBPROT2 and APBPROT3 and setting Bit 13 in all of these and Bit 29 in all except APBPROT1.

CPU access to APBUART2 involves resetting/setting Bit 13 and Bit 29 of APB3PROT0 to enable/disable access from the CPU and intervening bridge. If only CPU access to APBUART2 is required access from other bus masters can be left disabled.

Further Design Details

Device Data Structure:

 1  struct uart_info {
 2       unsigned int * BASE_ADDRESS // base address of APBUART2 registers
 3       unsigned int write_index : 8; // index of previous write done
 4       unsigned int read_index : 8; // index of previous read done
 5       unsigned int : 6; // spare
 6       unsigned int COMMAND_0 : 1; // initial part of command detected
 7       unsigned int COMMAND : 1; // command detected
 8       unsigned int CHAR_INT : 1;
 9       unsigned int FIFOHALF_INT : 1;
10       unsigned int FIFOFULL_INT : 1;
11       unsigned int PARITY_INT : 1;
12       unsigned int BREAK_INT : 1;
13       unsigned int BUF_FILLING : 1;
14       unsigned int BUF_OVERRUN : 1;
15       unsigned int RUNNING : 1;
16       char buffer[256]; // array size is one more than buffer size
17  }; // uart_info

Note: The buffer array will be treated as a circular buffer. If full new entries will be dropped rather than overwriting older entries and the BUF_OVERRUN bit be set. An empty buffer is indicated by read and write indices being equal, so the maximum number of entries in the buffer (255) is one less than the array size.

Note: write_index is changed only by the interrupt handler

read_index is changed only by the task

status bits are changed only in the interrupt handler or with interrupts disabled

The bits of the status field will typically be set by the I_HANDLER and reset by T_PROCESS.

Interrupt Handler and Task

I_HANDLER starts T_PROCESS but does not need to do so if T_PROCESS is already active.

T_PROCESS loops until the buffer is empty and there are no anomalies to handle, but must then exit rather than continue looping to allow lower priority tasks become active. I_HANDLER need only start T_PROCESS again if T_PROCESS has exited its main loop.

To facilitate this a status bit RUNNING is set by I_HANDLER when it starts T_PROCESS, and is cleared by T_PROCESS when it determines the buffer is empty and there is no anomaly to be handled.

As T_PROCESS still has to exit after clearing RUNNING, it must be possible for two T_PROCESS jobs to exist simultaneously, the old one finishing up, and the new one just started. OCEOS will not schedule the new job until the old one has completed so no more than two T_PROCESS jobs can be in existence at the same time and the maximum number of jobs for T_PROCESS can be limited to two.

Interrupt Handler

An APBUART2 interrupt is caused by

  1. A character arriving and no further character having arrived after four character times
  2. The RX FIFO becoming half full (eight characters)
  3. The RX FIFO being overrun
  4. A break condition on the RX line i.e. line held low for longer than 11 bits
  5. A parity error on the input

The interrupt handler I_HANDLER updates the status bits in the uart_info structure based on the APBUART2 status register, resets the APBUART2 status register bits, and if space is available in the buffer copies the data from the RX FIFO to the buffer.

As it copies the data to the buffer I_HANDLER recognises the presence of a single character command by setting the COMMAND bit, the start of a multi-character command by setting the COMMAND_0 bit, and the completion of a multi-character command by setting the COMMAND bit and clearing the COMMAND_0 bit.

If buffer space is not available the RX FIFO data is discarded, BUF_OVERRUN is set, and if COMMAND_0 is set it is reset since the continuation characters of the command have now been lost.

A task is not started if RUNNING is set as the current job will recognise the new conditions.

If RUNNING is not set I_HANDLER will set it and start T_PROCESS if any one of the following is true:

  1. an APBUART2 error condition has occurred
  2. BUF_OVERRUN has been set
  3. a command has been recognised in the input stream
  4. the buffer has become ¾ full.

This approach can result in T_PROCESS locking out lower priority tasks for an indefinite period if APBUART2 is heavily loaded with incoming data, and in general this will need to be taken into account in deciding the priority of T_PROCESS. (As the idle task is the only other task in this example this is not relevant here.)

In addition, if the processing involved in T_PROCESS is sometimes but not always heavy, perhaps due to an occasional command, it may be worth using a lower priority task than T_PROCESS to handle this heavy processing and start this task from T_PROCESS so as to ensure the primary buffer and RX FIFO are never overrun.

One the other hand, if a high priority command is recognised it may be desirable to have T_PROCESS start a high priority task to ensure the command is dealt with promptly, without delays due to T_PROCESS being pre-empted by a higher priority task, although this will increase the risk of overruns.

In general, deciding on the buffer size and on the relative priorities of various interrupts and of various tasks is likely to require careful consideration, although not an issue for this simple example.

Task

The task T_PROCESS is started by the interrupt handler I_HANDLER and does most of the processing involved in dealing with the incoming data stream.

As mentioned above, a jobs maximum of two jobs should be specified for T_PROCESS when it is created. It may be worth noting that while these two jobs may exist at the same time, one will complete execution before the other begins, as there is no time slicing in OCEOS and jobs of the same priority are scheduled for execution in the order of their creation.

T_PROCESS will require local storage for data and for status, local_storage and local_status.

As T_PROCESS can be interrupted while processing the data care is needed to ensure that local_status corresponds to the local_storage data being processed. It will be necessary to disable APBUART2 interrupts at some point, in order to guarantee this.

Also while local_storage may not need separate local storage (it might instead be implemented as pointers into the internal buffer) it seems likely that it will be necessary to have explicit local storage in T_PROCESS for local_status.

Processing: T_PROCESS is structured as a loop with the following main steps 1 to 7 below:

  1. While uart_info.read_index != uart_info.write_index
    1. Increment uart_info.read_index (mod 256)
    2. Copy characters from buffer to local_storage
  2. Disable APBUART2 interrupt (need to ensure status and data correspond)
    1. While uart_info.read_index != uart_info.write_index (usually already empty)
      1. Increment uart_info.read_index (mod 256)
      2. Copy character from buffer to local_storage
    2. Copy uart_info status bits to local_status
    3. Reset uart_info status bits to nominal (APBUART2 status bits reset by I_HANDLER)
  3. Enable UART interrupt
  4. If local_status not nominal (buffer overrun, RX_FIFO overrun, parity error, break)
    1. Process anomalous condition
  5. If local_status.COMMAND
    1. Repeat until end of local_storage
      1. Find next command in local_storage
      2. Process command
      3. Reset command search start
  6. Disable APBUART2 interrupt
    1. If (uart_info.read_index == uart_info_write_index) (no new data)

      AND (uart_info.status is nominal) (no new problems)

      1. Reset RUNNING
      2. Enable APBUART2 interrupt
      3. Exit LOOP (task exits)
  7. Enable APBUART2 interrupt (loop continues from 1.)

Application

An application that uses OCEOS

  1. sets up the functions to be used by the interrupt handlers and tasks,
  2. sets up the functions to be called to handle error conditions
  3. determines the memory areas for OCEOS fixed, dynamic and log data
  4. starts executing at main()

main(){ (the initialisation procedures below have been described earlier)

  1. Use clock gating to ensure APBUART2 is disabled
  2. Set interrupt controller IRQAMP for APBUART2, interrupt disabled (masked)
  3. Associate I_HANDLER with APBUART2 interrupt
  4. Create and initialize the device data structure (described earlier)
  5. Set the clock gating control to enable APBUART2
  6. Set the GPIO control to map APBUART2 RXD and TXD to GPIO pins
  7. Set APBUART2 CTRL and SCALER registers
  8. Set the memory protection registers to protect APBUART2
  9. Create and initialise an application configuration structure
  10. Pass the application configuration structure to oceos_init()
  11. Create T_PROCESS task with maximum two jobs, high priority
  12. Create T_IDLE task with maximum one job, low priority
  13. Finish OCEOS initialisation using oceos_init_finish()
  14. Pass fixed data, idle task T_IDLE and null pointer to oceos_start()

OCEOS now starts scheduling and runs T_IDLE initially, pre-empting it whenever T_PROCESS

is started by I_HANDLER. When T_IDLE starts it enables the APBUART2 interrupt, and then

loops indefinitely, perhaps putting the CPU in sleep mode at the end of each loop.

}

Two-phase build to miminise memory used

OCEOS can be build in two phases to reduce the memory requirement. Phase I performs the initialisation of OCEOS data areas, which are saved to an object file. Phase 2 enables the application to be built without the initialisation directives where the OCEOS simply needs to be started. This significantly reduces the memory footprint.

The steps to perform he two-phase build are as follows:

Step 1:

Modify linkcmds.memory located at C:\opt\bcc-2.1.0-llvm\sparc-gaisler-elf\bsp\gr716\leon3

to include a new section for fixed data. The section size must include fixed data, dynamic data and log data.

 1  MEMORY { 
 2  bootprom : ORIGIN = 0x00000000, LENGTH = 4K 
 3  extprom : ORIGIN = 0x01000000, LENGTH = 16M 
 4  spi0 : ORIGIN = 0x02000000, LENGTH = 32M 
 5  spi1 : ORIGIN = 0x04000000, LENGTH = 32M 
 6  dram : ORIGIN = 0x30001000, LENGTH = 60K 
 7  fixed : ORIGIN = 0x30000000, LENGTH = 4K 
 8  iram : ORIGIN = 0x31000000, LENGTH = 128K 
 9  extram : ORIGIN = 0x40000000, LENGTH = 256M 
10  }

Step 2:

Modify linkcmds located at C:\opt\bcc-2.1.0-llvm\sparc-gaisler-elf\bsp\gr716\leon3

to include new section for fixed data :

 1  INCLUDE linkcmds.memory
 2  REGION_ALIAS("REGION_TEXT", iram);
 3  REGION_ALIAS("REGION_FIXED_DATA", fixed);
 4  REGION_ALIAS("REGION_RODATA", dram);
 5  REGION_ALIAS("REGION_DATA_VMA", dram);
 6  REGION_ALIAS("REGION_DATA_LMA", dram);
 7  REGION_ALIAS("REGION_BSS", dram);
 8  REGION_ALIAS("REGION_EXTDATA_VMA", extram);
 9  REGION_ALIAS("REGION_EXTDATA_LMA", extram);
10  INCLUDE linkcmds.base

Step 3:

Modify linkcmds.base located at C:\opt\bcc-2.1.0-llvm\sparc-gaisler-elf\bsp\gr716\leon3

to include new section for fixed data :

1  .fixeddata : { 
2  __fixeddata_start = .; 
3  *(.fixeddata)
4  __fixeddata_end = .; 
5  } > REGION_FIXED_DATA

Step 4:

Create application and include oceos_init and oceos_finish without starting oceos.

This part should create mutexes, dataqs, semaphores if used.

Fixed data, dynamic data and log data should be declared in the order below.

1  U32_t __attribute__((section(".fixeddata"))) 
2  fixed_data[FIXED_DATA_ARRAY_SIZE_U32S] = {0}; 
3  U32_t __attribute__((section(".fixeddata"))) 
4  log_data[LOG_DATA_ARRAY_SIZE_U32S] ; 
5  U32_t __attribute__((section(".fixeddata"))) 
6  dynamic_data[DYN_DATA_ARRAY_SIZE_U32S];

Pointers to the task functions should be declared in arrays and kept between two stages

(will be used in oceos_start at second stage to replace in fixed data).

Step 5:

Run application on the board.

Use DMON command to capture fixed data area.


save fixed_data_start fixed_data_end fixed_data.bin bin


Step 6:

Use objcopy utility to convert bin file to object and symbols :

_binary_fixed_data_bin_start, _binary_fixed_data_bin_end

and _binary_fixed_data_bin_size , which can be used in application later.

Make sure to replace the default data section to fixeddata section.

$(OBJCOPY) --output-target elf32-sparc --rename-section .data=.fixeddata --input-target binary --binary-architecture sparc fixed_data.bin fixed_data.o


Step 7:

Remove initialization code from application.

Declare:

1  extern char __attribute__((section(".fixeddata"))) 
2  _binary_fixed_data_bin_start[];

and start OCEOS as follows:

status = oceos_start((U32_t *)&_binary_fixed_data_bin_start[0], t_dan, NULL);

The oceos_start directive will use fresh pointers to task function from task_start_func_ptr

and task_end_func_ptr and update it in fixed data, initialize dynamic data and start scheduling.

Step 8:

Add fixed_data.o to the linker. Linker should put content of this object file in fixeddata section created earlier.

Now the complete initialization process can be bypassed and OCEOS can be started faster with a smaller memory footprint.