Table of Contents
Previous Chapter The nodes in the symbol table are structs with components initialized in the declaration. During the initialization of the application, in the yInit function in generated code, a tree is built up from these nodes.
xSymbolTableRoot; xSystemId; xEnvId; xSrtN_SDL_Boolean; xSrtN_SDL_Character; xSrtN_SDL_Charstring; xSrtN_SDL_Duration; xSrtN_SDL_Integer; xSrtN_SDL_Natural; xSrtN_SDL_PId; xSrtN_SDL_Real; xSrtN_SDL_Time;xSymbolTableRoot is the root node in the symbol table tree. Below this node the system node is inserted (referred to by xSystemId). After the system node, there is a node representing the environment of the system (xEnvId). Then there is one node for each package referenced from the SDL system. This is true also for the package predefined containing the predefined data types. The nodes for the predefined data types, that are sons to the node for the package predefined, can be directly referenced by the names xSrtN_SDL_xxx, according to the list above.
Nodes in the symbol table are placed in the tree exactly according its place of declaration in SDL. A node that represent an item declared in a block is placed as a child to that block node, and so on. The hierarchy in the symbol table tree will directly reflect the block structure and declarations within the blocks and processes.
A small example can be found in Figure 560 on page 2194. The following node types will be present in the tree:
---------------------------------------------------------------------
Node Type Description
---------------------------------------------------------------------
xSystemEC Represent the system or the system instance.
xSystemTypeEC Represents a system type.
xPackageEC Represents a package.
xBlockEC Represent blocks and block instances.
xBlockTypeEC Represents a block type.
xBlockSubstEC Represents a block substructure and can be
found as a child of a block node.
xProcessEC Represent processes and process instances.
The environment process node is placed after
the system node and is used to represent the
environment of the system.
xProcessTypeEC Represents a process type.
xServiceEC Represents a service or service instance.
xServiceTypeEC Represents a service type.
xProcedureEC Represents a procedure.
xOperatorEC Represents an operator diagram, i.e. an ADT
operator with an implementation in SDL.
xSignalEC Represents a signal or timer type.
xTimerEC
xRPCSignalEC Represents the implicit signals (pCALL,
pREPLY) used to implement RPCs.
xSignalParEC There will be one signal parameter node, as a
child to a signal, timer, and RPC signal node,
for each signal or timer parameter.
xStartUpSignalEC Represents a start-up signal, that is, the sig
nal sent to a newly created process contain
ing the actual FPAR parameters. An
xStartUpSignalEC node is always placed
directly after the node for its process.
xSortEC
Represents a newtype or a syntype.
xSyntypeEC
Struct A sort node representing a struct has one
Component Node
struct component node as child for each
(xVariableEC) struct component in the sort definition.
xLiteralEC A sort node similar to an enum type has one
literal node as child for each literal in literal
list.
xStateEC Represents a state and can be found as a child
to process and procedure nodes.
xVariableEC
Represents a variable (DCL) or a formal
xFormalParEC parameter (FPAR) and can be found as chil
dren of process and procedure nodes
xChannelEC
Represents a channel, a signal route, or a
xSignalRouteEC gate.
xGate
xRemoteVarEC Represents a remote variable definition.
xRemotePrdEC Represents a remote procedure definition.
---------------------------------------------------------------------
The nodes (the struct variables) will in generated code be given names according to the following table:
ySysR_SystemName
(system, system type, system instance)
yPacR_PackageName
yBloR_BlockName
(block, block type, block instance)
yBSuR_SubstructureName
yPrsR_ProcessName
(process, process type, process instance)
yPrdR_ProcedureName (procedure, operator)
ySigR_SignalName
(signal, timer, startup signal, RPC signal)
yChaR_ChannelName (channel, signal route, gate)
yStaR_StateName
ySrtR_NewtypeName (newtype, syntype)
yLitR_LiteralName
yVarR_VariableName
(variable, formal parameter, signal
parameter, struct component)
yReVR_RemoteVariable
yRePR_RemoteProcedure
In most cases it is of interest to refer to a symbol table node via a pointer. By taking the address of a variable according to the table above, i.e.
& yPrsR_Process1such a reference is obtained. For backward compatibility, macros according to the following example is also generated for several of the entity classes:
#define yPrsN_ProcessName (&yPrsR_ProcessName)
typedef enum {
xExportEC,
xRemoteVarEC,
xRemotePrdEC,
xSignalrouteEC,
xStateEC,
xTimerEC,
xFormalParEC,
xLiteralEC,
xVariableEC,
xViewEC,
xBlocksubstEC,
xChannelsubstEC,
xPackageEC,
xProcedureEC,
xOperatorEC,
xProcessEC,
xProcessTypeEC,
xGateEC,
xSignalEC,
xSignalParEC,
xStartUpSignalEC,
xRPCSignalEC,
xSortEC,
xSyntypeEC,
xSystemEC,
xSystemTypeEC,
xBlockEC,
xBlockTypeEC,
xChannelEC,
xServiceEC,
xServiceTypeEC,
xMonitorCommandEC
} xEntityClassType;
typedef enum {
xPredef, xUserdef, xEnum,
xStruct, xArray, xGArray, xString,
xPowerSet, xGPowerSet, xInherits, xSyntype,
xUnion
} xTypeOfSort;
typedef char *xNameType;
typedef struct xIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xIdRec;
/*BLOCKSUBSTRUCTURE*/
typedef struct xBlockSubstIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xBlockSubstIdRec;
/*LITERAL*/
typedef struct xLiteralIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xLiteralIdRec;
/*PACKAGE*/
typedef struct xPackageIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xPackageIdRec;
/*SYSTEM*/
typedef struct xSystemIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xIdNode * Contents;
xPrdIdNode * VirtPrdList;
xSystemIdNode Super;
#ifdef XTRACE
int Trace_Default;
#endif
#ifdef XGRTRACE
int GRTrace;
#endif
#ifdef XMSCE
int MSCETrace;
#endif
} xSystemIdRec;
/*CHANNEL,SIGNALROUTE*/
#ifndef XOPTCHAN
typedef struct xChannelIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xSignalIdNode *SignalSet; /*Array*/
xIdNode *ToId; /*Array*/
xChannelIdNode Reverse;
} xChannelIdRec; /* And xSignalRouteEC.*/
#endif
/*BLOCK*/
typedef struct xBlockIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xBlockIdNode Super;
xIdNode * Contents;
xPrdIdNode * VirtPrdList;
xViewListRec * ViewList;
int NumberOfInst;
#ifdef XTRACE
int Trace_Default;
#endif
#ifdef XGRTRACE
int GRTrace;
#endif
#ifdef XMSCE
int MSCETrace;
int GlobalInstanceId;
#endif
} xBlockIdRec;
/*PROCESS*/
typedef struct xPrsIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xStateIdNode *StateList;
xSignalIdNode *SignalSet;
#ifndef XNOUSEOFSERVICE
xIdNode *Contents;
#endif
#ifndef XOPTCHAN
xIdNode *ToId; /*Array*/
#endif
int MaxNoOfInst;
#ifdef XNRINST
int NextNr;
int NoOfStaticInst;
#endif
xPrsNode *ActivePrsList;
xptrint VarSize;
#if defined(XPRSPRIO) || defined(XSIGPRSPRIO) || \
defined(XPRSSIGPRIO)
int Prio;
#endif
xPrsNode *AvailPrsList;
#ifdef XTRACE
int Trace_Default;
#endif
#ifdef XGRTRACE
int GRTrace;
#endif
#ifdef XBREAKBEFORE
#ifndef ULTRIXCC
char *(*GRrefFunc)
XPP((int, xSymbolType *));
#else
char *(*GRrefFunc) ();
#endif
int MaxSymbolNumber;
int SignalSetLength;
#endif
#ifdef XMSCE
int MSCETrace;
#endif
#ifdef XCOVERAGE
long int *CoverageArray;
long int NoOfStartTransitions;
long int MaxQueueLength;
#endif
void (*PAD_Function) XPP((xPrsNode));
xPrsIdNode Super;
xPrdIdNode * VirtPrdList;
xBlockIdNode InBlockInst;
#ifdef XBREAKBEFORE
char * RefToDefinition;
#endif
} xPrsIdRec;
#ifndef XNOUSEOFSERVICE
/*SERVICE*/
typedef struct xSrvIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xStateIdNode *StateList;
xSignalIdNode *SignalSet;
#ifndef XOPTCHAN
xIdNode *ToId;
#endif
xptrint VarSize;
#ifdef XBREAKBEFORE
#ifndef ULTRIXCC
char *(*GRrefFunc) XPP((int, xSymbolType *));
#else
char *(*GRrefFunc) ();
#endif
int MaxSymbolNumber;
int SignalSetLength;
#endif
#ifdef XCOVERAGE
long int *CoverageArray;
long int NoOfStartTransitions;
#endif
xSrvNode *AvailSrvList;
void (*PAD_Function) XPP((xPrsNode));
xSrvIdNode Super;
xPrdIdNode * VirtPrdList;
} xSrvIdRec;
#endif
/*PROCEDURE*/
typedef struct xPrdIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xStateIdNode *StateList;
xSignalIdNode *SignalSet;
xbool (*Assoc_Function)
XPP((xPrsNode));
xptrint VarSize;
xPrdNode *AvailPrdList;
#ifdef XBREAKBEFORE
#ifndef ULTRIXCC
char *(*GRrefFunc)
XPP((int, xSymbolType *));
#else
char *(*GRrefFunc) ();
#endif
int MaxSymbolNumber;
int SignalSetLength;
#endif
#ifdef XCOVERAGE
long int *CoverageArray;
#endif
xPrdIdNode Super;
xPrdIdNode * VirtPrdList;
} xPrdIdRec;
typedef struct xRemotePrdIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xRemotePrdListNode RemoteList;
} xRemotePrdIdRec;
/* SIGNAL, TIMER */
typedef struct xSignalIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xptrint VarSize;
xSignalNode *AvailSignalList;
#ifndef ULTRIXCC
xbool (*Equal_Timer)
XPP((void *, void *));
#else
xbool (*Equal_Timer) ();
#endif
#ifdef XFREESIGNALFUNCS
#ifndef ULTRIXCC
void (*Free_Signal) XPP((void *));
#else
void (*Free_Signal) ();
#endif
#endif
#ifdef XBREAKBEFORE
char *RefToDefinition;
#endif
} xSignalIdRec; /* And xTimerEC, xStartUpSignalEC,
and xRPCSignalEC.*/
/*STATE*/
typedef struct xStateIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
int StateNumber;
xInputAction *SignalHandlArray;
int *InputRef;
#ifndef ULTRIXCC
xInputAction (*EnablCond_Function)
XPP((XSIGTYPE, void *));
void (*ContSig_Function)
XPP((void *, int *, xIdNode *, int *));
#else
xInputAction (*EnablCond_Function) ();
void (*ContSig_Function) ();
#endif
int StateProperties;
#ifdef XCOVERAGE
long int *CoverageArray;
#endif
xStateIdNode Super;
#ifdef XBREAKBEFORE
char *RefToDefinition;
#endif
} xStateIdRec;
/*SORT*/
typedef struct xSortIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
#ifdef XFREEFUNCS
void (*Free_Function) XPP((void **));
#endif
#ifdef XREADANDWRITEF
int (*Read_Function) XPP((void *));
char *(*Write_Function) XPP((void *));
#endif
#ifdef XTESTF
xbool (*Test_Function) XPP((void *));
#endif
xptrint SortSize;
xTypeOfSort SortType;
xSortIdNode CompOrFatherSort;
xSortIdNode IndexSort;
long int LowestValue;
long int HighestValue;
} xSortIdRec;
/*VARIABLE,...*/
typedef struct xVarIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xSortIdNode SortNode;
xptrint Offset;
xptrint Offset2;
xbool IsAddress;
} xVarIdRec; /* And xFormalParEC and
xSignalParEC.*/
typedef struct xRemoteVarIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xptrint SortSize;
xRemoteVarListNode RemoteList;
} xRemoteVarIdRec;
There are also pointer types defined for each of the xECIdStruct according to the following example:
typedef XCONST struct xIdStruct *xIdNode;The type definitions above define the contents in the symbol table nodes. Each xECIdStruct, where EC should be replaced by an appropriate string, have the first five components in common. These components are used to build the symbol table tree. To access these components, a pointer to a symbol table node can be type cast to any of the xIdECNode types. The type xIdNode is used as such general type, for example when traversing the tree.
The five components present in all xIdNode are:
#ifdef XMSCE
#define GLOBALINSTID int GlobalInstanceId;
#else
#define GLOBALINSTID
#endif
#ifdef XENV_CONFORM_2_3
#define XSIGNAL_VARP void * VarP;
#else
#define XSIGNAL_VARP
#endif
#define SIGNAL_VARS \
xSignalNode Pre; \
xSignalNode Suc; \
int Prio; \
SDL_PId Receiver; \
SDL_PId Sender; \
xSignalIdNode NameNode; \
XSIGNAL_VARP \
GLOBALINSTID
typedef struct xSignalStruct *xSignalNode;
typedef struct xSignalStruct {
SIGNAL_VARS
} xSignalRec;
The xSignalNode type is thus a pointer type which is used to refer to allocated data areas of type xSignalRec. The components in the xSignalRec struct are used as follows:
Example 136
typedef struct {
SIGNAL_VARS
SDL_Integer Param1;
SDL_Boolean Param2;
} yPDef_sig;
typedef yPDef_sig *yPDP_sig;
These types would represent a signal sig(Integer, Boolean).
As all signals starts with the components defined in SIGNAL_VARS it is possible to type cast a pointer to a signal, to the xSignalNode type, if only the components in SIGNAL_VARS is to be accessed.
xSignalNode xGetSignal( xSignalIdNode SType, SDL_PId Receiver, SDL_PId Sender ) void xReleaseSignal( xSignalNode *S )xGetSignal takes a reference to the SignalIdNode identifying the signal type and two PId values (sending and receiving process instance) and returns a signal instance. xGetSignal first looks in the avail list for the signal type (the component AvailSignalList in the SignalIdNode for the signal type) and reuses any available signal there. Only if the avail list is empty new memory is allocated. The component VarSize in the SignalIdNode for the signal type provides the size information needed to correctly allocate the yPDef_SignalName even though the type is unknown for the xGetSignal function.
The function xReleaseSignal takes the address of an xSignalNode pointer and returns the referenced signal to the avail list for the signal type. The xSignalNode pointer is then set to 0.
The function xGetSignal is used:
Signal instances are sent using the function SDL_Output. That function takes a signal instance and inserts it into the input port of the receiving process instance.
If the receiver is not already in the ready queue (the queue containing the processes that can perform a transition, but which have not yet been scheduled to do so) and the current signal may cause an immediate transition, the process instance is inserted into the ready queue.
If the receiver is already in the ready queue or in a state where the current signal should be saved, the signal instance is just inserted into the input port.
If the signal instance can neither cause a transition nor should be saved, it is immediately discarded (the data area for the signal instance is returned to the avail list).
The input port is scanned during nextstate operations, according the rules of SDL, to find the next signal in the input port that can cause a transition. Signal instances may then be saved or discarded.
There is no specific input function, instead this behavior is distributed both in the runtime library and in the generated code. The signal instance that should cause the next transition to be executed is removed from the input port in the main loop (the scheduler), immediately before the PAD function for the current process is called. The PAD function is the function where the behavior of the process is implemented and is part of the generated code. The assignment of the signal parameters to local SDL variables is one of the first actions performed by the PAD function.
The signal instance that caused a transition is released and returned to the avail list in the nextstate or stop action that ends the current transition.
#define TIMER_VARS \
xSignalNode Pre; \
xSignalNode Suc; \
int Prio; \
SDL_PId Receiver; \
SDL_PId Sender; \
xSignalIdNode NameNode; \
GLOBALINSTID \
SDL_Time TimerTime;
typedef xTimerRec *xTimerNode;
typedef struct xTimerStruct {
TIMER_VARS
} xTimerRec;
The TIMER_VARS is and must be identical to the SIGNAL_VARS macro, except for the TimerTime component last in the macro. A timer with parameters have yPDef_timername and yPDP_timername types in generated code exactly as a signal (see previous section), except that SIGNAL_VARS is replaced by TIMER_VARS.During its life-time a timer have two different appearances. First it is a timer waiting for the timer time to expire. In that phase the timer is inserted in the xTimerQueue. When the timer time expires the timer becomes a signal and is inserted in the input port of the receiver just like any other signal. Due to the identical typedefs for xSignalRec and xTimerRec, there are no problems with type casting between xTimerNode and xSignalNode types.
When a timer is treated as a signal the components in the xTimerRec are used in the same ways as for a xSignalRec. While the timer is in the timer queue, the components are used as follows:
xTimerNode xTimerQueue;The variable is initialized in the function xInitKernel in sctsdl.c. xTimerQueue is initialized it refers to the queue head of the timer queue.
The queue head is an extra element in the timer queue that does not represent a timer, but is introduced as it simplifies the algorithms for queue handling. The TimerTime component in the queue head is set to a very large time value (xMaxTime).
The timer queue is thus a doubly linked list with a list head and it is sorted according to the timer times, so that the timer with lowest time is at the first position.
The xTimerRec structs are allocated and reused in the same way as signal.
From the SDL point of view, timers are handled in:
void SDL_Set( SDL_Time T, xSignalNode S )This function, which represents the Set operation, takes the timer time and a signal instance as parameters. It first uses the signal instance to make an implicit reset (see reset operation below) It then updates the TimerTime component in S and inserts S into the timer queue at the correct position.
The SDL_Set operation is used in generated code, together with xGetSignal, in much the same way as SDL_Output. First a signal instance is created (by xGetSignal), then timer parameters are assigned their values, and finally the Set operation is performed (by SDL_Set).
void SDL_Reset( xSignalNode *TimerS ) void SDL_SimpleReset( xPrsNode P, xSignalIdNode TimerId )Two functions are used to represent the SDL action reset. SDL_SimpleReset is used for timers without parameters and SDL_Reset for timers with parameters.
SDL_Reset uses the two functions xRemoveTimer and xRemoveTimerSignal to remove a timer in the timer queue and to remove a signal instance in the input port of the process. It then releases the signal instance given as parameter. This signal is only used to carry the parameter values given in the reset action.
The function SDL_SimpleReset is implemented in the same way as SDL_Reset, except that it creates its own signal instance (without parameters).
At a reset action the possibly found timer is removed from the timer queue and returned to the avail list. A found signal instance (in the input port) is removed from the input port and returned to the avail list for the current signal type.
static void SDL_OutputTimerSignal( xTimerNode T )The SDL_OutputTimerSignal is called from the main loop (the scheduler) when the timer time has expired for the timer first in the timer queue. The corresponding signal instance is then sent.
SDL_OutputTimerSignal takes a pointer to an xTimerRec as parameter, removes it from the timer queue and sends as an ordinary output using the function SDL_Output.
It can be checked if timer is active by using a call to the function SDL_Active. This function is used in generated code to represent the SDL operator active.
SDL_Boolean SDL_Active ( xSignalIdNode TimerId, xPrsNode P )There is one more place where timers are handled. When a process instance performs a stop action all timers in the timer queue connected to this process instance are removed. This is performed by calling the function xRemoveTimer with the first parameter equal to 0.----------------------------------------------------------------------- Note: Only timers without parameters can be tested. This is a restriction in the C Code Generator. -----------------------------------------------------------------------
Figure 554 : Representation of a Process Instance.
-----
(fig)
-----
The corresponding type definitions, which can be found in scttypes.h, are:
#ifdef XPRSSENDER
#define XPRSSENDERCOMP SDL_PId Sender;
#else
#define XPRSSENDERCOMP
#endif
#ifdef XTRACE
#define XTRACEDEFAULTCOMP int Trace_Default;
#else
#define XTRACEDEFAULTCOMP
#endif
#ifdef XGRTRACE
#define XGRTRACECOMP int GRTrace;
#else
#define XGRTRACECOMP
#endif
#ifdef XMSCE
#define XMSCETRACECOMP int MSCETrace;
#else
#define XMSCETRACECOMP
#endif
#ifdef XMONITOR
#define XINTRANSCOMP xbool InTransition;
#else
#define XINTRANSCOMP
#endif
#ifdef XMONITOR
#define XCALL_ADDR int CallAddress;
#else
#define XCALL_ADDR
#endif
#ifndef ULTRIXCC
#define XPAD_STRUCT_COMP \
void (*RestartPAD) XPP((xPrsNode VarP));
#else
#define XPAD_STRUCT_COMP void (*RestartPAD) ();
#endif
#ifndef XNOUSEOFSERVICE
#define XSERVICE_COMP xSrvNode ActiveSrv; xSrvNode
SrvList;
#else
#define XSERVICE_COMP
#endif
#define PROCESS_VARS \
xPrsNode Pre; \
xPrsNode Suc; \
int RestartAddress; \
xPrdNode ActivePrd; \
XPAD_STRUCT_COMP \
XCALL_ADDR \
XSERVICE_COMP \
xPrsNode NextPrs; \
SDL_PId Self; \
xPrsIdNode NameNode; \
int State; \
xSignalNode Signal; \
xInputPortRec InputPort; \
SDL_PId Parent; \
SDL_PId Offspring; \
int BlockInstNumber; \
XSIGTYPE pREPLY_Waited_For; \
xSignalNode pREPLY_Signal; \
XPRSSENDERCOMP \
XTRACEDEFAULTCOMP \
XGRTRACECOMP \
XMSCETRACECOMP \
XINTRANSCOMP
typedef struct {
xPrsNode PrsP;
int InstNr;
int GlobalInstanceId;
} xLocalPIdRec;
typedef xLocalPIdRec *xLocalPIdNode;
typedef struct {
int GlobalNodeNr;
xLocalPIdNode LocalPId;
} SDL_PId;
typedef struct xPrsStruct *xPrsNode;
typedef struct xPrsStruct {
PROCESS_VARS
} xPrsRec;
A PId value is thus a struct containing two components:
A xLocalPIdRec contains the following two components:
xPrsNode xReadyQueue;This component is initiated in the function xInitKernel and used throughout the runtime library to reference the ready queue.
Scheduling of events is performed by the function xMainLoop, which is called from the main function in generated code after the initialization is performed.
void xMainLoop()The strategy to have all interesting queues (the ready queue, the timer queue, and the input ports) sorted in the correct order is used in the library. Sorting is thus performed when an object is inserted into a queue, which means that scheduling is a simple task: select the first object in the timer queue or in the ready queue and submit it for execution.
There are several versions of the body of the endless loop in the function xMainLoop, which are used for different combinations of compilation switches. When it comes to scheduling of transitions and timer outputs they all have the following outline:
while (1) {
if ( xTimerQueue->Suc->TimerTime <= SDL_Now() )
SDL_OutputTimerSignal( xTimerQueue->Suc );
else if ( xReadyQueue->Suc != xReadyQueue) {
xRemoveFromInputPort(xReadyQueue->Suc->Signal);
xReadyQueue->Suc->Sender =
xReadyQueue->Suc->Signal->Sender;
(*xReadyQueue->Suc->RestartPAD)(xReadyQueue->Suc);
}
}
or, in descriptive terms:
while (1) {
if ( there is a timer that has expired )
send the corresponding timer signal;
else if ( there is a process that can execute
a transition ) {
remove the signal causing the transition
from input port;
set up Sender in the process to Sender of
the signal;
execute the PAD function for the process;
}
}
The different versions of the main loop handle different combinations of compilation switches. Other actions necessary in the main loop are dependent of the compilation switches. Example of such actions are:
Figure 555 : A xLocalPIdRec and a xVDef_ProcessNameWhen a process instance performs a stop action, the memory used for the process instance should be reclaimed and it should be possible to reuse in subsequent create actions. After the stop action, old (invalid) PId values might however be stored in variables in other process instances.
representing a Process instance. ----- (fig) -----
If a signal is sent to such an old PId value; that is, to a stopped process instance, it should be possible to find and perform appropriate actions. If the complete representation of a process instance is reused then this will not be possible. There must therefore remain some little piece of information and thus some memory for each process instance that has ever existed. This is the purpose of the xLocalPIdRec. These structs will never be reused. Instead the following (see Figure 556) will happen when the process instance in Figure 555 on page 2172 performs a stop action.
Figure 556 : The memory structure after the process in Figure 555 has
performeda stop action.
-----
(fig)
-----
A new xLocalPIdRec is allocated and its PrsP references the yVDef_ProcessName (InstNr is 0). The Self component in the yVDef_ProcessName is changed to reference this new xLocalPIdRec. The old xLocalPIdRec still references the yVDef_ProcessName. The yVDef_ProcessName is entered into the avail list for this process type. To reuse the data area for a process instance at a create operation it is only necessary to remove the yVDef_ProcessName from the avail list and update the InstNr component in the xLocalPIdRec referenced by Self.
Using this somewhat complicated structure to represent process instances allows a simple test to see if a PId value refers to an active or a stopped instance:
If P is a PId variable then the following expression:
P.LocalPId == P.LocalPId->PrsP->Self.LocalPIdis true if the process instance is active and false if it is stopped.
The basic behavior of the create and stop operations is performed by the functions SDL_Create and SDL_Stop.
void SDL_Create( xSignalNode StartUpSig, xPrsIdNode PrsId ) void SDL_Stop( xPrsNode PrsP )To create a process instance takes three steps performed in generated code:
The process instance is linked into the list of active process instances (the component ActivePrsList in the PrsIdNode representing the process instance set). Both the avail list and the active list are single linked lists (without a head) using the component NextPrs in the yVDef_ProcessName struct as link.
To have an equal treatment of the initial transition and other transitions, the start state is implemented as an ordinary state with the name "start state" It is represented by 0. To execute the initial transition a "startup" signal is sent to the process. The start state can thus be seen as a state with one input of the startup signal and with save for all other signals. This implementation is completely transparent in the monitor, where startup signals are never shown in any way.
------------------------------------------------------------- Note: The actual values for FPARs are passed in the startup signal. -------------------------------------------------------------Two IdNodes that are not part of the symbol table tree are created to represent a start state and a startup signal.
xStateIdNode xStartStateId; xSignalIdNode xStartUpSignalId;These xSysD components are initialized in the function xInitSymbolTable, which is part of sctsdl.c.
At a stop operation the function SDL_Stop is called. This function will release the signal that caused the current transition and all other signals in the input port. It will also remove all timers in the timer queue that are connected to this process instance by calling xRemoveTimer with the first parameter equal to 0. It then removes the process executing the stop operation from the ready queue and from the active list of the process type and returns the memory to the avail list of the current process instance set.
Next, in SDL_Output signals to the environment are handled. Three cases can be identified here:
If the signal should be saved, the signal is just inserted into the input port of the receiver.
If the signal should be discarded, the function xReleaseSignal is called to reused the data area for the signal.
When a signal is identified to be the signal that should cause the next transition by the current process instance (at an Output or Nextstate operation), the component Signal in the yVDef_ProcessName for the process is set to refer to the signal. The signal is still part of the input port list.
When the transition is to be executed, the signal is removed from the input port in the main loop (see "The Ready Queue, Scheduling" on page 2170) immediately before the PAD function for the process is called.
First in the PAD function, the parameters of the signal are copied to the local variables according to the input statement. In the ending Nextstate or Stop operation of the transition the signal instance is returned to the avail list.
typedef unsigned char xInputAction; #define xNotInSignalSet (xInputAction)0 #define xInput (xInputAction)1 #define xSave (xInputAction)2 #define xDiscard (xInputAction)3 #define xEnablCond (xInputAction)4 #define xPrioInput (xInputAction)5
xSignalNode SignalId, xPrsNode VarP, xbool CheckPrioInput )The parameters of this function is:
Figure 557 : Data structure used to evaluate the xFindInputAction.
-----
(fig)
-----
The algorithm to find the InputAction, the RestartAddr, and the RestartPAD is as follows:
The EnablCond_Functions are called from the function xFindInputAction, which is called from SDL_Output and SDL_Nextstate. If the enabling condition expression for the current signal is true then xInput is returned else xSave is returned. This information is then used to determine how to handle the signal in this state.
The ContSig_Functions are called from SDL_Nextstate, if the component ContSig_Function is not 0 and no signal that can cause an immediate transition is found during the input port scan. A ContSig_Function has the following prototype:
void ContSig_Function_Name ( void *, int *, xIdNode *, int *);where the first parameter is the pointer to the yVDef_ProcessName. The remaining parameters are all out parameters; the second contains the priority of the continuous signal with highest priority (=lowest value) that has an expression with the value true. Otherwise <0 is returned here. The third and fourth is only defined the second parameter >=0; the third is the IdNode for the process/procedure where the actual continuous signal can be found and the fourth is the RestartAddress connected to this continuous signal.
If a continuous signal expression with value true is found, a signal instance representing the continuous signal is created and inserted in the input port, and is thereafter treated as an ordinary signal. The signal type is continuous signal and is represented by an SignalIdNode (referenced by the variable xContSigId).
The check list is a list that contains the processes that wait in a state where enabling conditions or continuous signals need to be repeatedly recalculated.
A process is inserted into the check list if:
The check list is represented by the xSysD component:
xPrsNode xCheckList;The behavior of enabling conditions and continuous signals is in SDL modeled by letting the process repeatedly send signals to itself, thereby to repeatedly entering the current state. In the implementation chosen here, nextstate operations are performed "behind the scene" for all processes in the check list directly after a call to a PAD function is completed, that is directly after a transition is ended and directly after a timer output. This is performed by calling the function xCheckCheckList in the main loop of the program.
void * SDL_View ( xViewListRec *VList, SDL_PId P, xbool IsDefP, xPrsNode ViewingPrs, char * Reveal_Var int SortSize);
An export action is a simple operation. The current value of the variable is copied to the component representing the exported value. This is performed in generated code.
An import action is more complicated. It involves mainly a call of the function xGetExportAddr:
void * xGetExportAddr ( xRemoteVarIdNode RemoteVarNode, SDL_PId P, xbool IsDefP, xPrsNode Importer )RemoteVarNode is a reference to the RemoteVarIdNode representing the remote variable (implicit or explicit), P is the PId expression given in the import action and IsDef is 0 or 1 depending on if any PId expression was given in the import action or not, Importer is the importing process instance. The xGetExportAddr will check the legality of the import action and will, if no PId expression is given, calculate which process it should be imported from.
If no errors are found the function will return the address where the exported value can be found. This address is then casted to the correct type (in generated code) and the value is obtained. If no process possible to import from is found, the address of a variable containing only zeros is returned by the xGetExportAddr function.
------------------------------------------------------------------------ Note: The strategy for import actions is in one sense not equal to the model for import given in the SDL recommendation. An import action is in the recommendation modelled as a signal sent from the importing process to the exporting process asking for the exported value, and a signal with this value sent back again. The synchronization effects by this signal communication is lost in the implementation model we have chosen. Instead our model is much easier and faster and the primary part of the import action, to obtain the exported value, is the same. ------------------------------------------------------------------------
In scttypes.h the following types concerning procedures can be found:
#ifdef XMONITOR
#define XCALL_ADDR int CallAddress;
#else
#define XCALL_ADDR
#endif
#ifndef ULTRIXCC
#define XPAD_STRUCT_COMP \
void (*RestartPAD) XPP((xPrsNode VarP));
#else
#define XPAD_STRUCT_COMP void (*RestartPAD) ();
#endif
#define SERVICE_VARS \
xSrvNode NextSrv; \
xPrsNode ContainerPrs; \
int RestartAddress; \
xPrdNode ActivePrd; \
XPAD_STRUCT_COMP \
XCALL_ADDR \
xSrvIdNode NameNode; \
int State; \
XSIGTYPE pREPLY_Waited_For; \
xSignalNode pREPLY_Signal; \
XINTRANSCOMP
#ifndef XNOUSEOFSERVICE
typedef struct xSrvStruct *xSrvNode;
#endif
#ifndef XNOUSEOFSERVICE
typedef struct xSrvStruct {
SERVICE_VARS
} xSrvRec;
#endif
In generated code yVDef_ProcedureName structs are defined accord
typedef struct {
SERVICE_VARS
components for FPAR and DCL
} yVDef_ServiceName;
The components in the xSrvRec are used as follows:
YPAD_FUNCTION(yPAD_z00_P1)
{
YPAD_YSVARP
YPAD_YVARP(yVDef_z00_P1)
YPRSNAME_VAR("P1")
LOOP_LABEL_SERVICEDECOMP
CALL_SERVICE
/*-----
* Initialization (no START symbol)
------*/
BEGIN_START_TRANSITION(yPDef_z00_P1)
yAssF_SDL_Integer(yVarP->z002_Global,
SDL_INTEGER_LIT(10), XASS);
START_SERVICES
}
where LOOP_LABEL_SERVICEDECOMP and BEGIN_START_TARNSITION are empty macros, i.e. expanded to no code. The yAss_SDL_Integer statement in an assignment of a default value to a process variable. The macro CALL_SERVICE is expanded to:
if (yVarP->ActiveSrv != (xSrvNode)0) {
(*yVarP->ActiveSrv->RestartPAD)(VarP);
return; \
}
that is to a call of the PAD function of service reference by ActiveSrv.The macro START_SERVICE is expanded to a call to the function xStart_Services, which can be found in sctsdl.c.The function creates the service instances, sets up the ActiveSrv pointer for the process to the first service, and then schedules the process for a new transition. This means that the next action performed by the system will be the start transition by the first service instance. When the first service executes a nextstate or stop action in the end of its start transition, the process will be scheduled again to execute the start transition of the second service, and so on until all services in the process has executed its start transitions.
For ordinary transitions, i.e. reception of a signal, it is obvious from the code above that the ActiveSrv pointer is essential. It should refer to the service instance that is to be executed. When a signal is to be received by a process, it is the function xFindInputAction (in sctsdl.c) that determines how to handle the signal and if it is to be received, where is the code for that transition. This function now also determines and sets up the ActiveSrv pointer.
In scttypes.h the following types concerning procedures can be found:
#ifndef ULTRIXCC
#define XPRD_STRUCT_COMP \
xbool (*RestartPRD) XPP((xPrsNode VarP));
#else
#define XPRD_STRUCT_COMP xbool (*RestartPRD) ();
#endif
#define PROCEDURE_VARS \
xPrdIdNode NameNode; \
xPrdNode StaticFather; \
xPrdNode DynamicFather; \
int RestartAddress; \
XCALL_ADDR \
XPRD_STRUCT_COMP \
xSignalNode pREPLY_Signal; \
int State;
typedef struct xPrdStruct *xPrdNode;
typedef struct xPrdStruct {
PROCEDURE_VARS
} xPrdRec;
In generated code yVDef_ProcedureName structs are defined according to the following:
typedef struct {
PROCEDURE_VARS
components for FPAR and DCL
} yVDef_ProcedureName;
The components in the xPrdRec are used as follows:
Figure 558 : Structure of yVDef_ProcedureName After Four Nested Procedure
Calls.
-----
(fig)
-----
The SDL procedures are partly implemented using C functions and partly using the structure shown above. Each SDL procedure is represented by a C function, which is called to execute actions defined in the procedure. This function corresponds to the PAD function for processes. The formal parameters and the variables are however implemented using a struct defined in generated code. The procedure stack for nested procedure calls is implemented using the components StaticFather and DynamicFather, and does not use the C function stack.
xPrdNode xGetPrd( xPrdIdNode PrdId )and two functions called from generated code at a procedure call and a procedure return:
void xAddPrdCall( xPrdNode R, xPrsNode VarP, int StaticFatherLevel, int RestartAddress ) void xReleasePrd (xPrsNode VarP)A procedure call in SDL is in C represented by the following steps:
A procedure return is in generated code represented by calling the xReleasePrd followed by return 0, whereby the function representing the behavior of the SDL procedure is left.
The function representing the behavior of the SDL procedure is returned in two main situations:
To continue to execute at the correct symbol when a procedure should be resumed after a nextstate operation, the following code is introduced in the PAD function for processes containing procedure calls:
while ( yVarP->ActivePrd != (xPrdNode)0 )
if ((*yVarP->ActivePrd->RestartPRD)(VarP))
return;
This means that uncompleted procedures are resumed one after one from the bottom of the procedure stack, until all procedures are completed or until one of them returns 1, i.e. executes a nextstate operation, at which the process is left for the scheduler again.
Figure 559 : A Small SDL System
-----
(fig)
-----
During the initialization of the system, the symbol table is built up. The part of the symbol table starting with the system will then have the structure outlined in Figure 560 As we can see in this example the declarations in the SDL system are directly reflected by IdNodes.
----------------------------------------------------------------------- Note: Each channel and signal route is represented by two IdNodes, one for each direction. This is also true for an unidirectional channel or signal route. In this case the signal set will be empty for the unused direction. -----------------------------------------------------------------------
Figure 560 : The symbol table tree for the system in Figure 559.
------
.(fig)
------
Each IdNode representing a process, a signal route, or a channel will have a component ToId. A ToId component is an address to an array of references to IdNodes. The size of this array is dependent on the number of items this object is connected to. A process that has three outgoing signal routes will have a ToId array which can represent three pointers plus an ending 0 pointer.In the example in Figure 559 on page 2193 and Figure 560 on page 2194 there is no branching, so all ToId arrays will be of the size necessary for two pointers. Figure 561 shows how the IdNodes for the processes, signal routes and channels are connected to form paths, using the components ToId. In this case only simple paths are found (one from P1, via SR1, C, SR2, to P2, and one in the reverse direction). The generalization of this structure to handle branches is straightforward and discussed in the previous paragraph.
Figure 561 : The Connection of ToId for the System in
Figure 559 and Figure 560. ----- (fig) -----
For each process type the C Code Generator will generate:
The PAD function will be independent of the PAD function for a inherited type, each PAD function just implementing the action described in its process type.
A yVDef_ProcessName struct will on the other hand include all variables and formal parameters from the top of the inheritance chain and downwards. Example:
process type P1; fpar f1 integer; dcl d1 integer; ... endprocess; process type P2 inherits P1; fpar f2 integer; dcl d2 integer; ... endprocess;This will generate the following principle yVDef_... structs:
typedef struct {
PROCESS_VARS
SDL_Integer f1;
SDL_Integer d1;
} yVDef_P1;
typedef struct {
PROCESS_VARS
SDL_Integer f1;
SDL_Integer d1;
SDL_Integer f2;
SDL_Integer d2;
} yVDef_P2;
A pointer to yVDef_P2 can thus be casted to a pointer to yVDef_P1, if only the common component (in PROCESS_VARS) or the variables in P1 is to be accessed. This possibility is used every time the PAD function for an inherited process type is called.Each process instantiation will all be implemented as a xPrsIdNode. The Super component in such an object refers to the process type that is instantiated. No PAD function or yVDef_... struct will be generated. As sons to the PrsIdNode for a process instantiation, only such object are inserted that are different in different instantiations. For a process instantiation this is the gates. For other types of information the process instantiation uses the information given for its process type.
A very similar structure when it comes to IdNodes generated for block types and block instantiations are used by the code generator. There will be a BlockIdNode for both a block type and for a block instantiation. As sons to a block type, nodes that are the same in each block instantiation can be found (example: signal, newtype, syntype, block type, process type, procedure). As sons to a block instantiation, nodes that are needs to be represented in each block instantiation can be found (example: block instantiation, process instantiation, channel, signal route, gate, remote definitions).
--------------------------------------------------------------------- Note: A block or process (according to SDL-88), that is contained in a block type or a system type, is translated as if it was a type and in stantiation at the same place. ---------------------------------------------------------------------A way to look at the structure of IdNodes in a particular system is to use the command Symboltable in the monitor system. This command prints the IdNode structure as an indented list of objects.
This page intentionally left blank