OSEK/VDX Design-Patterns - WordPress.com · • implementation of client-server structures (e.g....
Transcript of OSEK/VDX Design-Patterns - WordPress.com · • implementation of client-server structures (e.g....
ProOSEK Design-Workshop
OSEK/VDX
Design-Patterns
ProOSEK Design-Workshop 2Copyright 3SOFT GmbH 2003
Example Application Areas
A cyclic task.
A task that must wait on several events.
Two tasks that should exchange data over shared memory.
A task that sends various calculations to a service task and which is then informed when these are ready
A task to receive and distribute CAN messages
ProOSEK Design-Workshop 3Copyright 3SOFT GmbH 2003
Example Application Areas
Wait functionality
Working with messages
Measurement of task runtime
ProOSEK Design-Workshop 4Copyright 3SOFT GmbH 2003
1 Cyclic Task – Version 1
Cyclic TaskCyclic Task
Alarm
monitors
starts
activates
Counter
InitInit
ProOSEK Design-Workshop 5Copyright 3SOFT GmbH 2003
1 Cyclic Task – Version 1
TASK(CyclTask){static unsigned char firsttime = 42;if (firsttime == 42){
SetRelAlarm(CycleAlarm, 100, 100);/* Configurator:
Alarm CycleAlarm activated by executing the Task CyclTask */
firsttime = 0;}
/* cyclic function */
TerminateTask();}
TASK(CyclTask){static unsigned char firsttime = 42;if (firsttime == 42){
SetRelAlarm(CycleAlarm, 100, 100);/* Configurator:
Alarm CycleAlarm activated by executing the Task CyclTask */
firsttime = 0;}
/* cyclic function */
TerminateTask();}
Initialization of the Alarm
Code to be executed
ProOSEK Design-Workshop 6Copyright 3SOFT GmbH 2003
1 Cyclic Task – Version 2
Cyclic TaskCyclic Task
Alarm
monitors
starts
sets
Counter
InitInit
WaitEvent
ProOSEK Design-Workshop 7Copyright 3SOFT GmbH 2003
1 Cyclical Task – Version 2
TASK(CyclTask){SetRelAlarm(CycleAlarm, 100, 100);/* Configurator:
Alarm CycleAlarm set by execution of the Event Cycle in CyclTask */
while(true){
WaitEvent(Cycle);ClearEvent(Cycle);
/* cyclic function */
}TerminateTask();}
TASK(CyclTask){SetRelAlarm(CycleAlarm, 100, 100);/* Configurator:
Alarm CycleAlarm set by execution of the Event Cycle in CyclTask */
while(true){
WaitEvent(Cycle);ClearEvent(Cycle);
/* cyclic function */
}TerminateTask();}
Initialization of the Alarm
Wait on the Event
ProOSEK Design-Workshop 8Copyright 3SOFT GmbH 2003
2 Waiting on events
Task ATask A
WaitEvent ( 1 | 2 | 3 )
Set Event 1
Task BTask B Set Event 2
Set Event 3Task CTask C
TaskTask
ProOSEK Design-Workshop 9Copyright 3SOFT GmbH 2003
2 Waiting on events – Case A
Case A) Every event has a corresponding function. Upon occurrence of an event the task should call the corresponding function
TASK(RecvTask){
EventMaskType NewEvents;while(true){WaitEvent(Event1 | Event2 | Event3);GetEvent(RecvTask, &NewEvents);ClearEvent(NewEvents);
if(NewEvent & Event1){ /* Function for Event1 */ }
if(NewEvent & Event2){ /* Function for Event2 */ }
if(NewEvent & Event3){ /* Function for Event3 */ }
}TerminateTask();}
TASK(RecvTask){
EventMaskType NewEvents;while(true){WaitEvent(Event1 | Event2 | Event3);GetEvent(RecvTask, &NewEvents);ClearEvent(NewEvents);
if(NewEvent & Event1){ /* Function for Event1 */ }
if(NewEvent & Event2){ /* Function for Event2 */ }
if(NewEvent & Event3){ /* Function for Event3 */ }
}TerminateTask();}
Wait on one event
Code to be executed per event
ProOSEK Design-Workshop 10Copyright 3SOFT GmbH 2003
2 Waiting on events – Case B
TASK(RecvTask){
EventMaskType NewEvents;EventMaskType SavedEvents=0;while(true){
WaitEvent(Event1 | Event2 | Event3);GetEvent(RecvTask, &NewEvents);ClearEvent(NewEvents);SavedEvents = SavedEvents | NewEvents;if((SavedEvents & Event1) && (SavedEvents & Event2)){
SavedEvents = SavedEvents & ~(Event1 | Event2);/* Function for the combination of Event1 and Event2*/
}if((SavedEvents & Event1) && (SavedEvents & Event3)){
SavedEvents = SavedEvents & ~(Event1 | Event3);/* Function for the combination of Event1 and Event3 */
}}
TerminateTask();}
TASK(RecvTask){
EventMaskType NewEvents;EventMaskType SavedEvents=0;while(true){
WaitEvent(Event1 | Event2 | Event3);GetEvent(RecvTask, &NewEvents);ClearEvent(NewEvents);SavedEvents = SavedEvents | NewEvents;if((SavedEvents & Event1) && (SavedEvents & Event2)){
SavedEvents = SavedEvents & ~(Event1 | Event2);/* Function for the combination of Event1 and Event2*/
}if((SavedEvents & Event1) && (SavedEvents & Event3)){
SavedEvents = SavedEvents & ~(Event1 | Event3);/* Function for the combination of Event1 and Event3 */
}}
TerminateTask();}
Case B) The task waits on particluar combinations of events. For every event combination there is a corresponding function.
ProOSEK Design-Workshop 11Copyright 3SOFT GmbH 2003
2 Waiting on events – Case C
Case C) The same semantics as in case B.
TASK(RecvTask){
static const EventMaskType combinedEventA = Event1 | Event2;static const EventMaskType combinedEventB = Event1 | Event3;EventMaskType NewEvents;EventMaskType StillToWait = Event1 | Event2 | Event3;while(true){
WaitEvent(StillToWait);GetEvent(RecvTask, &NewEvents);StillToWait ^= NewEvents;if((~StillToWait) & combinedEventA){
ClearEvent(combinedEventA);/* Function for the combination of Event1 and Event2*/
}if((~StillToWait) & combinedEventB){
ClearEvent(combinedEventB);/* Function for the combination of Event1 and Event3 */
}}
TerminateTask();}
TASK(RecvTask){
static const EventMaskType combinedEventA = Event1 | Event2;static const EventMaskType combinedEventB = Event1 | Event3;EventMaskType NewEvents;EventMaskType StillToWait = Event1 | Event2 | Event3;while(true){
WaitEvent(StillToWait);GetEvent(RecvTask, &NewEvents);StillToWait ^= NewEvents;if((~StillToWait) & combinedEventA){
ClearEvent(combinedEventA);/* Function for the combination of Event1 and Event2*/
}if((~StillToWait) & combinedEventB){
ClearEvent(combinedEventB);/* Function for the combination of Event1 and Event3 */
}}
TerminateTask();}
ProOSEK Design-Workshop 12Copyright 3SOFT GmbH 2003
2 Waiting on events – Case D
DeclareTask(ManyEvents);DeclareEvent(Ev1);DeclareEvent(Ev2);DeclareEvent(Ev3);
TASK(ManyEvents){
EventMaskType aktuell;EventMaskType FehltNoch = Ev1 | Ev2 | Ev3;for(;;){WaitEvent(FehltNoch);GetEvent(ManyEvents, &aktuell);ClearEvent(aktuell);FehltNoch ^= aktuell;if(FehltNoch == 0){/* Handle the event */FehltNoch = Ev1 | Ev2 | Ev3;
}}
}
DeclareTask(ManyEvents);DeclareEvent(Ev1);DeclareEvent(Ev2);DeclareEvent(Ev3);
TASK(ManyEvents){
EventMaskType aktuell;EventMaskType FehltNoch = Ev1 | Ev2 | Ev3;for(;;){WaitEvent(FehltNoch);GetEvent(ManyEvents, &aktuell);ClearEvent(aktuell);FehltNoch ^= aktuell;if(FehltNoch == 0){/* Handle the event */FehltNoch = Ev1 | Ev2 | Ev3;
}}
}
Case D) The function should be executed only when all events have occurred. (Special case of B)
ProOSEK Design-Workshop 13Copyright 3SOFT GmbH 2003
3 Shared Memory – Version 1
GetResource(X)
Task ATask A
Resource XResource X
Memory XY
ReleaseResource(X)
GetResource(X)ReleaseResource(X)
Read / Write Read / Write
Task BTask B
ProOSEK Design-Workshop 14Copyright 3SOFT GmbH 2003
3 Shared Memory – Version 1
DeclareTask(A);DeclareTask(B);DeclareResource(ResX);
TASK(A){GetResource(ResX);/* Access Memory XY */ReleaseResource(ResX);
}
TASK(B){GetResource(ResX);/* Access Memory XY */ReleaseResource(ResX);
}
DeclareTask(A);DeclareTask(B);DeclareResource(ResX);
TASK(A){GetResource(ResX);/* Access Memory XY */ReleaseResource(ResX);
}
TASK(B){GetResource(ResX);/* Access Memory XY */ReleaseResource(ResX);
}
Encapsulating access using OSEK Resources
ProOSEK Design-Workshop 15Copyright 3SOFT GmbH 2003
3 Shared Memory – Version 2
Task ATask A
Task BTask B
Memory XY
Read / Write
Read / Write
Configuration of tasks A & B:
non-preemptive,
prio(A) ≠ prio(B)
ProOSEK Design-Workshop 16Copyright 3SOFT GmbH 2003
4 Service Task
WaitEvent
Task ATask A
Service TaskService Task
Memory XY
1. Write Parameter
2. Activate Service Task
Set Event
ProOSEK Design-Workshop 17Copyright 3SOFT GmbH 2003
4 Service Task
DeclareTask(A);DeclareTask(ServiceTask);DeclareEvent(ServiceEnd);
// Global variables for the parameter and result of the Service Taskchar Param, Result;
TASK(A){
...Param = 42;ActivateTask(ServiceTask);WaitEvent(ServiceEnd);ClearEvent(ServiceEnd);...
}
TASK(ServiceTask){
/* Calculate the results */Result = (Param + 4) / 2;SetEvent(A, ServiceEnd);TerminateTask();
}
DeclareTask(A);DeclareTask(ServiceTask);DeclareEvent(ServiceEnd);
// Global variables for the parameter and result of the Service Taskchar Param, Result;
TASK(A){
...Param = 42;ActivateTask(ServiceTask);WaitEvent(ServiceEnd);ClearEvent(ServiceEnd);...
}
TASK(ServiceTask){
/* Calculate the results */Result = (Param + 4) / 2;SetEvent(A, ServiceEnd);TerminateTask();
}
ProOSEK Design-Workshop 18Copyright 3SOFT GmbH 2003
5 CAN Dispatcher
CAN IRQ: ActivateTask
Task CANTask CAN
Task ATask A
Task BTask B
Task CTask C
Message for Task A
Message for Task C
Set Event
•No polling necessary•Synchronization using OSEK resources•No polling necessary•Synchronization using OSEK resources
ProOSEK Design-Workshop 19Copyright 3SOFT GmbH 2003
5 CAN Dispatcher
DeclareTask(A); DeclareTask(B); DeclareTask(C); DeclareTask(CAN);
TASK(CAN){
/* determine which Rx-Mailbox the IRQ has activated,* depending on this the message 1,2 or 3 is sent. */switch(MailboxNr){
case 1: SendMessage(Mailbox1, Data); break;case 2: SendMessage(Mailbox2, Data); break;case 3: SendMessage(Mailbox3, Data); break;
}TerminateTask();
}
TASK(C) // For example, the Task C that is waiting on an event.{
...WaitEvent(SendMessage3);ReceiveMessage(Mailbox3, &myData);...
}
DeclareTask(A); DeclareTask(B); DeclareTask(C); DeclareTask(CAN);
TASK(CAN){
/* determine which Rx-Mailbox the IRQ has activated,* depending on this the message 1,2 or 3 is sent. */switch(MailboxNr){
case 1: SendMessage(Mailbox1, Data); break;case 2: SendMessage(Mailbox2, Data); break;case 3: SendMessage(Mailbox3, Data); break;
}TerminateTask();
}
TASK(C) // For example, the Task C that is waiting on an event.{
...WaitEvent(SendMessage3);ReceiveMessage(Mailbox3, &myData);...
}
ProOSEK Design-Workshop 20Copyright 3SOFT GmbH 2003
6 Wait Functionality
In the OSEK Standard there is no direct Wait Functionality supported.
Alarm A_Wait
monitors
sets E_Wecken
Task
.
.
./* wait for 10 Ticks */SetRelAlarm(A_Wait, 10, 0);
WaitEvent(E_Wecken);
ClearEvent(E_Wecken);...
.
.
./* wait for 10 Ticks */SetRelAlarm(A_Wait, 10, 0);
WaitEvent(E_Wecken);
ClearEvent(E_Wecken);...
starts
•No events are lost•Waiting does not result in any processor load•No events are lost•Waiting does not result in any processor load
ProOSEK Design-Workshop 21Copyright 3SOFT GmbH 2003
7 Working with Messages
State Messages: Unqueued Messages with copy or without copy• Distribution of system-wide information with consistency checking• ProOSEK4.0: Possibility of Broadcasting through several Notification-Events
State Messages: Unqueued Messages with copy or without copy• Distribution of system-wide information with consistency checking• ProOSEK4.0: Possibility of Broadcasting through several Notification-Events
Algorithm AAlgorithm A
Algorithm BAlgorithm B
TemperatureManagerManager
Speed
Remarks:• WithoutCopy saves space, but consistency checking must be done by the application (Message Resources)• on sending, the last message sent is overwritten• the messages must be initialized before application startup (MessageInit)• WithCopy has the advantage that all messages can be used (of interest from COM3.0)
Remarks:• WithoutCopy saves space, but consistency checking must be done by the application (Message Resources)• on sending, the last message sent is overwritten• the messages must be initialized before application startup (MessageInit)• WithCopy has the advantage that all messages can be used (of interest from COM3.0)
ProOSEK Design-Workshop 22Copyright 3SOFT GmbH 2003
7 Working with Messages
State Messages: Code example using WithCopyTask T_Algorithm_B uses several messages to calculate expected values. These messages must be consistent with one another This is achieved by using a message counter (a time-stamp would be better)
State Messages: Code example using WithCopyTask T_Algorithm_B uses several messages to calculate expected values. These messages must be consistent with one another This is achieved by using a message counter (a time-stamp would be better)
Speed
TASK(T_Algorithm_B){/* copy of the state message */tMsgSpeed currentV; /* contains the current mastercount*/tMsgCount actMsgCount;
/* ... */
/* current value of speed message */ReceiveMessage(MSG_Speed,
¤tV);
/* check message set is consistent */if( currentV.count != actMsgCount ){ /* error handling (examples):
- ask for new message valueand read message once more
- extrapolation with olderstored values
- servere error -> reset */ }
/* ... */}
TASK(T_Algorithm_B){/* copy of the state message */tMsgSpeed currentV; /* contains the current mastercount*/tMsgCount actMsgCount;
/* ... */
/* current value of speed message */ReceiveMessage(MSG_Speed,
¤tV);
/* check message set is consistent */if( currentV.count != actMsgCount ){ /* error handling (examples):
- ask for new message valueand read message once more
- extrapolation with olderstored values
- servere error -> reset */ }
/* ... */}
/* header: COMUserData.h */
#ifndef COM_USER_DATA_H#define COM_USER_DATA_H
typedef struct{ unsigned char count;unsigned char value;
} tMsgSpeed;
/* ... */
#endif
/* header: COMUserData.h */
#ifndef COM_USER_DATA_H#define COM_USER_DATA_H
typedef struct{ unsigned char count;unsigned char value;
} tMsgSpeed;
/* ... */
#endif
/* initialisation */
StatusType MessageInit(void){ speed.count = 0;/*speed at system start*/speed.value = 0;SendMessage(MSG_Speed,
&speed);return(E_OK);
}
/* initialisation */
StatusType MessageInit(void){ speed.count = 0;/*speed at system start*/speed.value = 0;SendMessage(MSG_Speed,
&speed);return(E_OK);
}
tMsgSpeed speed;
TASK(T_Manager){/* ... */
/* retrieve speed value somehow ... */
/* new message count */ speed.count++;
SendMessage(MSG_Speed, &speed);
/* ... */}
tMsgSpeed speed;
TASK(T_Manager){/* ... */
/* retrieve speed value somehow ... */
/* new message count */ speed.count++;
SendMessage(MSG_Speed, &speed);
/* ... */}
ProOSEK Design-Workshop 23Copyright 3SOFT GmbH 2003
7 Working with Messages
queued Messages: buffered consistent data exchangequeued Messages: buffered consistent data exchange
ServerServerClientClient
Remarks:• implementation of client-server structures (e.g. fast control task and “slow“ driver task)• asynchronous data exchange• processing follows on FIFO basis• a weighting of messages can be achieved using several message queues
(Receiver would then be correspondingly more complicated)• the message is consumed by reading• a message that is sent to a full message queue will be thrown away• exception handling for queue overflow is planned• fromCOM3.0: M:N configuration is possible
Remarks:• implementation of client-server structures (e.g. fast control task and “slow“ driver task)• asynchronous data exchange• processing follows on FIFO basis• a weighting of messages can be achieved using several message queues
(Receiver would then be correspondingly more complicated)• the message is consumed by reading• a message that is sent to a full message queue will be thrown away• exception handling for queue overflow is planned• fromCOM3.0: M:N configuration is possible
ProOSEK Design-Workshop 24Copyright 3SOFT GmbH 2003
7 Working with Messages
queued Messages: code examplequeued Messages: code example
TASK(T_DeviceClient){tMsgDevice myData;
/* ... */
/* we do not want ACK Event */myData.taskId = INVALID_TASK;
/* copy data to message */
if((SendMessage(MSG_Device, &myData)!= E_OK) &&GetMessageStatus(MSG_Device)!= E_OK))
{/* we have a problem, perhaps the
queue is full.do some exception handling */
}
/* continue working */
/* ... */
}
TASK(T_DeviceClient){tMsgDevice myData;
/* ... */
/* we do not want ACK Event */myData.taskId = INVALID_TASK;
/* copy data to message */
if((SendMessage(MSG_Device, &myData)!= E_OK) &&GetMessageStatus(MSG_Device)!= E_OK))
{/* we have a problem, perhaps the
queue is full.do some exception handling */
}
/* continue working */
/* ... */
}
TASK(T_DeviceServer){/* container for current data */tMsgDevice currentData;
/* receive current data */ ReceiveMessage(MSG_Device,
¤tData);
/* move data to slow device */ /* ... */
/* Send an Acknowledge, ifwished by Sender */
if(currentData.taskId!= INVALID_TASK)
{SetEvent(currentData.taskId,
EV_DeviceAck);}
(void) TerminateTask();}
TASK(T_DeviceServer){/* container for current data */tMsgDevice currentData;
/* receive current data */ ReceiveMessage(MSG_Device,
¤tData);
/* move data to slow device */ /* ... */
/* Send an Acknowledge, ifwished by Sender */
if(currentData.taskId!= INVALID_TASK)
{SetEvent(currentData.taskId,
EV_DeviceAck);}
(void) TerminateTask();}
ActivateTask(T_DeviceServer)
/* header: COMUserData.h */
#ifndef COM_USER_DATA_H#define COM_USER_DATA_H
typedef struct{ unsigned char taskId; unsigned char data[4];
} tMsgDevice;
/* ... */
#endif
/* header: COMUserData.h */
#ifndef COM_USER_DATA_H#define COM_USER_DATA_H
typedef struct{ unsigned char taskId; unsigned char data[4];
} tMsgDevice;
/* ... */
#endif
ProOSEK Design-Workshop 25Copyright 3SOFT GmbH 2003
8 Measuring task runtime
Problem:How can the runtime of all tasks in a system be measured?
Solution:Measurement routines in the Pre- and PostTaskHook
TaskA
TaskB
running
suspended
running
suspended
ready
running
suspended
Termination TaskATermination TaskB
Prio
rity
PreTaskHook
PostTaskHook
ProOSEK Design-Workshop 26Copyright 3SOFT GmbH 2003
8 Measuring task runtime
The Hooks are called on entry to the scheduler. That means that preemptive tasks that are interrupted are also accurately measured
There is only one Pre- und one PostTaskHook in the system
IRQ routines falsify the result slightly, in that their run-time is added to that of the interrupted task
(IRQ routines should be short so that the inaccuracy remains relatively small).
A high precision time-basis is needed for the measurement (µs range)
OSEK counters are not suitable (no possibility to read the counter value through the OSEK API)
ProOSEK Design-Workshop 27Copyright 3SOFT GmbH 2003
8 Measuring task runtime
Design:Measurement results are stored in a RAM table
Every entry is of type uint32_t
TMAX is a constant defined in „os.h“ (ProOSEK specific)
Accumulated Runtime in Ticks
Start Time in Ticks
runtime task 0 starttime task 0
runtime task 1 starttime task 1
... …
runtime task TMAX-1 starttime task TMAX-1
ProOSEK Design-Workshop 28Copyright 3SOFT GmbH 2003
8 Measuring task runtime
Task runtime: code exampleTask runtime: code example
/*-------------------------------------------------------*//* The PreTaskHook() is called every time before *//* a task enters the RUNNING task state. *//*-------------------------------------------------------*/void PreTaskHook(void){TaskType TaskID;GetTaskID(&TaskID);if (TaskID != INVALID_TASK){profilerData[START_TIME][TaskID] = profilerGetTicks();
}}
/*-------------------------------------------------------*//* The PreTaskHook() is called every time before *//* a task enters the RUNNING task state. *//*-------------------------------------------------------*/void PreTaskHook(void){TaskType TaskID;GetTaskID(&TaskID);if (TaskID != INVALID_TASK){profilerData[START_TIME][TaskID] = profilerGetTicks();
}}
Task executes
/*----------------------------------------------------------*//* The PostTaskHook() is called every time *//* a task leaves the RUNNING task state. *//*----------------------------------------------------------*/void PostTaskHook(void){TaskType TaskID;GetTaskID(&TaskID);if (TaskID != INVALID_TASK){profilerData[RUN_TIME][TaskID] +=profilerGetTicks() - profilerData[START_TIME][TaskID];
}}
/*----------------------------------------------------------*//* The PostTaskHook() is called every time *//* a task leaves the RUNNING task state. *//*----------------------------------------------------------*/void PostTaskHook(void){TaskType TaskID;GetTaskID(&TaskID);if (TaskID != INVALID_TASK){profilerData[RUN_TIME][TaskID] +=profilerGetTicks() - profilerData[START_TIME][TaskID];
}}
task runtime starttime
0 123 17
1 0 0
ProOSEK Design-Workshop 29Copyright 3SOFT GmbH 2003
8 Measuring task run-time
32Bit time basis on a C167 ControllerT6 base counter, T5 counts the T6 overflows
profilerGetTicks := (T5<<16 | T6)
A 20MHz CPU frequency and a limit of 32 for T6 gives a resolution of 1.6µs per timer tick. The timer overflows after ca. 115 minutes (→ longest measurable time is ca. 115 minutes)
Remark: This is just one possible implementation. If one can stand a higher interrupt load then one can, for example, use a timer and an extra 32-bit variable
T5 16Bit counter T6 16Bit counter
ProOSEK Design-Workshop 30Copyright 3SOFT GmbH 2003
8 Measuring task runtime
Measuring idle timeBy introducing an interruptable task with priority 0 (lowest priority), one can easily measure the operating idle time (corresponds to the runtime of this task!)
Possible enhancementsWarning on exceeding a CPU load threshold
Counter for task change in the Pre/Post task hook
Timer/Counter can be used to determine the system time