OSEK OS in Practice Part 2
Now that we learned how to use tasks, events and ISR's, we dive into counters, alarms, shared resources and hooks, in the backbone of the Autosar OS
In our previous articles OSEK OS Overview (opens in a new tab) and OSEK OS in Practice (opens in a new tab), we discussed all the components of the OSEK OS and dove into details regarding tasks and ISRs, by specifying the available APIs, their OIL description and peculiarities, respectively. Today, we will follow the same pattern and learn more about counters, alarms, the way OSEK OS handles shared resources and OS hooks.
Without further ado, let's start. Counters and alarms are OSEK OS's way of providing the ability to process recurring events. Recurring events are generated by counters, which will trigger alarms in return, to provide the recurring functionality to the application. Some common functionalities are task activation and event setting.
A counter is a component which abstracts you from a ticking source in the system. The ticking sources are dependent on the underlying hardware, where the counter is a standard component in the OSEK OS. There is always one counter in the OS, being the SystemCounter. You cannot modify a counter, as they are read only. But you can work with them at your own taste by using alarms. With an alarm, you can mask their value to your needs, and a hardware interrupt always has to be associated with a counter. There are 3 values that specify a counter:
- MaxAllowedValue - Maximum allowed value for the counter. After reaching it, returns to zero
- TicksPerBase - The number of ticks of your ticking base which will lead to an increase of the counter by 1
- MinCycle - The nimimum number of the counter before the alarm is triggered
A counter can be defined in OIL as such:
Counter myCounter {
TICKSPERBASE = 2;
MINCYCLE = 10;
MAXALLOWEDVALUE = 100;
};
In the former example, we have a counter named myCounter that has will increment itself after 2 ticks of its underlying ticking source, trigger an alarm every 10 counts and overflow at 100. Now, let's move on to alarms.
As we specified before, an alarm works together with a counter. The alarm performs an action, when triggered by the counter. Each alarm is associated with one counter, but a single counter can serve for multiple alarms at once. The alarm is set every time the counter reaches its cycle value, and can, out of many things, activate a task, set an event or just call an alarm handling function. The latter is declared as such:
ALARMCALLBACK(MyAlarmFunction) {
// alarm servicing goes here
}
Alarms are handled in the same way as Tasks or ISRs by the OS, depending on the implementation. Now, let's look at the APIs available for alarms:
-
StatusType SetAbsAlarm(AlarmType AlarmID, TickType start, TickType cycle) - Configure the alarm with the specified ID to trigger the first time at the absolute value start of the counter and to trigger the following times cycle ticks relatively to the start value. Some possible return values are E_OS_STATE, in case the alarm is already running, E_OS_ID, if the alarm does not exist and E_OS_VALUE, where start or cycle are out of bounds relatively to the counter value. In case cycle is set to zero, it's a one shot alarm
-
StatusType SetRelAlarm(AlarmType AlarmID, TickType increment, TickType cycle) - Configures an alarm with the specified ID to trigger increment value relatively to the counter source and trigger every cycle value again, after the increment value is reached. You can also configure a one shot alarm (set cycle to 0), and the possible errors are the same as specified before
-
StatusType CancelAlarm(AlarmType AlarmID) - Cancel an alarm. Possible error return values are E_OS_ID and E_OS_NOFUNC, in case the alarm has not started yet
-
StatusType GetAlarm(AlarmType AlarmID, TickRefType tick) - Get the number of ticks left before the alarm with the ID AlarmID triggers, where tick is a pointer to said number of ticks. The possible return values are the same as CancelAlarm
-
StatusType GetAlarmBase(AlarmType AlarmID, AlarmBaseRefType info) - Get the configuration of the underlying counter of the alarm specified by the AlarmID
A possible description in OIL for an alarm can be as follows:
Alarm myAlarm {
COUNTER = myCounter;
ACTION = ACTIVATETASK {
TASK = myTask;
};
AUTOSTART = TRUE {
ALARMTIME = 2;
CYCLETIME = 20;
APPMODE = myAppMode;
}
};
In the former example, we create an alarm named myAlarm based on the counter myCounter, which will activate the task myTask. We also configure it to start automatically, by setting an alarm time of 2 and a cycle time of 20.
Now, let's talk about resource sharing with operating systems and their main issues. It's a very common thing to have hardware and software resources being used across multiple tasks and ISRs (well, ISR2's in the OSEK OS). Usually, resource management systems, such as mutexes, are created to ensure the same resource is not being used by multiple tasks / ISRs at once, ensuring the resources are always in a deterministic state when we access them. The problem with shared resources such as the mutex are phenomena such as priority inversion and deadlocks. The first is due to a lower priority executable entity locking a resource and blocking execution of a higher priority executable entity, delaying the execution of the higher priority entity. The latter happens when two entities are delaying each other's execution infinitely because they want locked resources by each other.
To avoid these aforementioned problems, the OSEK OS implements a priority ceiling protocol, where each resource is statically assigned a ceiling priority. This priority shall be at least the same as the highest priority task that accesses it. When a task requires such resource and its priority is smaller than the ceiling priority of the resource, until the task releases the resource, its priority is set to the ceiling priority of the resource temporarily, until it releases the resource.
There are two API calls to deal with resources:
-
StatusType GetResource(ResourceType ResID) - Get a resource. The possible error return values are E_OS_ID, meaning the resource requested is invalid, or E_OS_ACCESS, when a resource is alreeady in use
-
StatusType ReleaseResource(ResourceType ResID) - Release a resource. The possible error return values are the same as GetResource
A resource can be described in OIL as such:
Resource myResource {
RESOURCEPROPERTY = STANDARD;
};
Lastly, let's talk about hooks. These are functions that the OS will allow you to call when certain states are reached. These are optional, but they can be very useful to act upon specific states. Moreover, these hooks have a greater priority than all tasks, cannot be preempted by ISR2's, and are able to use a subset of OS services. These hooks are:
-
StartupHook - Called during the initialization of the OS
-
ShutdownHook - Called before the end of the OS's shutdown procedure
-
PreTaskHook - Called before executing tasks
-
PostTaskHook - Called after task execution
-
ErrorHook - Called when an error return value is returned by an OS API
Alright, now that we know more about the OSEK OS, you will be in a better position to understand the Autosar OS, as the OSEK OS served as the base for it. If you want ot play around with the OSEK OS, you can download the TrampolineRTOS (opens in a new tab) and read the RTA-OSEK manual.
Author: Micael Coutinho (opens in a new tab)
References:
- Specification of Operating System - Autosar Specification (opens in a new tab)
- RTA-OSEK Reference Guide - ETAS (opens in a new tab)
© AutosarToday —@LinkedIn