Difference between revisions of "OCEOS/oceos introduction"
Okhoruzhyy (talk | contribs) |
Okhoruzhyy (talk | contribs) |
||
Line 332: | Line 332: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
OCEOS data areas layout | OCEOS data areas layout | ||
[[File:oceos data areas layout.jpg|500px| | [[File:oceos data areas layout.jpg|500px|frameless]] | ||
OCEOS system data graph | OCEOS system data graph | ||
[[File:oceos memory layout diargam.jpg|500px| | [[File:oceos memory layout diargam.jpg|500px|frameless]] | ||
= <span style="color:#0000ff">'''OCEOS Main Concepts'''</span> = | = <span style="color:#0000ff">'''OCEOS Main Concepts'''</span> = |
Revision as of 15:17, 25 March 2022
Introduction
OCEOS is a pre-emptive real time operating system (RTOS) with a small memory footprint intended for hard real time systems that use the GR716 micro-controller, SPARC LEON2/3/4 processors and ARM Cortex-M micro-processors.
It was developed by O.C.E. Technology with support from the European Space Agency (ESA) under project 4000127901/19/NL/AS and 4000135473/21/NL/GLC/js.
This document describes the features and use of OCEOS, and details its behavior and system calls.
- Real time software is often written as a set of trap/interrupt handlers and tasks managed by a RTOS.
- The trap/interrupt handlers start due to anomalous conditions or external happenings. They carry out the immediately necessary processing, and may ask the RTOS start a task to complete the processing.
- The RTOS starts tasks and schedules them for execution based on priority. In hard real time systems scheduling must ensure that each task completes no later than its deadline. Being early can also be a problem.
- OCEOS provides a timed output service (software timers) that allows an output be set for a precise system time independently of scheduling.
- OCEOS supports up to 255 tasks, and up to 15 current start requests for each task.
- Each task has a fixed priority.
- There are 254 task priorities, 1 (highest task priority) to 254 (lowest task priority).
- More than one task may have the same priority.
- Tasks of the same priority are FIFO scheduled.
Note
Time slicing between tasks does not occur in OCEOS.
- In OCEOS each task has a pre-emption threshold. A task can only be pre-empted by a task with a higher priority than this threshold. Pre-emptions and any traps/interrupts that occur will delay a task’s completion and potentially cause it to miss its deadline. Careful analysis is needed to ensure that task deadlines are always met.
- OCEOS supports this analysis and allows relatively simple determination of worst case behaviour.
- Problems such as unbounded priority inversion, chained blocking, and deadlocks cannot occur in OCEOS.
- OCEOS provides mutual exclusion semaphores (mutexes) to protect critical shared code or data, and inter-task communication using counting semaphores and data queues.
- A system state variable provides a summary of the current state of the system.
- Error conditions such as missed deadlines are logged and the system state variable updated. If the system state is not normal a user defined error handling function may be called and actions taken such as disabling a task or resetting the system.
- OCEOS does not allow dynamic creation of tasks at run time.
- Virtual memory is not supported.
- OCEOS is based on the Stack Resource Policy extension of the Priority Ceiling Protocol Baker 1991.
- OCEOS is provided as a library and is statically linked with an application.
- Services not needed by an application are omitted by the linker.
Purpose of the Software
OCEOS is a real-time operating system suitable for use in single CPU hard real-time systems. Its design excludes potential problems such as unbounded priority inversion, chained blocking, and deadlocks. It is compact and uses only a single system stack rather than a stack for each task. It schedules up to 255 fixed priority tasks and allows use of mutual exclusion semaphores, counting semaphores and data queues. Its data areas are statically allocated and provide information on task execution that can be accessed at any time. Data outputs and task start requests can be specified to occur at a given system time in microseconds independent of scheduling. A system log and optional context switch log are part of error detection and handling.
OCEOS is an object library for target architecture which provides directives to schedule fixed priority tasks, and supporting mutexes, semaphores, data queues, timed outputs, and interrupt handling.
There are also directives for logging, error handling, GR716 memory protection and GR716 watchdog control. Only those components used by the application are linked into the executable.
OCEOS Main Features
- Fixed priority preemptive scheduling
- Based on Stack Resource Policy – unbounded priority inversion, chained blocking, and deadlocks cannot occur
- Deterministic processing
- Fast real-time performance
- Single stack rather than separate stack for each task
- Small code footprint ( <10 kBytes for scheduling and mutex)
- Inter-task communication (Counting Semaphores and Data Queues)
- Synchronisation (Mutexes)
- Task preemption level
- Interrupt support (most of the directives can be called from IRQ handlers)
- Very short time for disabling interrupts
- Very low interrupt latency
- Nested interrupts support
- High precision timed actions (Software Timers) for data output and task start at pricise time
- Supports SPARC V8 LEON2/3/4 and ARM Cortex-M single core targets
- Fault Detection, Isolation and Recovety
- GR716 Memory protection functionality
- Enhancements for Cobham-Gaisler GR716 applications
- Sample Applications for Quick start
- Online Documentation
- Tutorial applications to demonstrate main features of OCEOS
- DMON debug tool support (execution timeline, CPU usage and profiling, commands to display information for OCEOS modules)
- Support & ISVV services available from OCE Technology Ltd.
- MISRA C Compliant
- Developed to ECSS Category B and ISO 26262 standards
- Developed in cooperation with European Space Agency (ESA)
OCEOS Configuration
TODO : describe oceos configuration with links to appropriate section in manual for more detailed information for each parameter
// 1: Includes OCEOS main header file
#include "oceos.h"
//----------------------------------------------------------------------------
// 2: Number of Log entries.
#define NUMBER_OF_LOG_ENTRIES 16 // 16 to 1024, default 64
//----------------------------------------------------------------------------
// 3: Number of tasks
#define NUMBER_OF_TASKS 3 // 1 to 255
//----------------------------------------------------------------------------
// 4: Number of ready queue entries, 0 to 254, see manual( MUST BE UPDATED BY USER);
// Defaults to number of tasks, as each task should have aa least one job;
// If Number of ready Q entries larger that number of total jobs => defaults to number of total jobs
#define NUMBER_OF_READYQ_ENTRIES 10
//----------------------------------------------------------------------------
// 5: Number of Mutexes
#define NUMBER_OF_MUTEXES 2 // 0 to 63
//----------------------------------------------------------------------------
// 6: Number of Semaphores
#define NUMBER_OF_SEMAPHORES 0 // 0 to 63
//----------------------------------------------------------------------------
// 7: Number of Data Qs
#define NUMBER_OF_DATAQS 0 // 0 to 63
//----------------------------------------------------------------------------
// 8: LOG data array size
#define LOG_DATA_ARRAY_SIZE_U32S 0x100// Calculated value. Read manual
//----------------------------------------------------------------------------
// 9: FIXED data array size
#define FIXED_DATA_ARRAY_SIZE_U32S 0x100// Calculated value. Read manual
//----------------------------------------------------------------------------
// 10: DYNAMIC data array size
#define DYN_DATA_ARRAY_SIZE_U32S 0x100// Calculated value. Read manual
//----------------------------------------------------------------------------
// 11: Timed Actions
#define NUMBER_OF_ACTION_ENTRIES 0 // User-modify field
//----------------------------------------------------------------------------
// 12: Log entries base 2
#define CS_LOG_DEF_ENTRIES_BASE2 0
//----------------------------------------------------------------------------
// 13: OCEOS stack start Address
#define OCEOS_STACK_START_ADDRESS 0
//----------------------------------------------------------------------------
// 14: OCEOS stack end address
#define OCEOS_STACK_LOW_BOUND_ADDRESS 0
/*
* Set up the application configuration structure and pass it to oceos_init
* (done this way the memory used for the structure is returned automatically)
*/
int application_init(){
/*
* Create the application configuration structure
*/
struct application_configuration app_config = {0};
/*
* Fill in the application parameters
*/
app_config.log_address = (adrs_t)log_data; // required
app_config.fixed_data_address = (adrs_t)fixed_data; // required
app_config.dynamic_data_address = (adrs_t)dynamic_data; // required
app_config.stack_start_address = OCEOS_STACK_START_ADDRESS; // 0 => no check
app_config.stack_lower_bound_address = OCEOS_STACK_LOW_BOUND_ADDRESS; // 0 => no check
app_config.system_error_function = &oceos_on_error; // NULL => ignore
app_config.log_full_function = &oceos_on_full_log; // NULL => ignore
#ifdef __sparc__
/**
* FOR SPARC TARGET ONLY.
* Not Used for ARM. Always Nested
*/
app_config.interrupt_nesting_enabled = TRUE; // Nesting Enabled
#endif
// used in setting up system log and fixed data array
app_config.log_number_of_entries = NUMBER_OF_LOG_ENTRIES; // 0 => use default
app_config.number_of_tasks = NUMBER_OF_TASKS; // >= 1
app_config.number_of_readyQ_entries = NUMBER_OF_READYQ_ENTRIES; // 0 => calculate size
app_config.number_of_mutexes = NUMBER_OF_MUTEXES;
app_config.number_of_counting_semaphores = NUMBER_OF_SEMAPHORES;
app_config.number_of_data_queues = NUMBER_OF_DATAQS;
#ifdef __sparc__
/**
* FOR SPARC TARGET ONLY.
* ARM SysTick is used for System Time
*/
app_config.sys_time_timer_index = SYS_TIME_TIMER_INDEX; // 0 => invalid index
#endif
app_config.timed_actions_queue_size = NUMBER_OF_ACTION_ENTRIES;
app_config.CS_log_entries_base2 = CS_LOG_DEF_ENTRIES_BASE2;
// System time is used by timed actions, semaphores with timeout, dataqs with timeout, logging and profiling.
// If all of above are not required then system time can be disabled. The system will not be interrupted by SysTick while CPU in sleep mode,
//but it is not the case for SPARC targets,
// as system time implemented by chaining two timers without interrupting.
app_config.use_oceos_system_time = TRUE;
// initialise OCEOS
enum DIRECTIVE_STATUS status;
status = oceos_init(app_config);
assert(SUCCESSFUL == status);
if (SUCCESSFUL == status) {
return 1;
} else {
printf("\n oceos_init failure\n");
return -1;
} // else
} // application_init()
OCEOS Memory Requirements
Memory usage depends on microcontroller, OCEOS modules and features, compilers and optimization level.
Table below provides memory requirements for different targets. Compiled with GNU GCC and optimization level 3.
Section | OCEOS Module | (-O3) Size ARM Cortex-M7(thumb) | (-O3) Size GR716 (REX) | (-O3) Size LEON4 |
---|---|---|---|---|
.text | oceos_pendsv_handler.o | 208 | ||
.text | initialisation.o | 2816 | ||
.text | oceos_start.o | 1460 | ||
.text | interrupt.o | 44 | ||
.text | tasks.o | 3372 | ||
.text | task_create.o | 400 | ||
.text | task_disable.o | 220 | ||
.text | task_enable.o | 144 | ||
.text | task_info.o | 160 | ||
.text | task_self.o | 76 | ||
.text | task_utils.o | 192 | ||
.text | task_kill.o | 108 | ||
.text | logging.o | 400 | ||
.text | logging_reset.o | 176 | ||
.text | logging_add.o | 244 | ||
.text | logging_remove.o | 180 | ||
.text | logging_size.o | 72 | ||
.text | logging_utils.o | 448 | ||
.text | oceos_timer.o | 340 | ||
.text | cpu_sleep.o | 208 | ||
.text | oceos_check_address_space.o | 4 | ||
Total Kernel | ||||
.text | dataq.o | 284 | ||
.text | dataq_clear.o | 364 | ||
.text | dataq_read_restart.o | 260 | ||
.text | dataq_read_restart_timeout.o | 404 | ||
.text | dataq_read_continue.o | 140 | ||
.text | dataq_write.o | 684 | ||
Total Dataq | ||||
.text | mutex.o | 164 | ||
.text | mutex_utils.o | 608 | ||
Total Mutex | ||||
.text | semaphore.o | 268 | ||
.text | semaphore_reset.o | 304 | ||
.text | semaphore_restart.o | 264 | ||
.text | semaphore_restart_timeout.o | 404 | ||
.text | semaphore_continue.o | 148 | ||
.text | semaphore_signal.o | 584 | ||
Total Semaphore | ||||
.text | timed_action.o | 2136 | ||
.text | timed_action_init.o | 208 | ||
.text | timed_action_sem_remove.o | 220 | ||
.text | timed_action_dataq_remove.o | 240 | ||
.text | timed_action_jobs.o | 200 | ||
.text | timed_action_output.o | 184 | ||
.text | timed_action_reset.o | 320 | ||
.text | timed_action_get_number.o | 172 | ||
.text | timed_action_jobs_remove.o | 116 | ||
.text | timed_action_output_remove.o | 84 | ||
.text | timed_action_timer.o | 96 | ||
.text | task_timed_start.o | 828 | ||
Total Timed Actions | ||||
.text | oceos_isr_cs_log.o | 628 | ||
Total | 21811 |
OCEOS 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.
Application developer must provide OCEOS with three data areas:
- Log data area
U32_t log_data[LOG_DATA_ARRAY_SIZE_U32S];
- Fixed data area
U32_t fixed_data[FIXED_DATA_ARRAY_SIZE_U32S] = {0}; // will be in data segment
- Dynamic data area
U32_t dynamic_data[DYN_DATA_ARRAY_SIZE_U32S];
OCEOS data areas layout OCEOS system data graph
OCEOS Main Concepts
An application that uses OCEOS typically starts at main(), performs device initialization and other activities including setting up interrupt handlers, then starts OCEOS in two phases, an initialization phase and a scheduling phase. Tasks, mutexes etc. are only created in the initialization phase, resulting in a fixed data structure that is passed to the scheduling phase. OCEOS normally does not return to the application but will do so if it detects a fatal error.
The specific application determines the tasks, mutexes and other OCEOS components used.
The interrupt/trap handlers respond to events, do the immediately necessary processing, and typically ask OCEOS to start a task to complete the processing.
OCEOS then creates an execution instance of the task, a ‘job’, and either puts it into immediate execution or on the ready queue or the timed jobs queue waiting to be scheduled for execution.
OCEOS schedules jobs based on their priority and may pre-empt the processor from a job with a lower pre-emption threshold before that job completes in order to start a higher priority job.
OCEOS maintains a system priority ceiling based on currently locked resources and the pre-emption threshold of the currently executing job. Only jobs with higher priority than this ceiling can pre-empt.
Jobs with the same priority are scheduled according to time of arrival on the ready queue. Time slicing or pre-emption between jobs of the same priority does not take place.
A job may have a deadline by which it must complete.
Note
It is the responsibility of the software developer to assign task priorities so that all jobs are guaranteed to meet their deadlines
In OCEOS mutual exclusion of tasks is supported by mutexes. Counting semaphores and data queues support task synchronization and the exchange of data between jobs.
Task start requests and data outputs (software timers) can be set to occur independently of scheduling at a given system time in microseconds.
OCEOS maintains a system state variable and system log. If the state variable is not normal OCEOS may call a user defined function to deal with whatever condition has occurred.
Tasks
In OCEOS all tasks are defined before scheduling begins and each task assigned a fixed priority, a pre-emption threshold, a current jobs limit, a maximum start to completion time, and an expected minimum inter start request time. A task’s pre-emption threshold limits the tasks that can pre-empt it to those with higher priority than this threshold. Tasks with short run times can use this to avoid context switch overheads.
Each task has two main states, enabled or disabled. OCEOS provides system calls to change task state. The state is usually enabled when the task is created, but can be initially disabled.
A disabled task will not be executed, and any attempt to start it will be logged and the system state variable updated.
A request to start an enabled task creates an execution instance of the task, a ‘job’. A count is kept for each task of the number of times it has been started, i.e. total number of jobs created.
Multiple jobs of the same task can be in existence simultaneously. A limit for the maximum number of these current jobs is set when a task is created, the ‘current jobs limit’.
Each task has a current jobs count. This increments each time a task is started and decrements on job completion unless completion creates a further job from this task.
A further execution instance of a task is not created if the current jobs count has reached the current jobs limit. Such job creation failures are logged and the system state updated.
For each task a record is kept of the shortest time between attempts to create a new job, and a record of the maximum time between these attempts.
For each task a record is kept also of the maximum time a job was waiting before starting, the minimum execution time, the maximum execution time, the maximum time from job creation to completion, and the maximum number of job pre-emptions. The system state variable is updated if necessary.
Task chaining is supported. A job can create a new job and pass this to the scheduler or place it on the timed actions queue.
Tasks provide a start address for a termination routine for use if task execution must be abandoned.
Jobs
The request to start a task initializes a 'job', an execution instance of the task. A parameter is provided to allow an optional structure pointer be passed to the job.
A job can require varying times to complete, with one exception its maximum execution time and maximum resource requirements are finite and known at compile time.
This one exception is the unique lowest priority ‘idle job’, which if it exists has no execution time limitation. If there is no idle task OCEOS will place the CPU in sleep mode.
Typically the ‘idle job’ is not idle, but used for system monitoring, logging, and initiating system correction activities. To save power it may put the CPU in sleep mode waiting for interrupts.
A job has two principal states, pending and active. A pending job has not started execution and is either on the ready queue or on the pending jobs queue of a semaphore or data queue, or on the timed actions queue. Only pending jobs wait on resources. In OCEOS a pending job becomes active when it first starts execution. An active job is either running on the CPU or on the ready queue after being pre-empted.
An active job may have to wait on the ready queue for the CPU but never has to wait for any other resource and is never on a pending jobs queue.
For each job a record is kept of the time it was created, the time it became active, the execution time, the time it completed, and the number of times it was pre-empted.
When a job completes, these job records are used to update the task records.
Data can be passed between jobs of the same or different tasks by using statically allocated variables, counting semaphores, or data queues.
A job may create another job and place this on the ready queue or on the timed actions queue.
Note
If job can not continue because semaphore count is zero or data queue is empty, the job is put on pending queue for that semaphore or data queue and restarted when semaphore count is incremented or data is written to data queue. The user must take care to save information in data section, so the restarted job could continue without loss of already acquired data.
Ready Queue
This priority queue contains pending and active jobs ordered highest priority first, and within priority earliest arrival first.
The scheduler places the first job on this queue into execution if and only if its priority is higher than the system priority ceiling. When it does so it updates the system priority ceiling to the pre-emption threshold of this job.
OCEOS places pending jobs on this queue due to an interrupt or trap, or as a result of a system call from the current job, a semaphore signal or queue write, or a timeout.
Each job in the Ready Queue is tagged with an ‘origin’ giving how the job came to be placed on the Ready Queue including time and identification of any other queue from which it was removed.
There can be multiple occurrences of jobs from the same task on the Ready Queue, each with its origin. Initially this queue either contains the Idle Job, which must have the lowest priority, or is empty.
Timed Actions Queue
This is a priority queue of pending actions (software timers) and their associated times, with queue priority based on these times.
It is linked to a high priority hardware timer which is set to interrupt at the time of the first action on the priority queue.
When this interrupt occurs actions on the queue within a timing tolerance of the current time are carried out and the timer is reset to interrupt at the start time of the earliest remaining action.
If the action is a task start request then at the set time the associated job is transferred to the ready queue, its origin set to timeout, and it is also removed from the pending list of any counting semaphore or data queue on which it is present. If the job is now the highest priority job on the ready queue it is started immediately, otherwise it must wait until that is the case.
If the action is a data output then at the set time it is performed immediately.
System Priority Ceiling
The value of this integer is the maximum priority of the priority ceilings of currently locked mutexes and the pre-emption threshold of the currently executing job. It is not directly accessible to an application.
Scheduling
OCEOS schedules jobs, i.e. execution instances of tasks created as a result of task start requests.
The scheduler places a job into execution if and only if its priority is higher than the system priority ceiling. When it does so it updates the system priority ceiling to the pre-emption threshold of this job.
Scheduling is pre-emptive and based on priority. The scheduler may pre-empt the processor from a lower priority job before that job completes to allow a higher priority job be started.
Jobs with the same priority are scheduled based on the order of their arrival on the ready queue. Time slicing or pre-emption between jobs of the same priority does not occur.
A job with a very short run time can avoid context switch overheads by having a high pre-emption threshold. This also allows pre-emption by some or all higher priority tasks be prevented. Such jobs with high pre-emption thresholds are scheduled based on their priority as usual.
In OCEOS a wait for a mutex causes the system priority ceiling to be raised to the priority ceiling of the mutex unless it was already higher. The system priority ceiling returns to its previous value when the mutex is signaled.
As a result the scheduler will not start any task that waits on that mutex until the mutex has been returned, as the mutex ceiling is the priority of the highest priority task that uses the mutex.
(Instead of a higher priority job pre-empting a lower priority job but before termination returning control to a lower priority job and waiting for it to return a mutex, the wait for the mutex is done first.)
In OCEOS context switching between tasks is minimized. A lower priority job only resumes execution when a higher priority job has completed, thus allowing stack sharing.
In OCEOS problems such as priority inversion, chained blocking, and deadlocks cannot occur, all tasks can share the same stack thus saving memory, and schedulability analysis is simplified.
Unnecessary blocking will occur for a job that does not use a mutex if it has a priority lower than the mutex priority ceiling and the mutex is currently held by a lower priority job.
This un-necessary blocking may seldom occur, is limited in duration, and can be avoided by setting task priorities appropriately.
This scheduling approach is based on the Stack Resource Policy extension of the Priority Ceiling Protocol Baker 1991.
Many data structures cannot be fully accessed with a single instruction. Errors can result if a job using such a structure is pre-empted by another job that also uses the structure.
To address this OCEOS provides up to 63 mutual exclusion semaphores (mutex). Each shared data structure or critical code segment typically is associated with its own mutex.
Any job that uses a shared item should first acquire its mutex from OCEOS, and when finished with the item return the mutex. The number of instructions for which the mutex is held must be finite.
OCEOS allows a mutex be held by only one job at any one time. A job that uses a shared data structure or critical code segment can use an associated mutex to exclude all other jobs.
N.B. An OS does not prevent a shared resource being accessed if no attempt is made to acquire its mutex. The software developer must ensure this is done before the shared resource is used.
Inter-Job Communication
OCEOS provides two types of inter-job communication, counting semaphores and data queues. Semaphores and data queues are handled by OCEOS in a way different to many other OS.
In most OS a job that waits on a zero semaphore or tries to read an empty data queue may be blocked at that point and wait there indefinitely or with an optional timeout.
In OCEOS an active job may be pre-empted but cannot otherwise be blocked, so three options are provided in case a counting semaphore is zero when waited on or a data queue is empty when read.
One option results in a value always being returned and the job continuing. The returned value will indicate that the semaphore was zero or queue empty, and the job can take this into account.
In the other options, if the semaphore is zero or queue empty the job terminates and will restart from its beginning when the semaphore is signaled or queue written, or after an optional timeout.
When the job becomes active again after a timeout and again encounters the second option the behavior is as with the first option, the job continues and takes into account that a timeout has occurred.
Note
Because a job restarts when a resource becomes available rather than waiting at the point where it sought the resource any local data developed up to that point will be lost unless stored non-locally for use after the restart. If required this can be done using static variables or structures, or using data queues or counting semaphores.
In OCEOS event communication between tasks or between tasks and interrupt handlers is done typically by using counting semaphores.
Pending Jobs Queue
Each counting semaphore and data queue has an associated queue of pending jobs that are waiting for that semaphore or queue. Pending jobs also wait on the timed actions queue.
Pending jobs queues contain only pending jobs and not active jobs, i.e. only jobs waiting to start execution.
A pending job can be on a semaphore pending jobs queue or on a data queue pending jobs queue and also on the timed actions queue.
The amount of time a job can spend on a semaphore or data queue pending jobs queue is of indefinite duration unless it is also on the timed actions queue.
When a job is removed from a semaphore or data queue pending jobs queue its origin is set accordingly. It is also removed from the timed actions queue if present.
When a timeout occurs and a job is removed from the timed actions queue its origin is set accordingly and it also is removed from a semaphore or data queue pending jobs queue if present.
Note
Mutexes do not having pending job queues. A pending job waiting for a mutex waits on the ready queue. Such waits are expected to be of short duration.
If it is necessary to pass data to a later job a job can do this before termination using static variables, static structures, data queues or counting semaphores.
All jobs on a pending jobs queue are moved to the ready queue in the order of their arrival on the pending jobs queue before any of them is scheduled. Usually only one job is waiting on a pending jobs queue, if more than one a lower priority job may have to return to the pending jobs queue because a higher priority job or an earlier job has consumed the item.
System State
OCEOS maintains a system state variable that is updated whenever there is anomalous behavior such as attempting to create more than the allowed number of jobs or writing to a queue that is full.
If this variable is not its normal value, depending on the anomaly OCEOS may call a user defined function to deal with the anomaly.
This typically reads the system log and the system state variable to determine the background to the detected anomaly.
Please consult the header file codes.h for the system state variable codes. These include anomalies such as (the indexes below are not the codes used):
- An attempt to start a disabled task
- An attempt to execute a task when its jobs limit is already reached.
- Job time from creation to completion exceeds allowed maximum for a task (deadline missed).
- Minimum time between job creations is less than the allowed minimum for task
- Ready queue unable to accept job as result of being full
- Mutex wait() when mutex already held
- Mutex signal() when not already held
- Mutex not returned before job terminates
- Attempt to add job to semaphore pending list when list full
- Data queue write when queue already full
- Attempt to add job to queue pending list when list full
- Timed job start write when timed actions queue already full
- Timed job start late job transfer to scheduler
- Timed output request when timed actions queue already full
- Timed output late
System Log
This contains time stamped records of anomalies detected during execution. It is structured as a circular buffer. System calls are provided to initialize the log, set its maximum size, and return the number of entries.
OCEOS will call a user defined function when the log is approximately 75% full.
Definitions and Abbreviated Terms
The following gives terminology used in relation to OCEOS RTOS and some clarification notes.
action: something to be done at a specified system time.
In OCEOS an action is done independently of task scheduling and is either a task start request or output of a value to an address.
area: a contiguous block of memory used for OCEOS internal data.
OCEOS does not use dynamic memory allocation to provide space for its internal data but instead sets up three statically defined areas based on the ASW configuration. The fixed data area holds information that does not change after all tasks etc. are created, the dynamic area holds information about tasks, jobs, etc. that changes as scheduling takes place, and the log area holds the system state variable and the system log, which are always present, and if used the context switch log. The start addresses of the three areas are given by the ASW and must be 32-bit word aligned. Starting from these addresses each area is created by OCEOS as a block of 32-bit words that starts with OCEOS_VERSION followed by the count in words of the area size and ending with END_SENTINEL. The size of each area depends on the characteristics of the application, the number of tasks, mutexes, etc., and does not change once set up. OCEOS provides a header file oceos_areas.h which makes area sizes and field offsets available as defined constants if certain application characteristics are predefined as constants. OCEOS also uses a single system stack and a small amount of space on the heap.
atomic: an operation which once started does not allow other actors carry out the operation until the current operation has completed.
application program interface (API): describes the application’s interface with OCEOS, usually referred to here as ‘directives’.
context switch: the change from executing the instruction codes of one task to executing those of another task.
Done by the OCEOS scheduler when it pre-empts a task.
context switch latency: the time taken to remember essential current task information and to set up or restore the new task’s information and put it into execution.
Please also refer to scheduling latency.
context switch log: a time-stamped record of context switches and switches to and from interrupt handlers for use by a debug monitor such as DMON.
counter: an unsigned integer typically updated by an interrupt or system call.
In OCEOS a counter may involve 8 bits, 16 bits or 32 bits. In OCEOS a counter does not wrap around through zero but remains at its maximum/minimum value when this has been reached.
counting semaphore: used to synchronize different tasks.
In OCEOS a counting semaphore has a 32-bit counter, a pending jobs list, a semaphore create directive oceos_sem_create used only in system initialisation, a directive to return the counter value, a directive to return the number of pending list jobs, and four atomic directives, oceos_sem_signal, oceos_sem_wait_continue, oceos_sem_wait_restart, and oceos_sem_wait_restart_timeout. Signal operations and wait operations on a semaphore usually occur in different tasks. In OCEOS waiting on a semaphore has three options: continue, restart, restart with wait timeout. If the semaphore counter is non-zero all three options behave in the same way, decrementing the counter and returning a status code. If the semaphore counter is zero what happen depends on the option the ASW has chosen. N.B. In OCEOS a job does not block at the point where it does a wait operation on a zero semaphore, depending on the option chosen it either continues or terminates and restarts later. If a job uses the restart option, any local data at that point that is to be available after the job restarts needs to be stored globally as local data is lost when the job terminates.
critical section: a sequence of instructions that should not be in use by more than one task at a time.
Usually protected by a mutex that is locked at the start of the critical section and released at the end. N.B. Critical sections should be as short as possible.
data queue: in OCEOS a queue of non-null void pointers used to exchange data between tasks and to synchronize tasks.
It has a oceos_dataq_create directive that is only used during system initialisation, a data queue, a pending jobs list, a directive that returns the number of pointers on the data queue, a directive that returns the number of jobs on the pending list, and four atomic directives, oceos_dataq_write, oceos_dataq_read_continue, oceos_dataq_read_restart, and oceos_dataq_read_restart_timeout. Write operations and read operations on a queue usually occur in different tasks. In OCEOS reading a queue has three options: continue, restart, restart with wait timeout. If the queue is non empty all three options behave in the same way, returning the pointer at the head of the queue. If the queue is empty what happen depends on the option the ASW has chosen. N.B. In OCEOS a job does not block at the point where it does a read operation on an empty queue, depending on the option chosen it either continues or terminates and restarts later. If a job uses the restart option, any local data at that point that is to be available after the job restarts needs to be stored globally as local data is lost when the job terminates.
deadlock: a scheduling condition where jobs cannot proceed because each holds a resource the other needs.
In OCEOS deadlocks cannot occur when mutexes are configured correctly.
directive: used by the application software to call an individual OCEOS function.
Directives provide control over tasks, message queues, semaphores, memory, timers etc.
dynamic area: area used by OCEOS for internal data that changes as part of normal scheduling operations, the area contents change but its size is constant.
It contains information such as e.g. the current system priority ceiling, the ready queue, states of tasks, jobs, mutexes etc. It is set up and initialised by oceos_start based on the fixed area information passed to it. Please also refer to ‘area’ above.
error handling: actions taken when OCEOS detects that a problem has occurred.
In OCEOS error handling may involve one or a combination of (i)returning an appropriate status code (ii) making a system log entry (iii) updating the system state variable (iv) calling a user defined problem handling function (v) ending scheduling and exiting. If called the problem handling function can use the log, the system state variable, task and job timings and other data to determine the action that should be taken. If OCEOS detects corruption of its data areas it exits and returns to the ASW, which can access the system log, system state variable, and other information such as task timings.
error hook: a reference to the user defined function called when an abnormal condition is detected.
event: in some OS, but not in OCEOS, a job can suspend its own execution until a specified event occurs.
In OCEOS an active job may be pre-empted by a higher priority job but never suspends itself. In OCEOS similar functionality is provided by a wait restart on a counting semaphore that is signalled later by an interrupt handler.
fixed area: area used by OCEOS for internal data that is defined in OCEOS initialisation and as tasks, mutexes etc. are created, this area does not change after oceos_init_finish is called.
It contains information such as number of tasks, task priorities, priority ceilings of mutexes, etc. The area is initialised by oceos_init and finalised after all tasks etc. have been created by oceos_init_finish, which adds an XOR checksum in the penultimate word. The fixed area is passed to oceos_start, which begins scheduling. Please also refer to ‘area’ above.
hook: a reference to a user defined function called by the operating system in certain conditions.
idle job: an execution instance of the unique lowest priority task, it is allowed to run indefinitely.
As OCEOS does not use time slicing and lower priority jobs wait for higher priority jobs to complete all other jobs should terminate after a finite time period. Typically this ‘idle job’ is not idle, but used for managing a watchdog timer, system monitoring and initiating system correction activities. To save power it may put the CPU in sleep mode waiting for interrupts (after disabling the watchdog if one is used).If no idle job is present OCEOS puts the CPU in sleep mode when no job is ready to run.
initialization code: this code executes when the CPU is reset and before main is called.
It is part of the BSP system used before main in the ASW. It is not part of OCEOS.
interrupt: a form of trap caused by an input to the CPU from an interrupt controller due to a change in state of one of the external links connected to the interrupt controller.
Please refer to ‘trap’.
interrupt handler: the code called when an interrupt occurs.
Please refer to ‘trap handler’.
interrupt latency: the time between the occurrence of the external interrupt condition and the execution of the first instruction of the interrupt handler.
interrupt level: the interrupt priority as determined by the CPU architecture and the interrupt controller.
interrupt nesting: occurs when an interrupt handler is interrupted by a higher priority interrupt.
Please see 'trap nesting'
interrupt service routine: the interrupt handler associated with the interrupt.
job: a record of a request to start a task or the resulting execution instance of the task.
Please refer to ‘task job’.
kernel mode: the CPU mode in which all instructions are available for use and all physical memory addresses accessible.
N.B. When using OCEOS both the ASW and OCEOS are expected to run in kernel mode.
log area: an OCEOS memory area used to store system state information including the system state variable and the system log and if used the context switch log.
As this area is usually accessed only when a problem occurs, it may be stored in relatively slow external memory without impacting performance, and if this is non-volatile aspects of OCEOS state are preserved across power cycles. Please also refer to ’area’.
manager: a group of OCEOS directives with related functionality.
Managers may be core (used whenever OCEOS is used) or optional (selected or not by the application developer).
module: OCEOS is structured as independent modules responsible for different OS features.
If a module is not used it is omitted at link time. Core manager modules are always present.
mutex: a binary semaphore used to provide mutual exclusion.
In OCEOS each mutex has a priority ceiling. This is set when the mutex is created to the priority of the highest priority task that uses the mutex. If this is done correctly the design of OCEOS ensures that unbounded priority inversion, chained blocking, and deadlocks cannot occur, a significant advantage compared to most other RTOS. The wait and signal operations used to acquire and release a mutex should occur in pairs in the same task, and in a nested manner if multiple mutexes are used. The number of instruction executions between wait and signal must be finite and should be as short as possible. N.B. Mutexes provide mutual exclusion for tasks and not for interrupt handler code. N.B. The ASW is responsible for associating a mutex with a shared resource and ensuring it is used whenever the shared resource is accessed.
mutual exclusion: it must be possible for a task that is updating a resource to exclude other tasks from accessing the resource until updating is complete.
This is usually achieved by associating a specific mutex with each resource, and ensuring that any task that accesses the resource first acquires the mutex (wait) and releases it when access is complete (signal). Please refer to ‘mutex’.
origin: this indicates whence a pending job came when it was transferred to the ready queue.
The job may have just been created, or have been transferred from a pending jobs list as a result of a counting semaphore being signalled or data queue being written, or from a timed action queue as a result of a timeout.
pending jobs list: Each counting semaphore and each data queue has a pending jobs list of jobs waiting to be restarted when the resource becomes available.
Jobs are added to the list if the restart option is used and an attempt to wait on the semaphore or read the data queue fails. Following a signal or write all jobs on a pending list are transferred to the ready queue in the order in which they were placed on the list (FIFO), and the scheduler is called. Note: In OCEOS a mutex does not have a pending jobs list, jobs are only put into execution if all mutexes they require are free.
pending jobs queue: A queue of jobs organised based on priority rather than on arrival order, with usually no more than one job at a time removed from the queue.
The timed actions queue is a priority queue based on activation time.
pre-empt: the scheduler may pre-empt the processor from the currently running job when a job with higher priority than the current system priority ceiling is ready to start.
pre-emption threshold: a task’s pre-emption threshold restricts the tasks that can pre-empt it to those with higher priority than this threshold.
Please refer to ‘task pre-emption threshold’.
priority: a measure of urgency or importance.
In OCEOS priorities range from 0 (highest priority) to 255 (lowesr priority). Priority levels 0 and 255 are reserved for internal OCEOS use. Task priorities are restricted to from 1 (highest) to 254 (lowest). Please refer to ‘task priority’.
priority ceiling: each mutex has a priority ceiling equal to the priority of the highest priority task that uses it.
This ceiling is determined by the ASW developer during code development and set when the mutex is created. OCEOS will only start a pending job if its priority is higher than the priority ceilings of all currently locked mutexes.
priority ceiling protocol: an approach to scheduling and to sharing resources.
OCEOS is based on an extension of this, the stack resource policy BAK91.
priority inversion: when a higher priority job has to wait in order to allow a lower priority job complete processing a shared resource and return a mutex.
In OCEOS this wait occurs before the higher priority task starts. The maximum length of the wait can be determined during code development.
priority inversion (unbounded): if a lower priority job can be pre-empted while holding a mutex the resulting delay to the return of the mutex by the low priority job can cause unbounded delays to a higher priority job that requires the mutex.
This cannot occur in OCEOS when mutexes are configured correctly.
priority queue: the order in which elements are removed is based on their priorities rather than on the order in which they arrived on the priority queue.
In OCEOS the ready queue is a priority queue based on job priority and order of arrival, the timed actions queue is a priority queue based on time.
queue: A first in first out (FIFO) queue such as an OCEOS data queue.
ready queue: a priority queue of jobs with priority based on job priority and within priority on order of arrival.
In OCEOS this holds references to pending jobs which have not yet started running, to active jobs that have been pre-empted, and to the currently executing job. A job is removed from the ready queue when it terminates. The OCEOS scheduler puts the first job on this queue into execution on the processor if and only if its priority is higher than the current system priority ceiling.
resource: may be a shared resource that can be accessed by different tasks.
Shared resources can give rise to many problems, please refer to 'mutual exclusion ' and ‘mutex’.
running: the running job is the job whose instructions are currently being executed on the processor.
In OCEOS an active job is running unless interrupted or pre-empted, it is never in a suspended state waiting on a resource (other than the CPU).
scheduler: a central part of an RTOS such as OCEOS, it determines which job should be executed on the processor.
In order to allow another job begin the scheduler may pre-empt the processor from the currently executing job.
scheduling latency: the time between the execution of the last instruction used in the old task and the execution of the first instruction used in the new task when the scheduler switches the processor between tasks.
It includes the context switch latency and the time taken by other scheduler actions.
scheduling policy: determines whether the currently executing job should be pre-empted.
In OCEOS this is based on the stack resource policy with fixed task priorities and single resources BAK91. Pre-emption occurs if and only if the priority of the new job to be started is higher than the current system priority ceiling. N.B. By setting appropriate task priorities RMS and other systems can be used with OCEOS.
semaphore: Please refer to ‘counting semaphore’ and also to ‘mutex’.
shutdown hook: address of routine called to shut down the system.
signal: in OCEOS refers to a type of directive used to unlock a mutex or to attempt to increment a counting semaphore.
Please refer to ‘mutex’ and ‘counting semaphore’.
sleep mode: activities in the CPU are powered off except those required to monitor external interrupts.
When an interrupt is detected the CPU vectors to the corresponding interrupt handler and resumes normal execution.
software component: system settings data, initialisation code, trap handler, application tasks, and applicable OCEOS modules.
stack resource policy: an extension of the priority ceiling protocol that makes unbounded priority inversion, chained blocking and deadlocks impossible when mutexs are configured correctly and allows all tasks to share a single stack rather than requiring a separate stack for each task BAK91.
N.B. OCEOS is based on this policy.
start a task: create a job, put this job on the ready queue, and call the scheduler after allowing any interrupt nesting present to unwind.
Please see 'task start'
status code: a code returned when an OCEOS directive is used indicating success or failure.
In most cases this is one of a predefined set of codes that indicate the type of problem if a problem has occurred, in one case a problem is indicated by return of a NULL pointer. N.B. The returned status code should always be checked when an OCEOS directive is used.
suspended: no jobs of the task are currently pending or active.
system log: used to record log entries made by OCEOS or by the ASW.
Stored in the OCEOS log data area whose start address is given by the application developer. Structured as an array of 64-bit log entries. The maximum number of log entries, which should be in the range 16 to 1024, is given by the application developer, with default 64. The log is treated as a circular buffer and the application developer can define a function to be called when the log becomes ¾ full. Log entries are preserved across system reset and if stored in non-volatile memory across power on-off cycles. Please refer to 'log area'.
system priority ceiling: an integer in the range 0 to 255, for a job to start it must be higher priority than the system priority ceiling.
Its value is set to 255 (lowest priority) at system start and subsequently determined by the pre-emption threshold of the currently executing job and the priority ceilings of any mutexes currently held.
system priority ceiling stack: used to hold successive system priority ceiling values on a LIFO basis, initialized to hold the value 255 (lowest priority).
In OCEOS this is a byte array with up to 256 entries (64 words), depending on the number of tasks in an application. Note: should not be confused with the system stack.
system state variable: this is updated by OCEOS when certain error conditions have been detected.
When OCEOS does so it may also call a user defined problem handling function that can access this variable, the system log, and timing and other information about tasks, etc. in deciding the action to take. This variable can be accessed by the ASW at any time and can be reset by the application. The variable is reset initially by oceos_init. A copy of this variable is also maintained by OCEOS, this copy is intended to provide a long term record of any problems that have occurred and can be reset and checked by the ASW.
task: the ASW involves a number of relatively independent tasks and also interrupt handling routines.
Each task has a principal function that is called to start job execution, and an optional termination function that can be used to terminate task execution in an orderly way if a task has to be aborted. Each task has a fixed priority and fixed pre-emption threshold and a fixed limit to the number of its jobs that can be pending or active simultaneously. The fixed task pre-emption threshold facilitates avoiding context switch overheads for task with very short execution times, and enables task pre-emption be restricted to tasks with significantly higher priorities if desired. OCEOS can be asked to start a task (create a job) before a previous execution of the task has completed, or even begun. A pointer is passed with a task start request that can be used to select the structure processed by a job. Please refer to ‘task job’. A task can be enabled or disabled, in the disabled state attempts to put it into execution fail but are logged. Data stored for each task includes number of times executed, maximum number of current execution instances, maximum number of times pre-empted, minimum time between start requests, maximum activation wait time, maximum execution time and maximum time from start request to completion. This data is updated each time one of the task’s jobs completes.
task chaining: a task can cause another task to start, optionally after a specified time.
When a task starts another task it may be pre-empted by the new task. A task can cause itself to restart, but should always choose an appropriate time delay before the restart occurs to ensure lower priority tasks are not locked out.
task job: a request to start a task or the resulting execution instance of the task.
A task start request can occur before a job created by a previous request has completed execution, or even begun execution, so a task can have a number of jobs waiting to begin execution. The maximum allowed number of jobs for a task is specified when the task is created. A request to start a task is accompanied by a pointer which is passed to the resulting job allowing different jobs of the same task act on different data. All jobs must complete in finite time with the exception of the idle job. Space to record each job’s information is set aside in the dynamic area by oceos_start. A job is ‘free’ if its space is not in use, ‘pending’ if it has not yet started execution, ‘active’ if it has started execution. Only pending jobs are placed on pending jobs lists of semaphores or data queues or on the timed actions queue. Active jobs can be pre-empted by higher priority jobs but otherwise run to completion and are never on pending jobs lists or on the timed actions queue. Data stored for each job includes origin, creation time, activation time, execution time, time to completion, and number of pre-emptions. This job data is used to update the task data when a job terminates.
task jobs limit: this integer is the maximum number of a task’s jobs that can be current, i.e. pending or active at the same time.
It is set when the task is created in the range 1 to 15. It should be set to 2 or more unless it is certain that a task start request cannot occur while a task job is already active or pending. N.B. An attempt to create more than this number of current jobs will fail with an error status being returned, a log entry being made, and the system state variable being updated.
task pre-emption threshold: a task’s pre-emption threshold restricts the tasks that can pre-empt it to those with higher priority than this threshold.
A task’s pre-emption threshold is fixed and set when the task is created. It is never lower priority than the task’s priority. A high threshold can be used to prevent pre-emption and avoid context switch overheads for tasks with short run times. A threshold can also be used to allow only the highest priority tasks pre-empt a task while dis-allowing pre-emption by some higher priority tasks.
task priority: a measure of the urgency associated with executing a task’s instructions.
In OCEOS task priorities are integers in the range 1 (highest task priority) to 254 (lowest task priority) and are fixed when a task is created. More than one task can have the same priority. If a task does not terminate (e.g. the idle task) it must have the lowest task priority to avoid locking out lower priority tasks, and should be the only task with that priority. Tasks with the same priority are executed in FIFO order. Time-slicing or pre-emption between tasks of the same priority does not occur in OCEOS. Please refer also to ‘priority’. N.B. Selecting appropriate priorities for tasks is a key responsibility of the application developer.
task start: create a job, put this job on the ready queue, and call the scheduler after allowing any interrupt nesting present to unwind.
If the task has higher priority than the current system priority ceiling the scheduler will pre-empt the current active job and place the new job into execution. In OCEOS after a job is created it remains in a ‘pending’ state until it is first put into execution on the processor, becoming ‘active’ once it has started execution. N.B. If the number of current jobs for this task is already at this task’s jobs limit, a new job will not be created and an error will be reported.
task state: in OCEOS a task has two states, enabled and disabled.
OCEOS provides directives to switch a task between states. A task start request only creates a job if the task is enabled. Disabling a task terminates all pending jobs of that task. A task can only be re-enabled once a previous disabling has completed.
terminate: when a job finishes execution it is said to terminate.
In OCEOS every job except perhaps the idle job terminates after a finite execution time. A job usually terminates itself by exiting its principal function or by calling its termination function. In OCEOS an active job automatically terminates and becomes a pending job on the pending jobs list of a counting semaphore or data queue and/or on the timed actions queue if nothing is available when accessing the semaphore or data queue and the restart option has been chosen. The job restarts when the semaphore is signalled or queue written, or after a timeout. OCEOS terminates all a task’s pending jobs when it disables a task. On job termination OCEOS updates task parameters such as maximum time to completion and maximum number of pre-emptions.
timed actions queue: a priority queue of pending actions and their associated starts times, with queue priority based on time.
It is linked to a timer which is set to interrupt at the time of the first action on the priority queue. After the timed actions have been carried out the timer is reset to interrupt at the start time of the earliest remaining action.
timed action: an action to be done at a specified time involving either transferring a pending job to the ready queue or outputting a value to an address.
Involves specifying a forward time tolerance and a backward time tolerance. When a timed action occurs this action and other actions whose forward timing tolerances include the current time are carried out. The backward tolerance allows the action be performed if the current time is later than the requested time by no more than this amount. If set to zero late actions are not performed. All late actions are logged and the system state variable updated.
trap: causes a transfer of control to an address associated with the trap’s identity number.
There are three main types of trap. Exceptions are generated by the CPU itself as a result of some exceptional condition such as divide by zero. Software traps are generated by special software trap instructions that give the trap number. Interrupts are caused by external devices usually via an interrupt controller. N.B. Traps should be disabled for the minimum time possible to avoid missing interrupts or delaying the response to them.
trap handler: code that takes the steps immediately necessary to deal with a trap (including interrupts).
This code may request OCEOS to start a task, if for an interrupt this code typically resets the cause of the interrupt.
trap vector table: traps (including interrupts) use their identity number to vector to a location in this table which determines the handler code to use.
user mode: code running in user mode must delegate most access to hardware to kernel mode software, typically using a software trap or traps.
This mode is often not used in embedded software where the application code typically requires direct access to the hardware. N.B. In OCEOS application code is expected to run in kernel mode not in user mode.
wait: in OCEOS refers to a type of directive used to attempt to lock a mutex or to decrement a counting semaphore.
OCEOS does not provide a wait(event) directive, a similar capability can be provided by an interrupt caused by an event signaling a counting semaphore. Please refer to ‘mutex’ and ‘counting semaphore’.
waiting: a state in which a task/job has commenced execution but is not able to continue and must wait for a resource, usually retaining its current stack frame and other context and allowing lower priority tasks/jobs run. In OCEOS this state cannot occur.
In OCEOS waiting is done only by a pending job, i.e. a job that has not yet started execution and so has no current stack frame, such jobs are described as ‘pending’ rather than ‘waiting’. An active job, i.e. a job that has commenced execution, may be pre-empted by a higher priority job and have to wait to regain the CPU until a higher priority task finishes, but never is delayed by anything except termination of a higher priority job or the ending of an interrupt handling routine.
warning: an OCEOS action that indicates execution was not normal.
OCEOS provides different warnings, including returned status codes, system log entries, system state updates, calls to a user defined function, and in the worst case exit from OCEOS.
watchdog timer: a timer connected to the processor reset input so that a system reset will be caused if the timer times out.
Usually the lowest priority task has responsibility for resetting the timer before this happens. The timeout interval is chosen so that all higher priority tasks will have completed within the interval thus allowing return to the lowest priority task that then resets the timer. Protects against system lockups due to infinite loops and transient hardware problems. N.B. Creation and management of a watchdog timer is the responsibility of the ASW.