IBM
Contents Index Previous Next



Tight Integration


Note:

The source file and examples for Tight Integrations are not included in the standard delivery. They are available as free downloads from the IBM Rational Support web site.

Note:

This presentation is focused on the general principles and models used in a Tight Integration. When specific RTOS primitives are needed in the presentation, examples from the VxWorks implementation are used. The implementation and RTOS calls used in other integrations are covered in separate annexes to this chapter, one for each RTOS.

There are two models of Tight Integration. In the Standard Model one SDL process instance is mapped to one OS task. In the Instance Set Model an entire instance set (all instances of a process) is mapped to one OS task. Scheduling between OS tasks is managed by the RTOS scheduler; this means that preemption is normally used, though only on an instance set level in the Instance Set Model. SDL semantics are preserved in a Tight Integration, for example setting a timer implies an automatic reset first.

The start-up of a system, i.e. creation of static processes, initialization of synonyms and creation of an environment task and a timer task, is handled by a generated initialization function called yInit. Normally this function is called from another initialization function, where some additional initializations take place before the yInit function is called.

Timers in the system are handled by one central timer task. This task receives messages1, each containing a request to set a timer, and will send messages back as the timers expire.

Common Features

File Structure

The files related to the tight integration concept can be found in the following directory structure after extracting the RTOS integration: .../RTOS/TightIntegrations/<RTOS>/TightIntegration/. The same files are used for both the Standard Model and the Instance Set Model.

Each RTOS directory contains the following files:

The SDL_PId Type

The SDL_PId (SDL Process ID) type has different meanings in the Standard and the Instance Set Models. In the Standard Model it represents the message queue, while it represents the process instance in the Instance Set Model. This is because the entire instance set will have the same message queue in the last case.

#ifdef X_ONE_TASK_PER_INSTANCE_SET
typedef xEPrsNode SDL_PId;
#else
typedef MSG_Q_ID  SDL_PId;
#endif

Signals

The signal header consists of a struct with information needed to handle the signal inside an SDL system. The signal header struct is defined in the RTOS-specific file sct<RTOS>.h.

typedef struct xSignalHeaderStruct *xSignalHeader;
typedef struct xSignalHeaderStruct {
  int            SignalCode;
  xSignalHeader  Pre, Suc;
  SDL_PId        Sender;
  void          *SigP;
  #ifdef X_ONE_TASK_PER_INSTANCE_SET
    SDL_PId        Receiver;
  #endif
  #ifdef XMSC_TRACE
    int            SignalId;
    int            IsTimer;
  #endif
} xSignalHeaderRec;

The signal header stores SignalCode, in this case an integer, two pointers Pre and Suc used when saving the signal in the save queue, and Sender, holding the SDL_PId of the sending SDL process. In the Instance Set Model there is an extra parameter Receiver, necessary to make a distinction between the SDL processes in an instance set task.

If the signal contains parameters they are allocated in the same function call. Example:

OutputSignalPtr = xAlloc (sizeof (xSignalHeaderRec) 
+ sizeof(yPDef_z05_s2));

The second parameter to the xAlloc function is a struct representing the signal parameters of the signal. In this case, with one integer, it is defined in the following way:

typedef struct {
  SIGNAL_VARS
  SDL_Integer     Param1;
} yPDef_z05_s2;

The macro SIGNAL_VARS is in most RTOS empty.

There is an extra element in the SignalHeader defined as a void pointer. This pointer SigP is set to point to the parameter area.

 OutputSignalPtr->SigP = OutputSignalPtr+1; 

This pointer is used in the Signal-Free-Function to address the parameter-part of either a signal-structure as also a timer-signal-structure.

Note:

The SDL signal parameters are always named Param1, Param2, etc.

Assignment of the signal parameter is done in generated code and not in a macro. Example:

SIGNAL_ALLOC_ERROR
yAssF_SDL_Integer(((yPDef_z05_s2*)
  OUTSIGNAL_DATA_PTR)->Param1 ,yVarP->z023_param1, 
  XASS);

The macro OUTSIGNAL_DATA_PTR macro is defined:

#define OUTSIGNAL_DATA_PTR (yOutputSignalPtr->SigP)

After expansion of the whole expression the code will be:

((yPDef_z05_s2 *) ((xSignalHeader) yOutputSignalPtr 
+ 1))->Param1 = yVarP->z023_param1;
Signal reception

The support function xInputSignal is used for receiving signals in both models of Tight Integration. The implementation and the parameters are different though.

Timer Signals

A timer signal is defined similarly to an ordinary signal but will contain some additional elements representing time-out time, etc. The timer header struct looks like this:

typedef struct xTimerHeaderStruct *xTimerHeader;
typedef struct xTimerHeaderStruct {
      int            SignalCode;
      xTimerHeader   Pre, Suc;
      SDL_PId        Sender;
      void          *SigP;
#ifdef X_ONE_TASK_PER_INSTANCE_SET
      SDL_PId        Receiver;
#endif
#ifdef XMSC_TRACE
      int            SignalId;
      int            IsTimer;
#endif
      SDL_Time       TimerTime;
      int            TimerToSetOrReset;
      xbool          (* yEq)();
      xbool          TestParams;
      xTimerHeader   Param;
} xTimerHeaderRec;

Note:

An ordinary signal is identical to the first part of a timer signal. This makes it possible to type-cast between the two types as long as only elements in the common part of the headers are used.

Time

When the System time is required, for example when using NOW, the macro SDL_NOW is used. The macro is in turn mapped to the function SDL_Clock() (in sctos.c). This function is implemented differently depending on the RTOS representation of time. In VxWorks it returns the result of calling the RTOS function tickGet. SDL_Time is normally implemented as int or unsigned long int.

Mapping Between SDL Time and RTOS Time

The macro SDL_DURATION_LIT specifies the mapping between the SDL time in seconds and the local RTOS representation of time. In VxWorks the system time is given in ticks and the translation is defined as follows:

#define SDL_DURATION_LIT(R,I,D) \
  ((I)*1000 + (D)/1000000)

R is the real type representation of the time in seconds. I and D are the integer and decimal parts of an integer type representation of the time. I is in seconds and D in nanoseconds. The code generator will generate all three numbers but either R, or I and D will be used depending on the RTOS.

Timers

All timer activity in the SDL system is handled by a dedicated timer task. The timer task accepts requests in the form of messages (in VxWorks). It then keeps the requests for setting a timer sorted in a timer queue and uses some OS mechanism to wait for the first request to time out. The mechanism used can be either an OS timer, or a timeout in the waiting for new requests. When a request times out, the timer task sends a signal back to the task that first sent the request. The function calls and OS signaling involved in setting and waiting for an SDL timer can be viewed in Figure 572.

Figure 572 : Function calls and OS signaling when setting and waiting for a timer.

UML notation is being used, thus the full arrowheads represent function calls and the half arrowheads represent messages. Parameters have been left out, except for the name of the timer in the ResetTimer request. Note that a reset is performed first, as required in the SDL specification.

To be able to implement the full semantics of SDL timers a number of support functions have been implemented:

Addressing SDL Processes

There are two ways to address SDL processes from an external task. Either the xFindReceiver function can be called to find an arbitrary receiver, or the file pidlist.pr can be used to provide a list of the SDL processes and then address the receiver explicitly via the input queue ID of its OS task.

The xFindReceiver Function

When sending a signal into the SDL system where the receiver is not known a support function called xFindReceiver can be used. This function takes the following parameters:

The following files are needed to get access to SDL types, signal numbers and signal parameter types: scttypes.h, <system_name>.hs and <system_name>.ifc.

Example of how to use the xFindReceiver function:

#include "scttypes.h"
#include "<system_name>.ifc"
#include "<system_name>.hs"

void MyExtTask(void) {

   xSignalHeader yOutputSignalPtr;
   int Err;

   /*Allocate signal header and signal parameter
     buffer */
   yOutputSignalPtr =  
     (xSignalHeader)xAlloc(sizeof(xSignalHeaderRec) 
      + sizeof(yPDef_go);

   /*Setup signal header */
   yOutputSignalPtr->SignalCode = go;
   yOutputSignalPtr->Sender = xEnvPId;

   /*Give value 100 to integer parameter */
   ((yPDef_go *)(yOutputSignalPtr+1))->Param1 = 100;

   /*Send signal from environment */
   Err = msgQSend(xFindReceiver(go, xEnvPrs, 0),
           (char*) yOutputSignalPtr, 
           sizeof(xSignalHederRec)+sizeof(yPDef_go),
           0 ,0);
}

The following types, signal definitions and global variables are used in the example:

The File pidlist.pr

An alternative way to get the PId for the Receiver is to use an ADT defined in the ADT library called pidlist.pr. This file defines an ADT called PidList and an operator called PId_Lit. With this ADT it is possible to directly address any static process instance in the system, both from internal SDL processes and from external OS-tasks. You can find more information about this feature in How to Obtain PId Literals.

Note:

If you need the pidlist.pr ADT in a Tight Integration then you must use the version in the .../RTOS/TightIntegrations/SDL/ directory.

The Standard Model

In the Standard Model of the Tight Integration each SDL process is implemented as an OS task. Preemption and the use of process priorities is only limited to what the OS supports.

Processes

Process Creation

An SDL process is created in the following way (in the VxWorks integration):

  1. A start-up signal is allocated.
  2. A message queue is created. Some operating systems create the message queue automatically when the task is created. This is explained for each operating system in the annexes to this chapter.
  3. The task is created with the message queue ID as a start-up parameter. In the case of VxWorks, the task will have a name starting with VXWORKSPAD_. This is a function which will first initialize some internal variables and then call the PAD function.
  4. A function (xAllocPrs) is called to create a representation of the new instance in the global symbol tree.
  5. The start-up signal is sent. When this signal is received in the task the start transition of the process is executed.
Process Termination

The following actions are carried out when a process terminates:

  1. The save queue and the message queue are emptied.
  2. The save queue is deleted.
  3. A message is sent to the xTimerTask with a request to remove all active timers of the process.
  4. xFreePrs is called to free the PrsNode.
  5. The message queue is deleted. In some operating systems this is done automatically when the task is deleted.
  6. The task is deleted.
PAD functions

Each PAD (Process Activity Definition) function will contain an eternal loop with an OS receive statement. When a process instance is created it is the PAD function that is called in the OS Create primitive.

The start-up and execution of a PAD function works like this:

  1. The support function xInputSignal is called. This function will wait for the start-up signal, that is always received first, and then return to the PAD function.
  2. The PAD function goes to the label Label_Execute_Transition. This label is the start of a code block containing a switch statement that evaluates the process variable RestartAddress. The code under each different case then represents a transition. At the end of this block the process variable State is updated and execution continues at Label_New_Transition.
  3. In Label_New_Transition a new call is made to xInputSignal and execution then continues at Label_Execute_Transition.

The structure of a PAD function is described below (with pseudo-code shown in italics):

void yPAD_z01_pr1 (void *VarP)
{
  Variable declarations
  xInputSignal is called to receive the start-up signal
  ...........
  goto Label_Execute_Transition;
  ...........
  Label_New_Transition:
  xInputSignal is called to receive a signal

  Label_Execute_Transition:
  Local declarations
  switch (yVarP->RestartAddress) {
  case 0:
    Execute the start transition
    Update the process state variable
    goto Label_New_Transition;
  case 1:
    Execute the transition
    Update the process state variable
    goto Label_New_Transition;
  ...........
  }
  ...........
}

Scheduling

Since each SDL process is implemented as an OS task, scheduling between processes will be handled completely by the OS.

Start-up

Start-up of a Standard Model Tight Integration can be described as follows (pseudocode is shown in italics):

MyMain() {
/* initialization of semaphores etc */
 yInit();
 Give startup semaphores
 taskSuspend(Mymain);
}

yInit() {
 Create the timer task
 Create an environment task or only an environment queue
 for(i=1;i<=NoOfStaticProcessTypes;i++){
  for(j=1;j<=NoOfStaticInstancesOfEachProcesstype;
      j++){
    Allocate a startup signal
    Create a message queue
    Create a task
    Call xAllocPrs
    Send the startup signal
  }
 }
  Assign SDL synonyms
}

The semaphore is used for synchronizing start-up of static processes. No static process is allowed to execute its start transition before all static processes are created, because a start transition can have signal sending to other static instances.

The MyMain function is placed among other support functions in the file sctsdl.c.

The yInit function is generated by the code generator and placed last in the generated file for the system.

The Instance Set Model

The Instance Set Model is based on the same principles as the Standard Model with the difference that the instance set is the basic unit rather than the process instance.

Processes

Both the instances and the instance sets are represented in the symbol table. In addition to the three parts that always make up an SDL process there is also an extra struct for the instance set, defining for example the input queue which is common to all the instances. Further, there is a PAD function for each instance, but also for the instance set.

Process Representation

An SDL_PId is represented by an xEPrsNode, pointing to an xEPrsStruct. An xEPrsNode also represents a process instance in the symbol table both in the Standard Model and the Instance Set Model.

typedef struct xEPrsStruct {
  xEPrsNode       NextPrs;
  SDL_PId         Self;
  xPrsIdNode      NameNode;
  int             BlockInstNumber;
  xPrsNode        VarP;
} xEPrsRec;
Instance Set Data

The datatype xPrsInstanceSetVars is only used in the Instance Set Model. It defines common data for all instances of the set, like the save queue and the size of the instance data.

typedef struct {
  xSignalHeader   SaveQ;
  xSignalHeader   CurrentInSaveQ;
  xSignalHeader   yInSignalPtr;
  char            name[100];
  unsigned        PrsDataSize;
} xPrsInstanceSetVars.
PAD functions

In the Instance Set Model there is a PAD function for each process instance but also for each instance set. The instance set PAD functions will be called at system start-up and contain an eternal loop in the same fashion as PAD functions in the Standard Model. Instance PAD functions are only called to execute transitions.

Process Creation

All instance sets, even for dynamic processes, are created at system start-up. Since the OS task and the signal queues are created with the instance set, the creation of an instance requires less labor than in the Standard Model. For the instance set creation the macro INIT_PROCESS_TYPE is used.

Process Termination

The instance set task is never terminated. Termination of a process instance will not remove the save queue, the input queue and the task. This is done at system termination. All queues, including the active timer queue, are emptied of messages to the terminated process though.

Signal queues

The message queue id of the receiver's instance set is accessed through NameNode in the receiver's xEPrsStruct and the variable PROCID.

Example from xSDLResetInTimerProcess:

Err=msgQSend ((MSG_Q_ID) (yInSignalPointer->Sender)
  ->NameNode->PROCID, (char *) yInSignalPointer, 
  sizeof (xTimerHeaderRec), 0, 0);

In this case the receiver is the same as the original sender.

Signal sending

A support function xHandle_Sig is used when sending signals, instead of the macro RTOSSEND as in the Standard Model. This difference is shown in bold in the code below:

#ifdef X_ONE_TASK_PER_INSTANCE_SET
#define SDL_2OUTPUT(PRIO, VIA, SIG_NAME, SIG_IDNODE,\ 
   RECEIVER, SIG_PAR_SIZE, SIG_NAME_STRING)\
   XOS_TRACE_OUTPUT(SIG_NAME_STRING) \
   XMSC_TRACE_OUTPUT(RECEIVER, yOutputSignalPtr, \
     SIG_NAME_STRING) \
   xHandle_Sig(yOutputSignalPtr,SDL_SELF,SIG_PAR_SIZE,\
    RECEIVER,(RTOSTASK_TYPE)RECEIVER-> \
    NameNode->PROCID RTOSHANDLESIG_PAR);
#else
#define SDL_2OUTPUT(PRIO, VIA, SIG_NAME, SIG_IDNODE, \
   RECEIVER, SIG_PAR_SIZE, SIG_NAME_STRING)\
   XOS_TRACE_OUTPUT(SIG_NAME_STRING) \
   XMSC_TRACE_OUTPUT(RECEIVER, yOutputSignalPtr,\ 
     SIG_NAME_STRING) \
   RTOSSEND(&yOutputSignalPtr, RECEIVER,SIG_PAR_SIZE)
#endif

Scheduling

Scheduling between instance sets is handled by the operating system. Within the instance sets, however, scheduling is based on the signal queue. When the instance set PAD function is executing, it takes the first signal in the input queue and calls the PAD function of the addressed SDL process. The instance PAD function then executes one transition and returns control to the scheduling loop of the instance set PAD function.

Integrating with external code

You can easily integrate the SDL system with external code, for example written in C. Just use the hooks described below for inserting C statements in the main() function of the SDL system.

The hooks are in the form of #define macros located in a file called scthooks.h. A file called scthooks.h_template with empty macros can be found in the INCLUDE directory. Use this file as a template for your own application. You will find usage examples in the Examples directory.

HOOK_GLOBAL_DECLARATIONS

This hook lets you declare function prototypes etc. at file scope.

HOOK_MAIN_DECLARATIONS

This hook lets you declare variables for use in the main() function.

HOOK_MAIN_START_OF_CODE

Any code inserted here will execute first in the main() function.

HOOK_MAIN_AFTER_PROCESS_RELEASE

Any code inserted here will execute as soon as all static processes have been created and are allowed to run.

HOOK_MAIN_AFTER_SIGNAL_RECEPTION

The main() function of the SDL system enters an infinite loop after having created all static processes. This loop is used to receive signals sent to the environment queue.

Use the HOOK_MAIN_AFTER_SIGNAL_RECEPTION to insert code for processing these signals.

1

SDL signals will be implemented as messages in VxWorks.


http://www.ibm.com/rational
Contents Index Previous Next