PXROS
PXROS
PXROS-HR
Version 1.1
I Overview 4
1 Introduction 5
1.1 What is PXROS-HR? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Why an operating system? . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 What is a realtime operating system . . . . . . . . . . . . . . . . . . . . . 5
1.4 Special features of PXROS-HR . . . . . . . . . . . . . . . . . . . . . . . . 6
3 PXROS-HR Architecture 10
3.1 Overview - Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 Functional units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2.1 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2.2 Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3.1 Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.2 MemoryClass object . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.3 Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.4 Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3.5 Objectpools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3.6 Interrupt objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3.7 Objects for realising timer functionality . . . . . . . . . . . . . . . . 17
3.4 Inter-process communication . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.4.1 Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.4.2 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.5 Safety characteristics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.5.1 Hardware memory protection . . . . . . . . . . . . . . . . . . . . . 20
3.5.2 Memory protection and encapsulation . . . . . . . . . . . . . . . . . 20
3.5.3 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.5.4 Error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
II Usersguide 26
5 Summary 27
8 Further information 60
8.1 PxSysInfo services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
8.2 The Nameserver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.3 Reloading Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
8.4 PXROS-HR Service Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
8.5 Event logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
III Appendix 65
A Example code 66
A.1 For starters: a simple application . . . . . . . . . . . . . . . . . . . . . . . 66
A.2 Data exchange between Handler and Task . . . . . . . . . . . . . . . . . . 70
A.3 Messagepools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
A.3.1 Creating a Messagepool . . . . . . . . . . . . . . . . . . . . . . . . 72
A.3.2 Deleting a Messagepool . . . . . . . . . . . . . . . . . . . . . . . . . 73
Overview
1 Introduction
The following document describes the features and application of the operating system
PXROS-HR. It is subdivided into two parts:
The first part provides a general outline and describes the philosophy, special character-
istics and architecture of PXROS-HR.
The second part explains in depth the practical application and programming of the
system; examples show how a PXROS-HR application is constructed.
Soft realtime means a time limit may be exceeded in individual cases without causing
unusable results.
A realtime operating system is characterised by the fact that it guarantees realtime con-
ditions on operating system level. This usually means providing mechanisms that allow
the application software to react to (external) events in time.
Examples:
The process of recording, processing and transferring audio data has to be performed
within certain time limits. If these limits are exceeded from time to time, minor distur-
bances (pops) within the audio stream will be the result, which have no negative effects.
This is an example for a soft realtime requirement.
The process of reading and analysing a measured signal, e.g. in a motor control, and the
reaction to this signal has to be performed within a certain time period. If this is not the
case, disruptions might occur or the motor might break down. This is an example for a
hard realtime requirement.
Note:
Hardware memory protection prevents error propagation throughout
the system. Hardware memory protection (MPU) is handled by the
operating system.
tem Tasks, which usually have high privileges, provide basic services and can also
provide important application functionlaities. They form a system platform similar
to the operating system of a personal computer. Application Tasks are potentially
error-prone (e.g. supplied modules) and thus have lower privileges. These Tasks
usually implement application-specific functionalities rather than system function-
alities. Application Tasks can also be reloaded.
Note:
The basic system also consists of individual capsules on Task level
with different rights (see Figure 3.2 on page 11).
Microkernel: PXROS-HR contains a microkernel, i.e. only the basic functionalities of the
operating system are implemented in the kernel. Additional functionalities, such as
the TCP/IP stack or file system, are realised in the form of separate modules running
as application Tasks.
3.2.1 Tasks
Tasks are the basic elements of a PXROS-HR system. Tasks represent parallel processing
within a software. They are prioritised among each other, yet never of higher priority than
the Handlers.
Tasks run in a quasi-parallel way; however, since there is only one processor physically,
the Tasks have assigned their computing time by the operating system. In practice, this
means that if a Task is willing to run and has a higher priority than the current Task, it
will replace the current Task.
Note:
A Task of equal priority is not supplanted. Exception: if timeslicing
is activated, this means, as soon as its timeslice has expired, the Task
can be supplanted by one of equal priority.
PXROS-HR uses a permission concept for Tasks, i.e. Tasks have to be authorised in order
to access system resources. These types of permission are called privileges (hardware-
based) or access rights (based on the operating system) (see section 3.5 on page 20).
Note:
Under PXROS-HR it is possible to reload Tasks into the basic system
during runtime.
3.2.2 Handlers
Handlers are functions that are called whenever a hardware or software interrupt occurs.
Handlers have a higher priority than any Task.
To speed up the processing of a Handler or to avoid blocking, Handlers have a restricted
access to PXROS-HR system services.
There are two types of Handlers: local Handlers, which are assigned to certain Tasks, and
global Handlers.
Global Handlers, also called fast Handlers, work on interrupt level, meaning these are the
fastest and have the highest priority within the system. Global Handlers must be processed
as quick as possible since otherwise the processing of interrupts can be delayed. Global
Handlers are prioritised among each other; a handler of higher priority can interrupt one
of lower priority.
Global Handlers have all-encompassing permissions.
To install a global Handler, a Task has to have the appropriate permission.
The application of global Handlers can be possible, for example, to realise equidistant
sampling.
Note:
A context Handler is executed in the context of the creating Task and
has access to the address space of this Task.
To install a local Handler, a Task has to have the appropriate permission. The installed
Handler will then have the same rights as the creating Task.
Note:
A local Handler is executed in the context of the creating Task and
has access to the address space of this Task.
Local Handlers have lower priority than global Handlers, and higher priority than Tasks.
To install a local Handler, a Task has to have the appropriate permission. The installed
Handler will then have the same rights as the creating Task.
3.3 Objects
All the elements of PXROS-HR are regarded as objects. There are two basic types of
objects: generic objects, which belong to an abstract base class and do not implement
any functionality, and specific objects, which are derived.
3.3.1 Tasks
Task objects are used for modelling PXROS-HR processes, the so-called Tasks, with their
properties such as priority, stack size, protected memory assigned, etc. (see also subsec-
tion 3.2.1 on page 12).
When using several memory classes, Tasks can have different types of memory assigned
(slow memory / fast memory). Furthermore, by assigning a memory class exclusively to
one Task, memory shortage during runtime can be avoided in this Task, which would
otherwise occur if another Task utilises all the available memory.
3.3.3 Messages
Messages are objects, which are used by Tasks to exchange data. They also serve for
synchronising Tasks, since a sender Task can wait for a recipient Task to release a Message.
The content of a Message is not copied; a Message object is given a portion of memory
belonging to the owner of the Message. After sending the Message, this memory portion
changes into the possession of the recipient where it remains until the recipient releases,
forwards or returns it.
3.3.4 Mailboxes
Mailboxes are the communication terminals for Message exchange. A Task sends a Message
object to a mailbox, while a recipient can wait for arriving Messages at this mailbox.
Note:
Mailboxes can contain an arbitrary number of Message objects.
Private mailboxes are a special feature. In general, mailboxes can be made available for
any Task, yet each Task has its own private mailbox, which is unavailable for other Tasks.
The usual case of inter-Task-communication is an exchange of Messages between private
mailboxes.
3.3.4.2 Messagepools
Messagepools are another special feature. They are storehouses for Message objects
created beforehand. This means, Messages are created during initialisation and stored in
a Messagepool. This procedure has several advantages:
1. Creating Messages during runtime can be avoided; the necessary Messages can be
extracted from the Messagepool
2. There is no shortage during runtime, e.g. because at a certain time there are no
more objects available.
This shows the usefulness of the feature that several Tasks can have access to the same
mailbox.
3.3.4.3 Mailboxhandler
Handlers can be defined for mailboxes. If a Handler was installed at a mailbox, then this
Handler is called whenever a Message is sent to this mailbox.
This feature is of particular usefulness if a Task wishes to receive Messages from several
mailboxes.
3.3.5 Objectpools
Object pools are used for storing objects until they are needed. There is a standard system
pool, where all objects can be found at the time of initialisation. Other object pools can
be created and filled with objects. These pools can be assigned to individual Tasks in
order to avoid object shortages during runtime, as they would occur with shared object
pools (see subsection 7.4.1 on page 40).
Delay objects are the most common objects for modelling timers: a Delay object makes
sure that a Handler function is called after a specified period.
A periodic event object (Pe) is a specialised version of a Delay object. A Task creating a
Pe object receives events in regular intervals.
Note:
Objects that were released after utilisation, are returned to the object
pool they were taken from.
3.4.1 Messages
A Message object represents an element of communication that can transfer data.
A Task wishing to send a Message has to provide a data buffer for this Message.
The data buffer for a Message can be obtained in two ways: the sending Task can state a
memory class (see subsection 3.3.2 on page 15), from which the memory is to be taken,
i.e. it borrows the memory from this memory class and can use it as if it were its own
memory for as long as it owns the Message.
The other possibility is to provide the Message object with memory of the Tasks own
memory.
The data buffer can be filled with random data. As soon as the Message was sent to a
mailbox, the corresponding buffer is no longer part of the memory area of the Task, i.e.
the Task may no longer access this memory area.
If a recipient Task reads the Message from the mailbox, the contained buffer is allocated to
its memory area. The Task can access this area until it passes the Message on or releases
it. Once the Message was released, the contained memory is reallocated to its original
owner, i.e. the memory class or Task.
3.4.2 Events
An Event is used for signalling an occurrence to a Task. In comparison to Messages, Events
have the advantage of not using up any system resources. Up to 32 different Events can
be signalled, part of which can be freely defined by the application, and part of which are
predefined.
A Task can wait for several Events at the same time and is awakened as soon as one of
the Events occurs.
Note:
The MPU can even detect stack overflow if the memory is appro-
priately apportioned, i.e. if the stack is located at the end of the
individual memory.
Apart from this, memory can be mapped onto the address area of the Task if the Task
receives a Message object.
In addition, extended memory areas can be defined for a Task. This method is, however,
subject to a loss in performance since the memory areas are stored in virtual protection
registers that have to be mapped to the hardware registers before they can be accessed.
If a Task reaches out of its memory areas, the MPU detects this violation of memory
protection and triggers a trap, thus avoiding error propagation. The cause of the error
can be detected and the erroneous module can be selectively deactivated. Functional safety
of the systems is thus guaranteed.
This type of memory protection makes it possible to realise the concept of encapsulation:
A Task and its local Handlers are regarded as being located within a capsule, being thus
separated from the rest of the system, which in itself also consists of capsules. Communi-
cation is achieved purely via Message objects and Events.
This leads to the separation of the individual functional units. The capsules can be con-
sidered as each running on a processor of its own. Complexity of the system is thus
reduced, and the probability of error propagation. Should, nevertheless, an error occur,
its effects are restricted to the corresponding capsule. Thanks to these characteristics, test
procedures for applications can be modularised.
3.5.3 Permissions
Tasks in general do not have any permission to access resources; such access has to be
granted explicitly in individual cases. The permission concept knows two types of permis-
sion: privileges and access rights.
Privileges are permission levels of the hardware. They can be subdivided into two cate-
gories: system privileges allow full access and are only available to the operating system
and certain interrupt Handlers; user privileges exist in several levels and can be assigned
to parts of the application.
The following privilege levels exist:
Usermode0 allows access to the internal memory area exclusively
Usermode1 allows access to the internal memory area and to the periphery
Systemmode allows full access (only available to the operating system and certain inter-
rupt Handlers)
Access rights are managed by the operating system. The following access rights can be
granted:
PXACCESS HANDLERS for installing Handlers that own system rights
PXACCESS INSTALL HANDLERS for installing Handlers that own the rights of the
Task
PXACCESS INSTALL SERVICES for installing certain PXROS-HR services as Han-
dlers
PXACCESS REGISTER for executing system functions that have access to special reg-
isters of the processor
PXACCESS SYSTEMDEFAULT give access to the system memory class and system
object pool
PXACCESS RESOURCES allows a Task to access, in addition to its own resources
(those that were assigned to it during creation or that it created itself), but also to
resources that it does not own
PXACCESS NEW RESOURCES allows a Task to create object pools or memory classes
PXACCESS SYSTEM CONTROL allows a Task to suspend or continue other Tasks
PXACCESS MODEBITS allows a Task to set its modebits
PXACCESS OVERRIDE ABORT EVENTS allows a Task to override its aborting events
In cases where a system service is called without having the required access rights, an
error code is returned.
calls include calls with faulty parameters, such as invalid object IDs, missing permissions
or invalid system function calls within a Handler.
Second: Exceptional treatment aborts the regular control flow: invalid memory access
operations cause the MPU to trigger a trap. The trap can be processed by an application-
specific trap handler routine.
4.1.2 Nameserver
Giving access to special PXROS-HR objects, the Nameserver acts as an addressbook
where the requested objects can be browsed. For example: A reloaded Task can request
another Task, which it wishes to communicate with.
Objects can be registered with the Nameserver by a unique name. Later this name can
be used for requesting the object from the Nameserver. Any attempt to register an object
by a name that already exists, will fail; an error code is returned in such a case.
Names are assigned in accordance with a specific pattern: a name always consists of
four numbers ranging from 0 to 255. This combination of numbers, which has to be
unique within the system, makes it possible to group objects. The combination 1,1,5,x,
for example, is reserved for the file system (see also section 8.2 on page 61).
Note:
This service is necessary for a reloaded Task to communicate with the
rest of the system.
4.1.3 Tracing
The PXROS-HR tracing service makes it possible to record system calls, message exchange
and events for debugging purposes. The called function and its arguments as well as a
time stamp are stored in a buffer. These stored data can then be read by the application
and interpreted by an analysing tool such as PxView (see subsection 4.2.3 on page 25).
Users Guide V1.1 4.2 Additional services
Note:
The system monitor is capable of intercepting memory protection
traps, and passing them on to the debugger.
4.2.2 PXfile
PXfile makes the functionalities of a file system available, which can either be a local FAT
system of a remote NFS system. The NFS type requires PXtcp.
Usersguide
5 Summary
This part of the PXROS-HR user manual describes the practical operation of the system.
Examples will illustrate how applications are created under PXROS-HR.
A PXROS-HR application usually consists of one or more Tasks, as well as local and
global Handlers, which communicate with each other by means of message objects or
Events. A Task has a dedicated main function, which is called when the Task is started.
Normally, this Task function is never quit, i.e. it contains an infinite loop.
First an initialisation Task is started that creates further Tasks and carries out the neces-
sary initialisation processes. Once initialisation is finished, the priority of the initialisation
Task is usually set to the lowest level, and it is used as an idle Task, i.e. it is only used if
no other Task or Handler is active.
6 Layout of a programming interface
The PXROS-HR programming interface (API) has a schematical layout:
Function and type identifiers of the PXROS-HR interface always start with the prefix Px,
macros with PX.
With function names, said prefix is followed by the type of the object, which the function
refers to, followed by the action that is performed. Example: PxTaskGetPrio returns the
priority of a Task.
Type identifiers consist of the prefix, a unique name and the suffix _t or _T, e.g. PxTask_t.
7 Programming the core system
The following chapter describes how to program a PXROS-HR application. For this pur-
pose, the basic concepts and terms, as well as best practice procedures are explained
and illustrated by examples.
PxMcType_t is_sysmc_type:
the type of system memory class; permitted values are
PXMcVarsized and PXMcVarsizedAdjusted
PxSize_t is_sysmc_size:
size of a block of the system memory class
(is ignored if a variable size is defined)
Users Guide V1.1 7.2 The first simple application
PxAligned_t is_sysmc_blk:
pointer to the beginning of the system memory class
PxSize_t is_sysmc_blksize:
overall size of the system memory class
PxUInt_t is_obj_number:
number of PXROS objects
PxObjId_t is_obj_namelength:
maximum length of the object names, including zero fillers
PxTaskSpec_ct is_inittask:
Task specification of the Inittask
subsubsection 7.2.1.3 on page 31
PxInt_t is_monitoring:
This flag states if requests of variable memory blocks
and creation of Tasks is to be logged
PxGlobalSchedExt_ct is_schedext:
this parameter is presently not in use
PxMcType_t is_taskmc_type:
type of memory class of the Inittask; permitted values are
PXMcVarsized and PXMcVarsizedAdjusted
PxSize_t is_taskmc_size:
is ignored
PxSize_t is_taskmc_blksize:
overall size of the memory class of the Inittask
PxProtectRegion_T is_sys_data_protection[4]:
protected data areas of the system
PxProtectRegion_T is_sys_code_protection[1]:
protected code area of the system
PxProtectRegion_T is_task_code_protection[1]:
protected code area of the application
Calling PxInit starts the Inittask and only returns if an error occurred during initialisation.
In this case, the function returns a corresponding error code:
PXERR INIT ILLALIGN Erroneous alignment of system memory class
PXERR INIT ILLMCTYPE System memory class is not of type PXMcVarsized or PXMcVarsizedAdjusted
PXERR INIT NOMEM Not enough memory available
PXERR PROT ILL REGION Invalid memory protection areas defined
After a successful PxInit call, the PXROS-HR system services are available.
The Inittask is the first Task of the system. It is automatically started after system ini-
tialisation and corresponds in layout and handling to a normal Task (see subsection 7.2.3
on page 34). The differences lie in that it is created and started by PxInit , and that its
memory class is not stated in the Task specification but defined in PxInitSpec_T. This
is because no PXROS-HR system calls are available before system initialisation, so no
memory class can be created at this time.
The Inittask can be used for initialising the application and for creating and starting the
application Tasks. Typically, the priority of the Inittask set to lowest priority after the
initialisation phase, i.e. it is only activated if no other Task or Handler is active (Idletask).
Note:
The existence of an Idletask is not compulsory. The Inittask could also
be terminated after execution, yet it should be kept running since it
doesnt use up any processing time (lowest priority), and makes it
easier to analyse the scheduling behaviour of the system, for example
via PxView (see subsection 8.5.2 on page 64).
.ts_taskstack.stk_size = PXStackDontCheck,
.ts_taskstack.stk_src.stk = &inittask_stack[INITTASK_STACKSIZE],
.ts_inttaskstack.stk_type = PXStackFall,
.ts_inttaskstack.stk_size = PXStackDontCheck,
.ts_inttaskstack.stk_src.stk = &inittask_intstack[INITTASK_INTSTACKSIZE],
.ts_abortstacksize = 0
};
= (PxUInt_t)&PxTricSystemDataUpperBound,
.is_sys_data_protection[1].prot
= WRProtection,
PxError_t error;
error = PxInit(&InitSpec);
7.2.2 Startup-initialisation
Before the initialisation of PXROS-HR, possible hardware configurations might be carried
out. Since this usually has to done with system permission, an application cannot carry
out these procedures since it runs in user mode. This problem can be circumvented if the
application passes random functions, which can be used for the purpose to the operating
system.
_PxInitCall( function ,params ...) adds the function function with its parameters params to an
Note:
The function PxInitializeBeforePxInit has to be called before the ini-
tialisation PXROS-HR, i.e. before PxInit !
Essentially, a PXROS-HR Task consists of a Task function, which is called when the Task
is started. After its creation a Task is started by sending an activation event, i.e. the Task
function is called, provided one or more activation events were defined. If no activation
event was defined when the Task was created, the Task is immediately started. Normally,
this Task function is never quit, i.e. it contains an infinite loop.
In exceptional cases a Task can be terminated by calling the system service PxDie. This
function does not return. To be able to use PxDie, a Service Task has to be initialised
beforehand (see section 8.4 on page 62).
The initialisation of the Task is carried out during creation via the function PxTaskCreate.
The necessary parameters for initialising the Task are passed to this function:
opool : the object pool, from which the Task object is taken.
taskspec : the Task specification, which contains the necessary data for initialisation (see
subsubsection 7.2.3.2 on page 34)
prio : the priority of the Task
actevents : Events activating the Task; usually only one
Task specification is a structure of the type PxTaskSpec_T (the type designators PxTaskSpec_t
and PxTaskSpec_ct mentioned in the reference manual relate to pointers or constant point-
ers to PxTaskSpec_T). This structure is defined as follows:
PxUChar_t ts_name :
name of the Task; a random string
PxMc_t ts_mc :
Standard memory class of the Task
PxOpool_t ts_opool :
Standard objectpool of the Task
PxStackSpec_T ts_taskstack :
Stack specification of the Task (see page 36)
PxStackSpec_T ts_inttaskstack :
Interrupt stack specification of the Task (see page 36)
PxUChar_t ts_prio :
Initial priority of the Task; this parameter is kept only for
compatibility; it is ignored
PxEvents_t ts_actevents :
Activation events for the Task, this parameter is kept only for
compatibility; it is ignored
PxTicks_t ts_timeslices :
size of the time slices for this Task;
if this value is set to 0, no time slices are used
PxSize_t ts_abortstacksize :
size of the abort stacks, stated by the number of frames
PxTaskSchedExt_t ts_sched_extension :
not implemented
PxArg_t ts_sched_initarg :
not implemented
PxArg_t ts_privileges :
Privileges of the Task
PxUInt_t ts_accessrights :
Access rights of the Task
PxTaskContext_ct ts_context :
Description of the address space of the Task (see page 36)
PxProtectRegion_ct ts_protect_region :
extended memory areas of the Task
Notes:
A standard memory class can be accessed via the macro PXMcTaskdefault
The standard object pool of the Task can be accessed via the macro PXOpoolTaskdefault
Signalising an activation event starts the Task, i.e. after its creation the Task waits
until it receives an activation event; usually only one activation event is stated
The size of the Abort stacks determines the maximum nesting depth of PxExpectAbort
calls (see section 7.10 on page 58)
ts_protect_region defines extended memory areas of the Task. Extended memory
areas can be used in addition to the normal memory context of the Task. During
normal operation this should be avoided since the access to extended memory areas
is rather slow, yet in special cases this might be necessary, e.g. if a Task needs access
to a register set, or if access to configuration data is required.
The type PxStackSpec_T is used for describing Task and interrupt stacks. It contains
information on the stack type (ascending/descending), stack size, and from where the
memory is taken (for details, see the PXROS-HR reference manual).
The PxTaskContext_t structure describes the memory context of the Task. It contains two
protection regions (see below, PxProtectRegion_t). The first defines the read-only data area
of the Task, the second defines the read-write data area of the Task.
The structure PxProtectRegion_t defines protected memory areas, which can be made avail-
able to the Task. It contains the upper and lower boundary of the memory area (the first
valid and the first invalid address), as well as the access permission: no access; read only;
write only; read and write.
Sample code for creating and initialising the Task specification:
// Definition of the memory context of the Task; since the elements of
// protection[0] are set to 0, the read-only memory area is inherited
// from the creator of the Task
These parameters are passed from the system as soon as the function is called, i.e. if
the corresponding Task is activated for the first time. The parameters have the following
meaning:
myID ID of the Task
myMailbox Private mailbox of the Task
myActivationEvents Mask of the received activation events
If activation events were defined when creating the Task (see subsubsection 7.2.3.2 on
page 34), the Task will only become active if one or more of these events are received.
The Task function can contain almost any code, yet it may not be terminated. This means,
it must either contain an infinite loop or be terminated via a blocking system call (e.g.
PxAwaitEvents(0)).
A typical Task function consists of a declaration and initialisation section and an infinite
loop. In this loop the function waits for events and/or messages. If an Event or a message
is received, the Task becomes ready for execution. If it has the highest priority of all Tasks
in ready state, it is executed.
Example for a Task function:
void InitTask_Func(PxTask_t myID,
PxMbx_t myMailbox,
PxEvents_t myActivationEvents)
{
// Declarations and initialisations
int foo = 0;
int bar;
if(myActivationEvents == EV_ACT_EV1)
{
bar = 1;
}
else
{
bar = 42;
}
// Main loop
while(1)
{
// Parallel waiting for events and messages
PxMsgEvent_t msgev;
msgev = PxMsgReceive_EvWait(myMailbox,EV_EVENT_MASK);
if(PxMsgIdIsValid(msgev.msg))
{
// message received
do_sth_with_msg(msgev.msg);
}
if(msgev.events != 0)
{
// event received
do_sth_with_events(msgev.events);
}
}
}
7.2.4 Summary
Summary of the Initialisation of a PXROS-HR system:
A complete module for implementing the initialisation of PXROS-HR and for creating a
Task can be found in the appendix A.1.
needed, thus making memory utilisation more efficient. Memory classes are not usually
used for requesting memory explicitly, but mainly for making memory available to message
objects. By default, a Task has two memory classes at its disposal: PXMcSystemdefault, the
standard memory class of the system, and PXMcTaskdefault, the standard memory class
of the Task, which was assigned to it during creation. To use PXMcSystemdefault the Task
must have the appropriate rights (see subsection 3.5.3 on page 21).
7.6 Communication
Communication and synchronisation of different Tasks is achieved via Events and mes-
sages. Messages are objects containing data, while Events are flags signalling events.
7.6.1 Events
An Event is a flag signalling a certain event. A Task can wait for certain Events by the
function PxAwaitEvents(PXEvents_t events). The parameter events is a bitmask stating for
which Events the Task shall wait. PxAwaitEvents blocks until one or more of the events
defined in events occur. If events is set to zero, PxAwaitEvents will not return.
Note:
The uppermost four bits (28 - 31) are reserved for the operating sys-
tem and should not be used by the application!
...
#define EV_EV1 1
#define EV_EV2 (1<<1)
#define EV_EV3 (1<<2)
...
PXEvents_t ret_ev;
...
PxGetSavedEvents returns the Events received but not yet processed by the Task. This
function returns immediately.
The non-blocking resetting of the Events of a Task is carried out by
PxResetEvents(PXEvents_t events). The parameter events states, which Events are to be
reset. This function returns a bitmask stating, which Events were reset.
...
PXEvents_t ret_ev;
...
...
...
...
7.6.2 Messages
Messages are PXROS-HR objects containing a data buffer and are used for data inter-
change between Tasks. A Task creating a message object is called the Owner of the
message; the Task currently having access to a message object is called the User of the
message.
Message objects can be requested in two different ways: They can either be created by
PxMsgRequest or by PxMsgEnvelop. The differences lies in that that with PxMsgRequest the
data memory of the message is taken from a memory class, while with the envelope
mechanism the memory has to be provided by the requesting Task.
Message objects can be requested by Tasks but not by Handlers.
There are several ways of requesting message objects via PxMsgRequest:
PxMsgRequest(PxSize_t size, PxMcId_t mcid, PxOpoolId_t opool):
This function returns a message object taken from the object pool
opool. It contains a data buffer of the size size . The data buffer is
taken from the memory class mcid. Should the object pool be empty,
this function will block until objects are available again.
PxMsgRequest_NoWait(PxSize_t size, PxMcId_t mcid, PxOpoolId_t opool):
This function returns a message object taken from the object pool
opool. It contains a data buffer of the size size . The data buffer is
taken from the memory class mcid. Should the object pool be empty,
the returned Handle is invalid. This function returns immediately.
Note:
PxMsgRequest is a way for a Task to request memory dynamically.
If the envelop mechanism is to be used, one of the following functions can be executed:
PxMsgEnvelop_EvWait(PxMsgData_t data,PxSize_t size,PxOpool_t opool): This function
packs the buffer data from the memory area of the Task into a message object taken
from the object pool opoolid and returns this package. Should the object pool be
empty, this function will block until objects are available again.
PxMsgEnvelop_NoWait(PxMsgData_t data,PxSize_t size,PxOpool_t opool)): This function
packs the buffer data from the memory area of the Task into a message object taken
from the object pool opoolid and returns this package. Should the object pool be
empty, this function will return an invalid Handle.
PxMsgEnvelop_EvWait(PxMsgData_t data,PxSize_t size, PxOpool_t opool,PxEvents_t ev):
This function packs the buffer data from the memory area of the Task into a mes-
sage object taken from the object pool opoolid and returns this package. Should the
object pool be empty, this function will block until objects are available again or
until one of the Events specified in ev occurs.
Note:
If the envelop mechanism is used, the data area of the message is not
taken from a memory class; instead the creating Task has to provide
a buffer from its own memory area.
Note:
To be precise, the memory buffer packed via PxMsgEnvelop is not really
taken from the memory area of the Task; the Task could, theoretically,
still access this area. The buffer should, however, be treated as if it
were extracted from the address space of the Task.
If a message is fitted with a Release mailbox (via the function PxMsgInstallRelmbx(PxMsg_t msgid, PxM
the behaviour of PxMsgRelease... changes: the corresponding message object is not re-
leased but stored in the mentioned Release mailbox instead (see subsubsection 7.6.2.2
on page 44).
After a message was sent via the function
PxMsgAwaitRel(PxMsg_t msg), the Owner of the message can wait for release. To do
so, PxMsgSetToAwaitRel(PxMsg_t msg) has to be called before sending. Then, if the
recipient of the message calls PxMsgRelease, the message object is not released but
returned to the sender instead.
Note:
If a message was marked via PxMsgSetToAwaitRel, the function
PxMsgAwaitRel(PxMsg_t msg) has to be used for waiting for the release
call.
7.6.2.2 Mailboxes
To fulfil their purpose, message pools have to be specified as release mailboxes for the
contained message objects, so that they can return to the pool after release by the message
recipient.
The following code exemplifies how a message pool can be created and used:
...
PxMbx_t mpool;
PxMsg_t msg;
PxMsg_t tmpmsg;
for(i=0;i<10;i++) // request 10 messages
{
tmpmsg = PxMsgRequest(MSG_SIZE, my_memclass,my_opool);
if(PxMsgIdIsValid(tmpmsg))
{
PxMsgInstallRelmbx(tmpmsg,msgpoool); // set release mailbox
tmpmsg = PxMsgSend(tmpmsg,msgpool) // send message to messag pool
if(PxMsgIdIsValid(tmpmsg))
do_error_handling();
}
else
{
do_error_handling();
}
}
}
else
{
do_error_handling();
}
...
fill_msg_data();
Sending and receiving messages is always done via mailboxes. The sender sends a message
to a mailbox, and the recipient reads the message from this mailbox.
A message is sent via the function PxMsgSend, the necessary parameters are the message
object to be sent and the mailbox to which the message is to be sent. After sending,
the sender does no longer have access to the message object, meaning the Handle of the
message is invalid after sending. In addition, the sender passes the exclusive access right
to the data area of the message to the recipient, i.e. a pointer to this area is invalid.
Note:
A message object and the corresponding data area belong exclusively
to one user, i.e. they are allocated to one Task or one Handler.
if(retval.events != 0)
{
// one or more Events were received
...
}
A Task or a Handler can access the content of a message as soon as the message was re-
quested from the object pool or taken from a mailbox. The function PxMsgGetData(PxMsg_t msg)
or PxMsgGetData_Hnd(PxMsg_t msg) returns a pointer to the data area of the message ob-
ject msg, PxMsgGetBuffersize(PxMsg_t msg) returns the size of the data buffer and PxMsgGetSize(PxMsg_t m
returns the size of the data within the buffer.
If access to a data area is no longer required, yet the message is not to be released or sent,
the data area should be released via the function
PxMsgRelDataAccess(PxMsg_t msg) since parallel access is limited to a maximum of two
message buffers.
Note:
If access to a data area was released via
PxMsgRelDataAccess(PxMsg_t msg), a pointer to this area may
only be used after it was reset via PxMsgGetData, thus having renewed
access to the data.
The following example shows how to create, send and receive a message how to access the
received data.
Task1 creates and sends a message to Task2:
// Task1
...
...
if(!PxMsgIdIsValid(msg))
{
do_error_handling();
return;
}
error = PxMsgSetToAwaitRel(msg);
if(error != PXERR_NOERROR)
{
do_error_handling();
return;
}
if(!PxMsgIdIsValid(msg))
{
do_error_handling();
return;
}
...
...
...
Access to the data area can be restricted for a user of the message. Either the type of
access or the size of the accessible area can be restricted.
WRProtection : permits unrestricted access to the data area, i.e. the user has the right
to read and write
WriteProtection : permits the user to write into the data buffer
ReadProtection : permits the user to read from the data buffer
NoAccessProtection : permits no access to the data buffer
Access protection can only be changed by the owner of the message.
The user of a message can restrict the data area of a message; this is achieved via
PxMsgSetData and PxMsgSetSize. The start address of the data area is reset via PxMsgSetData;
the new address has to lie within the original data area. The size of the data area is au-
tomatically adjusted, so that the last address lies within the original data area.
The size of the data area can be reset via PxMsgSetSize; care has to be taken that the last
address of the new data area lies within the original data area.
An example:
Task1 sends a message to Task2:
...
struct foo
{
int x;
int y;
struct bar b;
int z;
} data;
...
// create message and pack data into message
msg = PxMsgEnvelop(&data,sizeof(data),defaultopool);
PxMsgSetToAwaitRel(msg);
...
// send to Task2
PxMsgSend(msg,task2mbx);
...
// wait for release of message
msg = PxMsgAwaitRel(msg);
Task2 passes the message on to Task3; Task3 shall only have access to the component b
of the structure foo:
...
struct foo* pData;
...
// receive message
msg = PxMsgReceive(task2mbx);
...
pData = PxMsgGetData(msg);
...
// restrict start address and size of the data area to Element b of
// the structure foo
// Set start address to b
PxMsgSetData(msg,&pData->b);
...
// send to Task3
PxMsgSend(msg,task3mbx);
...
Note:
When starting the system, interrupts are switched off, so it is neces-
sary to activate them explicitly after initialising the interrupt source
and installing the Handlers.
Note:
Due to the fact that a local Handler is first entered into a list, no
assumption can be made regarding its execution time. Real time be-
haviour is not given!
Before a local Handler can be installed, an interrupt object has to be requested via
PxInterruptRequest. A Handler is installed via:
PxIntInstallHandler(PxUnit_t intno,
PxInterrupt_t intobj,
void (*inthandler)(PxArg_t),
PxArg_t arg)
Note:
It should be ensured that the time basis is the same for the initialisa-
tion of the timer interrupts and when calling PxTickSetTicksPerSecond.
The time passed since the first call of PxTickDefine_Hnd can be queried via
PxTickGetTimeInMilliSeconds.
and
PxDelaySched_Hnd(PxDelay_t delayId, PxTicks_t ticks, void (handler)(PxArg_t), PxArg_t arg)
a function can be registered for subsequent processing. In this context, delayId is the
Handle of the delay object, ticks the number of Ticks after which the function is to be
executed, handler the pointer to the function to be executed, and arg the argument passed
to the function.
...
ticks = PxGetTicksFromMillis(100);
error = PxDelaySched_Hnd(delay,ticks,handler_function,delay);
...
}
...
void TaskFunc()
{
...
PxDelay_t delay;
PxTicks_t ticks;
PxError_t error;
...
delay = PxDelayRequest(defaultopool);
if(PxDelayIdIsValid(delay))
{
ticks = PxGetTicksFromMillis(100);
error = PxDelaySched(delay,ticks,handler_function,delay);
if(error != PXERR_NOERROR)
{
do_error_handling();
}
...
...
}
opool : the object pool from which the timeout object is to be taken
ticks : the number of Ticks after which the Events are to be sent
ev : the Events to be sent after expiry of the timeout
The timer starts after the function call PxToStart(PxTo_t to) and be terminated via PxToStop(PxTo_t to)
if it is not yet expired.
Periodical timers (PxPe_t) differ from timeout objects in that they do not just send a
single timeout Event but periodical ones.
This function receives the address of the desired register as a parameter and returns the
contend of the register, zero in case of an error.
Note:
If the function returns zero, it cannot be determined whether this is
the actual content of the register or an erroneous access operation.
The function
PxError_t PxRegisterWrite(PxInt_t *register, PxInt_t value)
writes the value value into the register with the address register . If the access was success-
ful, the function will return PXERR_NOERROR; in case of an error, it will return either
PXERR_PROT_ILL_REGION or PXERR_ACCESS_RIGHT.
the bits masked in mask in the register register are set to the value stated in value . If the
access was successful, the function will return PXERR_NOERROR; in case of an error, it
will return either PXERR_PROT_ILL_REGION or PXERR_ACCESS_RIGHT.
void func(int i)
{
...
}
void Task1Func()
{
...
PxEvents_t retval;
...
retval = PxExpectAbort(EV_ABORT,func,42);
}
else // the function was aborted
{
...
}
...
}
void Task2Func()
{
...
PxTaskSignalEvents(Task1,EV_ABORT);
...
}
...
/*
* request number of all objects within the system
*/
numObjects = PxSysInfoGetNumberOfObjects();
/*
* iterate over objects and request properties
*/
for (i = 0; i < numObjects; i++)
{
/*
* request object ID and type
*/
obj = PxObjIdSet(i);
otype = PxSysInfoGetObjType(obj);
if (otype == _PXObjIllObject)
{
/*
* list of objects is processed
*/
break;
}
if (otype == _PXObjUnused)
{
/*
* count free objects
*/
freeObjects++;
}
Users Guide V1.1 8.2 The Nameserver
}
return freeObjects;
}
Note:
Addresses starting with 1 are reserved; applications should not use
the address space 1.x.x.x.
PxError_t ServiceTaskCode(void)
{
PxMbx_t relmbx;
PxEvents_t ev;
PxError_t err;
relmbx = PxMbxRequest(PXOpoolTaskdefault);
/* or, if the local Mailbox is otherwise unused:
relmbx = PxTaskGetMbx(PxGetId());
*/
if (PxMbxIdError(relmbx) != PXERR_NOERROR)
return PxMbxIdError(relmbx);
if (err != PXERR_NOERROR)
return err;
while(1)
{
ev = PxAwaitEvents(PXSERVICE_TASK_DIED | PXSERVICE_HND_MSGREL);
// a Task is terminated
if (ev & PXSERVICE_TASK_DIED)
{
err = PxDieService();
if (err != PXERR_NOERROR)
do_error_handling();
}
// a message is released
if (ev & PXSERVICE_HND_MSGREL)
{
err = PxMsgrelService();
if (PxMsgIdError( msg) != PXERR_NOERROR)
do_error_handling();
}
}
}
8.5.2 PxView
An analysis of the trace buffer can be carried out at will, yet the use of the PxView tool
is recommended. The PxView library has to be included for this purpose (libpxview.a,
and the header file pxview.h).
The PxView library provides certain functionalities, which make it possible to manipulate
the trace mechanism, and to pass logged data via communication interface to the PxView-
Client, which carries out the display of the logged data.
For information on the use of PxView, see the PxView reference manual.
Appendix
A Example code
A.1 For starters: a simple application
#include <pxdef.h>
// Priority of Task1
#define TASK1_PRIO 4
#define PXROS_NAMESIZE 12
#define SYSMEMSIZE 20000
#define TASK1MEMSIZE 10000
// Function prototypes
PxAligned_t inittask_stack[INITTASK_STACKSIZE];
PxAligned_t inittask_intstack[INITTASK_INTSTACKSIZE];
.protection[1].lowerBound = (PxUInt_t)&INIT_DATA_BASE,
.protection[1].upperBound = (PxUInt_t)&INIT_DATA_END,
.protection[1].prot = WRProtection,
};
// Specification of Inittask
static const PxTaskSpec_T InitTaskSpec =
{
.ts_name = "InitTask",
.ts_fun = InitTask_Func,
.ts_mc = PXMcSystemdefault,
.ts_opool = PXOpoolSystemdefault,
.ts_prio = 0,
.ts_privileges = PXUser1Privilege,
.ts_context = &InitTaskContext,
.ts_taskstack.stk_type = PXStackFall,
.ts_taskstack.stk_size = PXStackDontCheck,
.ts_taskstack.stk_src.stk = &inittask_stack[INITTASK_STACKSIZE],
.ts_inttaskstack.stk_type = PXStackFall,
.ts_inttaskstack.stk_size = PXStackDontCheck,
.ts_inttaskstack.stk_src.stk = &inittask_intstack[INITTASK_INTSTACKSIZE],
.ts_abortstacksize = 0
};
.protection[1].lowerBound = (PxUInt_t)&TASK1_DATA_BASE,
.protection[1].upperBound = (PxUInt_t)&TASK1_DATA_END,
.protection[1].prot = WRProtection,
};
// Specification of Task1
static const PxTaskSpec_T Task1Spec =
{
.ts_name = "Task1",
.ts_fun = Task1_Func,
.ts_mc = PXMcSystemdefault,
.ts_opool = PXOpoolSystemdefault,
.ts_prio = 0,
.ts_privileges = PXUser1Privilege,
.ts_context = &Task1Context,
.ts_taskstack.stk_type = PXStackFall,
.ts_taskstack.stk_size = PXStackDontCheck,
.ts_taskstack.stk_src.stk = &task1_stack[TASK1_STACKSIZE],
.ts_inttaskstack.stk_type = PXStackFall,
.ts_inttaskstack.stk_size = PXStackDontCheck,
.ts_inttaskstack.stk_src.stk = &task1_intstack[TASK1_INTSTACKSIZE],
.ts_abortstacksize = 0
};
/* protection definition */
.is_sys_data_protection[0].lowerBound = (PxUInt_t)&PxTricSystemRamBegin,
.is_sys_data_protection[0].upperBound = (PxUInt_t)&PxTricSystemRamEnd,
.is_sys_data_protection[0].prot = ReadProtection,
.is_sys_data_protection[1].lowerBound = (PxUInt_t)&PxTricSystemDataLowerBound,
.is_sys_data_protection[1].upperBound = (PxUInt_t)&PxTricSystemDataUpperBound,
.is_sys_data_protection[1].prot = WRProtection,
.is_sys_data_protection[2].lowerBound = (PxUInt_t)&PxTricSystemIntDataLowerBound,
.is_sys_data_protection[2].upperBound = (PxUInt_t)&PxTricSystemIntDataUpperBound,
.is_sys_data_protection[2].prot = WRProtection,
.is_sys_data_protection[3].lowerBound = (PxUInt_t)0,
.is_sys_data_protection[3].upperBound = (PxUInt_t)0,
.is_sys_data_protection[3].prot = NoAccessProtection,
.is_sys_code_protection[0].lowerBound = (PxUInt_t)&PxTricSystemExtCodeLowerBound,
.is_sys_code_protection[0].upperBound = (PxUInt_t)&PxTricSystemExtCodeUpperBound,
.is_sys_code_protection[0].prot = XProtection,
.is_task_code_protection[0].lowerBound = (PxUInt_t)&PxTricSystemExtCodeLowerBound,
.is_task_code_protection[0].upperBound = (PxUInt_t)&PxTricSystemExtCodeUpperBound,
.is_task_code_protection[0].prot = XProtection,
};
// Code of Inittask
static void InitTask_Func(PxTask_t myID,
PxMbx_t myMailbox,
PxEvents_t myActivationEvents)
{
PxTask_t taskID = PxTaskCreate(PXOpoolSystemdefault,
&Task1Spec,
TASK1_PRIO,
0);
PxTaskSetPrio(myID,MIN_PRIO);
}
if(myActivationEvents == EV_ACT_EV1)
{
bar = 1;
}
else
{
bar = 42;
}
// Main loop
while(1)
{
// parallel waiting for events and messages
PxMsgEvent_t msgev;
msgev = PxMsgReceive_EvWait(myMailbox,EV_EVENT_MASK);
if(PxMsgIdIsValid(msgev.msg))
{
// message received
do_sth_with_msg(msgev.msg);
}
if(msgev.events != 0)
{
// event received
do_sth_with_events(msgev.events);
}
}
}
int main(void)
{
PxError_t error;
error = PxInit(&InitSpec);
if (error != PXERR_NOERROR)
{
PxPanic();
return 1;
}
return 0;
}
#define MY_HANDLER_EVENT 1
...
// Requesting a message
// Memory has to be the same system memory class
//
commMsg = PxMsgRequest(sizeof(struct TaskHandlerComm) + COMM_BUFFERSIZE,
PXMcSystemdefault,
PXOpoolTaskdefault);
if (!PxMsgIdIsValid(commMsg))
{
PxPanic();
}
while (1)
{
...
if (ev & MY_HANDLER_EVENT)
{
// Handler has received data and entered
// them in communication structure
ProcessInputData(myCommMem);
}
...
}
}
if (DataReadyForTask())
{
// Complete set of data received, Task is
// informed
PxTaskSignalEvents_Hnd(myTaskHandlerComm->commTask,
myTaskHandlerComm->commEv);
}
}
A.3 Messagepools
A.3.1 Creating a Messagepool
// Avoid interruptions
oldmode = PxSetModebits(PXTmodeDisableAborts | PXTmodeDisableRemprocs);
if (!PxMbxIdIsValid(Poolmbx))
{
// Creation error
PxSetError(poolmbx.error);
PxClearModebits(oldmode);
return poolmbx;
}
{
// Error has occurred, Messagepool is
// deleted
err = msg.error;
// Return Messagepool
return poolmbx;
}
// Avoid interruptions
oldmode = PxSetModebits(PXTmodeDisableAborts | PXTmodeDisableRemprocs);
// Delete Mailbox
return PxMbxRelease(poolmbx);
}