📄 emCompress-Embed User Guide & Reference Manual
📄 emCompress-LZMA User Guide & Reference Manual
📄 emCompress-ToGo User Guide & Reference Manual
📄 emCrypt User Guide & Reference Manual
📄 emDropbox User Guide & Reference Manual
📄 emFile User Guide & Reference Manual
📄 emFloat User Guide & Reference Manual
📄 emNet User Guide & Reference Manual
📄 emRun User Guide & Reference Manual
📄 emSecure-ECDSA User Guide & Reference Manual
📄 emSecure-RSA User Guide & Reference Manual
📄 emSSH User Guide & Reference Manual
📄 emSSL User Guide & Reference Manual
📄 emUSB-Device User Guide & Reference Manual
📄 emUSB-Host User Guide & Reference Manual
📄 emVNC User Guide & Reference Manual
📄 emWeb User Guide & Reference Manual
📄 emWin User Guide & Reference Manual
📄 IoT Toolkit User Guide & Reference Manual
📄 SEGGER Assembler User Guide & Reference Manual
📄 SEGGER Compiler User Guide & Reference Manual
📄 SEGGER Linker User Guide & Reference Manual
📄 SEGGER SystemView User Guide
📄 SEGGER Online Documentation
📄 AppWizard User Guide & Reference Manual
📄 embOS Real-Time Operating System User Guide & Reference Manual
📄 embOS-Ultra Real-Time Operating System User Guide & Reference Manual

embOS-Ultra
Real-Time Operating System User Guide & Reference Manual
Document: UM01076
Software Version: 5.16.0
Document revision: 0

Introduction and Basic Concepts

What is embOS?

embOS is a priority-controlled multitasking system, designed to be used as an embedded operating system for the development of real-time applications for a variety of microcontrollers.

embOS is a high-performance tool that has been optimized for minimal memory consumption in both RAM and ROM, as well as high speed and versatility. Throughout the development process of embOS, the limited resources of microcontrollers have always been kept in mind. The internal structure of the real-time operating system (RTOS) has been optimized in a variety of applications with different customers, to fit the needs of industry. Fully source-compatible implementations of embOS are available for a variety of microcontrollers, making it well worth the time and effort to learn how to structure real-time programs with real-time operating systems.

embOS is highly modular. This means that only those functions that are required are linked into an application, keeping the ROM size very small. A couple of files are supplied in source code to make sure that you do not loose any flexibility by using embOS libraries and that you can customize the system to fully fit your needs.

The tasks you create can easily and safely communicate with each other using a number of communication mechanisms such as semaphores, mailboxes, and events.

Some features of embOS include:

Differences between embOS and embOS-Ultra

The main difference between embOS and embOS-Ultra is that the latter requires no periodic system tick. Instead, with embOS-Ultra, system tick interrupts occur only when the scheduler needs to perform some time-based action.

embOS with periodic system tick

embOS uses a hardware timer to generate periodic system tick interrupts which are utilized as a time base. In most applications the system tick occurs each millisecond, but can also be changed to occur with any other period. Since the period might differ, all timeouts and periods are specified in system tick instead of, for example, milliseconds.

Even if there is only one task that is executed for several consecutive system ticks (which means the scheduler will not be executed during this time), the system tick interrupt will still occur periodically and thereby “waste” computation time. Furthermore, time-based functionality like task delays or timeouts are always aligned to the system tick interrupt. A task delay cannot expire between two system tick interrupts, but with the next system tick interrupt only which then triggers the scheduler. Therefore tasks that shall delay for a period that is shorter than a system tick, can only accomplish this by actively waiting until the desired period has elapsed.

embOS-Ultra with flexible system tick

embOS-Ultra does not rely on a periodic system tick, but uses a flexible system tick that is specifically configured by the operating system to occur whenever a time-based action is required. This avoids unnecessary system tick interrupts and also allows delays and timeouts to expire at arbitrary points in time (limited by the frequency of the used hardware timer only). As there are no periodic tick interrupts, however, the system time can no longer be held in system ticks, but is held in counter cycles instead. For the same reason, timed embOS-Ultra API functions use milliseconds instead of system ticks unless explicitly stated otherwise (in which case microseconds or counter cycles are used instead).

Hardware timer

While embOS requires the target hardware to provide a hardware timer, embOS-Ultra requires the target hardware to provide a hardware timer and a continuously running counter (although the latter may also be part of the former). With embOS-Ultra, the hardware timer is used to generate the system tick interrupt while the continuously running counter provides a time base to calculate the current system time in counter cycles.

For example, applications could use a hardware timer that generates interrupts when its continuously running counter matches a specific value. In that case, the counter would serve for long-term stability while the compare register is used to generate interrupts when required. Alternatively, it also is possible to use any hardware timer for generating interrupts and an additional continuously running counter for long-term stability. In both cases the continuously running counter should never be stopped by the application since it is essential to long-term stability.

The frequencies of the used timer and counter may differ, specifically when using different timers/counters. In this case, the embOS system time matches counter cycles. Unless explicitly stated otherwise, the embOS-Ultra manual always refers to counter cycles when it mentions “cycles”.

The maximal period of the hardware timer is of no relevance to embOS-Ultra: If the next time-based action lies further in the future than the maximal period of the used hardware timer, the operating system will simply set up the timer multiple times until the desired point in time is reached.

For more information on how to implement the hardware timer routines, please refer to Board Support Packages.

embOS ports

embOS is available for many core and compiler combinations. The embOS sources are written in C but a small part is written in assembler and therefore core and compiler specific. Hence, an embOS port is always technically limited to one core or core family and one compiler. An embOS port includes several board support packages for different devices and evaluation boards. Each board support package includes a project for a specific IDE. In most embOS ports the same IDE is used for all board support packages.

Additional documentation

Some embOS aspects are core and compiler specific and explained in a separate embOS manual which is shipped in the according embOS port shipment.

Example Cover of embOS Cortex-M ES Manual

Naming convention

All embOS ports use the same naming convention: embOS_<core>_<compiler>. For example: embOS_CortexM_ES, embOS for Cortex-M and Embedded Studio

Version number convention

SEGGER releases new embOS versions with new features and bug fixes. As soon as a new embOS version is released embOS ports are updated to this version.

Generic embOS

Each release of the generic embOS sources has a unique version number:

V<Major>.<Minor>.<Patch>

For example:

V5.10.1

Major: 5
Minor: 10
Patch: 1

Major and minor values are used for new features. The patch value is used for bug fixes only.

embOS Ports

An updated embOS port has the same version number as the used generic embOS sources, plus an additional revision for the port. This is because an embOS port may be updated for changes in the CPU/compiler specific part, while still using the same generic embOS sources. The complete version number for a specific embOS port is defined as:

V<Major>.<Minor>.<Patch>.<Revision>

For example:

V5.10.1.0

Major: 5
Minor: 10
Patch: 1
Revision: 0

Tasks

In this context, a task is a program running on the CPU core of a microcontroller. Without a multitasking kernel (an RTOS), only one task can be executed by the CPU. This is called a single-task system. A real-time operating system, on the other hand, allows the execution of multiple tasks on a single CPU. All tasks execute as if they completely “owned” the entire CPU. The tasks are scheduled for execution, meaning that the RTOS can activate and deactivate each task according to its priority, with the highest priority task being executed in general.

Threads vs. Processes

Threads are tasks that share the same memory layout, hence any two threads can access the same memory locations. If virtual memory is used, the same virtual to physical translation and access rights are used.
With embOS, all tasks are threads: they all have the same memory access rights and translation (in systems with virtual memory).

Processes are tasks with their own memory layout. Two processes cannot normally access the same memory locations. Different processes typically have different access rights and (in case of MMUs) different translation tables. Processes are not supported with the current version of embOS.

Single-task systems (superloop)

The classic way of designing embedded systems does not use the services of an RTOS, which is also called “superloop design”. Typically, no real time kernel is used, so interrupt service routines (ISRs) are used for the real-time parts of the application and for critical operations (at interrupt level). This type of system is typically used in small, simple systems or if real-time behavior is not critical.

Typically, since no real-time kernel and only one stack is used, both program (ROM) size and RAM size are smaller for simple applications when compared to using an RTOS. Obviously, there are no inter-task synchronization problems with a superloop application. However, superloops can become difficult to maintain if the program becomes too large or uses complex interactions. As sequential processes cannot interrupt themselves, reaction times depend on the execution time of the entire sequence, resulting in a poor real-time behavior.

Advantages & disadvantages

Advantages

Disadvantages

Using embOS in superloop applications

In a true superloop application, no tasks are used, hence the biggest advantage of using an RTOS cannot be utilized unless the application is re-written for multitasking. However, even with just one single task, using embOS offers the following advantages:

Migrating from superloop to multi-tasking

A common situation is that an application exists for some time and has been designed as a single-task super-loop-application. At some point, the disadvantages of this approach result in a decision to use an RTOS. The typical question now usually is: How do I do this?

The easiest way is to start with one of the sample applications that come with embOS and to add the existing “super-loop code” into one task. At this point, you should also ensure that the stack size of this task is sufficient. Later, additional functionality is added to the software and can be put in one or more additional tasks; the functionality of the super-loop can also be distributed over multiple tasks.

Multitasking systems

In a multitasking system, there are different ways to distribute CPU time among different tasks. This process is called scheduling.

Task switches

There are two types of task switches, also called context switches: Cooperative and preemptive task switches.

A cooperative task switch is performed by the task itself. As its name indicates, it requires the cooperation of the task: it suspends itself by calling a blocking RTOS function, e.g. OS_TASK_Delay() or OS_TASKEVENT_GetBlocked().

A preemptive task switch, on the other hand, is a task switch that is caused externally. For example, a task of higher priority becomes ready for execution and, as a result, the scheduler suspends the current task in favor of that task.

Cooperative multitasking

Cooperative multitasking requires all tasks to cooperate by using blocking functions. A task switch can only take place if the running task blocks itself by calling a blocking function such as OS_TASK_Delay() or OS_MAILBOX_GetBlocked(). This is illustrated in the diagram below.

If tasks in a pure cooperative multi-tasking system do not cooperate, the system “hangs”. This means that other tasks have no chance of being executed by the CPU while the first task is being carried out. Even if an ISR makes a higher-priority task ready to run, the interrupted task will be resumed and completes before the task switch is made.

A pure cooperative multi-tasking system has the disadvantage of longer reaction times when high priority tasks become ready for execution. This makes their usage in embedded real-time systems uncommon.

Preemptive multitasking

Real-time operating systems like embOS operate with preemptive multitasking. The highest-priority task in the READY state always executes as long as the task is not suspended by a call of any blocking operating system function. A high-priority task waiting for an event is signaled READY as soon as the event occurs. The event can be set by an interrupt handler, which then activates the task immediately. Other tasks with lower priority are suspended (preempted) for as long as the high-priority task is executing. Usually, real-time operating systems utilize a timer interrupt that interrupts tasks and thereby allows to perform task switches whenever timed task switches are necessary.

Preemptive multitasking may be switched off in sections of a program where task switches are prohibited, known as critical regions. embOS itself will also temporarily disable preemptive task switches during critical operations, which might be performed during the execution of some embOS API functions.

Scheduling

There are different algorithms used by schedulers to determine which task to execute. But all schedulers have one thing in common: they distinguish between tasks that are ready to be executed (in the READY state) and other tasks that are suspended for some reason (delay, waiting for mailbox, waiting for semaphore, waiting for event, etc). The scheduler selects one of the tasks in the READY state and activates it (executes the body of this task). The task which is currently executing is referred to as the running task. The main difference between schedulers is the way they distribute computation time between tasks in the READY state.

Priority-controlled scheduling algorithm

In real-world applications, different tasks require different response times. For example, in an application that controls a motor, a keyboard, and a display, the motor usually requires faster reaction time than the keyboard and the display. E.g., even while the display is being updated, the motor needs to be controlled. This renders preemptive multitasking essential. Round-robin might work, but as it cannot guarantee any specific reaction time, a more suitable algorithm should be used.

In priority-controlled scheduling, every task is assigned a priority. Depending on these priorities, a task is chosen for execution according to one simple rule:

Note

The scheduler activates the task that has the highest priority of all tasks and is ready for execution.

This means that every time a task with a priority higher than the running task becomes ready, it becomes the running task, and the previous task gets preempted. However, the scheduler can be switched off in sections of a program where task switches are prohibited, known as critical regions.

embOS uses a priority-controlled scheduling algorithm with round-robin between tasks of identical priority. One hint at this point: round-robin scheduling is a nice feature because you do not need to decide whether one task is more important than another. Tasks with identical priority cannot block each other for longer periods than their time slices. But round-robin scheduling also costs time if two or more tasks of identical priority are ready and no task of higher priority is, because execution constantly switches between the identical-priority tasks. It usually is more efficient to assign distinct priority to each task, thereby avoiding unnecessary task switches.

Round-robin scheduling algorithm

With round-robin scheduling, the scheduler has a list of tasks and, when deactivating the running task, it activates the next task that is in the READY state. Round-robin can be used with either preemptive or cooperative multitasking. It works well if you do not need to guarantee response time. Round-robin scheduling can be illustrated as follows:

The possession of the CPU changes periodically after a predefined execution time among all tasks with the same priority. This time is specified in time slices and may be defined individually for each task.

Priority inversion / priority inheritance

The rule the scheduler obeys is:

Activate the task that has the highest priority of all tasks in the READY state.

But what happens if the highest-priority task is blocked because it is waiting for a resource owned by a lower-priority task? According to the above rule, it would wait until the low-priority task is resumed and releases the resource. Up to this point, everything works as expected. Problems arise when a task with medium priority becomes ready during the execution of the higher prioritized task.

When the higher priority task is suspended waiting for the resource, the task with the medium priority will run until it finishes its work, because it has a higher priority than the low-priority task. In this scenario, a task with medium priority runs in place of the task with high priority. This is known as priority inversion.