MT/content/mapping.tex

928 lines
47 KiB
TeX
Raw Normal View History

2020-08-20 17:39:46 +02:00
\chapter{Mapping}
\label{chapter:mapping}
% {{{ Mapping Intro
Systems are analyzable on different levels of abstraction as shown in
\autoref{fig:trace_event_levels}. Depending on the use case, one or another
level is more sufficient to perform the required analysis. For example, a
hardware designer does not care about task states while a system engineer is
usually not interested in voltage levels of transistors in memory.
For the timing analysis of an embedded system a trace on system level is
required because timing requirements are usually specified for system entities
such as tasks or signals. Hence, system level traces contain the information
necessary to validate an application with respect to its timing behavior.
A trace long enough, so that all relevant entities appear with sufficient
frequency for the timing analysis, is required. For example, at least two task
instances must be activated in one trace to calculate the activate-to-activate
time. Additionally, it is important not to influence the timing of an
application by trace measurement. Consequently, the only sufficient trace
technique for the timing analysis of embedded systems is hardware tracing
according to \autoref{tab:trace_tool_overview}.
\begin{figure}[]
\centering
\centerline{\includegraphics[width=\textwidth]{./media/mapping/concept_measurement_btf.pdf}}
\caption[Hardware to \gls{btf} trace basic idea]{Hardware tracing records
events on hardware level. This is not sufficient for the timing analysis of
an embedded system. Thus, it is necessary to transform the hardware events to
system events. This requires two steps. In the first step hardware events
are transformed to software events. This step is done by the trace software
and requires the application binary. The next transformation step produces a
trace on system level, e.g.\ in the \gls{btf} format. An \gls{orti} file as
well as additional information that can for example, come from a timing
model file (\gls{rte}) are required for this step.}
\label{fig:mapping_concept}
\end{figure}
Hardware tracing records events on hardware level. As stated above this level
is not sufficient for the timing analysis of an embedded system. Thus, it is
necessary to transform hardware events to system events as shown in
\autoref{fig:mapping_concept}. Two steps are required for this transformation.
Hardware level events must be transformed into software level events which are
then further processed into system level events.
The first step is done by the trace software. It is capable of analyzing and
interpreting the hardware events that are recorded from the processor. Via the
application binary files it is possible to map the raw memory addresses
contained in the hardware events to the corresponding symbols of the real
application as depicted in \autoref{fig:hardware_software_idea}.
\begin{figure}[]
\centering
\centerline{\includegraphics[width=\textwidth]{./media/mapping/hardware_software_idea.pdf}}
\caption[Hardware event to software event idea]{The trace software is capable
of transforming a hardware level event to a software level event. This
involves for example, changing memory addresses with the actual symbol names
based on the application binary (\gls{elf} file). Further actions may be
required depending on the trace device. Note that the displayed hardware
event is just a generalization, the actual structure can be different
depending on the trace device vendor.}
\label{fig:hardware_software_idea}
\end{figure}
Depending of the trace device further steps may be required. For example, some
trace devices produce timestamps, relative to the previous event which must
then be transformed into absolute timestamps. Another example are program flow
traces. Hardware level program flow events are usually only recorded for
instructions that change the flow of an application as described in
\autoref{subsection:hardware_tracing}. Only with the application binary it is
possible for the software to reconstruct a complete program flow trace.
Based on the software level trace, a system level trace can be generated in the
next step. A suitable system level trace format is \gls{btf} which is
described in \autoref{chapter:btf}. It is capable of representing the behavior
of an application in a way that is eligible for its timing analysis. Different
additional information, e.g.\ the \gls{orti} file is required to
execute the transformation from software to system level trace.
% }}}
% {{{ Mapping Proceeding
\section{Mapping Proceedings}
Transformation from hardware to software level is done by the trace software.
Corresponding to the composition of an on-chip trace device it creates two
types of traces on software level: a data trace and a function trace.
Let $i$ be an index in $\mathbb{N}_{0}$ denoting an individual event
occurrence. Then a data event can be defined as an octuple
\begin{equation}
\label{eq:data_event}
d_{i} = (t_i, \pi_i, a_i, v_i, c_i)
\end{equation}
where $t_i \in \mathbb{N}_{0}$ is the timestamp in nanoseconds, $\pi_i$ is the
name of the accessed variable, $a_i \in \{R, W\}$ is the way in which the
variable is accessed either $R$ for read or $W$ for write, $v_i \in
\mathbb{N}$ is the value that was read or written and $c_i$ is the core name
on which the access has occurred.
Consequently, a data trace can be defined as a sequence of data events where
$n \in \mathbb{N}_{0}$ is the number of events in the trace.
\begin{equation}
\label{eq:data_trace}
D = (d_1, d_2, \dots, d_n)
\end{equation}
Let $j$ be an index in $\mathbb{N}_{0}$ denoting an individual event
occurrence. Then a function event can be defined as a quadruple
\begin{equation}
\label{eq:function_event}
f_j = (t_j, \pi_j, \theta_j, c_j)
\end{equation}
where $t_j \in \mathbb{N}_{0}$ is the timestamp in nanoseconds, $\pi_j$ is the
name of the accessed function, $\theta_j \in \{A, \Omega\}$ indicates
whether the function has started ($A$) or terminated ($\Omega$), and $c_j$
is the core name on which the function event has occurred.
After that a function trace can be defined as a sequence of function events
where $m \in \mathbb{N}_{0}$ is the numbers of events in the trace.
\begin{equation}
\label{eq:function_trace}
F = (f_1, f_2, \dots, f_m)
\end{equation}
Based on \autoref{eq:btf_trace}, \autoref{eq:data_trace}, and
\autoref{eq:function_trace} the goal is to describe a function $g$ so that
\begin{equation}
g: (D,\, F) \rightarrow B,
\end{equation}
where the timestamps $t$ of the events in $D$, $F$, and $B$ are relative to the
same point in time. However, $D$ and $F$ alone are not sufficient for the
transformation from software to hardware level because of three reasons.
Firstly, the events on software level do not provide enough information to
decide which variable maps to a certain entity on system level. For example,
the state of each task is stored in a certain variable. Whenever the state
changes, this variable changes too and a data event is generated. However,
the transformation function does not know that the variable maps to the state
of a task. Because of that the \gls{orti} file described in
\autoref{subsection:osek_oil_and_orti} is required. Via this file it is
possible to relate variables to the corresponding system objects.
Secondly, not all entity types specified by \gls{btf} for example,
runnables and signals are included in the \gls{orti} file. The former are
included in the function trace, the latter in the data trace. But if the
transformation function is not able to distinguish regular functions from
runnables and regular variables from signals this information cannot be used.
Thus, it is necessary to provide a list of those entities to the transformation
function.
Finally, it is necessary to keep track of the internal state of an application.
If the \gls{orti} file is available it can be detected that a certain task has
changed its state. Consequently, a \gls{btf} event must be generated. Without
the knowledge about the previous task state however, it is not possible to
decide which task action has occurred. If the task changes into the running
state, this could mean that the task has started for the first time resumed
from ready state or continued to run after polling a resource.
Because of this reasons the function $g$ must be redefined as
\begin{equation}
g': (D,\, F,\, o,\, l,\, S) \rightarrow (B,\, S')
\end{equation}
where $o$ is the \gls{orti} file of the traced application, $l = (l_r,\, l_s)$
is a tuple that contains a list of runnables $l_r$ and a list of signal names
$l_s$, and $S$ and $S'$ are the system states before and after the
transformation. The information must be part of the system state $S$ is
discussed in the next sections.
% }}}
% {{{ ORTI Mapping
\section{ORTI Mappings}
\textbf{Task} entities are capable of executing twelve actions according to
\autoref{fig:process_state_chart} plus the additional notification event if the
\gls{mta} limit is exceeded. The lifecycle of a task entity starts with its
activation.
An \textbf{activation} can be detected via the \gls{orti} \emph{task status}
attribute. If no other task instance of the same task entity is active in the
system, a task whose state changes to ready is activated. However, this does
not work if a task instance of the same task is already active in the system.
This can happen if multiple task activations are allowed by the
\glsdesc{osekcc}. In case of a \gls{mta} the corresponding \gls{osek}
\emph{task status} attribute already indicates an active state (any state that
is not suspended) and will not change to ready again.
Consequently, another way to detect task activations is required. Via the
\gls{orti} \emph{currentactivations} attribute, the number of open activations
for each task can be detected. Whenever this attribute is incremented, a new
task activation \gls{btf} event must be created. Therefore, it is necessary to
keep track of the number of activations for each task entity in the system.
Only if the previous number of activations for a task is known, it is possible
to decide whether the value is incremented or decremented when a new data write
event occurs. Thus, the number of current activations for each process is a
relevant information and must be part of the system state $s$.
Since tasks have a lifecycle it is necessary to keep track of the instances for
each task entity. Whenever a new task is activated the instance counter must
be incremented and the counter value is assigned to the task. The same
procedure is necessary for all other entities that have a lifecycle. The
latest instance counter value for each entity must be available in the system
state $s$ to create correct \gls{btf} events. Additionally, it is necessary to
add newly created tasks to a list of task instances active in the system. When
a task's lifecycle ends, i.e., the task terminates, it is removed from this
list.
A \textbf{stimulus} is required to activate a task. Stimuli can be
\textbf{triggered} by process and by simulation entities. A stimulus triggered
by another process represents an \glsdesc{ipa} (\gls{ipa}). An \gls{ipa} is
implemented via the \lstinline{ActivateTask} service routine. The \gls{orti}
\emph{servicetrace} attribute can be used to detect when this routine is
executed. Whenever the \lstinline{ActivateTask} routine is entered and a task
is running on the same core a stimulus event is created with the task as the
source entity.
Alarms are the second way to activate tasks. The \emph{alarmtime} attribute
indicates how many ticks are left until an alarm expires. The \gls{orti} file
also contains the action that is executed by an alarm. Thus, a stimulus can be
triggered whenever an alarm that activates a task reaches an \emph{alarmtime}
value of zero.
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & ORTI attribute & System state \\
\hline
trigger (ipa) & servicetrace (ActivateTask) & running task \\
trigger (alarm) & alarmtime & - \\
\end{tabular}
\caption[Stimulus event mapping]{In \gls{btf}, a stimulus must be triggered
so that it can activate a task. On target a task can be triggered via an
\gls{ipa} or by an alarm. The first can be detected via the
\emph{servicetrace} attribute, while the latter is indicated if the
\emph{alarmtime} attribute reaches the value zero.}
\label{tab:stimulus_mapping}
\end{table}
A triggered stimulus must be added to the system state. Later, when the actual
task activation is executed by the \gls{os} the latest stimulus is removed
from the system state and used to create a correct \gls{btf} event.
\autoref{tab:stimulus_mapping} summarizes how stimulus events are detected.
A \textbf{task} \textbf{start} event occurs if a task which was previously
active changes to running. There are two cases for which preempt and resume
actions must be created. The first case is a normal state change that can be
detected via the \emph{task status} attribute. A task is \textbf{preempted} if
the state changes from running to ready and \textbf{resumed} if the state
changes from ready to running.
However, the task state is not updated by the \gls{os} when a task is preempted
by an \gls{isr}. Consequently, a task preempt event must also be created, if
the \emph{runningisr2} attribute indicates that a new \gls{isr} is running on
the core and a resume event must follow once the \gls{isr} terminates
execution.
A task \textbf{terminate} event occurs if a running task changes into the
suspended state. The previous state must not be known because a task can only
be terminated from the running state.
However, there is a special case for task terminate events. As mentioned in
\autoref{subsection:osek_architecture}, a task with pending activations
switches directly into the ready state, after the current instance terminates.
To work around this problem it is necessary to detect when a certain task
instance executes the \lstinline{TerminateTask} service routine via the
\emph{servicetrace} attribute. If this happens a flag in the system state must
be set to indicate that the respective task instance has been terminated.
Whenever a task changes from running to ready this flag must be checked
to decide whether the corresponding event is a preemption or a termination.
A \textbf{wait} event occurs if a running task waits for an event that is not
set. In this case the \gls{os} will change the task state to waiting and the
task is removed from the core. A \textbf{release} event occurs once the event is set
and the \gls{os} changes the task state to ready.
\begin{code}
\begin{lstlisting}[caption={[Resource polling] The \gls{btf} polling state
indicates that a process is actively waiting for a resource. This listing
shows how this might be impolemented in C.},
label={listing:resource_polling}]
TASK(EngineManager) {
/* Wait actively until EngineResource becomes available. */
while(GetResource(EngineResource) != E_OK);
engineRPM = calculateEngineRPM();
ReleaseResource(EngineResource);
TerminateTask();
}
\end{lstlisting}
\end{code}
\textbf{Poll} actions are more difficult to detect, since they are not directly
related to a concept specified by \gls{osekos}. The idea of the \gls{btf}
polling state is to indicate that a task is actively waiting for a resource.
In code this can be implemented via a loop in which a resource is requested
repeatedly until it becomes available as shown in
\autoref{listing:resource_polling}.
Via \emph{servicetrace} and \emph{lasterror}
it can be detected that a process has requested a locked resource: the
\emph{servicetrace} attribute indicates when the \lstinline{GetResource}
service routine is called and \lstinline{E_OS_RESOURCE} is written to the
\emph{lasterror} attributed in case the resource is locked.
However, a single request does not necessarily mean that a change into the
polling state is happening. Instead a task might just execute one code
segment, if the resource is available and a different one, if it is not.
Therefore, it is necessary to set a \emph{previous request} flag for a task
instance that has requested a locked resource once. If another request follows
in the same running interval a poll event is generated. Once there are no more
requests, the last request must have been successful and a run event is created
to indicated the state change from polling to running. Then the previous
request flag must be cleared.
A \textbf{park} action must be created if a task that is in polling state is
changed into the ready state. Next, it is necessary to detect resource state
changes of the resource which the parking task has been polling. If the
respective resource changes into an unlocked state, a \textbf{release\_parking}
event is created. On the other hand, if the resource stays locked and the task
changes back into running state, a \textbf{poll\_parking} event is required.
The \textbf{mtalimitexceeded} notification event is the last task event that
must be detected. This event is created, if a task activation gets triggered,
but no actual task instance is added to the system. An \gls{osek} compliant
\gls{os} writes an \lstinline{E_OS_LIMIT} error into the \emph{lasterror}
attribute, if a task activation is triggered, but the maximal \gls{mta} value
is already reached. To create a valid \gls{btf} event it is necessary to know
for which task entity the error is created. Since \gls{orti} does not provide
this information the creation of \emph{mtalimitexceeded} events is not
feasible. \autoref{tab:task_mapping} gives an overview of the task mapping.
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & ORTI attribute & System state \\
\hline
activate & currentactivations & currentactivations, last stimulus \\
start & state (running) & state (active) \\
resume & state (running) & state (ready) \\
resume & runningisr2 & running task \\
preempt & state (ready) & task not terminated \\
preempt & runningisr2 & running task \\
terminate & state (suspended) & active tasks \\
terminate & state (ready) & task terminated \\
wait & state (waiting) & - \\
release & state (ready) & state (waiting) \\
poll & lasterror & servicetrace, previous request \\
run & servicetrace & state (polling) \\
park & state (ready) & state (polling) \\
poll\_parking & state (running) & state (parking) \\
release\_parking & resource state & state (parking) \\
mtalimitexceeded & lasterror & entity cannot be detected \\
\end{tabular}
\caption[Task event mapping]{Different pieces of information are required to
detect all possible task actions. The states in the \gls{orti} attributes
column are \gls{osek} task states while the states in the system information
column are \gls{btf} process states. The previous state is necessary to
create correct events. For example, a task state change to running could
mean a \gls{btf} start, resume or run event.
For some actions, it is necessary to use multiple approaches to detect them.
For example, a task terminate event happens if the \gls{osek} state of
changes to suspended. However, if another entity of the same task is already
activated, a change to suspended does not occur. To catch this case it is
necessary to set a \emph{task terminated} attribute for a task instance when
it calls the \lstinline{TerminateTask} service routine.}
\label{tab:task_mapping}
\end{table}
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & ORTI attribute & System state \\
\hline
activate & - & - \\
start & runningisr2 & \gls{isr} stack \\
resume & runningisr2 & \gls{isr} stack \\
preempt & runningisr2 & \gls{isr} stack \\
terminate & runningisr2 & \gls{isr} stack \\
\end{tabular}
\caption[\gls{isr} event mapping]{The \emph{runningisr2} attribute is used to
detect basic \gls{isr} actions. Because \glspl{isr} are not allowed to wait
for events, waiting state related actions must not be created. All other
actions can be detected in the same way as for task instances as shown in
\autoref{tab:task_mapping}.}
\label{tab:isr_mapping}
\end{table}
\textbf{\glspl{isr}} and tasks share the same \gls{btf} state model. However,
\gls{osek} does not specify a detailed state model for \glspl{isr} as it does
for tasks. Consequently, the basic process actions activate, start, resume,
preempt, and terminate are detected differently compared to task actions as
shown in \autoref{tab:isr_mapping}. \glspl{isr} are not allowed to wait for
events. Therefore, waiting related process state transitions must not be
considered. The detection of semaphore polling events works equally to task
events and is therefore not discussed again.
An \glsdesc{isr} is triggered by a hardware interrupt. This means if the
hardware detects a certain condition, e.g., an \gls{io} pin state changes from
high to low, the program flow is interrupted and a certain code section that is
mapped to this interrupt is executed. Depending on the trace device, it may or
may not be feasible to detect the activation of an interrupt via the
corresponding \gls{isr} control register.
In the former case, it is possible to create a stimulus and the resulting
activate event by detecting when the interrupt activate bit is set in the
corresponding control register. Otherwise, the \textbf{activate} event must be
created when the \gls{isr} changes into the running state for the first time.
In this case trigger, activate, and start event are all created with the same
timestamp.
\begin{figure}[]
\centering
\centerline{\includegraphics[width=\textwidth]{./media/mapping/isr_stacking.pdf}}
\caption[Running \gls{isr} stacking]{A stack can be used to track the active
\glspl{isr} in a system. This is necessary to create appropriate \gls{btf}
events. For example, the event when \emph{isr\_foo} is set as the running
\gls{isr}, is different, depending on the current state of the stack. If the
\gls{isr} is already on the stack, a resume event must be created, otherwise a
start event.}
\label{fig:isr_stacking}
\end{figure}
The currently running category two \gls{isr} is indicated by the
\emph{runninngisr2} \gls{orti} attribute. Each \gls{isr} has an unique
\gls{id} that is written into the variable, if the respective entity is
running. Otherwise runningisr2 is zero which indicates that no \gls{isr} is
active. Mapping from \gls{id} to name is included in the \gls{orti} file. If
\emph{runningisr2} changes to the \gls{id} of a certain \gls{isr}, it is not
possible to decide whether this instance runs for the first time or whether it
is resumed, after it has been preempted by an \gls{isr} with higher priority as
shown in \autoref{fig:isr_stacking}.
Therefore, it is necessary to keep track of the active \gls{isr} instances in
the system, e.g.\ via a stack. Whenever the value of \emph{runningisr2}
changes it is checked whether the corresponding \gls{id} is already on the
stack. If so, the \gls{isr} was already running and has been
\textbf{preempted}. Consequently, the \gls{isr} that caused the preemption has
terminated and must be popped off the stack. The \gls{isr} that has been
preempted must be \textbf{resumed}.
The other case is that the new \gls{isr} has not been running yet, i.e.\ is not
on the stack. This means that the \gls{isr} on top of the stack, if there is
one gets \textbf{preempted} and the new \gls{isr} is \textbf{started} and
pushed on the stack. If \emph{runningisr2} becomes zero the last \gls{isr} is
popped of the stack and \textbf{terminated}.
As the name indicates, \emph{runningisr2} is only written for category two
interrupt routines. Regular \glspl{isr} are not managed by the \gls{os} and
therefore not detectable via \gls{orti} attributes. Instead function trace
must be utilized to detect when a category one \gls{isr} is started or
terminated. To map the function names to actual \gls{isr} entities, a list of
category one \glspl{isr} is required. If such a list is available, the
proceeding is the same as described above.
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & ORTI attribute & System state \\
\hline
start & - & running process \\
terminate & - & running process \\
suspend & task state & running process, process runnables \\
resume & task state & running process, process runnables \\
\end{tabular}
\caption[Runnable event mapping]{Runnable start and stop events can be
detected via function tracing. The source entity for a runnable event is the
process in whose context the runnable is executed. A runnable is suspended
when the corresponding process is preempted. If the process resumes, the
runnable is resumed, too.
One runnable can be called in the context of another runnable. This means
multiple runnables can be running within the same process context at the same
point in time. If this is the case, all running runnables must be suspended
and resumed.}
\label{tab:runnable_mapping}
\end{table}
\textbf{Runnable} actions are detectable via function events. Start and
terminate events must be created for function entry and function exit events.
A program flow trace contains the information about all functions in the
system. A list of runnable entity names is thus required to check whether a
function is a runnable or not.
Suspend events must be created, if the process context in which a runnable is
running is preempted and a resume event is required if the corresponding
process resumes. This means that whenever a process is deallocated, a
potentially active runnable must be suspended. Once the process is
reallocated the runnable also resumes.
Additionally, runnables can be nested, i.e.\ one runnable can be executed by
another runnable. If this happens it is important to suspend and resume all
running runnables, if the corresponding process is preempted and resumed.
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & ORTI attribute & System state \\
\hline
write & - & running process \\
read & - & running process \\
\end{tabular}
\caption[Signal event mapping]{Signals can be read or written. To create
valid \gls{btf} signal events, it is necessary to know which process is
currently running on the core, i.e., which process executed the read or
write.}
\label{tab:signal_mapping}
\end{table}
\textbf{Signal} events are detectable via data events. To decide which data
event corresponds to a signal event a list of signal names must be available.
With this list it can be decided if a certain data event results in a signal
event or not. The source entity for signal read events is the currently
running process as shown in \autoref{tab:signal_mapping}. If no process is
running an entity of type simulation can be used to set the value of the
signal.
\textbf{Event} actions are easily detectable via the \emph{servicetrace}
attribute. Via this attribute it is possible to create set, wait, and clear
event actions. However, in order to create valid event actions, it is also
necessary to know the event entity that relates to the respective action.
\gls{orti} does not specify event related attributes. Because \gls{orti} does
not specify OS event related attributes, it is not possible to create valid
actions for this entity type.
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & ORTI attribute & System state \\
\hline
ready & resource object & - \\
lock & resource locker & - \\
unlock & resource locker & - \\
full & resource locked, servicetrace & - \\
overfull & resource locked, servicetrace & - \\
\end{tabular}
\caption[Resource event mapping]{\gls{osek} resources can only be locked or
unlocked which means they do not support all semaphore actions. Lock and
unlock actions can be detected via the \gls{orti} locker attribute.
Full and overfull events are created if an already locked resource is
requested again. This is detectable via the \emph{servicetrace} attribute.
The resource for which the \emph{resource locked} attributed was read the
last time is the resource for which the error has occurred.}
\label{tab:resource_mapping}
\end{table}
\begin{table}[]
\centering
\begin{tabular}{r l l}
Action & ORTI attribute & System state \\
\hline
requestsemaphore & resource locker & - \\
assigned & resource locker & - \\
waiting & resource locked & - \\
released & resource locker & previous locker \\
\end{tabular}
\caption[Semaphore process event mapping]{Via the resource locker attribute
it is possible to detect if a resource has successfully requested a
semaphore.
The \emph{resource locker} attribute changes to the no task \gls{id} if the
resource is no longer locked. For this case it is necessary to know the task
that has previously locked the resource in order to create the correct
release event.
Waiting actions can be created by detecting data read events to the
\emph{resource locked} attribute.}
\label{tab:semaphore_process_mapping}
\end{table}
\textbf{Resource} entities must be initialized via the ready action before they
can be used in a \gls{btf} trace. This can be done at the beginning of a trace
with the timestamp zero. The \gls{orti} file contains a list of all resource
objects that are part of the application.
Since resources can only be locked or unlocked, they cannot change into the
semaphore used state. Consequently, only the state transition actions shown in
\autoref{tab:resource_mapping} can occur for resource events. Additionally,
only a subset of the process semaphore actions are required to represent the
behavior of resources.
Via the \gls{orti} \emph{resource locker} attribute it is possible to detect by
which task entity a resource is locked. This means a lock event can be
generated whenever the \gls{id} of a certain task is written to this attribute.
On the other hand, an unlock event is created when \emph{resource locker}
indicates that the respective entity is currently not locked by any task.
Moreover, it is necessary to assign a process to the locked resource once it
is locked by the task and to release it when the resource is released as
shown in \autoref{tab:semaphore_process_mapping}.
Full and overfull actions are created when a locked resource is polled by a
process. The semaphore waiting action is used to indicated the identity of the
polling process. As shown above, it is possible to detect whether a process is
polling a resource via the \emph{servicetrace} and \emph{lasterror} \gls{orti}
attributes. \emph{Lasterror} is set to \lstinline{E_OS_ACCESS} in case a
resource is already locked. The resource for which the polling occurs is
detectable via the \emph{resource locked} attribute. Whenever a certain
resource is requested the \gls{os} will read this attribute to decide whether
a request is allowed or not.
% }}}
% {{{ OS Specific Mapping
\section{OS Specific Mappings}
It is not feasible to create all \gls{btf} events relying solely on the
\gls{orti} file. For example, it is necessary to have a list of runnable and
signal names in order to create valid events for those entity types. But even
for entities that are supported by the \gls{orti} interface not all events
can be generated. It is possible to detect if the activation limit
of a task is exceeded however, it is not possible to determine for which task
entity this happens.
Nevertheless, even though not all events are detectable via \gls{orti} alone,
an \gls{osekos} stores the information of interest internally. During a task
activation the \gls{os} must decide whether the \gls{mta} limit is reached or
not. To do so it is necessary to compare the current amount of
pending activations to the value of maximal allowed activations. Consequently,
the \gls{os} has to read certain information from memory which results in data
trace events.
Based on this argument all other events can be reconstructed, if the
corresponding \gls{os} specific operations are known. On the downside, it is
no longer possible to rely on a standardized interface like \gls{orti}. This
means the algorithm that does the transformation must be customized depending
on the \gls{os}. In this section the adaptations required to create a
\gls{btf} trace for the \gls{osek} compliant Erika Enterprise (\gls{ee})
\glsdesc{os} \cite{erika} are shown. In
\autoref{section:evaluation_test_bench} the reasons for choosing \gls{ee} are
discussed.
\textbf{Task} \emph{mtalimitexceeded} events cannot be created based on
\gls{orti} alone because the task entity for which the event occurs is not
detectable. One way to get this information is to remember which task's
\emph{currentactivations} attribute was read the last time. The \gls{os} has to
decide whether a task instance can be created once an activation is triggered.
To do so it compares the maximum allowed activations with the current number
of activations of a task. In other words, the \gls{os} reads the
\emph{currentactivations} attribute for the task that should be activated. If
the \gls{mta} limit is exceeded an error code is written.
\begin{figure}[]
\centering
\centerline{\includegraphics[width=\textwidth]{./media/mapping/mtalimitexceeded.pdf}}
\caption[Call stack for inter-core process activation]{A
\emph{mtalimitexceeded} event must be created if the \lstinline{E_OS_LIMIT}
error is set via the \emph{lasterror} \gls{orti} attribute. However, this is not
correct for Erika Enterprise multi-core applications. For a failing
inter-core inter-process activation the error code is written two times, once
on the source and once on the target core. Therefore, special care must be
taken, so that the \gls{btf} event is created only once.}
\label{fig:mtalimitexceeded}
\end{figure}
\begin{code}
\begin{lstlisting}[caption={[Task activations limit exceeded] Erika Enterprise
keeps track of the remaining activations that are allowed for a task entity.
If the value is zero and another activation occurs an \lstinline{E_OS_LIMIT}
error is set.}, label={listing:mtalimitexceeded}]
if ( EE_th_rnact[TaskID] == 0U ) {
ev = E_OS_LIMIT;
} else {
/* Do activation. Code removed for clarity. */
ev = E_OK;
}
if (ev != E_OK ) {
EE_ORTI_set_lasterror(ev);
EE_oo_notify_error_ActivateTask(TaskID, ev);
}
\end{lstlisting}
\end{code}
As it turns out this approach is not sufficient for multi-core systems.
Activation of a task entity by a task on another core via
\lstinline{ActivateTask} is implemented by a \glsdesc{rpc} (\gls{rpc}) as shown
in \autoref{fig:mtalimitexceeded}. The \gls{rpc} triggers an \gls{isr} on the
other core which performs the required action. In case of an inter-process
activation the \lstinline{ActivateTask} routine is executed again, but this
time on the core the target task is allocated to. If the \gls{mta} limit of
the task is exceeded an \lstinline{E_OS_LIMIT} error event is written and a
\lstinline{mtalimitexceed} event is created.
However, the remote procedure call is notified by the remote \gls{isr} once the
service routine has finished. The corresponding error code is also returned
back to the initial core and written to the \emph{lasterror} attribute. The
resulting problem is that the transformation algorithm would create another
\emph{mtalimitexceeded} event based on the last read from the pending
activations variable on the initial core which is not correct.
A way to work around this problem can be derived by looking at a part of the
source code of the \lstinline{ActivateTask} implementation shown in
\autoref{listing:mtalimitexceeded}. It shows that \gls{ee} keeps track of the
remaining activations of each task in an array called \lstinline{EE_th_rnact}.
If the field for a specific task becomes zero, an \lstinline{E_OS_LIMIT} error
is written. This means if a task should be activated on one core and this
activation fails due to too many pending activations this will become clear by
a data read event to \lstinline{EE_th_rnact} directly followed by a write event
to the \emph{lasterror} attribute. For a remote activation there are multiple
other data events between the error and the previous read to
\lstinline{EE_th_rnact}. Therefore, no incorrect \emph{mtalimitexceeded} event
is created.
\begin{figure}[]
\centering
\centerline{\includegraphics[width=\textwidth]{./media/mapping/deltaqueue.pdf}}
\caption[Alarm delta queue implementation]{\gls{ee} implements alarms
via a delta queue. There is one queue, containing of the corresponding
alarms, for each counter. Each alarm has a delta value that indicates after
how many ticks in relation to the previous alarm it must be executed. Only
the delta of the first alarm in the queue must be decremented for each counter
tick. If an alarm expires it is removed from the queue, and inserted again in
case it is cyclic.
In this example Alarm 2 expires after three ticks. Since Alarm 5 has a
delta of zero it expires at the same counter cycle. Alarm 4 expires after
six cycles, i.e.\ the sum of its own and all previous deltas.
}
\label{fig:deltaqueue}
\end{figure}
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & Variable & Additional Information \\
\hline
mtalimitexceeded & lasterror & previous data read event \\
trigger (alarm) & alarm action type & \gls{orti} \\
\end{tabular}
\caption[OS task and stimulus event mapping]{Via \gls{orti} it is not
possible to detect for which task an \lstinline{E_OS_LIMIT} event has been
created. However, the data read event before this error can be used to get
this information.
Additionally, alarm trigger events cannot be created via the \emph{alarmtime}
attribute in Erika Enterprise, because it is not implemented in an \gls{osek}
compliant way. Instead, read events to the \lstinline{ActionType} attribute
of an alarm can be used to detect when a stimulus event must be created.}
\label{tab:task_mapping_os}
\end{table}
\textbf{Stimulus} events must be created for inter-process and alarm
activations as shown in \autoref{tab:stimulus_mapping}. An alarm activation
stimulus is created if the \gls{orti} \emph{alarmtime} attribute becomes zero.
However, \gls{ee} \gls{os} does not update this attribute in compliance with
the \gls{osek} specification \cite{erikaaltick}. Hence, another technique is
required to detect alarm events.
\gls{ee} keeps track of all active alarms in a delta queue as shown in
\autoref{fig:deltaqueue}. There is one queue for each counter. Whenever a
counter is incremented the delta of the first element in the queue is
decremented. If the delta of the first alarm in the queue becomes zero this
alarm and all following alarms with a delta of zero expire and the
corresponding actions are executed.
For an expiring alarm the \gls{os} is required to execute the corresponding
action. As shown in \autoref{tab:task_mapping_os} each alarm has an
\lstinline{ActionType} attribute. Via this attribute the \gls{os} determines
the correct action for an alarm. In other words, if an alarm expires this
attribute must be read and a data read event is generated. Consequently, a
\gls{btf} stimulus event is created whenever the action type attribute of
an alarm is read. The exact action executed by an alarm, e.g.\ which task is
activated for a process activation is read from the \gls{orti} file.
\textbf{Event} actions must include the information about the affected event.
For example, if a task sets an event it is necessary to know the target task
and event for this action. \gls{orti} allows it to detect when an event
related service routine is executed however, no information about the event
itself is made available.
\begin{code}
\begin{lstlisting}[caption={[Set event] Erika Enterprise uses the
\lstinline{EE_th_event_active} array to keep track of the events set for each
task. If a new event is set the mask is updated by connecting the previous
events and the new event via bitwise or. It is not possible to set an event
for a suspended task.},
label={listing:set_event}]
if ( EE_th_status[TaskID] == SUSPENDED ) {
ev = E_OS_STATE;
} else {
/* Set the event mask only if the task is not suspended */
EE_th_event_active[TaskID] |= Mask;
/* Check if the TASK was waiting for an event we just set */
if ((EE_th_event_waitmask[TaskID] & Mask) != 0U)
{
/* Activate task here */
}
}
\end{lstlisting}
\end{code}
\begin{table}[]
\centering
\begin{tabular}{r|l l}
Action & Variable & Additional Information \\
\hline
wait\_event & \lstinline!EE_th_event_waitmask! & previous wait mask\\
clear\_event & \lstinline!EE_th_event_active! & previous active mask\\
set\_event & \lstinline!EE_th_event_active! & previous active mask\\
all actions & - & event bit from eecfg.h \\
\end{tabular}
\caption[OS specific event mapping]{Erika Enterprise uses two arrays to keep
track of the event states for each task entity. Via write events to these
arrays and the previous event state for a task instance correct \gls{btf}
events can be generated.}
\label{tab:os_event_mapping}
\end{table}
Erika Enterprise uses two arrays to keep track of the event related state of a
task: In \lstinline{EE_th_event_active} the events currently set for a
specific task instance are stored and \lstinline{EE_th_event_waitmask} includes
the information about which events a task entity is waiting for. Each field in
the array corresponds to one task and each bit of a field is related to a
certain event. Whenever a task is terminated both event masks are cleared.
Using these arrays it is possible to create correct events as shown in
\autoref{tab:os_event_mapping}. Whenever an \gls{os} event related service
routine is executed the corresponding event mask is updated. For example, if
an event is set for a specific task, the event mask is updated based on the new
event. This means the events which are currently set for a task and the new
event are connected via the bitwise \emph{or} operation as shown in
\autoref{listing:set_event}.
Hence, a data write event to one of those arrays is created whenever a event
service routine is executed. However, only the new state of the bitmask
becomes available. To determine the event \gls{id} it is necessary to remember
the previous state of the mask. By executing a bitwise \emph{exclusive-or}
operation on previous and current mask, the bit of the current event is
computed.
Unfortunately, this information is still not enough to create a valid \gls{btf}
event. For each bit it is necessary to know the corresponding entity name.
\glsdesc{ee} defines the bitmask for each \gls{os} event in the \emph{eecfg.h}
file which is created during the code generation process. By parsing the event
defines the mapping between bit and event name is retrieved.
\begin{code}
\begin{lstlisting}[caption={[Spin in for global resource request] In case a
global resource (a resource used on multiple cores) is requested, Erika
Enterprise uses a spinlock mechanism to lock the CPU until the resource
becomes available.},
label={listing:get_resource_spin}]
/* if this is a global resource, lock the others CPUs */
if (isGlobal) {
EE_hal_spin_in((EE_TYPESPIN)ResID);
}
\end{lstlisting}
\end{code}
\textbf{Resource} events or in \gls{btf} terms semaphore events, can be
created based on the information provided by \gls{orti} as shown in
\autoref{tab:resource_mapping}. However, certain semaphore events like
waiting can only occur in multi-core systems. In a single-core system it is
not possible that one task polls a resource that is already occupied because
of the priority ceiling protocol.
Erika Enterprise implements inter-core resource requests via spinlocks. If a
task requests a resource that is locked by a task on another core, the service
routine does not return an error code but starts spinning as shown in
\autoref{listing:get_resource_spin}. As a consequence, the mapping for full,
overfull, and waiting actions introduced in the previous section does not
work.
To solve around this problem, it is necessary to understand how spinlocks are
implemented in Erika Enterprise. The state of each spinlock is stored in the
\lstinline{EE_hal_spin_status} array where each field corresponds to a separate
spinlock. A value of one indicates that the spinlock is locked otherwise the
value is zero. The \lstinline{EE_hal_spin_in} method is implemented via the
atomic compare-and-swap operation. This method is used to write a one into a
certain spinlock field, but only if the spinlock is currently free.
Compare-and-swap returns a value that indicates whether the operation was
successful or not. In the latter case the operation is executed again until it
succeeds.
Compare-and-swap operations result in a data access to the variable for which
the operation is executed. Therefore, it is possible to detect when a spinlock
is polled based on data access events to \lstinline{EE_hal_spin_in}. This
information can then be used to create correct semaphore events as shown in
\autoref{tab:os_semaphore_process_mapping}.
Whenever the \emph{resource locker} attribute is read within the context of the
\lstinline{GetResource} service routine, the corresponding resource entity must
be stored in the system state. If the resource is free, a write event to the
\emph{resource locker} attribute follows and the corresponding \gls{btf} events
can be created as described above.
If there is no write event to the \emph{resource locker} attribute the
resource is currently locked and the \gls{os} starts spinning which is
detectable by continuous data access events to the field of
\lstinline{EE_hal_spin_status} relating to the requested semaphore.
Consequently, the running process is assigned to the semaphore via the waiting
action and an overfull action must be created. The process is now in polling
mode. Once there are no further accesses to \lstinline{EE_hal_spin_status}
the request was successful, the task state changes to running and the resource
state to full.
\begin{table}[]
\centering
\begin{tabular}{r l l}
Action & Variable & Additional Information \\
\hline
waiting & \lstinline!EE_hal_spin_status! & running task, requested resource \\
full & \lstinline!EE_hal_spin_status! & requested resource \\
overfull & \lstinline!EE_hal_spin_status! & requested resource \\
\end{tabular}
\caption[OS specific semaphore event mapping]{Not all \gls{btf} semaphore
actions can be created based on \gls{orti} alone for an Erika Enterprise
multi-core application. This is because inter-core resource requests are
implemented via spinlocks. Spinlock operations can be detected via the
\lstinline{EE_hal_spin_status} array.}
\label{tab:os_semaphore_process_mapping}
\end{table}
% }}}