This chapter covers the following topics:
Note that the Master Library is only used together with the Cadvanced and Cbasic Code Generators. It cannot be used together with the Cmicro Code Generator.
This chapter describes the source code of the runtime library for applications generated by the SDT Cadvanced/Cbasic Code Generator. Applications generated by the Cmicro Code Generator are not covered.
The chapter covers basically three topics:
The runtime library is structured into a number of files. These are:
scttypes.h
sctlocal.h
sctpred.h
sctsdl.c
sctpred.c
sctutil.c
sctmon.c
sctpost.c
sctos.c
post.h, sdt.h, itex.h, dll.h
sctworld.o
post.o
sctworld.lib
libpost.lib
(the statically linked library)post.lib
(for dynamically linking)post.dll
(the dynamically linked library)On UNIX, all files except sctworld.o
can be found in the directory $telelogic/sdt/sdtdir/
<machine dependent dir>
/INCLUDE
where <machine dependent dir> is for example sunos5sdtdir
on SunOS 5, and hppasdtdir
on HP. The sctworld.o
file can be found in the corresponding kernel directory:
$telelogic/sdt/sdtdir/<machine dependent dir>
/
<kernel>
In Windows, all files except sctworld.lib
can be found in the directory<Telelogic Tau installation directory>
\sdt\sdtdir\wini386\include
. The sctworld.lib
file can be found in the corresponding kernel directory: <Telelogic Tau installation directory>
\sdt\sdtdir\wini386\
<kernel>
. There are different kernel directories for the Borland and the Microsoft compiler. The Borland directories have the prefix scta
while the Microsoft compiler directories have the prefix sctam
.
This file contains type definitions and extern declarations of variables and functions. The file is included by sctsdl.c, sctpred.c, sctutil.c, sctmon.c, sctpost.c, sctos.c, and by each generated C file.
This file contains type definitions and extern declarations of variables and functions that are used only in the kernel. This file is not included in generated code.
This file contains type definitions and extern declarations handling the predefined data types in SDL (except PId, which is in scttypes.h
). Macros used to implement the predefined generators in SDL can also be found here. This file is included in generated code via scttypes.h
.
In this file the implementation of the SDL operations can be found, together with the functions used for scheduling. In more detail, this file contains groups of functions for:
The functions implementing the operations defined in the SDL predefined data types can be fund in this file. Operators for PId is implemented in sctsdl.c
.
This file contains basic read and write functions together with functions to handle reading and writing of values of abstract data types, including the predefined data types. It also contains the functions for MSC trace.
The sctmon.c
file contains the functions that implement the monitor interface, that is, interpreting and executing monitor commands.
This file contains all the basic functions that are used to connect a simulator with the other parts of SDT.
In this file, some functions that represent the dependencies of hardware, operating system and compiler are placed.
The basic functions necessary for an application are a function to read the clock and a function to allocate memory.
To move a generated C program plus the runtime library to a new platform (including a new compiler), the major changes are to be made in this file, together with writing a new section in scttypes.h
to describe the properties of the new compiler.
This is a pre-linked object file containing
sctsdl.o (sctsdl.obj
in Windows)
sctutil.o (sctutil.obj
in Windows)sctmon.o (sctmon.obj
in Windows)
sctpost.o (sctpost.obj
in Windows)
sctos.o (sctos.obj
in Windows)
post.o (post.lib
in Windows)
A pre-linked file gives a faster link operation when generating an application. This file is of course only present if the hardware platform supports pre-linking.
These files are included in sctpost.c
if the communication mechanism with other SDT applications should be part of the actual object code version of the library. The file post.h
contains the function interface, while sdt.h
contains message definitions.
This file contains the implementation of functions needed to send messages, via the Postmaster, to other tools in SDT.
The symbol table is used for storing information mainly about the static properties of the SDL system, such as the block structure, connections of channels and the valid input signal set for processes. Some dynamic properties are also placed in the symbol table; for example the list of all active process instances of a process instance set. This is part of the node representing the process instance set.
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.
The symbol table is created in two steps:
yInit
function (in generated code) updates some components in the nodes and builds a tree from the nodes. This operation is not needed in an application!The following names can be used to refer to some of the nodes that are always present. These names are defined in scttypes.h
.
xSymbolTableRoot xEnvId xSrtN_SDL_Bit xSrtN_SDL_Bit_String xSrtN_SDL_Boolean xSrtN_SDL_Character xSrtN_SDL_Charstring xSrtN_SDL_Duration xSrtN_SDL_IA5String xSrtN_SDL_Integer xSrtN_SDL_Natural xSrtN_SDL_Null xSrtN_SDL_NumericString xSrtN_SDL_Object_Identifier xSrtN_SDL_Octet xSrtN_SDL_Octet_String xSrtN_SDL_PId xSrtN_SDL_PrintableString xSrtN_SDL_Real xSrtN_SDL_Time xSrtN_SDL_VisibleString
xSymbolTableRoot
is the root node in the symbol table tree. Below this node the system node is inserted. 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 496. The following node types will be present in the tree:
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, synt.variable) 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_Process1
such 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)
The following type definitions, from the file scttypes.h
, are used in connection with the symbol table:
typedef enum { xRemoteVarEC, xRemotePrdEC, xSignalrouteEC, xStateEC, xTimerEC, xFormalParEC, xLiteralEC, xVariableEC, xBlocksubstEC, xPackageEC, xProcedureEC, xOperatorEC, xProcessEC, xProcessTypeEC, xGateEC, xSignalEC, xSignalParEC, xStartUpSignalEC, xRPCSignalEC, xSortEC, xSyntypeEC, xSystemEC, xSystemTypeEC, xBlockEC, xBlockTypeEC, xChannelEC, xServiceEC, xServiceTypeEC, xCompoundStmtEC, xSyntVariableEC xMonitorCommandEC } xEntityClassType; typedef enum { xPredef, xUserdef, xEnum, xStruct, xArray, xGArray, xCArray, xOwn, xORef, xRef, xString, xPowerSet, xGPowerSet, xBag, xInherits, xSyntype, xUnion, xUnionC, xChoice } 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 #ifdef XIDNAMES xNameType ModuleName; #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,GATE*/ #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 x
EC
IdStruct
according to the following example:
typedef XCONST struct xIdStruct *xIdNode;
The type definitions above define the contents in the symbol table nodes. Each x
EC
IdStruct
, 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 xId
EC
Node
types. The type xIdNode
is used as such general type, for example when traversing the tree.
The five components present in all xIdNode
are:
EC
of type xEntityClassType
. This component is used to determine what sort of SDL object the node represents. xEntityClassType
is an enum type containing elements for all entity classes in SDL.First
, Suc
, and Parent
of type xIdNode
. These components are used to build the symbol table tree. First
refers to the first child of the current node. Suc
refers to the next brother, while Parent
refers to the father node. Only Parent
is needed in an application.Name
of type xNameType
, which is defined as char *
. This component is used to represent the name of the current SDL object as a character string. Not needed in an application.Next there are components depending on what entity class that is to be represented. Below we discuss the non-common elements in the other x
EC
IdStruct
.
ModuleName
of type xNameType
. If the package is generated from ASN.1, this component holds the name of the ASN.1 module as a char *
.Content
of type xIdNode *
. This component contains a list of all channels at the system level.VirtPrdList
of type xPrdIdNode *
. This is a list of all virtual procedures in this system instance.Super
of type xSystemIdNode
. This is a reference to inherited system type. In a system this component in null. In a system instance it is a reference to the instantiated system type.Trace_Default
of type int
. This component contains the current trace value defined for the system.GRTrace
of type int
. This component contains the current GR (graphical) trace value defined for the system.MSCETrace
of type int
. This component contains the current MSCE (Message Sequence Chart Editor) trace value defined for the system.For channels, signal routes, and gates there are always two consecutive xChannelIdNodes
in the symbol table, representing the two possible directions for a channel, signal route, or gate. The components are:
SignalSet
of type xIdNode *
. This component represents the signal set of the channel in the current direction (a unidirectional channel has an empty signal set in the opposite direction). SignalSet
is an array with components referring to the xSignalIdNodes
that represent the signals which are members of the signal set. The last component in the array is always a NULL
pointer (the value (xSignalIdNode)0
).ToId
of type xIdNode *.
This is an array of xIdNode
s, where each array component is a pointer to a symbol table node representing an SDL object, which this Channel/Signal route/Gate is connected to (connected to in the sense: to the SDL objects that signals are sent forward to).ToId
are channels, signal routes, gates, processes, and services. The last component in the array is always a NULL
pointer (the value (xIdNode)0)
. See also Channels and Signal Routes.Reverse
of type xChannelIdNode
. This is a reference to the symbol table node that represents the other direction of the same channel, signal route, or gate.Super
of type xBlockIdNode
. In a block, this component is NULL
. In a block type this component is a reference to the block that this block inherits from (NULL
if no inheritance). In a block instance, this is a reference to the block type that is instantiated.Contents
of type xIdNode *
. In a block instance, these components contains list of:VirtPrdList
of type xPrdIdNode *
. This is a list of all virtual procedures in this block instance.ViewList
of type xViewListRec *
. This is a list of all revealed variables in the block or block instance.NumberOfInst
of type int
. This is the number of block instances in a block instance set. The component is thus only relevant for a block instance.Trace_Default
of type int
. This component contains the current value of the trace defined for the block.GRTrace
of type int
. This component contains the current value of the GR trace defined for the block.MSCETrace
of type int
. This component contains the current MSCE (Message Sequence Chart Editor) trace value defined for the block.GlobalInstanceId
of type int
. This component is used to store a unique id needed when performing MSCE trace.StateList
of type xStateIdNode *
. This is a list of references to the xStateIdNodes
for this process or process type. Using the state value of an executing process, this list can be used to find the corresponding xStateIdNode
.SignalSet
of type xIdNode *
. This represents the valid input signal set of the process or process type. SignalSet
is an array with components that refer to xSignalIdNodes
that represent the signals and timers which are part of the signal set. The last component in the array is always a NULL
pointer (the value (xSignalIdNode)0)
.Contents
of type xIdNode *
. This is an array containing references to the xSrvIdNodes
of the services and service instances in this process.ToId
of type xIdNode *
. This is an array of xIdNode
, where each array component is a pointer to an IdNode representing an SDL object that this process or process instance is connected to (connected to in the sense: to the SDL objects that signals are sent forward to). ToId
are channels, signal routes, gates, processes, and services. The last component in the array is always a NULL
pointer (the value (xIdNode)0)
. See also section Channels and Signal Routes.MaxNoOfInst
of type int
. This represents the maximum number of concurrent processes that may exist according to the specification for the current process or process instance. An infinite number of concurrent processes is represented by -1.NextNo
of type int
. This is the instance number that will be assigned to the next instance that is created of this process instance set.NoOfStaticInst
of type int
.This component contains the number of static instance of this process instance set that should be present at start up. Used for process and process instance.ActivePrsList
of type xPrsNode *
. This is the address of a pointer to the "first" in the (single linked) list of active process instances of the current process or process instantiation. NextPrs
component in the xPrsRec
struct that is used to represent a process instance. The order in the list is such that the first created of the active process instances is last, and the latest created is first.VarSize
of type xptrint
. The size, in bytes, of the data area used to represent the process (the struct: yVDef_
ProcessName
).Prio
of type int
. This represents the process priority.AvailPrsList
of type xPrsNode
. This is the address to the avail list pointer for process instances that have stopped. The data area can later be reused in subsequent Create actions on this process or process instantiation.Trace_Default
of type int
. This component contains the current value of the trace defined for the process.GRTrace
of type int
. This component contains the current value of the GR trace defined for the process.GRrefFunc
, which is a pointer to a function that, given a symbol number (number assigned to a process symbol), will return a string containing the SDT reference to that symbol.MaxSymbolNumber
of type int
. This component is the number of symbols contained in the current process or process type.SignalSetLength
of type int
. This component is the number of signals contained in the signal set of the current process or process type.MSCETrace
of type int
. This component contains the current MSCE (Message Sequence Chart Editor) trace value defined for the process.CoverageArray
of type long int
. This component is used as an array over all symbols in the process. Each time a symbol is executed the corresponding array component is increased by 1.NoOfStartTransitions
of type long int
. This component is used to count the number of times the start transition of the current process is executed. This information is presented in the coverage tables.MaxQueueLength
of type long int
. This component is used to register the maximum input port length for any instance of the current process. The information is presented in the coverage tables.PAD_Function
, which is a pointer to a function. This pointer refers to the yPAD_
ProcessName
function for the current process. This function is called when a process instance of this type is to execute a transition. The PAD_Functions
will of course be part of generated code, as they contain the action defined in the process graphs.Super
of type xPrsIdNode
. In a process this component is NULL
. In a process type this component is a reference to the process type that this process type inherits from (NULL
if no inheritance). In a process instance set, this is a reference to the process type that is instantiated.VirtPrdList
of type xPrdIdNode *
. This is a list of all virtual procedures in this process instantiation.InBlockInst
of type xBlockIdNode
. This component is a reference to the block instance set (if any) that this process or process instantiation is part of.RefToDefinition
of type char *
. This is the SDT reference to this process.StateList
of type xStateIdNode *
. This is a list of the references to the xStateIdNodes
for this service or service type. Using the state value of an executing service, this list can be used to find the corresponding xStateIdNode
.SignalSet
of type xIdNode *
. This represents the valid input signal set of the service or service type. SignalSet
is an array with components that refer to xSignalIdNodes
that represent the signals and timers which are part of the signal set. The last component in the array is always a NULL
pointer (the value (xSignalIdNode)0)
.ToId
of type xIdNode *
. This is an array of xIdNode
, where each array component is a pointer to an IdNode representing an SDL object that this service or service instance is connected to (connected to in the sense: to the SDL objects that signals are sent forward to). ToId
are channels, signal routes, gates, processes, and service. The last component in the array is always a NULL
pointer (the value (xIdNode)0)
. See also section Channels and Signal Routes.VarSize
of type xptrint
. The size, in bytes, of the data area used to represent the service (the struct: yVDef_
ServiceName
).GRrefFunc
, which is a pointer to a function that, given a symbol number (number assigned to a service symbol), will return a string containing the SDT reference to that symbol.MaxSymbolNumber
of type int
. This component is the number of symbols contained in the current service or service type.SignalSetLength
of type int
. This component is the number of signals contained in the signal set of the current service or service type.CoverageArray
of type long int
. This component is used as an array over all symbols in the service. Each time a symbol is executed the corresponding array component is increased by 1.NoOfStartTransitions
of type long int
. This component is used to count the number of times the start transition of the current service is executed. This information is presented in the coverage tables.AvailSrvList
of type xSrvNode
. This is the address to the avail list pointer for service instances that have stopped. The data area can later be reused.PAD_Function
, which is a pointer to a function. This pointer refers to the yPAD_
ServiceName
function for the current service. This function is called when a service instance of this type is to execute a transition. The PAD_Functions
will of course be part of generated code, as they contain the action defined in the service graphs.Super
of type xSrvIdNode
. In a service this component is NULL
. In a service type this component is a reference to the service type that this service type inherits from (NULL
if no inheritance). In a service instantiation this is a reference to the service type that is instantiated.VirtPrdList
of type xPrdIdNode *
. This is a list of all virtual procedures in this service instantiation.Note that operator diagrams and compound statements containing variable declarations are treated as procedures. However, such objects can, for example, not contain states.
StateList
of type xStateIdNode *
. This is a list of references to the xStateIdNodes
for this process or process type. Using the state value of an executing process, this list can be used to find the corresponding xStateIdNode
.SignalSet
of type xIdNode *
. This represents the valid input signal set of the process or process type. SignalSet
is an array with components that refer to xSignalIdNodes
that represent the signals and timers which are part of the signal set. The last component in the array is always a NULL
pointer (the value (xSignalIdNode)0)
.Assoc_Function
, which is a pointer to a function. This pointer refers to the y
ProcedureName
function for the current procedure. This function is called when the SDL procedure is called and will execute the appropriate actions. The y
ProcedureName
functions will, of course, be part of generated code as they contain the action defined in the procedure graphs. VarSize
of type xptrint
. The size, in bytes, of the data area used to represent the procedure (struct yVDef_
ProcedureName
).AvailPrdList
of type xPrdNode *
. This is the address of the avail list pointer for the data areas used to represent procedure instances. At a return action the data area is placed in the avail list and can later be reused in subsequent Calls of this procedure type.GRrefFunc
, which is a pointer to a function that given a symbol number (number assigned to a procedure symbol) will return a string containing the SDT reference to that symbol.MaxSymbolNumber
of type int
. This component is the number of symbols contained in the current procedure.SignalSetLength
of type int
. This component is the number of signals contained in the signal set of the current procedure.CoverageArray
of type long int
. This component is used as an array over all symbols in the procedure. Each time a symbol is executed the corresponding array component is increased by 1.Super
of type xPrdIdNode
. This component is a reference to the procedure that this procedure inherits from (NULL
if no inheritance).VirtPrdList
of type xPrdIdNode *
. This is a list of all virtual procedures in this procedure.RemoteList
of type xRemotePrdListNode
. This component is the start of a list of all processes that exports this procedure. This list is a linked list of xRemotePrdListStructs
, where each node contains a reference to the exporting process.VarSize
of type xptrint
. The size, in bytes, of the data area used to represent the signal (the struct: yPDef_
SignalName
).AvailSignalList
of type xSignalNode *
. This is the address to the avail list pointer for signal instances of this signal type.Equal_Timer
, which is a pointer to a function. This pointer only refers to a function when this node is used to represent a timer with parameters. Equal_Timer
functions will be part of generated code. These functions are called from the functions xRemoveTimer
and xRemoveTimerSignal
, both defined in sctsdl.c
Free_Signal
, which is a function. This function takes a signal reference and returns any dynamic data referenced from the signal parameters to the pool of available memory.RefToDefinition
of type char *
. The SDT reference to the definition of the signal or timer.StateNumber
of type int
. The int value used to represent this state.SignalHandlArray
of type xInputAction
*. This component refers to an array of xInputAction
, where xInputAction
is an enum type with the possible values xDiscard
, xInput
, xSave
, xEnablCond
, xPrioInput
.SignalSet
array in the node representing the process in which this state is contained. Each position in the SignalHandlArray
represents the way the signal in the corresponding position in the SignalSet
array in the process should be treated in this state. SignalHandlArray
is equal to xDiscard
, which corresponds to the 0
value last in the SignalSet
. SignalHandlArray
contains the value xInput
, xSave
, or xDiscard
at a given index, the way to handle the signal is obvious. If the SignalHandlArray
contains the value xEnablCond
, it is, however, necessary to calculate the enabling condition expression to know if the signal should cause an input or should be saved. This calculation is exactly the purpose of the EnablCond_Function
described below.InputRef
of type int *
. This component is an array. If the SignalHandlArray
contains xInput
, xPrioInput
, or xEnablCond
at a certain index, this InputRef
contains the symbol number for the corresponding input symbol in the graph.EnablCond_Function
, which is a function that returns xInputAction
. If the state contains any enabling conditions, this pointer will refer to a function. Otherwise it refers to 0
. An EnablCond_Function
takes a reference to an xSignalIdNode
(referring to a signal) and a reference to a process instance and calculates the enabling condition for the input of the current signal in the current state of the given process instance. xInput
or xSave
. The EnablCond_Functions
will of course be part of generated code, as they contain enabling condition expressions. These functions are called from the function xFindInputAction
in the file sctsdl.c
. xFindInputAction
is used by the SDL_Output
and SDL_Nextstate
functions.ContSig_Function
, which is a function returning int
. If the state contains any continuous signals, this pointer will refer to a function. Otherwise it refers to 0
.StateProperties
of type int
. In this component the three least significant bits are used to indicate:Now
, Active
(timer is active), Import
, View
, and Sender
. StateProperties
is used in the function SDL_Nextstate
to take appropriate actions when a process enters a state.CoverageArray
of type long int
. This component is used as an array over the signalset (+1) of the process. Each time an input operation is performed, the corresponding array component is increased by 1. The last component, at index equal to the length of the signalset, is used to record the number of continuous signals "received" in the state. The information stored in this component is presented in the coverage table.Super
of type xPrdIdNode
. This component is a reference to the procedure that this procedure inherits from (NULL
if no inheritance).RefToDefinition
of type char *
. The SDT reference to the definition of the state (one of the symbols where this state is defined).Free_Function
, which is a function. This function pointer is non-0
only for Charstring and for types containing Charstrings, as well as for types where the user has provided a free function. The Free_Functions
are used to return dynamic memory to the pool of dynamic memory.Read_Function
, which is a function. These functions are used to read values of the current sort (mainly in the monitor). The pointer is non-0 for the predefined SDL data types and for the types where the user has provided a Read_Function
.Write_Function
, which is a function. These functions are used to write values of the current sort (mainly in the monitor). The pointer is non-0 for the predefined SDL data types and for the types where the user has provided a Write_Function
.Test_Function
, which is a function returning xbool
. This function is non-0 for all types containing range conditions. The function pointers are used by the monitor system to check the validity of a value when assigning it to a variable.SortSize
of type xptrint
. This component represents the size, in bytes, of a variable of the current sort.SortType
of type xTypeOfSort
. This component indicates the type of sort. Possible values are: xPredef
, xUserdef
, xEnum
, xStruct
, xArray
, xGArray
, xCArray
, xRef
, xString
, xPowerSet
, xBag
, xGPowerSet
, xInherits
, xSyntype
, xUnion
, xUnionC
, and xChoice
.xArray, xGArray, xCArray
CompOrFatherSort
of type xSortIdNode
. This is a pointer to the SortIdNode
that represents the component sort.IndexSort
of type xSortIdNode
. This is a pointer to the SortIdNode
that represents the index sort. In a xCArray the index sort in always Integer.LowestValue
is used as the offset of Data
in the xxx_ystruct
, otherwise it is 0.HighestValue
is used as the size of the xxx_ystruct
. In xArray it is 0, while in xCArray it is the highest index, i.e. the Length - 1.xString
CompOrFatherSort
of type xSortIdNode
. This is a pointer to the SortIdNode
that represents the component sort.xPowerSet, xGPowerSet, xBag, xRef
CompOrFatherSort
of type xSortIdNode
. This is a pointer to the SortIdNode
that represents the component sort.xInherits
CompOrFatherSort
, of type xSortIdNode
. This is a pointer to the SortIdNode
that represents the inherited sort.xSyntype
CompOrFatherSort
, of type xSortIdNode
. This is a pointer to the SortIdNode
that represents the father sort (the newtype from which the syntype originates, even if it is a syntype of a syntype).IndexSort
, of type xSortIdNode
. This is a pointer to the SortIdNode that represents the represents the father sort (the newtype or syntype from which the syntype originates).LowestValue
, of type int
. If the syntype can be used as an index in an array (translated to a C array) then this value is the lowest value in the syntype range, otherwise it is 0.HighestValue
, of type int
. If the syntype can be used as an index in an array (translated to a C array) then this value is the highest value in the syntype range, otherwise it is 0. The LowestValue
and HighestValue
are used by the monitor when it handles arrays with this type as index type.SortNode
of type xSortIdNode
. This component is a pointer to the SortIdNode that represents the sort of this variable or parameter.Offset
of type xptrint
. This component represents the offset, in bytes, within the struct that represents the process or procedure variables, the signal parameter, or the SDL struct. In other words, this is the relative place of this component within the struct.Offset2
of type xptrint
. For a formal parameter in a process this component represents the offset, in bytes, of a formal parameter in the StartUpSignal
. For an exported variable in a process this component represents the offset, in bytes, of the exported value for this variable.IsAddress
of type xbool
. This component is only used for procedure formal parameters and is then used to indicate if the parameter in IN (IsAddress
is (xbool)0
) or IN/OUT (IsAddress
is (xbool)1
).SortSize
of type xptrint
. This component is the size of the type of the exported variables.RemoteList
of type xRemoteVarListNode
. This component is the start of a list of all processes that exports this variable. This list is a linked list of xRemoteVarListStruct
s, where each node contains a reference to the exporting process and the Offset
where to find the exported value.A signal is represented by a struct type. The xSignalRec
struct, defined in scttypes.h
, is a struct containing general information about a signal except from the signal parameters. In scttypes.h
the following information about signals can be found:
#ifdef XMSCE #define GLOBALINSTID int GlobalInstanceId; #else #define GLOBALINSTID #endif #if defined(XSIGPATH) && defined(XMSCE) #define ENVCHANNEL xChannelIdNode EnvChannel; /* Used if env split into channels in MSC trace */ #else #define ENVCHANNEL #endif #ifdef XENV_CONFORM_2_3 #define XSIGNAL_VARP void * VarP; #else #define XSIGNAL_VARP #endif ##ifdef XCORBA #define THREAD_VARS THREAD_T ThreadId; #else #define THREAD_VARS #endif define SIGNAL_VARS \ xSignalNode Pre; \ xSignalNode Suc; \ int Prio; \ SDL_PId Receiver; \ SDL_PId Sender; \ xSignalIdNode NameNode; \ GLOBALINSTID \ ENVCHANNEL \ XSIGNAL_VARP \ THREAD_VARS 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:
Pre
and Suc
. These pointers are used to link a signal into the input port of the receiving process instance. Suc
is also used to link a signal into the avail lists for the current signal type. This list can be found in the SignalIdNode
that represents this signal type. If the signal is in the avail list Pre
is 0
. Prio
is used to represent the priority of the signal instance. Signal priorities are used by continuous signals and by ordinary signals if signal priorities are defined (signal priority is a possible extension provided in the product).Receiver
is used to reference the receiver of the signal. It is either set in the output statement (OUTPUT TO), or calculated (OUTPUT without TO).Sender
is the PId
value of the sending process instance. This value is necessary to provide the SDL function SENDER.NameNode
is a reference to the xSignalIdNode
representing the signal type and thus defines the signal type of this signal instance.VarP
is a pointer introduced via the macro XSIGNAL_VARP to make signal compatible with SDT 2.3. Normally this components is not present.EnvChannel
is used to identify the outgoing channel in MSCE trace.GlobalInstanceId
is used in the MSCE trace as a unique identification of the signal instance.ThreadId
is used by CORBA applications.A signal without parameters are represented by a xSignalStruct
, while for signals with parameters a struct type named yPDef_
SignalName
and a pointer type referencing this struct type (yPDP_
SignalName
) are defined in generated code. The struct type will start with the SIGNAL_VARS
macro and then have one component for each signal parameter, in the same order as the signal parameters are defined. The components will be named Param1, Param2, and so on.
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.
In sctos.c there are two functions, xGetSignal
and xReleaseSignal
, where data areas for signal are handled:
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:
The function xReleaseSignal
is used by:
In this subsection the signal handling operation is only outlined. More details will be given in the section treating processes. See Output and Input of Signals.
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.
A timer with parameters is represented by a type definition, where the timer parameters are defined, in exactly the same way as for a signal definition, see Data Structure Representing Signals and Timers. At runtime, all timers that are set and where the timer time has not expired, are represented by a xTimerRec
struct and a signal instance:
#define TIMER_VARS \ xSignalNode Pre; \ xSignalNode Suc; \ int Prio; \ SDL_PId Receiver; \ SDL_PId Sender; \ xSignalIdNode NameNode; \ GLOBALINSTID \ ENVCHANNEL \ 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:
Pre
and Suc
are pointers used to link the xTimerRec
into the timer queue (the queue of active timers, see below).TimerTime
is the time given in the Set
operation.The queue mentioned above, the timer queue for active timers is represented by the component xTimerQueue in the variable xSysD
:
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 (xSysD.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:
The timer output is the event when the timer time has expired and the timer signal is sent. After that, a timer signal is treated as an ordinary signal. These operations are implemented as follows:
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 )
Note: Only timers without parameters can be tested. This is a restriction in the Cadvanced/Cbasic Code Generator. |
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
.
A process instance is represented by two structs, an xLocalPIdRec
and a struct containing both the general process data and the local variables and formal parameters of the process (yVDef_
ProcessName
), see also Figure 490. The reason for having both the xLocalPIdRec
and the yVDef_
ProcessName
will be discussed under Create and Stop Operations.
Figure 490 : Representation of a process instance
|
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 #if defined(XMONITOR) || defined(XTRACE) #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:
xLocalPIdRec
struct.The use of the global node number is discussed in the Building an Application.
A xLocalPIdRec
contains the following three components:
PrsP
of type xPrsNode
. This component is a pointer to the xPrsRec
struct that is part of the representation of the process instance.InstNr
of type int
. This is the instance number of the current process instance, which is used in the communication with the user in the monitor and in dynamic error messages.GlobalInstanceId
is used in MSCE traces to have a unique identification of the process instance.A xPrsRec
struct contains the following components described below. As each yVDef_ProcessName
struct contains the PROCESS_VARS
macro as first item, it is possible to cast pointer values between a pointer to xPrsRec
and a pointer to a yVDef_ProcessName
struct.
Pre
and Suc
of type xPrsNode
. These components are used to link the process instance in the ready queue (see below).RestartAddress
of type int
. This component is used to find the appropriate SDL symbol to continue execute from.ActivePrd
of type xPrdNode
. This is a pointer to the xPrdRec
that represents the currently executing procedure called from this process instance. The pointer is 0 if no procedure is currently called.RestartPAD
, which is a pointer to a PAD
function. This component refers to the PAD
function where to execute the sequence of SDL symbols. RestartPAD
is used to handle inheritance between process types.CallAddress
of type int
. This component contains the symbol number of the procedure call currently executed by this process.ActiveSrv
of type xSrvNode
. This component contains a reference to the currently active service (or latest active service) in this process.SrvList
of type xSrvNode
. This component contains a reference to the first service contained in this process. The component NextSrv
in the struct representing a service can be used to find next active service in the process.NextPrs
of type xPrsNode
. This component is used to link the process instance either in the active list or in the avail list for this process type. The start of these two lists are the components ActivePrsList
and AvailPrsList
in the IdNode
representing the current process type.Self
of type SDL_PId
. This is the PId
value of the current process instance.NameNode
of type xPrsIdNode
. This is a pointer to the PrsIdNode
representing the current process or process instantiation.State
of type int
. This component contains the int value used to representing the current state of the process instance.Signal
of type xSignalNode
. This is a pointer to a signal instance. The referenced signal is the signal that will cause the next transition by the current process instance, or that caused the transition that is currently executed by the process instance.InputPort
of type xInputPortRec
. This is the queue head in the doubly linked list that represents the input port of the process instance. The signals are linked in this list using the Pre
and Suc
components in the xSignalRec
struct.Parent
of type SDL_PId
. This is the PId
value of the parent process (according to the rules of SDL). A static process instance has parent equal to NULL
.Offspring
of type SDL_PId
. This is the PId
value of the latest created process instance (according to the rules of SDL). A process instance that has not created any processes has offspring equal to NULL
.BlockInstNumber
of type int
. If the process is part of a block instance set, this component indicates which of the blocks that the process belongs to.pREPLY_Waited_For
of type xSignalIdNode
. When a process is waiting in the implicit state for the pREPLY
signal in a RPC call, this components is used to store the IdNode
for the expected pREPLY
signal.pREPLY_Signal
of type xSignalNode
. When a process receives a pCALL
signal, i.e. accepts a RPC, it immediately creates the return signal, the pREPLY
signal. This component is used to refer to this pREPLY
signal until it is sent.Sender
of type SDL_PId
. This component represents the SDL concept Sender.Trace_Default
of type int
. This component contains the current value of the trace defined for the process instance.GRTrace
of type int
. This component contains the current value of the GR trace defined for the process instance.MSCETrace
of type int
. This component contains the current MSCE trace value for the process instance.InTransition
of type xbool
. This component is true while the process is executing a transition and it is false while the process is waiting in a state. The monitor system needs this information to be able to print out relevant information.The ready queue is a doubly linked list with a head. It contains the process instances that can execute an immediate transition, but which has not been allowed to complete that transition. Process instances are inserted into the ready queue during output operations and nextstate operations and are removed from the ready queue when they execute the nextstate or stop operation that ends the current transition. The head in the ready queue, which is an object in the queue that does not represent any process but is inserted only to simplify the queue operations, is referenced by the xSysD
component:
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 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:
xInEnv
functionA process instance is, while it is active, represented by the two structs:
xLocalPIdRec
yVDef_ProcessName
struct. These two structs are dynamically allocated. A PId
value is also a struct (not allocated) containing two components, GlobalNodeNr
and LocalPId
, where LocalPId
is a pointer to the xLocalPIdRec
. Figure 491 shows how the xLocalPIdRec
and the yVDef_ProcessName
structs representing a process instance are connected.
Figure 491 :
A xLocalPIdRec and a yVDef_ProcessName
representing a Process instance
|
When 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.
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 492) will happen when the process instance in Figure 491 performs a stop action.
Figure 492 :
The memory structure after the process in
Figure 491 has performed a stop action
|
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.LocalPId
is 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:
xGetSignal
to obtain the start-up signal. SDL_Create
with the start-up signal as parameter, together with the PrsIdNode
representing the process to be created.In xGetProcess
the process instance is removed from the avail list of the process instance set (the component AvailPrsList
in the PrsIdNode
representing the process instance set), or if the avail list is empty new memory is allocated.
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.
There are three actions performed in generated code to send a signal. First xGetSignal
is called to obtain a data area that represents the signal instance, then the signal parameters are assigned their values and finally the function SDL_Output
is called to actually send the signal. First in the SDL_Output
function there are a number of dynamic tests (check if receiver in TO-clause is not NULL
and not stopped, check if there is a path to the receiver). If the output does not contain any TO-clause and the Cadvanced/Cbasic Code Generator has not been able to calculate the receiver, the xFindReceiver
function is called to calculate the receiver according to the rules of SDL.
Next, in SDL_Output
signals to the environment are handled. Three cases can be identified here:
xOutEnv
is called.xOutPM
) is called.xEnv
).Finally, internal signals in the SDL system are treated. Here also three cases can be identified (how this is evaluated is described last in this subsection):
If the signal can cause an immediate transition, the signal is inserted into the input port of the receiver, and the receiving process instance is inserted into the ready queue.
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) 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.
There are two places in the run-time kernel where it is necessary to evaluate how to handle signals (input, save, discard,...):
This calculation is implemented in the run-time kernel function xFindInputAction
.
typedef unsigned char xInputAction; #define xDiscard (xInputAction)0 #define xInput (xInputAction)1 #define xSave (xInputAction)2 #define xEnablCond (xInputAction)3 #define xPrioInput (xInputAction)4 static xInputAction xFindInputAction( xSignalNode SignalId, xPrsNode VarP, xbool CheckPrioInput )
The parameters of this function is:
SignalId
, which is a pointer to a signal.VarP
, which is a pointer to a process instance.CheckPrioInput
, which is a boolean value indicating is the function should check only for priority inputs or for ordinary inputs.As a result the function should return:
xInput
or xPrioInput
, then the RestartPAD
and RestartAddr
components in the VarP
struct should be updated with information about where this input can be found.After this last update the correct transition can be started by the scheduler by just calling the function referenced by RestartPAD
, which the as first action performs switch RestartAddr
and starts execute the input symbol.
Figure 493 : Data structure used to evaluate the xFindInputAction
|
The algorithm to find the InputAction
, the RestartAddr
, and the RestartPAD
is as follows:
Index
) where SignalId->NameNode is found. If the signal is not found, this signal is not in the signal set of the process, and the algorithm terminates returning the result xDiscard.yVarP->RestartAddr
is also updated to yVarP->RestartPAD
is updated to ProcessId->PAD_Function.StateId
assigned the value StateId->Super.The nextstate operation is implemented by the SDL_Nextstate
function, where the following actions are performed:
Signal
in the yVDef_ProcessName
) is released and the state variable (component State
in the yVDef_ProcessName
) is updated to the new state.Decision and Task operations are implemented in generated code, except for the Trace-functions implemented in the sctutil.c and sctmon.c
file and for informal and any decisions that uses some support functions in sctmon.c
. A Decision is implemented as a C if-statement, while the assignments in a Task are implemented as assignments or function calls in C.
A compound statement without variable declarations is translated just to the sequence of action it contains, while a compound statement with variable declarations is translated to a procedure (without parameters). Statements within a compound statement are translated according to the normal rules. The new statement types in compound statements are translated as:
The expressions involved in continuous signals and enabling conditions are implemented in generated code in functions called yCont_
StateName
and yEnab_
StateName
. These functions are generated for each state containing continuous signals respectively enabling conditions. The functions are referenced through the components ContSig_Function
and EnablCond_Function
in the StateIdNode
for the state. These components are 0
if no corresponding functions are generated.
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 component StateProperties
in the StateIdNode
reflects if any such expression is present in the state.
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.
A view expression is part of an expression in generated code and implemented by calling the function SDL_View
.
void * SDL_View ( xViewListRec *VList, SDL_PId P, xbool IsDefP, xPrsNode ViewingPrs, char * Reveal_Var int SortSize);
VList
is a list of all revealed variables in this block.P
is the PId
expression given in the view statement.IsDefP
is 1 is the view expression contained a PId value, 0 otherwise.ViewingPrs
is the process instance performing the view operation.Reveal_Var
is the name of the revealed variable as a string. The Reveal_Var
parameter is only used in error messages and is remove under certain conditions.SortSize
is the size of the data type of the viewed variable.The SDL_View
function performs a test that the view expression is not NULL
, refers to a process in the environment, or to a stopped process instance. If no errors are found the address of the revealed variable is returned as result from the SDL_View
function. Otherwise the address of a variable containing only zeros is returned.
For an exported variable there are two components in the yVDef_ProcessName
struct. One for the current value of the variable and one for the currently exported value of the variable. For each exported variable there will also be a struct that can be linked into a list in the corresponding RemoteVarIdNode
. This list is then used to find a suitable exporter of a variable in an import action.
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.
A service is represented by a struct type. The xSrvRec
struct defined in scttypes.h
, is, just like xPrsRec
for processes, a struct containing general information about a service, while the parameters and variables of the service are defined in generated code in the same way as for processes.
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 according to the following:
typedef struct { SERVICE_VARS components for FPAR and DCL } yVDef_ServiceName;
The components in the xSrvRec
are used as follows:
NextSrv
of type xSrvNode
. Reference to next service contained in this process.ContainerPrs
of type xPrsNode
. Reference to the process instance containing this service.RestartAddress
of type int
. This component is used to find the appropriate SDL symbol to continue execution from.ActivePrd
of type xPrdNode
. This is a pointer to the xPrdRec that represents the currently executing procedure called from this service instance. The pointer is 0 if no procedure is currently called.RestartPAD
, which is a pointer to a PAD function. This component refers to the PAD function where to execute the sequence of SDL symbols. RestartPAD is used to handle inheritance between service types.CallAddress
of type int
. This component contains the symbol number of the procedure call performed from this procedure (if any).NameNode
of type xSrvIdNode
. This is a pointer to the IdNode representing the service or service instantiation.State
of type int
. This component contains the int value used to represent the current state of the service instance.pREPLY_Waited_For
of type xSignalIdNode
. When a service is waiting in the implicit state for the pREPLY signal in a RPC call, this components is used to store the IdNode for the expected pREPLY signal.pREPLY_Signal
of type xSignalNode
. When a service receives a pCALL signal, i.e. accepts a RPC, it immediately creates the return signal, the pREPLY signal. This component is used to refer to this pREPLY signal until it is sent.InTransition
of type xbool
. This component is true while the service is executing a transition and it is false while the service is waiting in a state. The monitor system needs this information to be able to print out relevant information.From the scheduler's point view, it is not of interest if a process contains services or not. It is still the process instance that is scheduled in the ready queue and the PAD function of the process that is to be called to execute a transition. The PAD function for a process containing services performs three different actions:
The structure for a PAD function for a process with services are 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.
A procedure is represented by a struct type. The xPrdRec
struct defined in scttypes.h
, is, just like xPrsRec
for processes, a struct containing general information about a procedure, while the parameters and variables of the procedure are defined in generated code in the same way as for processes.
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:
NameNode
of type xPrdIdNode
. This is a pointer to the IdNode
representing the procedure type.StaticFather
of type xPrdNode
. This is a pointer that represents the scope hierarchy of procedures (and the process at the top), which is used when a procedure instance refers to non-local variables. An example is shown in Figure 494. StaticFather == 0
means that the static father is the process.DynamicFather
of type xPrdNode
. This is a pointer that represents that this procedure is called by the referenced procedure. DynamicFather == 0
means that this procedure was called from the process. This component is also used to link the xPrdRec
in the avail list for the procedure type.RestartAddress
of type int
. This component is used to find the appropriate SDL symbol to continue execution from.CallAddress
of type int
. This component contains the symbol number of the procedure call performed from this procedure (if any).RestartPRD
is a pointer to a procedure function. This component refers to the PRD
function where to execute the next sequence of SDL symbols. RestartPRD
is used to handle inheritance between procedures.pREPLY_Signal
of type xSignalNode
. When a process receives a pCALL signal, i.e. accepts a RPC, it immediately creates the return signal, the pREPLY
signal. This component is used to refer to this pREPLY
signal until it is sent.State
of type int
. This is the value representing the current state of the procedure instance.In Figure 494 an example of the structure of yVDef_ProcedureName
after four nested procedure calls are presented. Note that procedure Q is declared in the process, procedure R and S in Q and T in S.
Figure 494 :
Structure of yVDef_ProcedureName
after four nested procedure calls
|
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.
Procedure calls and procedure returns are handled by three functions, one handling allocation of the data areas for procedures:
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:
xGetPrd
to obtain a data area for the procedure.xAddPrdCall
to link the procedure into the static and dynamic chains.yProcedureName
function.The parameters to xAddPrdCall
are as follows:
R
. A reference to the xPrdNode
obtained from the call of xGetPrd
.VarP
. A reference to the yVDef_ProcessName, i.e. the data area for variables and parameters of the process (even if it is a procedure that performed the procedure call).StaticFatherLevel
. This is the difference in declaration levels between the caller and the called procedure. This information is used to set up the StaticFather
component correctly. RestartAddress
. This is the symbol number of the SDL symbol directly after the procedure call. The symbol number is the switch case label generated for all symbols.The xGetPrd
returns a pointer to an xPrdRec
, which can then be used to assign the parameter values directly to the components in the data area representing the formal parameters and variables of the procedure. Note that IN/OUT parameters are represented as addresses in this struct.
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:
Return
is reached (the function returns 0) Nextstate
is reached (the function returns 1). If 0 is returned then the execution should continue with the next SDL symbol after the procedure call, while if 1 is returned the execution of the process instance should be terminated and the scheduler (main loop) should take control. This could mean that a number of nested SDL procedure calls should be terminated.
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.
The ChannelIdNodes
for channels, signal routes, and gates are used in the functions xFindReceiver
and xIsPath
, which are both called from SDL_Output
, to find the receiving process when there is no TO clause in the Output statement, respectively to check that there is a path to the receiver in the case of a TO clause in the Output statement. In both cases the paths built up using the ToId
components in the IdNodes
for processes, channels and signal routes are followed. To show the structure of these paths we use the small SDL system given in Figure 495.
Figure 495 : A small SDL system
|
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 496. As we can see in this example the declarations in the SDL system are directly reflected by IdNodes
.
Figure 496 : The symbol table tree for the system in Figure 495
. |
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 495 and Figure 496 there is no branching, so all ToId
arrays will be of the size necessary for two pointers. Figure 497 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 497 :
The connection of ToId
for the system in
Figure 495 and Figure 496
|
The probably most important new feature in SDL-92 is the introduction of the object oriented features, such as TYPE, INHERITS, VIRTUAL, and REDEFINED. Here we start by discussing process types.
For each process type the Cadvanced/Cbasic Code Generator will generate:
PrsIdNode
PAD
functionyVDef_ProcessName
struct.In the PrsIdNode
there is one component (Super) that will refer to the PrsIdNode
for the process type inherited by this process type. As sons to a PrsIdNode
, IdNodes
for declaration that are common for all instantiation of the process type can be found. Examples of such IdNodes
are: nodes for variables, formal parameters, signals, timers, procedures, states, newtypes, and syntypes. Any typedefs or help functions for such units are also treated in the process type.
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 instantiation 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 section gives an overview of the C code that is generated by the SDT Cadvanced/Cbasic Code Generator. The purpose is to help to see the correspondence between the SDL diagrams and the generated code. This knowledge will in turn make it easier for the user to trace a C compilation or runtime error back to the originating SDL definition or action.
The generated code is described in detail (although not in all details). There are explanations and examples showing what the generated code looks like, but not why it looks like this. There are also some comments indicating in what circumstances different generated objects (functions, types, variables, and so on) are used.
In many places in the generated C code, a C name will consist of a prefix plus the SDL name of the current SDL object. In this section prefixes will not be indicated in the examples.
The code that is described here is the complete code. Parts of the code are not used in applications (but together with the monitor), and are thus removed by the use of compilation switches. This is not shown in here.
In the generated code, comments containing SDT references can be found. These comments are not shown in the examples, but are of course an important aid to finding the originating SDL statement or declaration. For more information about SDT references, see SDT References. Each generated compilation unit consists of a subtree in the static structure containing the system, the blocks, the processes, the services and the procedures. For an example see Figure 498. A compilation unit is generated either on one file, a .c
file, or on two files, a .c
and a .h
file.
In this section the following terminology is used:
Figure 498 : Block structure and compilation unit structure (example)
|
A generated file will consist of a number of sections, which will be discussed below:
If this unit has any subunits or it is a unit for a package, a system type, block type, or process type, then point 2 and 3 are generated on a .h
file and the rest on a .c
file, otherwise all code is generated on a .c
file.
Code generation on the .c
file for the current unit is started by generating the include statement of the scttypes.h
file:
/* Program generated by SDT C Advanced, v. 3.5 */ #define XSCT_CADVANCED #define SCT_VERSION_3_5 #include "scttypes.h"
If packages are used, the .h
file for the packages and for the system types, block types, and process types defined in the package are included. If the unit has father units, their .h
files are included starting from the top:
#include "packagename.h" #include "systemname.h" #include "blockname.h"
If the unit has any subunits, or is a unit for a system type, a block type, or a process type, its own .h
file is then included:
#include "unitname.h"
If this is a unit for a system then this is followed by:
XSYSTEMVARS
The name of this file is then generated as a char variable:
#ifdef XCTRACE static char xFileName[] = "mall.c"; #endif
If the unit has any subunits or is a unit for a system type, a block type, or a process type, now code generation is continued on the .h
file for the current unit. Otherwise all code is generated on the .c
file.
In this "Types and Forward References" section, each entity defined in SDL will have its own subsection. Example of such entities are: blocks, block types, block instantiations, processes, process types, process instantiations, services, service types, service instances, procedures, signals, timers, newtypes, syntypes, synonyms, variables, formal parameters, and so on. Only entities belonging to this separate unit is handled.
The SDL entities in separate unit forms a tree, either with the system or a package as root, or if it is subunit, with a block, block type, process, process type, service, service type, or procedure as root. This tree of entities is traversed in prefix order. Example:
If a block B contains two processes P1 and P2 and one signal S, these entities are treated in the order:
Newtypes, syntypes, and synonyms are always treated before other entities defined in the same scope (a block for example). The order for these entities are (see also Newtype, Syntype, Synonym and #CODE (#TYPE section):
There are some special treatment when it comes to types and instantiations. A BlockIdNode
is created both for a block type and for a block instance. The node below these BlockIdNodes
differs however. Below the node for the block type, nodes are created for such entities that are equal in each instantiation. Examples are nodes for newtypes, syntype, signals, procedures, process types. Below a block instantiation, nodes that in some respect differs between each block instantiation are generated. Example of such nodes are nodes for process instantiations, channels, signal routes, gates, remote definitions. Process types - process instantiations, service types - service instantiations, and system types - system instantiations have similar properties, when it comes to sub-nodes.
Each subsection for an SDL entity starts with a comment to identify the entity.
/***** * PROCESS P1 * <<SYSTEM mall/BLOCK B1>> * #SDTREF(TEXT,mall.pr,30) ******/
The information presented here is the type of entity and the entity name, the qualifier for the entity, and the SDT reference for the entity (see SDT References).
The following entity classes are treated in subsections below:
The following is generated for a system:
/***** * SYSTEM mall * #SDTREF(TEXT,mall.pr,1) ******/ extern XCONST struct xSystemIdStruct ySysR_mall;
That is extern definition of the IdStruct
for the system.
The following is generated for a system instance:
/***** * SYSTEM INST systype1 * #SDTREF(TEXT,systype1.pr,2) ******/ extern XCONST struct xSystemIdStruct ySysR_z_systype1; extern XCONST xIdNode ySysP_z_systype1[];
where ySysP_z_systype1 will become a list of channels defined in the system.
The following is generated for a system type:
/***** * SYSTEM TYPE S0 * <<PACKAGE slib>> * #SDTREF(TEXT,systype1.pr,7) ******/ extern XCONST struct xSystemIdStruct ySysR_z_slib_0_S0; #define yChaE_z_slib_01_C1 0 #define yChaE_z_slib_02_C2 1
where the yChaE_... can be used as indexes in the ySysP_z_systype1 list of channels (see example above). Using this list and these indexes it is possible to refer to channels generated in the system instance from Cadvanced/Cbasic code generated for the system type, for example in an OUTPUT VIA.
The code generated for a package is just a definition of a PackageIdStruct
.
/***** * PACKAGE slib * #SDTREF(TEXT,systype1.pr,5) ******/ extern struct xPackageIdStruct yPacR_z_slib__slib;
Note: A package is always a separate unit and that both a |
For a block substructure the following BlockSubstIdStruct
is defined. In a block instantiation no node for block substructure are generated.
/***** * SUBSTRUCTURE BType * <<SYSTEM inst6/BLOCK TYPE BType>> * #SDTREF(TEXT,inst6.pr,19) ******/ extern XCONST struct xBlockSubstIdStruct yBSuR_z80_BType;
For a block and for a block instantiation the following BlockIdStruct
are generated:
/***** * BLOCK B1 * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,28) ******/ extern XCONST struct xBlockIdStruct yBloR_z0_B1;
If the block contains processes and there are revealed variables in the any of the processes, then a definition of what will become a list of revealed variables is introduced. Also some defines for indexing this list are generated:
extern XCONST struct xViewListStruct yView_z0_B1[]; #define yView_z_predefined_6_revVar 0
If the block contains processes but no signal routes (implicit signal routes), or it is a block instantiation, then a definition of what will become a list replacing the signal routes is introduced. Also some defines for indexing in this list are generated.
extern XCONST xIdNode yBloP_z0_B1[]; #define yPrsE_z00_P1 0 #define yPrsE_z01_P2 1
For a block type the following code is generated:
/***** * BLOCK TYPE B1Type * <<PACKAGE sib/SYSTEM TYPE S0>> * #SDTREF(TEXT,systype1.pr,16) ******/ extern XCONST struct xBlockIdStruct yBloR_z_slib_06_B1Type; #define yChaE_z_systemlib_061_G 0 #define yPrsE_z_systemlib_060_P1 0
where the defines can be used to index in the yBloP_BlockName
list generated for a block instance of this block type.
For a process or process type the following code is generated:
/***** * PROCESS P1 * <<SYSTEM mall/BLOCK B1>> * #SDTREF(TEXT,mall.pr,30) ******/ extern XCONST XSIGTYPE yPrsS_z00_P1[]; extern XCONST xStateIdNode yPrsT_z00_P1[]; extern XCONST struct xPrsIdStruct yPrsR_z00_P1; #define yPrsN_z00_P1 (&yPrsR_z00_P1) #ifdef XCOVERAGE extern long int yPrsC_z00_P1[]; #endif YPAD_PROTOTYPE(yPAD_z00_P1) #ifndef XNOSTARTUPIDNODE extern XCONST struct xSignalIdStruct ySigR_z00_P1; #define ySigN_z00_P1 (&ySigR_z00_P1) #endif
The first part is a number of extern definition of the data structures used to represent the process, and the extern definition of the PAD
function, implementing the behavior of the process.
StateIdNodes
for this processPrsIdStruct
for the processSignalIdNode
for the startup signal for this process.After that we have the yVDef_Process
name struct, which will be discussed separately below.
typedef struct { PROCESS_VARS /* #SDTREF(TEXT,mall.pr,33) */ SDL_Integer z004_remVar; SDL_Integer yExp_z004_remVar; /* #SDTREF(TEXT,mall.pr,34) */ SDL_Integer z005_revVar; /* #SDTREF(TEXT,mall.pr,36) */ SDL_Integer z006_I; } yVDef_z00_P1; typedef yVDef_z00_P1 *yVDP_z00_P1; typedef struct { SIGNAL_VARS STARTUP_VARS } yPDef_z00_P1; typedef yPDef_z00_P1 *yPDP_z00_P1; XPROCESSDEF_H(P1,"P1",z00_P1,yPAD_z00_P1, yVDef_z00_P1) #ifdef XBREAKBEFORE #define ySym_z00_P1 26 extern char * yRef_z00_P1 XPP((int, xSymbolType *)); #endif
After the yVDef_ProcessName
struct a similar struct, only containing the FPARs of the process is generated. This struct, yPDef_ProcessName
is used as definition of the startup signal. At the end there is a macro ySym_ProcessName
which is the number of "symbols" in the process graph (counted after some transformations). Last there is an extern definition of the function yRef_ProcessName
. This function is used to translate symbol numbers to SDT references.
For a process instantiation only the extern definition of the PrsIdStruct
is necessary, as most of the other information needed for the process instance can be reused from the process type.
For each process and process type there will be a struct containing one component for each formal parameter (FPAR) and each variable (DCL). For an exported variable there will be one extra component to represent the currently exported value. There will also be a component for each type of expression that is used in decisions, except for the types Integer, Real, Boolean, Character, PId. Take, for example, a decision with a Charstring question:
DECISION CharstringVar; ('abc') : ...; ELSE : ...; ENDDECISION;
then there will be a component where the question value can be stored. Together with the struct type the will also be a pointer type to the struct type. In the example below we assume that the process has two formal parameters, FPAR_var1
and FPAR_var2
, two variables, DCL_var1
and DCL_var2
, where the second variable is exported, and a decision where the question type is TypeName5
.
typedef struct { PROCESS_VARS TypeName1 FPAR_var1; TypeName2 FPAR_var2; TypeName3 DCL_var1; TypeName4 DCL_var2; TypeName4 yExp_DCL_var2; TypeName5 yDcn_TypeName5; } yVDef_ProcessName; typedef yVDef_ProcessName *yVDP_ProcessName;
In a process type, all variables, formal parameters, and so on, from the all the inherited process types are included in the struct above, starting from the top of the inheritance chain.
For a service or a service type code very similar to the code for a process or process type is generated. Example:
/***** * SERVICE S1 * <<SYSTEM serv3/BLOCK Block1/PROCESS P1>> * #SDTREF(TEXT,serv3.pr,27) ******/ extern XCONST XSIGTYPE ySrvS_z011_S1[]; extern XCONST xStateIdNode ySrvT_z011_S1[]; extern XCONST struct xSrvIdStruct ySrvR_z011_S1; #define ySrvN_z011_S1 (&ySrvR_z011_S1) #ifdef XCOVERAGE extern long int ySrvC_z011_S1[]; #endif YPAD_PROTOTYPE(yPAD_z011_S1) #ifndef XOPTCHAN extern XCONST_NOPART xIdNode ySrvO_z011_S1[]; #endif #ifdef XBREAKBEFORE #define ySym_z011_S1 8 extern char * yRef_z011_S1 XPP((int, xSymbolType *)); #endif typedef struct { SERVICE_VARS /* declarations of service local variables */ } yVDef_z011_S1; typedef yVDef_z011_S1 *yVDP_z011_S1;
The major difference between the code generated for a service and for a process is that yPrs
is replaced by ySrv
, and that not all items are generated for a service, the start up signal for example. For more information about the object above please see the corresponding object for processes.
A procedure (or an operator diagram or a compound statement containing variables) has much in common with a process, when it comes to implementation. It is represented by a PrdIdStruct
(extern definition here), a yVDef_ProcedureName
struct defining the variables and formal parameters of the procedure (defined here), and by the procedure function, implementing the behavior of the procedure (extern definition here).
/***** * PROCEDURE Proc2 * <<SYSTEM mall/BLOCK B1/PROCESS P2>> * #SDTREF(TEXT,mall.pr,89) ******/ YPRD_PROTOTYPE(z011_Proc2) extern XCONST struct xPrdIdStruct yPrdR_z011_Proc2; #define yPrdN_z011_Proc2 (&yPrdR_z011_Proc2) #ifdef XBREAKBEFORE #define ySym_z011_Proc2 2 extern char * yRef_z011_Proc2 XPP((int, xSymbolType *)); #endif typedef struct { PROCEDURE_VARS /* #SDTREF(TEXT,mall.pr,90) */ SDL_Integer z0110_P1; /* #SDTREF(TEXT,mall.pr,90) */ SDL_Integer *z0111_P2; } yVDef_z011_Proc2; typedef yVDef_z011_Proc2 *yVDP_z011_Proc2;
For each procedure there will be a struct containing one component for each formal parameter (FPAR) and each variable (DCL). There will also be a component for each type of expression that is used in decisions, except for the types Integer, Real, Boolean, Character, PId. For a decision with a Charstring question:
DECISION CharstringVar; ('abc') : ...; ELSE : ...; ENDDECISION;
there will be a component where the question value can be stored. Together with the struct type, there will also be a pointer type to the struct type:
typedef struct { PROCEDURE_VARS TypeName1 FPAR_var1; TypeName2 *FPAR_var2; TypeName3 DCL_var1; TypeName4 DCL_var2; TypeName5 yDcn_TypeName5; } yVDef_ProcedureName; typedef yVDef_ProcedureName *yVDP_ProcedureName;
where FPAR_var1
is assumed to be an IN parameter, while FPAR_var2
is assumed to be an IN/OUT parameter. Note that an IN/OUT parameter is represented as an address. In a procedure, all variables, formal parameters, and decision variables, from all the inherited procedures are included in the struct above, starting from the top of the inheritance chain.
For a signal or a timer a definition of a SignalIdStruct
is generated. If the signal or timer contains parameters, a struct yPDef_SignalName
with one component for each parameter is generated.
/***** * SIGNAL Sig3 * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,4) ******/ #ifndef XNOSIGNALIDNODE extern XCONST struct xSignalIdStruct ySigR_z3_Sig3; #define ySigN_z3_Sig3 (&ySigR_z3_Sig3) #endif typedef struct { SIGNAL_VARS SDL_Integer Param1; SDL_Character Param2; } yPDef_z3_Sig3; typedef yPDef_z3_Sig3 *yPDP_z3_Sig3;
The following declarations are generated for a remote procedure in SDL.
/***** * REMOTE PROCEDURE rpc1 * <<SYSTEM rpc1>> * #SDTREF(TEXT,rpc1.pr,8) ******/ extern struct xRemotePrdIdStruct yRePR_z3_rpc1; #ifndef XNOSIGNALIDNODE extern XCONST struct xSignalIdStruct ySigR_pCALL_z3_rpc1; extern XCONST struct xSignalIdStruct ySigR_pREPLY_z3_rpc1; #define ySigN_pCALL_z3_rpc1 (&ySigR_pCALL_z3_rpc1) #define ySigN_pREPLY_z3_rpc1 (&ySigR_pREPLY_z3_rpc1) #endif typedef struct { SIGNAL_VARS SDL_Integer Param1; SDL_Integer Param2; } yPDef_pCALL_z3_rpc1; typedef yPDef_pCALL_z3_rpc1 *yPDP_pCALL_z3_rpc1; typedef struct { SIGNAL_VARS SDL_Integer Param2; } yPDef_pREPLY_z3_rpc1; typedef yPDef_pREPLY_z3_rpc1 *yPDP_pREPLY_z3_rpc1;
For a remote procedure one RemotePrdIdStruct
is defined together with two SignalIdStructs
and yPDef_... structs, for the two implicit signals used to implement an RPC call. The pCALL
signal has one signal parameter for each procedure parameter, while the pREPLY
signal has one parameter for each in/out parameter to the procedure. Empty yPDef_ structs are not generated.
The following definition of a VarIdStruct
is generated for each variable and formal parameter in processes, process types, procedures, and operator diagrams.
/***** * DCL I * <<SYSTEM mall/BLOCK B1/PROCESS P1>> * #SDTREF(TEXT,mall.pr,36) ******/ #ifndef XOPTDCL extern XCONST struct xVarIdStruct yVarR_z006_I; #endif
For a remote variable (the remote definition, either it is explicit or implicit) a definition of a RemoteVarIdStruct
is generated.
/***** * REMOTE VARIABLE remVar * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,26) ******/ #ifndef XNOREMOTEVARIDNODE extern struct xRemoteVarIdStruct yReVR_z7_remVar; #define yReVN_z7_remVar (&yReVR_z7_remVar) #endif
The following definition of a StateIdStruct
is introduced for each state. The StateName
(z002_State3 in example below) is used as the representation of the state in, for example, the state variable in a process. This name is defined to an appropriate integer value.
/***** * STATE State3 * <<SYSTEM mall/BLOCK B1/PROCESS P1>> * #SDTREF(TEXT,mall.pr,72) ******/ #define z002_State3 3 extern XCONST struct xStateIdStruct yStaR_z002_State3; #define yStaN_z002_State3 (&yStaR_z002_State3)
If the state contains enabling conditions then an extern definition for an enabling condition function is generated:
#ifndef XNOENABCONDFUNC extern xInputAction yEnab_z001_State2 XPP((XSIGTYPE, void *)); #endif
If the state contains continuous signals then an extern definition for an continuous signal function is generated:
#ifndef XNOCONTSIGFUNC extern void yCont_z001_State2 XPP((void *, int *, xIdNode *, int *)); #endif
A channel, signal route or gate is represented by two ChanneIdStructs
, one for each direction.
/***** * CHANNEL C1 * <<PACKAGE systemlib/SYSTEM TYPE S0>> * #SDTREF(TEXT,systype1.pr,9) ******/ #ifndef XOPTCHAN extern XCONST struct xChannelIdStruct yChaR_z1_C1; extern XCONST struct xChannelIdStruct yChaRR_z1_C1; #define yChaN_z1_C1 (&yChaR_z1_C1) #endif
For each synonym definition that can be translated to a macro (predefined data type and computable at code generation time) the following code is generated:
#define SynonymName SynonymValue
The unit is scanned for Newtypes, Syntypes, and #CODE directives (among declarations). The search algorithm is described in Implementation.
If newtype or syntype is found:
xSortIdStruct
:#ifndef XOPTSORT extern XCONST struct xSortIdStruct ySrtR_sort1; #define ySrtN_sort1 (&ySrtR_sort1) #endif
#include "filename"
If #CODE directive is found the following is generated:
Example of a type section for a struct.
/***** * NEWTYPE struct1 (TYPE section) * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,6) ******/ typedef struct struct1_s { SDL_Integer a; SDL_Integer b; } struct1; /* #SDTREF(TEXT,mall.pr,7) */ COMMENT((This is the #type section)) #ifndef XOPTSORT extern XCONST struct xSortIdStruct ySrtR_struct1; #define ySrtN_struct1 (&ySrtR_struct1) #endif
The unit is scanned for Newtypes and Syntypes. In the order the types are found the following is generated:
extern void yAss_TypeName XPP(( TypeName *, TypeName , int ));
extern void yDef_TypeName XPP(( TypeName * ));
#define yDef_TypeName1(yVar) \ *(yVar) = DefaultExpression; #define yDef_TypeName2(yVar) \ yDef_TypeName3(yVar)The first macro is used in most circumstances, and the second is used for a syntype, without default value, of a structured type. In this case the default function for the base type is reused.
extern char * yWri_TypeName XPP(( void * ));
extern int yRead_TypeName XPP(( void * ));
extern SDL_Boolean yEq_TypeName XPP(( TypeName, TypeName ));
extern TypeName yMake_TypeName XPP(( ComponentTypeName ));
extern TypeName yMake_TypeName XPP(( Component1TypeName, Component2TypeName ))
extern yComp1Make_TypeName XPP(( Comp1TypeName )); extern yComp2Make_TypeName XPP(( Comp2TypeName ));
#ifdef XTESTF extern xbool yTest_TypeName XPP(( TypeName )); #endif #ifdef XERANGE extern TypeName yTstA_TypeName XPP(( TypeName )); #else #define yTstA_TypeName(yExpr) yExpr #endif #ifdef XEINDEX extern TypeName yTstI_TypeName XPP(( TypeName )); #else #define yTstI_TypeName(yExpr) yExpr #endifThe
yTstA_
TypeName
and yTstI_
TypeName
are used to check the validity of an assignment to a syntype variable (yTstA_
) and to check the validity of an index expression when indexing an array.
Note: Note that the compilation flags |
yTstA_
TypeName
and yTstI_
TypeName
functions are shown in the section Newtype, Syntype and their use is discussed in Translation of SDL Expressions.extern TypeName LiteralName XPP(( void ));For each operator that should have a generated heading:
extern ResultTypeName OperatorName XPP(( appropriate parameters ));
For each synonym that cannot be translated to a macro (this includes the external synonyms) a variable declaration is generated. If code is generated on a .h
file, extern declarations are placed here and the current variable declarations will be generated later.
extern TypeName1 SynonymName; #ifndef ExtSynonymName extern TypeName2 ExtSynonymName; #endif
The variables representing the SDL synonyms are assigned values during startup of the program in the init function, see Initialization.
All #CODE directives (among declarations) are scanned in their relative order and the #HEADING sections are copied.
All #CODE directives (among declarations) are scanned in their relative order and the #BODY sections are copied.
Here the declaration of the variables and functions, defined in section Types and Forward References, can be found. The symbol table tree discussed in this previous section is here once more traversed in the same order.
The following declarations are generated for a system:
/***** * SYSTEM mall * #SDTREF(TEXT,mall.pr,1) ******/ XCONST struct xSystemIdStruct ySysR_z_mall = {xSystemEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySymbRootVar xIdNames("mall") XCOMMON_EXTRAS, (xIdNode *)0, (xPrdIdNode *)0, (xSystemIdNode)0 xTrace(-1) xGRTrace(-1) xMSCETrace(-1) XSYS_EXTRAS}; /***** * ENV ******/ #ifndef XOPTCHAN static xIdNode yEnvO_env[] = { (xIdNode)&yChaRR_z1_Channel1, (xIdNode)0 XTRACHANNELLIST}; #endif
The components in the xSystemIdNode for the system, can be compared with the type definitions for the IdSystemStruct
, see section Type Definitions from Abstract Data Types and #CODE (#TYPE Section).
For a system instance a similar sequence of code is generated. The differences are in the IdNode
for the system:
/***** * SYSTEM INST systype1 * #SDTREF(TEXT,systype1.pr,2) ******/ XCONST xIdNode ySysP_z_systype1[] = {(xIdNode)&yChaR_z1_C1, (xIdNode)&yChaR_z2_C2, (xIdNode)0}; XCONST struct xSystemIdStruct ySysR_z_systype1 = {xSystemEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySymbRootVar xIdNames("systype1") XCOMMON_EXTRAS, ySysP_z_systype1, (xPrdIdNode *)0, (xSystemIdNode)0 xTrace(-1) xGRTrace(-1) xMSCETrace(-1) XSYS_EXTRAS};
Here we see the implementation of the ySysP_z_systype1 list of channels, discussed in the subsection about systems in Types and Forward References. Note also the ySysP_z_systype1 is stored in the IdStruct
for the system instance.
For a system type the following code is generated:
/***** * SYSTEM TYPE S0 * <<PACKAGE slib>> * #SDTREF(TEXT,systype1.pr,7) ******/ XCONST struct xSystemIdStruct ySysR_z_slib_0_S0 = {xSystemTypeEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yPacR_z_slib__slib xIdNames("S0") XCOMMON_EXTRAS, (xIdNode *)0, (xPrdIdNode *)0, (xSystemIdNode)0 xTrace(-1) xGRTrace(-1) xMSCETrace(-1) XSYS_EXTRAS};
Only the declaration of a PackageIdStruct
is generated for a package.
/***** * PACKAGE slib * #SDTREF(TEXT,systype1.pr,5) ******/ struct xPackageIdStruct yPacR_z_slib__slib = {xPackageEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)0 xIdNames("systemlib") XCOMMON_EXTRAS xIdNames("systemlib") (XPAC_EXTRAS};
A BlockSubstIdStruct
according to the example below is generated for block substructures, except in block instantiations.
/***** * SUBSTRUCTURE BType * <<SYSTEM inst6/BLOCK TYPE BType>> * #SDTREF(TEXT,inst6.pr,19) ******/ XCONST struct xBlockSubstIdStruct yBSuR_z80_BType = {xBlocksubstEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yBloR_z8_BType xIdNames("BType") XCOMMON_EXTRAS XBLS_EXTRAS};
For a block or block instantiation the following BlockIdStruct declaration in generated. The yBloP_BlockName
list is only generated for a block containing processes but no signal routes and for a block instantiation. In block this list contains the processes, process instantiations in the block, and the out-going channels from the block. The purpose of the list is to replace the (implicit) signal routes.
/***** * BLOCK B1 * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,28) ******/ XCONST xIdNode yBloP_z0_B1[] = {(xIdNode)&yPrsR_z00_P1, (xIdNode)&yPrsR_z01_P2, (xIdNode)0}; XCONST struct xBlockIdStruct yBloR_z0_B1 = {xBlockEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_mall xIdNames("B1") XCOMMON_EXTRAS, (xBlockIdNode)0, yBloP_z0_B1, (xPrdIdNode *)0, yView_z0_B1, 1 xTrace(-1) xGRTrace(-1) xMSCETrace(-1) xMSCETrace(0) XBLO_EXTRAS};
In a block instance the yBloP_BlockName
has two purposes, one is the replace implicit signal routes, the other is to store references to different entities in the block instance, so generated code for the corresponding block instance can access the entities in the instance. For example, an OUTPUT VIA a signal route should access the signal route in the block instance, even though the code is generated for the block type.
This yBloP_BlockName
will contain the following entities:
If the block contains processes with revealed variables a yView
list is generated. This contains value tuples for each revealed variable with {PrsIdNode
for revealing process, revealing service, offset in yVDef
struct for value}.
XCONST struct xViewListStruct yView_z0_B1[] = { {&yPrsR_z00_P1, xService(0) xOffsetOf(yVDef_z00_P1, z005_revVar)}, {0, xService(0) 0} };
For a block type the following BlockIdStruct
declaration is generated:
/***** * BLOCK TYPE B1Type * <<PACKAGE slib/SYSTEM TYPE S0>> * #SDTREF(TEXT,systype1.pr,16) ******/ XCONST struct xBlockIdStruct yBloR_z_slib_06_B1Type= {xBlockTypeEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_systemlib_0_S0 xIdNames("B1Type") XCOMMON_EXTRAS, (xBlockIdNode)0, (xIdNode *)0, (xPrdIdNode *)0, (xViewListRec *)0, 1 xTrace(-1) xGRTrace(-1) xMSCETrace(-1) xMSCETrace(0) XBLO_EXTRAS};
A process or a process type is represented basically by a PrsIdStruct
(declared here), a yVDef_ProcessName
struct (typedef in Type and Forward References section), and the yPAD_ProcessName
function (declared here). In the example below
StateIdNodes
for this process.PrsIdStruct
for the process.SignalIdNode
for the startup signal for this process, while ySigA_z00_P1 is the avail list for this signal type./***** * PROCESS P1 * <<SYSTEM mall/BLOCK B1>> * #SDTREF(TEXT,mall.pr,30) ******/ #ifdef XCOVERAGE long int yPrsC_z00_P1[ySym_z00_P1+1]; #endif XCONST XSIGTYPE yPrsS_z00_P1[] = {SIGNAL_NAME(Sig1, &ySigR_z1_Sig1), SIGNAL_NAME(Sig2, &ySigR_z2_Sig2), SIGNAL_NAME(Sig3, &ySigR_z3_Sig3), SIGNAL_NAME(t1, &ySigR_z003_t1), (XSIGTYPE)0}; XCONST xStateIdNode yPrsT_z00_P1[] = {&xStartStateIdRec, &yStaR_z000_State1, &yStaR_z001_State2, &yStaR_z002_State3}; static XPRSNODE yPrsA_z00_P1 = (XPRSNODE)0; static XPRSNODE yPrsB_z00_P1 = (XPRSNODE)0; XCONST struct xPrsIdStruct yPrsR_z00_P1 = {xProcessEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yBloR_z0_B1 xIdNames("P1") XCOMMON_EXTRAS, yPrsT_z00_P1, yPrsS_z00_P1 xService(0) xOptChan(yPrsO_z00_P2), SDL_INTEGER_LIT(1) xNrInst(1) xNrInst(SDL_INTEGER_LIT(1)), &yPrsB_z00_P1, (xptrint)sizeof(yVDef_z00_P1) xPrsPrioPar(xDefaultPrioProcess), &yPrsA_z00_P1 xTrace(-1) xGRTrace(-1) xBreakB(yRef_z00_P1) xBreakB(ySym_z00_P1) xBreakB(4) xMSCETrace(-1) xCoverage(yPrsC_z00_P1) xCoverage(0) xCoverage(0), yPAD_z00_P1, (xPrsIdNode)0, (xPrdIdNode *)0, (xBlockIdNode)0 xBreakB("#SDTREF(TEXT,mall.pr,30)") XPRS_EXTRAS(z00_P2)}; #ifndef XNOSTARTUPIDNODE static xSignalNode ySigA_z00_P1 = (xSignalNode)0; XCONST struct xSignalIdStruct ySigR_z00_P1 = {xStartUpSignalEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yBloR_z0_B1 xIdNames("-") XCOMMON_EXTRAS, (xptrint)sizeof(yPDef_z00_P1), &ySigA_z00_P1, 0 xFreS(0) SIGCODE(STARTUPSIGNAL) xBreakB("#SDTREF(TEXT,mall.pr,30)") XSIG_EXTRAS}; #endif XPROCESSDEF_C(P1,"P1",z00_P1,yPAD_z00_P1,yVDef_z00_P1) XCONST_NOPART xIdNode yPrsO_z00_P2[] = {(xIdNode)&yChaR_z07_sr1, (xIdNode)&yChaRR_z08_local, (xIdNode)0}; #endif
The macro XPROCESSDEF_C is not used in the Master Library.
After these data declaration, any exported variable or exported procedure will cause the generation of a list element that will be inserted in the remote definition for the entity.
static struct xRemoteVarListStruct yExpR_z004_remVar = {(xRemoteVarListNode)0, &yPrsR_z00_P1, xService(0) xOffsetOf(yVDef_z00_P1, yExp_z004_remVar)};
After that a function is generated that is used to translated a symbol number to a symbol type and a SDT reference.
/*+++++ * GR ref function for process P1 * #SDTREF(TEXT,mall.pr,30) ++++++*/ #ifdef XBREAKBEFORE #ifndef XNOPROTO extern char * yRef_z00_P1 (int SymbolNo, xSymbolType *SymbolType) #else extern char * yRef_z00_P1 (SymbolNo, SymbolType) int SymbolNo; xSymbolType *SymbolType; #endif { switch (SymbolNo) { case 0: *SymbolType = xsStart; return "#SDTREF(TEXT,mall.pr,44)"; case 1: *SymbolType = xsInput; return "#SDTREF(TEXT,mall.pr,54)"; case ....... default : return ""; } } #endif
As last information for a process the yPAD
function is generated. In this the implementation of the behavior of the process is implemented. This function will be discussed in detail, in the section Process Behavior.
/*+++++ * Function for process P1 * #SDTREF(TEXT,mall.pr,30) ++++++*/ YPAD_FUNCTION(yPAD_z00_P1) { }
For a process instantiation most of the information needed to represent it can be reused from its process type. It is only necessary to generate the avail list and active list pointers and the PrsIdStruct
. Then of course the process gates have to be handled and the connection data areas, for connecting the process and the gate into paths.
Note: A process (SDL-88 process) in a block type or a system type, is treated as a process type and process instantiation at the same place. |
The following code is generated for a service.
/***** * SERVICE S1 * <<SYSTEM serv3/BLOCK Block1/PROCESS P1>> * #SDTREF(TEXT,serv3.pr,27) ******/ #ifdef XCOVERAGE long int ySrvC_z011_S1[ySym_z011_S1+1]; #endif XCONST XSIGTYPE ySrvS_z011_S1[] = {SIGNAL_NAME(Sig1R, &ySigR_z04_Sig1R), SIGNAL_NAME(TimerS1, &ySigR_z0112_TimerS1), (XSIGTYPE)0}; XCONST xStateIdNode ySrvT_z011_S1[] = {&xStartStateIdRec, &yStaR_z0110_S1State, &yStaR_z0111_S1State2}; static xSrvNode ySrvA_z011_S1 = (xSrvNode)0; XCONST struct xSrvIdStruct ySrvR_z011_S1 = {xServiceEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yPrsR_z01_P1 xIdNames("S1") XCOMMON_EXTRAS, ySrvT_z011_S1, ySrvS_z011_S1 xOptChan(ySrvO_z011_S1), (xptrint)sizeof(yVDef_z011_S1) xBreakB(yRef_z011_S1) xBreakB(ySym_z011_S1) xBreakB(2) xCoverage(ySrvC_z011_S1) xCoverage(0), &ySrvA_z011_S1, yPAD_z011_S1, (xSrvIdNode)0, (xPrdIdNode *)0 XSRV_EXTRAS}; #ifndef XOPTCHAN XCONST_NOPART xIdNode ySrvO_z011_S1[] = {(xIdNode)&yChaR_z013_P1sr1, (xIdNode)0}; #endif
First for a service we have the declaration of the coverage array for the symbols in the procedure, then we have the state list, the avail list pointer, and the SrvIdStruct
for the service. Last is the list of connected service signal routes for this service.
After these declaration of a yRef_ServiceName
function and the PAD function for the service can be found. For more information about these function please see the corresponding information about processes.
The following code is generated for a procedure. A operator diagram is treated just as a procedure.
/***** * PROCEDURE Proc2 * <<SYSTEM mall/BLOCK B1/PROCESS P2>> * #SDTREF(TEXT,mall.pr,89) ******/ #ifdef XCOVERAGE long int yPrdC_z011_Proc2[ySym_z011_Proc2+1]; #endif static XCONST xStateIdNode yPrdT_z011_Proc2[] = {&xStartStateIdRec}; static xPrdNode yPrdA_z011_Proc2 = (xPrdNode)0; XCONST struct xPrdIdStruct yPrdR_z011_Proc2 = {xProcedureEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yPrsR_z01_P2 xIdNames("Proc2") XCOMMON_EXTRAS, yPrdT_z011_Proc2, yPrsS_z01_P2, z011_Proc2, (xptrint)sizeof(yVDef_z011_Proc2), &yPrdA_z011_Proc2 xBreakB(yRef_z011_Proc2) xBreakB(ySym_z011_Proc2) xBreakB(0) xCoverage(yPrdC_z011_Proc2), (xPrdIdNode)0, (xPrdIdNode *)0 XPRD_EXTRAS};
First for a procedure we have the declaration of the coverage array for the symbols in the procedure, then we have the state list, the avail list pointer, and the PrdIdStruct
for the procedure.
After these declarations the yRef_ProcedureName
function can be found. This function is used to translate a symbol number to a symbol type and a SDT reference to the symbol.
/*+++++ * GR ref function for procedure Proc2 * #SDTREF(TEXT,mall.pr,89) ++++++*/ #ifdef XBREAKBEFORE #ifndef XNOPROTO extern char * yRef_z011_Proc2 (int SymbolNo, xSymbolType *SymbolType) #else extern char * yRef_z011_Proc2 (SymbolNo, SymbolType) int SymbolNo; xSymbolType *SymbolType; #endif { switch (SymbolNo) { case 0: *SymbolType = xsStart; return "#SDTREF(TEXT,mall.pr,91)"; case 1: *SymbolType = xsAssignmentStatement; return "#SDTREF(TEXT,mall.pr,92)"; case 2: *SymbolType = xsReturn; return "#SDTREF(TEXT,mall.pr,93)"; default : return ""; } } #endif
Last for the procedure the procedure function, implementing the behavior of the procedure can be found. The details in this function will be discussed in detail in the section Process Behavior.
/*+++++ * Function for procedure Proc2 * #SDTREF(TEXT,mall.pr,89) ++++++*/ #ifndef XNOPROTO YPRD_FUNCTION(z011_Proc2) { }
For a signal or timer a SignalIdStruct
is generated here:
/***** * SIGNAL Sig1 * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,4) ******/ #ifndef XNOSIGNALIDNODE XCONST struct xSignalIdStruct ySigR_z1_Sig1 = {xSignalEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_mall xIdNames("Sig1") XCOMMON_EXTRAS, (xptrint)sizeof(XSIGNALHEADERTYPE), 0, 0 xFreS(0) SIGCODE(Sig1) xBreakB("#SDTREF(TEXT,mall.pr,4)") XSIG_EXTRAS}; #endif
If the signal contains parameter then one VarIdStruct
is generated for each signal parameter:
#ifndef XOPTSIGPARA XCONST struct xVarIdStruct ySPaR1_z3_Sig3 = {xSignalParEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySigR_z3_Sig3 xIdNames(" ") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(yPDef_z3_Sig3, Param1), (xptrint)0, (xbool)0 XSPA_EXTRAS}; #endif
A remote procedure declaration in SDL generates several IdStructs
, one for the remote procedure and two for the implicit signals used to implement a remote procedure call. Additionally one VarIdStruct
is generated for each signal parameter of the implicit signals (just as for an ordinary signal). The implicit pCALL
signal has one signal parameter for each procedure parameter, while the implicit pREPLY
signal has one parameter for each in/out parameter to the procedure.
/***** * REMOTE PROCEDURE rpc1 * <<SYSTEM rpc1>> * #SDTREF(TEXT,rpc1.pr,8) ******/ struct xRemotePrdIdStruct yRePR_z3_rpc1 = {xRemotePrdEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_rpc1 xIdNames("rpc1") XCOMMON_EXTRAS, 0}; #ifndef XNOSIGNALIDNODE static xSignalNode ySigA_pCALL_z3_rpc1 = (xSignalNode)0; static xSignalNode ySigA_pREPLY_z3_rpc1 = (xSignalNode)0; XCONST struct xSignalIdStruct ySigR_pCALL_z3_rpc1 = {xRPCSignalEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_rpc1 xIdNames("pCALL_rpc1") XCOMMON_EXTRAS, (xptrint)sizeof(yPDef_pCALL_z3_rpc1), &ySigA_pCALL_z3_rpc1, 0 xFreS(0) SIGCODE(pCALL_rpc1) xBreakB("#SDTREF(TEXT,rpc1.pr,8)") XSIG_EXTRAS}; XCONST struct xSignalIdStruct ySigR_pREPLY_z3_rpc1 = {xRPCSignalEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_rpc1 xIdNames("pREPLY_rpc1") XCOMMON_EXTRAS, (xptrint)sizeof(yPDef_pREPLY_z3_rpc1), &ySigA_pREPLY_z3_rpc1, 0 xFreS(0) SIGCODE(pREPLY_rpc1) xBreakB("#SDTREF(TEXT,rpc1.pr,8)") XSIG_EXTRAS}; #endif #ifndef XOPTSIGPARA XCONST struct xVarIdStruct ySPaR1_pCALL_z3_rpc1 = {xSignalParEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySigR_pCALL_z3_rpc1 xIdNames(" ") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(yPDef_pCALL_z3_rpc1, Param1), (xptrint)0, (xbool)0 XSPA_EXTRAS}; XCONST struct xVarIdStruct ySPaR2_pCALL_z3_rpc1 = {xSignalParEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySigR_pCALL_z3_rpc1 xIdNames(" ") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(yPDef_pCALL_z3_rpc1, Param2), (xptrint)0, (xbool)0 XSPA_EXTRAS}; XCONST struct xVarIdStruct ySPaR2_pREPLY_z3_rpc1 = {xSignalParEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySigR_pREPLY_z3_rpc1 xIdNames(" ") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(yPDef_pREPLY_z3_rpc1, Param2), (xptrint)0, (xbool)0 XSPA_EXTRAS}; #endif
The following declaration is an example of a VarIdStruct
for a process variable.
/***** * DCL I * <<SYSTEM mall/BLOCK B1/PROCESS P1>> * #SDTREF(TEXT,mall.pr,36) ******/ #ifndef XOPTDCL XCONST struct xVarIdStruct yVarR_z006_I = {xVariableEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yPrsR_z00_P1 xIdNames("I") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(yVDef_z00_P1, z006_I), (xptrint)0, (xbool)0 XVAR_EXTRAS}; #endif
For a remote variable the following declaration is generated:
/***** * REMOTE VARIABLE remVar * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,26) ******/ #ifndef XNOREMOTEVARIDNODE struct xRemoteVarIdStruct yReVR_z7_remVar = {xRemoteVarEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_mall xIdNames("remVar") XCOMMON_EXTRAS, (xptrint)sizeof(SDL_Integer), (xRemoteVarListNode)0}; #endif
The last component in the struct is a list of all exported variables connected to this remote definition. This list is built up in the yInit
function via calls of the function xInsertExportedVar
:
xInsertExportedVar(&yExpR_z004_remVar, &yReVR_z7_remVar);
For a state a StateIdStruct
, together with some list, are generated. The list are used as follows:
yStaH_StateName
is a list of the actions (input, save,...) that should take place when a signal is received in this state. The index in the list, for a given signal, is the index where the signal is found in the signalset of the process or procedure where this state is defined.yStaI_StateName
is a list of the symbol numbers for input symbols (and priority inputs). Index as for yStaH_StateName
above.yStaC_StateName
is the coverage table for input of signal in this state is counted. The last (and extra) component is this array is used to count the number of continuous signals received in this state./***** * STATE State3 * <<SYSTEM mall/BLOCK B1/PROCESS P1>> * #SDTREF(TEXT,mall.pr,72) ******/ static XCONST xInputAction yStaH_z002_State3[] = {xDiscard, xDiscard, xInput, xInput, xDiscard}; static XCONST int yStaI_z002_State3[] = {0, 0, 5, 6, 0}; #ifdef XCOVERAGE static long int yStaC_z002_State3[4+1]; #endif XCONST struct xStateIdStruct yStaR_z002_State3 = {xStateEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&yPrsR_z00_P1 xIdNames("State3") XCOMMON_EXTRAS, z002_State3, yStaH_z002_State3, yStaI_z002_State3, 0, 0, 0 xCoverage(yStaC_z002_State3), (xStateIdNode)0 xBreakB("#SDTREF(TEXT,mall.pr,72)") XSTA_EXTRAS};
If the state contains one or more enabling conditions, an enabling condition function is generated that handles all enabling conditions in this state. A pointer to this function is stored in the StateIdStruct
for the state.
/*------------------------------------------------- -- Enabling condition function for state State2 --------------------------------------------------*/ #ifndef XNOENABCONDFUNC #ifndef XNOPROTO extern xInputAction yEnab_z001_State2 (XSIGTYPE SignalId, void * VarP) #else extern xInputAction yEnab_z001_State2 (SignalId, VarP) XSIGTYPE SignalId; void * VarP; #endif { yVDef_z00_P1 * yVarP = (yVDef_z00_P1 *)VarP; if ((SignalId == SIGNAL_NAME(Sig1, &ySigR_z1_Sig1))) { /* #SDTREF(TEXT,mall.pr,65) */ if (xGT_SDL_Integer(yVarP->z006_I, SDL_INTEGER_LIT(10))) return xInput; return xSave; } return xSave; } #endif
If the state contains one or more continuous signals, a continuous signal function is generated that handles all continuous signals in this state. A pointer to this function is stored in the StateIdStruct
for the state.
/*------------------------------------------------- -- Continuous signal function for state State2 --------------------------------------------------*/ #ifndef XNOCONTSIGFUNC #ifndef XNOPROTO extern void yCont_z001_State2 (void *VarP, int *Prio, xIdNode *IdNode, int *Addr) #else extern void yCont_z001_State2 (VarP, Prio, IdNode, Addr) void * VarP; int *Prio; xIdNode *IdNode; int *Addr; #endif { yVDef_z00_P1 * yVarP = (yVDef_z00_P1 *)VarP; /* #SDTREF(TEXT,mall.pr,68) */ if (xGT_SDL_Integer(yVarP->z004_remVar, SDL_INTEGER_LIT(20))) { *Prio = 10; *IdNode = (xIdNode)&yPrsR_z00_P1; *Addr = 4; return; }; *Addr = 0; return; } #endif
A channel, signal route or gate is represented by two ChannelIdStructs
, one for each direction. An example of such a declaration is given below.
/***** * CHANNEL C1 * <<PACKAGE systemlib/SYSTEM TYPE S0>> * #SDTREF(TEXT,systype1.pr,9) ******/ #ifndef XOPTCHAN static XCONST XSIGTYPE yChaS1_z1_C1[] = {SIGNAL_NAME(ok1, &ySigR_z_systemlib_04_ok1), (XSIGTYPE)0}; static XCONST XSIGTYPE yChaS2_z1_C1[] = {(XSIGTYPE)0}; XCONST struct xChannelIdStruct yChaR_z1_C1 = {xChannelEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_systype1 xIdNames("C1") XCOMMON_EXTRAS, yChaS2_z1_C1, yChaI2_z31_G, &yChaRR_z1_C1 XCHA_EXTRAS}; XCONST struct xChannelIdStruct yChaRR_z1_C1 = {xChannelEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_systype1 xIdNames("C1") XCOMMON_EXTRAS, yChaS1_z1_C1, (xIdNode *)&(yBloP_z0_B2[2]), &yChaR_z1_C1 XCHA_EXTRAS}; #endif
The two lists yChaS1_z1_C1 and yChaS2_z1_C1 represent the signal set of the two directions of the channel. The ToId component in a channel, signal route, or gate is used for connecting such objects together with processes into paths. For this purpose the yBloP_BlockName
component in a block is used. Sometimes a list with names:
yChaI1_Name, yChaI2_Name, yChaO1_Name, yChaO1_Name
is generated and used for such connections.
Example of generated code for a struct called struct1
with two integer components a
and b
:
/***** * NEWTYPE struct1 (BODY section) * <<SYSTEM mall>> * #SDTREF(TEXT,mall.pr,6) ******/ #ifndef XOPTSORT XCONST struct xSortIdStruct ySrtR_z4_struct1 = {xSortEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySysR_z_mall xIdNames("struct1") XCOMMON_EXTRAS xFreF(0) xAssF(0) xEqF(yEq_z4_struct1) xRaWF(0) xRaWF(0) xTestF(0), (xptrint)sizeof(z4_struct1), xStruct, (xSortIdNode)0, (xSortIdNode)0, 0, 0 XSRT_EXTRAS}; #endif #ifndef XOPTSTRUCT /*------------STRUCT COMPONENTS-------------*/ static XCONST struct xVarIdStruct yVarR_z48_a = {xVariableEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySrtR_z4_struct1 xIdNames("a") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(z4_struct1, a), (xptrint)0, (xbool)0 XVAR_EXTRAS}; static XCONST struct xVarIdStruct yVarR_z49_b = {xVariableEC xSymbTLink((xIdNode)0, (xIdNode)0), (xIdNode)&ySrtR_z4_struct1 xIdNames("b") XCOMMON_EXTRAS, &xSrtR_SDL_Integer, xOffsetOf(z4_struct1, b), (xptrint)0, (xbool)0 XVAR_EXTRAS}; #endif /*------------------DEFAULT-----------------*/ #ifndef XNOPROTO extern void yDef_z4_struct1( z4_struct1 *yVar ) #else extern void yDef_z4_struct1( yVar ) z4_struct1 *yVar; #endif { xDef_SDL_Integer(&((*yVar).a)); xDef_SDL_Integer(&((*yVar).b)); } /*------------------EQUAL-------------------*/ #ifndef XNOPROTO extern SDL_Boolean yEq_z4_struct1( z4_struct1 yExpr1, z4_struct1 yExpr2 ) #else extern SDL_Boolean yEq_z4_struct1( yExpr1, yExpr2 ) z4_struct1 yExpr1, yExpr2; #endif { if ( yNEqF_SDL_Integer(yExpr1.a, yExpr2.a) ) return SDL_False; if ( yNEqF_SDL_Integer(yExpr1.b, yExpr2.b) ) return SDL_False; return SDL_True; } /*-------------------MAKE-------------------*/ #ifndef XNOPROTO extern z4_struct1 yMake_z4_struct1( SDL_Integer ya, SDL_Integer yb ) #else extern z4_struct1 yMake_z4_struct1( ya, yb ) SDL_Integer ya; SDL_Integer yb; #endif { z4_struct1 yVar; memset((void *)(&yVar), 0, sizeof(z4_struct1)); yAssF_SDL_Integer(yVar.a, ya, XASSMAKE); yAssF_SDL_Integer(yVar.b, yb, XASSMAKE); return yVar; } /* #SDTREF(TEXT,mall.pr,7) */ COMMENT((This is the #body section))
SortIdNode
for this newtype or syntype is declared. This is followed by the IdNodes for struct components or literals (if valid).#ifndef XNOPROTO extern void yAss_TypeName ( TypeName *yVar, TypeName yExpr, int AssType ) #else extern void yAss_TypeName (yVar, yExpr, AssType) TypeName *yVar; TypeName yExpr; int AssType; #endif { code to perform assignment }
#ifndef XNOPROTO extern void yDef_TypeName( TypeName *yVar ) #else extern void yDef_TypeName( yVar ) TypeName *yVar; #endif { code to assign the default value to variable yVar }
#ifndef XNOPROTO extern SDL_Boolean yEq_TypeName( TypeName yExpr1, TypeName yExpr2 ) #else extern SDL_Boolean yEq_TypeName ( yExpr1, yExpr2 ) TypeName yExpr1, yExpr2; #endif { code to check if yExpr1 and yExpr2 are equal }
#ifndef XNOPROTO extern TypeName yMake_TypeName( ComponentTypeName yExpr) #else extern TypeName yMake_TypeName( yExpr ) ComponentTypeName yExpr; #endif { code to return an array with all components equal to yExpr }
#ifndef XNOPROTO extern TypeName yMake_TypeName( Component1TypeName yComponent1, Component2TypeName yComponent2 ) #else extern TypeName yMake_TypeName ( yComponent1,yComponent2 ) Component1TypeName yComponent1; Component2TypeName yComponent2; #endif { code to return a struct with value according to the parameters }
#ifndef XNOPROTO extern TypeName yComponent1Make_TypeName( Component1TypeName yComponent1 ) #else extern TypeName yComponent1Make_TypeName ( yComponent1 ) Component1TypeName yComponent1; #endif { code to return a choice with value according to the parameters }
#ifdef XTESTF #ifndef XNOPROTO extern xbool yTest_TypeName(TypeName yExpr) #else extern xbool yTest_TypeName( yExpr ) TypeName yExpr; { code to check if yExpr is in the range conditions for this newtype or syntype } #endif #ifdef XERANGE #ifndef XNOPROTO extern TypeName yTstA_TypeName(TypeName yExpr ) #else extern TypeName yTstA_TypeName( yExpr ) TypeName yExpr; #endif { if (! yTest_TypeName(yExpr) ) { xErrorSubrange(ySrtN_TypeName, xWriteSort(&yExpr, ySrtN_TypeName)); return yExpr; } #endif #ifdef XEINDEX #ifndef XNOPROTO extern TypeName yTstI_TypeName(TypeName yExpr ) #else extern TypeName yTstI_TypeName( yExpr ) TypeName yExpr; #endif { if (! yTest_TypeName(yExpr) ) { xErrorIndex(ySrtN_TypeName, xWriteSort(&yExpr, ySrtN_TypeName)); return lowest value of type; } return yExpr; } #endif
#ifndef XNOPROTO extern ResultTypeName OperatorName( ParameterType1 yParam1, ParameterType2 yParam2 ) #else extern ResultTypeName OperatorName (yParam1, yParam2) ParameterType1 yParam1; ParameterType2 yParam2; #endif { ResultTypeName Result; xPrintString( "Operator xx in sort xx is called\n"); xPrintOpParameter("Parameter 1: %s\n", &yParam1, ySrtN_ParameterType1); xPrintOpParameter("Parameter 2: %s\n", &yParam2, ySrtN_ParameterType2); if (! xReadOperator((void *)&Result, ySrtN_ResultTypeName)) yDef_ResultTypeName(&Result); return Result; }
The init function is used to initialize the generated program and has the following structure:
extern void yInit XPP((void)) { int Temp; YINIT_TEMP_VARS BEGIN_YINIT #ifdef XMONITOR xTranslatorVersion = "Program generated by SDT C Advanced, version 3.5"; #endif xInsertIdNode((xIdNode)&ySysR_z_mall); code to build the rest of the symbol table, to create static process instances, and to initialize the synonyms implemented as variables }
The name of the init function is yInit
in a unit for a system or system instance and yInit_
UnitName
in a non-system unit.
In the code to build the symbol table tree, nodes that represent the static structure of the SDL system are inserted into the tree, using the function xInsertIdNode
:
xInsertIdNode((xIdNode)&yBloR_B1);
This function updates the Suc
and First
pointers in the IdNodes
. In an application this operation is not necessary. In that case xInsertIdNode
is defined as an empty macro!
The yInit
function will also contain some additional code for the symbol table. For example: for each exported variable the statement below will be executed to add knowledge about the exporting processes in the IdNode
for a remote variable.
xInsertExportedVar(&yExpR_remVar, &yReVR_remVar);
The same structure is used for exported procedures, but the function is then called xInsertExportedPrd.
In the yInit function calls to yInit functions for subunits are inserted. In the yInit
for the system or system instance unit, the yInit
functions for the used packages are also inserted.
For each process or process instantiation there will be one macro call of INIT_PROCESS_TYPE
. This is in the Master Library expanded as nothing.
Depending on how many static instances that should be created of the process or process instantiation, the will be either:
SDL_STATIC_CREATE
(0 instances)SDL_STATIC_CREATE
(1 instance)SDL_STATIC_CREATE
(>1 instances)Example of code for a process P1 with one static instance.
INIT_PROCESS_TYPE(P1, yPrsN_z00_P1, "z00_P1", SDL_INTEGER_LIT(1), SDL_INTEGER_LIT(1), yVDef_z00_P1, xDefaultPrioProcess, yPAD_z00_P1) #ifdef SDL_STATIC_CREATE SDL_STATIC_CREATE(P1, z00_P1, yPrsN_z00_P1, "P1", ySigN_z00_P1, yPDef_z00_P1, yVDef_z00_P1, xDefaultPrioProcess, yPAD_z00_P1, 0) #endif
The macro SDL_STATIC_CREATE
above is expanded to:
SDL_Create( xGetSignal(ySigN_z00_P1, SDL_NULL, SDL_NULL), yPrsN_z00_P1, 0);
SynonymName1 = synonym expr 1; yAss_TypeName(&SynonymName2, synonym expr 2, XASS); #ifndef ExtSynonymName xReadSynonym(&ExtSynonymName, xSrtN_TypeName, "ExtSynonymName"); #endif
SynonymName1
and SynonymName2
are ordinary synonyms and Ext
SynonymName
is an external synonym.
This section will treat the yPAD_ProcessName
and yProcedureName
functions, that are used to implement the actions performed by processes.
There will be one function for each process and process type and each procedure, where the actions performed by the process or procedure during transitions are placed. Functions that represent processes and procedures have the structure given below, where the sections referenced in these examples are discussed in the next subsections.
The prototypes for the process and procedure functions contain some macros. These are expanded in the examples below.
/*---------- Process ProcessName ----------*/ #ifndef XNOPROTO void yPAD_ProcessName( xPrsNode P ) #else void yPAD_ProcessName( P ) xPrsNode P; #endif { local variable section section handling procedure calls switch statement translation of symbols }
/*-------- Procedure ProcedureName ---------*/ #ifndef XNOPROTO xbool ProcedureName( xPrsNode P ) #else xbool ProcedureName( P ) xPrsNode P; #endif { local variable section switch statement translation of symbols }
The following local variables will always be present:
YPAD_YSVARP YPAD_YVARP(yVDef_z00_P1) YPAD_TEMP_VARS YPRSNAME_VAR("P1")
which after macro expansion becomes:
void *ySVarP = VarP->Signal; register VDEF_TYPE * yVarP = (VDEF_TYPE *)VarP; xPrdNode yTempPrd; xSignalNode yOutputSignal;
If it is a procedure that is generated then the following is generated:
YPAD_YSVARP YPRD_YVARP(yVDef_z01_P2) yVDef_z011_Proc2 * yPrdVarP; YPRD_TEMP_VARS YPRDNAME_VAR("Proc2")
This is expanded to almost the same code as for the process:
void *ySVarP = VarP->Signal; register VDEF_TYPE * yVarP = (VDEF_TYPE *)VarP; yVDef_z011_Proc2 * yPrdVarP; xPrdNode yTempPrd; xSignalNode yOutputSignal;
If the process or procedure contains any decisions of type Integer, Boolean, Real, Character, or PId, there will be decision variables for the used types:
SDL_Integer yDcn_SDL_Integer; SDL_Boolean yDcn_SDL_Boolean; SDL_Real yDcn_SDL_Real; SDL_Character yDcn_SDL_Character; SDL_PId yDcn_SDL_PId;
If the process or procedure contains any outputs with a via list then:
xIdNode yViaList[length of longest via plus one];
If the process contains any procedure call the following code is introduced to handle the case when the execution of the process is interrupted within a procedure call (single-step in the monitor or states in the procedure):
#ifndef XNOPROCATSTARTUP while (yVarP->ActivePrd != (xPrdNode)0 ) { CALL_PROCEDURE_STARTUP #ifdef XBREAKBEFORE if (yVarP->ActivePrd != (xPrdNode)0 ) { XBETWEEN_SYMBOLS(yVarP->ActivePrd-> RestartAddress, 1361) } else { XBETWEEN_SYMBOLS(yVarP->RestartAddress, 1363) } #endif } #endif
where CALL_PROCEDURE_STARTUP
is defined as:
if ( (*yVarP->ActivePrd->RestartPRD)(VarP) ) return;
To be able to resume the execution at any symbol in the SDL graph there is a switch statement surrounding all the code for the SDL actions. If the generated code is executed together with the monitor, the switch is used to restart execution at any symbol, while if the code is just executing code, only the symbols that start a transition, like input, continuous signal, and symbols after procedure calls are of interest for restart.
If code is generated for a process, the switch statement looks as follows:
switch (yVarP->RestartAddress) {
If code is generated for a procedure, the switch statement looks as follows:
switch (yVarP->ActivePrd->RestartAddress) {
If the process that is translated contains services, there will be no switch statement. Instead the following code is generated in the PAD function:
CALL_SERVICE /*----- * Initialization (no START symbol) ------*/ BEGIN_START_TRANSITION(yPDef_z00_P1) ... initialization of process variables ... START_SERVICES
The following actions (+label) are treated:
Start, Input, Priority input, Continuous signal, Task, Output, Create, Decision, Set, Reset, Export, Call, Nextstate, Join, Stop, Return, and Label.
Between the translation of two symbols the following code is generated (not before Join and Label):
XBETWEEN_SYMBOLS(SymbolNumber, LineNo) /*----- * TYPE OF SYMBOL * #SDTREF(TEXT,mall.pr,97) ------*/ #ifdef XCASELABELS case SymbolNumber: #endif
where SymbolNumber
is an integer identifying the symbol and LineNo
is the line number of this statement in the C file. If the previous symbol is a procedure call then the
#ifdef XCASELABEL
is replaced by
#ifdef XCASEAFTERPRDLABELS
The macro XBETWEEN_SYMBOLS (or XBETWEEN_SYMBOLS_PRD
if symbol in a procedure) is in a simulator expanded to a call of the function xBreakBefore
, which determines if the monitor system should be entered between these two symbols on not.
The start symbol is translated according to the following example:
/*----- * START * #SDTREF(TEXT,mall.pr,96) ------*/ case 0: BEGIN_START_TRANSITION(yPDef_z01_P2) yAssF_SDL_Integer(yVarP->z010_I, ((yPDef_z01_P2 *)ySVarP)->Param1, XASS); XAT_FIRST_SYMBOL(0) xDef_SDL_Integer(&yVarP->z012_K);
In the start symbol, which always have the case label 0, assignment of the FPARs from the startup signal and assignments of start values to all local variables in the process are made. In a procedure only the assignment of start values for variables are performed.
An input or a priority input is translated according to the following example:
/*----- * INPUT Sig2 * #SDTREF(TEXT,mall.pr,62) ------*/ case 2: XDEBUG_LABEL(State2_Sig2) XAT_FIRST_SYMBOL(2) XOS_TRACE_INPUT("Sig2") yAssF_SDL_Integer(yVarP->z006_I, ((yPDef_z2_Sig2 *)ySVarP)->Param1, XASS);
where XDEBUG_LABEL
and XOS_TRACE_INPUT
are expanded to nothing. The macro XAT_FIRST_SYMBOL
is in a simulator expanded to a function call to setup the graphical trace. The important action performed in an input symbol is to copy the parameters of the signal to the local process variables, as specified in the input statement.
A continuous signal is translated according to the same model as used for inputs. A continuous signal, however, never has any parameters:
/*----- * CONTINUOUS SIGNAL * #SDTREF(TEXT,mall.pr,68) ------*/ case 4: XAT_FIRST_SYMBOL(4) XBETWEEN_SYMBOLS(23, 992)
The assignment statements in a task are individually (as symbols of their own) translated to assignments in C, while informal text in a task is ignored (except in trace). The following structure is generated:
/*----- * ASSIGNMENT I := ... * #SDTREF(TEXT,mall.pr,47) ------*/ #ifdef XCASELABELS case 10: #endif yAssF_SDL_Integer(yVarP->z006_I, SDL_INTEGER_LIT(10), XASS); #ifdef XTRACE xTraceAssign("I := ", &(yVarP->z006_I), xSrtN_SDL_Integer); #endif
The assignment is always generated using a yAssF_
... macro, which is either expanded to assignment in C or the call of an assignment function. For more information about variables and expressions see the section Translation of SDL Expressions.
OUTPUT statements are translated to the following basic structure:
An OUTPUT of, for example, a signal called Sig2 with one parameter will be translated to:
/*----- * OUTPUT Sig2 * #SDTREF(TEXT,mall.pr,55) ------*/ #ifdef XCASELABELS case 15: #endif ALLOC_SIGNAL_PAR(Sig2, ySigN_z2_Sig2, ToExpr, yPDef_z2_Sig2) SIGNAL_ALLOC_ERROR yAssF_SDL_Integer(((yPDef_z2_Sig2*) OUTSIGNAL_DATA_PTR) ->Param1, yVarP->z006_I, XASS); SDL_2OUTPUT(xDefaultPrioSignal, (xIdNode *)0, Sig2, ySigN_z2_Sig2, ToExpr, sizeof(yPDef_z2_Sig2), "Sig2") SIGNAL_ALLOC_ERROR_END
The macro ALLOC_SIGNAL_PAR
will be expanded to a function call of xGetSignal
. This function (in sctos.c
) will create and initialize a signal instance. The macro SDL_2OUTPUT
will be expanded to a call of the function SDL_Output
, which is the function where the signal is sent.
ToExpr
is a translation of the expression after TO in the OUTPUT statement. If no such expression is found, the value xNotDefPId
is generated at this place.
xDefaultPrioSignal
is replaced by the actual signal priority, if such a priority is assigned in the output or in the signal definition.
There are a number of versions of the ALLOC_SIGNAL
and SDL2_OUTPUT
macros.
ALLOC_SIGNAL
: Allocate signal without parametersALLOC_SIGNAL_PAR
: Allocate signal with parametersSDL2_OUTPUT
: Send signal, TO clause in outputSDL2_OUTPUT_COMPUTED_TO
: Send signal, no TO clause in output, but receiver has been calculated by code generatorSDL2_OUTPUT_NO_TO
: Send signal, no TO clause in output, and receiver has not been calculated by code generator.If a VIA clause is given in the OUTPUT, then (xIdNode *)0
is replaced by yViaList
, and the SDL_Output
statement will be preceded by a number of assignment statements where yViaList
is assigned an appropriate value.
Example of via list containing two channels:
yViaList[0] = yChaN_ChannelName1; yViaList[1] = yChaN_ChannelName2; yViaList[2] = (xIdNode)0;
CREATE statements are translated to the following basic structure:
A CREATE of, for example, a process called P2 with one parameter will be translated to:
/*----- * CREATE P2 * #SDTREF(TEXT,mall.pr,49) ------*/ #ifdef XCASELABELS case 12: #endif ALLOC_STARTUP_PAR(P2, ySigN_z01_P2,yPDef_z01_P2) STARTUP_ALLOC_ERROR yAssF_SDL_Integer(((yPDef_z01_P2 *) STARTUP_DATA_PTR)->Param1, yVarP->z006_I, XASS); SDL_CREATE(P2, z01_P2, yPrsN_z01_P2, "P2", ySigN_z01_P2, yPDef_z01_P2, yVDef_z01_P2, xDefaultPrioProcess, yPAD_z01_P2) STARTUP_ALLOC_ERROR_END
The macro ALLOC_STARTUP_PAR
will be expanded to the call of the function xGetSignal
, while SDL_CREATE
will be expanded to a call of the function SDL_Create
.
There are a number of versions of the ALLOC
macro:
ALLOC_STARTUP
: No FPAR in created process, i.e. no parameters in the startup signalALLOC_STARTUP_PAR
: Parameters in startup signalALLOC_STARTUP_THIS
: Create THIS.The value xDefaultPrioProcess
will be replaced by the process priority if a #PRIO directive is used.
We distinguish between three types of decision:
The informal decisions and the any decisions are treated last in this subsection about decisions.
if
statement. If the decision is of the Boolean sort then the following structure is generated:#ifdef XTRACE xDcn_SDL_Boolean = QuestionExpression; xTraceDecision(&(xDcn_SDL_Boolean), xSrtN_SDL_Boolean); if (xDcn_SDL_Boolean) { #else if (QuestionExpression) { #endif .... } else { .... }
#ifdef XTRACE yDcn_TypeName = QuestionExpression; xTraceDecision(&(yDcn_TypeName), ySrtN_TypeName); if (Decision condition using yDcn_TypeName) { #else if (Decision condition using QuestionExpression) { #endif .... } else { .... }
yDcn_TypeName = QuestionExpression; #ifdef XTRACE xTraceDecision(&(yDcn_TypeName),ySrtN_TypeName); #endif if (Decision condition 1 using yDcn_TypeName) { .... } else if (Decision condition 2 using yDcn_TypeName) { .... } else { .... }
if
statement is generated:#ifdef XEDECISION else { xErrorDecision(xWriteSort(&yDcn_TypeName, ySrtN_TypeName)); return; } #endifThe transitions within a decision are included directly in the
if
statement.decision any; () : task i := 1; () : task i := 2; () : task i := 3; enddecision;is generated as:
/*----- * DECISION * #SDTREF(TEXT,dec.pr,25) ------*/ #ifdef XCASELABELS case 5: #endif BEGIN_ANY_DECISION(3) DEF_ANY_PATH(1, 6) DEF_ANY_PATH(2, 7) DEF_ANY_PATH(3, 8) END_DEFS_ANY_PATH(3) BEGIN_FIRST_ANY_PATH(1) XBETWEEN_SYMBOLS(6, 375) /* translation of "task i := 1" */ END_ANY_PATH BEGIN_ANY_PATH(2) XBETWEEN_SYMBOLS(7, 390) /* translation of "task i := 2" */ END_ANY_PATH BEGIN_ANY_PATH(3) XBETWEEN_SYMBOLS(8, 405) /* translation of "task i := 3" */ END_ANY_PATH END_ANY_DECISION
XBETWEEN_SYMBOL
macros are used to represent the statements in that decision path. An informal decision according to the following example:
decision 'Question1'; ('answer1') : task i := 1; ('answer2') : task i := 2; ('answer3') : task i := 3; enddecision;
is generated as:
/*----- * DECISION * #SDTREF(TEXT,dec.pr,45) ------*/ #ifdef XCASELABELS case 19: #endif BEGIN_INFORMAL_DECISION(3, SDL_CHARSTRING_LIT( "LQuestion1", "Question1")) DEF_INFORMAL_PATH(1, SDL_CHARSTRING_LIT( "Lanswer1", "answer1"), 20) DEF_INFORMAL_PATH(2, SDL_CHARSTRING_LIT( "Lanswer2", "answer2"), 21) DEF_INFORMAL_PATH(3, SDL_CHARSTRING_LIT( "Lanswer3", "answer3"), 22) END_DEFS_INFORMAL_PATH(3) BEGIN_FIRST_INFORMAL_PATH(1) XBETWEEN_SYMBOLS(20, 578) /* translation of "task i := 1" */ END_INFORMAL_PATH BEGIN_INFORMAL_PATH(2) XBETWEEN_SYMBOLS(21, 593) /* translation of "task i := 2" */ END_INFORMAL_PATH BEGIN_INFORMAL_PATH(3) XBETWEEN_SYMBOLS(22, 608) /* translation of "task i := 3" */ END_INFORMAL_PATH END_INFORMAL_DECISION
XBETWEEN_SYMBOL
macros are used to represent the statements in that decision path.
SET on timer without parameters:
A set operation on a timer without parameters is generated according to the following example:
/*----- * SET t1 * #SDTREF(TEXT,mall.pr,63) ------*/ #ifdef XCASELABELS case 19: #endif SDL_SET_DUR(xPlus_SDL_Time(SDL_NOW, SDL_DURATION_LIT(5.0, 5, 0)), SDL_DURATION_LIT(5.0, 5, 0), t1, ySigN_z003_t1, yTim_t1, "t1")
This macro is expanded to a call of the function SDL_Set. There are three version of the SDL_SET
macro:
SDL_SET
, which is the general translationSDL_SET_DUR
, which is used if the time expression is of the form NOW + a duration expressionSDL_SET_TICKS
, which is used if the time expression is of the form NOW + TICKS(...).A SET operation on a timer with parameters is generated using the following structure which is similar to the structure used for outputs:
/*----- * SET timer3 * #SDTREF(TEXT,timer2.pr,37) ------*/ #ifdef XCASELABELS case 12: #endif ALLOC_TIMER_SIGNAL_PAR(timer3,ySigN_z004_timer3, yPDef_z004_timer3) TIMER_SIGNAL_ALLOC_ERROR yAssF_SDL_Integer(((yPDef_z004_timer3 *) TIMER_DATA_PTR)->Param1, SDL_INTEGER_LIT(1), XASS); SDL_SET_DUR_WITH_PARA(xPlus_SDL_Time(SDL_NOW, SDL_DURATION_LIT(5.0, 5, 0)), SDL_DURATION_LIT(5.0, 5, 0), timer3, ySigN_z004_timer3, yPDef_z004_timer3, yEqT_z004_timer3, yTim_timer3, "timer3") TIMER_SIGNAL_ALLOC_ERROR_END
The SDL_SET_DUR_WITH_PAR
macro is expanded to a call of the function SDL_Set
. There are three version of the SDL_SET
macro for timers with parameters:
SDL_SET_WITH_PAR
, which is the general translation.SDL_SET_DUR_WITH_PAR
, which is used if the time expression is of the form NOW + a duration expression.SDL_SET_TICKS_WITH_PAR
, which is used if the time expression is of the form NOW + TICKS(...).A RESET operation on a timer without parameters is generated using the macro SDL_RESET
, according to the following example:
/*----- * RESET t1 * #SDTREF(TEXT,mall.pr,74) ------*/ #ifdef XCASELABELS case 24: #endif SDL_RESET(t1, ySigN_z003_t1, yTim_t1, "t1")
The SDL_RESET
macro is expanded to a call of the function SDL_SimpleReset
.
A reset operation on a timer with parameters is generated exactly as a set operation with parameters, except that the macro SDL_SET_WITH_PARA
is replaced by:
SDL_RESET_WITH_PARA(yEqT_z004_timer3, yTim_timer3, "timer3")
This macro is expanded to:
SDL_Reset(&yOutputSignal);
The following assignment is generated for an export statement:
/*----- * EXPORT remVar * #SDTREF(TEXT,mall.pr,46) ------*/ #ifdef XCASELABELS case 9: #endif #ifdef XTRACE xTraceExport("remVar"); #endif yAssF_SDL_Integer(yVarP->yExp_z004_remVar, yVarP->z004_remVar, XASS);
A PROCEDURE CALL (to a procedure called Proc
) is generated as:
/*----- * CALL Proc2 * #SDTREF(TEXT,mall.pr,99) ------*/ #ifdef XCASELABELS case 4: #endif ALLOC_PROCEDURE(z011_Proc2, yPrdN_z011_Proc2, sizeof(yVDef_z011_Proc2)) PROCEDURE_ALLOC_ERROR yAssF_SDL_Integer(((yVDef_z011_Proc2 *) PROC_DATA_PTR)->z0110_P1, yVarP->z010_I,XASS); ((yVDef_z011_Proc2 *)PROC_DATA_PTR)->z0111_P2 = &(yVarP->z012_K); CALL_PROCEDURE(z011_Proc2, yPrdN_z011_Proc2, 0, 1) PROCEDURE_ALLOC_ERROR_END
The ALLOC_PROCEDURE
macro will be expanded to a call of the function xGetPrd
. After that the actual parameters are assigned to the formal parameters. Note that for an in/out parameter, it is the address of the parameter that is copied.
The CALL_PROCEDURE
macro is typically expanded to something like:
xAddPrdCall(yTempPrd, VarP, levels, restartaddr); if ( yProcedureName (VarP) ) return;
where levels
is an integer representing the number of declaration levels between the caller and the called procedure. restartaddr
is the return address, that is the symbol number of the symbol immediately following the procedure call. Both these parameters are obtained from the macro call.
There are several versions of the ALLOC_PROCEDURE
and CALL_PROCEDURE
macros.
ALLOC_PROCEDURE
: general macroALLOC_VIRT_PROCEDURE
: macro used when calling a virtual procedureALLOC_THIS_PROCEDURE
: macro used to implement call THISCALL_PROCEDURE
: general macroCALL_PROCEDURE_IN_PRD
normal procedure call in a procedureCALL_VIRT_PROCEDURE
: procedure call of a virtual procedureCALL_VIRT_PROCEDURE_IN_PRD
: procedure call of a virtual procedure in a procedureCALL_THIS_PROCEDURE
: macro used to implement call THIS.In SDL-92 it is allowed to perform value returning procedure calls, that is procedure calls performed within an expression. According to the model for this in SDL, such procedure call are calculated before the symbol where it is used.
TASK V1 := (call P(a)) + (call Q(b,1));
is translated as if it was transformed to:
CALL P(a, Temp1); CALL Q(b,1,Temp2) TASK V1 := Temp1 + Temp2;
where Temp1
and Temp2
are implicit temporary variables, and the extra parameter in the calls are used to pass the result value out from the procedure.
A remote procedure call is translated according to its transformation model in SDL. In SDL an RPC call:
CALL rpc1(1, intVar) TO pidVar;
is transformed to:
OUTPUT pCALL_rpc1(i, intVar) TO pidVar; NEXTSTATE tempState; STATE tempState; INPUT pREPLY_rpc1(intVar);
The difference between the code generated for ordinary outputs, nextstates and the transformed actions according to the example above, is that some other macros are used:
The output of the pCALL
signal is for example translated using the macro SDL_2OUTPUT_RPC_CALL, while the implicit nextstate above is translated using SDL_RPCWAIT_NEXTSTATE.
In the exporter of procedures implicit transitions are inserted:
STATE *; INPUT pCALL_rpc1(variables for in+in/out params); CALL rpc1(as in inupt above); OUTPUT pREPLY_rpc1(in/out parameters) TO SENDER; NEXTSTATE -;
This is implemented as:
pCALL
.pREPLY
signal.pCALL
to pREPLY
.pCALL
signal and in/out parameters as the address of the corresponding parameter in pREPLY
.pREPLY
signal.A NEXTSTATE is generated as a call to the xGRSetSymbol
function followed by an application of one of the macros below:
SDL_NEXTSTATE SDL_DASH_NEXTSTATE SDL_NEXTSTATE_PRD (used in procedures) SDL_DASH_NEXTSTATE_PRD (used in procedures) SDL_DASH_NEXTSTATE_SRV (used in service)
This macros are expanded to calls of the function SDL_Nextstate
followed by a return statement.
A JOIN is translated to a goto
in C.
goto L_LabelName;
If the referenced label is in a super type, the join statement is translated to a macro.
A STOP action is translated to the macro SDL_STOP
. After macro expansion the code becomes a call of the SDL_Stop
function followed by a return statement.
A RETURN is generated as an application of the macro
SDL_RETURN
. After macro expansion this becomes:
xReleasePrd(VarP); return (xbool)0;
An SDL LABEL is simply translated to a C label. The C name will be L_SDLLabelName
.
In this section some of the translation rules for expressions are described. For more information see The Cadvanced/Cbasic Code Generatorwhere, for example, the translation rules for literals and operators in the predefined abstract data types are given.
If an expression is assigned to a subrange variable (in a task, input, output,...) a test function is generated to check the consistency of the value:
yTstA_TypeName(Expression)
where yTstA_
TypeName
is either a consistency check function or a macro that replaces the macro call by only the expression itself.
If an expression is used as index expression of an array, yTstI_
TypeName
is used in exactly the same way to check the consistency of the index expression.
An SDL variable is represented in C by:
yVarP->VariableName1 yPrdVarP->VariableName2 ((yVDP_ProcedureName)VarP->ActivePrd ->StaticFather)->VariableName3 (*(yPrdVarP->VariableName4))
The examples are:
->StaticFather
determines the difference in declaration levels.A VIEW expression is translated to a macro call SDL_VIEW
. This is later expanded to a function call of SDL_View
.
(*(SDL_Integer *)SDL_VIEW(SDL_PARENT, (xbool)1, "revVar", (xViewListRec *) &(((xBlockIdNode)XNAMENODE-> Parent)->ViewList[yView_z_predefined_6_revVar]), sizeof(SDL_Integer)))
An IMPORT expression is translated to a macro call of the macro XGETEXPORTADDR
, which is expanded to a function call of the function xGetExportAddr
:
(*(SDL_Integer *)XGETEXPORTADDR( &yReVR_z7_remVar, SDL_NULL, (xbool)0))
NOW is translated to the macro SDL_NOW
which is expanded to SDL_Now().
SELF, PARENT, OFFSPRING, SENDER are translated to:
P->Self P->Parent P->Offspring P->Signal->Sender
A TIMER ACTIVE expression is translated to:
SDL_ACTIVE(TimerName, ySigN_TimerName, yTim_TimerName)
which is expanded to:
SDL_Active(ySigN_TimerName, VarP)
A conditional expression in SDL is translated to a conditional expression in C.
This section deals with the allocation and deallocation of dynamic memory in SDT.
Note: This information is only valid when the Master Library is used. The OS integrations might have different strategies for memory allocation. |
Information is provided about the following topics:
Dynamic memory is used for a number of objects in a run-time model for applications generated by the Cadvanced/Cbasic Code Generator. These objects are:
To help to estimate the need for memory for an application we will give information about the size of these objects and about how many of the objects are created. The size information given is true for generated applications, that is, ones that do not, for example, contain the monitor. The type definitions given are stripped of components that will not be part of an application. The full definitions may be found in the file scttypes.h
.
Each process instance is represented by two structs that will be allocated on the heap. In scttypes.h
the type xLocalPIdRec
is defined and in generated code yVDef_ProcessName
structs are defined:
typedef struct { xPrsNode PrsP; } xLocalPIdRec; typedef struct { xPrsNode Pre; xPrsNode Suc; int RestartAddress; xPrdNode ActivePrd; YPAD_RESULT_TYPE (*RestartPAD) (); #ifndef XNOUSEOFSERVICE xSrvNode ActiveSrv; xSrvNode SrvList; #endif xPrsNode NextPrs; SDL_PId Self; xPrsIdNode NameNode; int State; xSignalNode Signal; xInputPortRec InputPort; SDL_PId Parent; SDL_PId Offspring; int BlockInstNumber; xSignalIdNode pREPLY_Waited_For; xSignalNode pREPLY_Signal; /* variables and formal parameters in the process */ } yVDef_ProcessName;
To calculate the size of the structs above it is necessary to know more about the components in the structs. The types xPrsNode
, xPrdNode,xSignalNode
, xPrsIdNode, xStateIdNode, and xSignalIdNode are all pointers, while SDL_PId
is a struct containing an int and a pointer. The xInputPortRec is a struct with two pointers and one int.
This means that it is possible to calculate the size of the xLocalPIdRec
and the xPrsRec
struct using the following formulas, if the compiler does not use any strange alignment rules:
The size of xPrsRec can be reduced by 2 x sizeof (address) if the code is compiled with the XNOUSEOFSERVICE flag. Then, of course, the SDL concept service cannot be used. The size of yVDef_ProcessName
is the size of the xPrsRec
plus the size of the variables and parameters in the process. Any overhead introduced by the C system should also be added. The size of the formal parameter and variables is of course dependent on the declarations in the process. The translation rules for SDL types, both predefined and user defined, can be found in The Cadvanced/Cbasic Code Generator.
For each process instance set in the system the following number of structs of a different kind will be allocated:
xLocalPIdRec
for each process instance created. These structs will not be reused, as they serve as identification of process instances that have existed (see also optimizations below).yVDef_ProcessName
structs as the maximum concurrently executing process instances of the process instance set (maximum during the complete execution of the program).The yVDef_ProcessName
structs are reused by having an avail list where this struct is placed when the process instance it represents perform a stop action. There is one avail list for each process type. When a process instance should be created, the runtime library first looks at the avail list and reuses an item from the list. Only if the avail list is empty new memory is allocated.
If the compilation switch XPRSOPT
is defined then:
xLocalPIdRec
s are reused together with the xPrsRec
s.xLocalPIdRec
s contain an additional int component.Services are handled very similar to processes. The following struct type are allocated for each service instance.
typedef struct xSrvStruct { xSrvNode NextSrv; xPrsNode ContainerPrs; int RestartAddress; xPrdNode ActivePrd; void (*RestartPAD) XPP((xPrsNode VarP)); xSrvIdNode NameNode; int State; XSIGTYPE pREPLY_Waited_For; xSignalNode pREPLY_Signal; } xSrvRec;
This means that:
The size of yVDef_ServiceName
is the size of the xSrvRec plus the size of the variables in the service. yVDef_ServiceName
struct are reused in the same way as for processes (see previous section).
Signals are handled in much the same way as processes. A signal instance is represented by one struct (in generated code generated).
typedef struct { xSignalNode Pre; xSignalNode Suc; int Prio; SDL_PId Receiver; SDL_PId Sender; xIdNode NameNode; /* Signal parameters */ } yPDef_SignalName;
This struct type contains one component for each signal parameter. The component types will be the translated version of the SDL types of the parameters.
This means that it is possible can calculate the size of a xSignalRec
, which is the same as a struct for a signal without parameters, using the following formula:
The size of a yPDef_SignalName
struct is thus equal to the size of the xSignalRec
plus the size of the parameters. The translation rules for SDL types, both the predefined and user defined, can be found in The Cadvanced/Cbasic Code Generator.
For each signal type in the system the following number of data areas will be allocated:
yPDef_SignalName
struct as the maximum number of signals (during the complete execution of the program) of the signal type that are sent but not yet received in an input operation.The yPDef_SignalName
struct is reused by having an avail list, where the struct is placed when the signal instance they represent is received. The exact point where the signal instance is returned to the avail list is when the transition caused by the signal instance is ended by a nextstate or stop action. There is one avail list for each signal type. When a signal instance should be created, for example during an output operation, the runtime library first looks at the avail list and reuses an item from this list. Only if the avail list is empty new memory is allocated.
Note: There is one common avail list for all signals without parameters. |
The memory needed for timers can be calculated in the same way as for signals with one exception, each timer contains an extra SDL_Time
component, i.e. two extra 32-bit integers.
Procedures and processes have much in common in terms of memory allocation. A procedure is, during the time it exists from call to return, represented by a struct; the yVDef_ProcedureName
.
typedef struct { xPrdIdNode NameNode; xPrdNode StaticFather; xPrdNode DynamicFather; int RestartAddress; YPRD_RESULT_TYPE (*RestartPRD) (); xSignalNode pREPLY_Signal; int State; /* Formal parameters and variables */ } yVDef_ProcedureName;
The struct type contains one component for each formal parameter or variable. The component types will be the translated version of the SDL types of the parameters, except for an IN/OUT parameter which is represented as an address.
The size of the xPrdRec
struct (which is the same as a procedure without variables and formal parameters) can be calculated using the following formula:
The size of a yVDef_ProcedureName
struct is the size of the xPrdRec
plus the size of the formal parameter and variables defined in the procedure. The translation rules for SDL types, both the predefined and user defined can be found in The Cadvanced/Cbasic Code Generator.
For each type of procedure in the system the following number of data areas will be allocated:
yVDef_ProcedureName
structs as the maximum number of concurrent calls (during the complete execution of the program) of the procedure. Concurrent calls occur both when a procedure calls itself recursively within one process instance, and when several process instances of the same process type calls the same procedure during overlapping times.The yVDef_ProcedureName
struct is reused by having an avail list, where this two struct is placed when the procedure instance executes a return action. There is one avail list for each procedure type. When a procedure instance should be created, that is, at a call operation, the runtime library first looks at the avail list and reuses an item in the list. Only if the avail list is empty new memory is allocated.
The predefined SDL type charstring is implemented as char *
in C and thus requires dynamic memory allocation. The predefined data types Bit_String and Octet_String are also implemented using dynamic memory.
The implementation of the SDL sorts Charstring, Bit_String, and Octet_String is both flexible in length and all memory can be reused.
The mechanism used to release unused memory is to call the xFree
function in the file sctos.c
, which uses the standard function free to release the memory. For more details please see the section Changing the Standard Memory Allocation Procedure.
Charstrings, Bit_Strings, and Octet_Strings are also handled correctly if they are part of structs or arrays. When, for example, a new value is given to a struct having a charstring component, the old charstring value will be released. For all structured types containing any of these types there will also be a Free
function that is utilized to release all dynamic memory in the structured variable.
The allocation and deallocation of memory is handled by the functions xAlloc
and xFree
in the file sctos.c
. The functions in this file are used for the adoption of the generated applications to the operating system or hardware. The sctos.c
file is described in detail in The sctos.c File.
In generated code and in the run-time library the functions xAlloc
and xFree
are used in each situation where memory is needed or can be released. xAlloc
receives as parameter a requested size in bytes and returns the address to a data area of the requested size. All bytes in the data area are set to zero. xFree
takes the address of a pointer and returns the data area referenced by the pointer to the pool of free memory. It also sets the pointer to 0.
The xAlloc
and xFree
functions are usually implemented using some version of the C standard functions for allocation (malloc
, calloc
) and deallocation (free
). Other implementations are of course possible as long as the interface described in the previous section is fulfilled. In a micro controller, for example, it is probably necessary to handle allocation and deallocation directly towards the physical memory.
To prevent memory fragmentation we have used our own avail lists in almost all circumstances. Memory fragmentation is phenomena occurring when a program allocates and de-allocates data areas (of different sizes) in some "random" order. Then small pieces of memory here and there are lost, since their sizes are to small to fit an allocation request. This can lead to a slowly increasing demand for memory for the application.
Note that deallocation of memory is only used for data types, for Charstring and data types where it is decided to implement the data type with dynamic memory. This means that if Charstring variables are not used and the user has not introduced the need for deallocation of memory himself, no memory deallocation will occur. In this case it is of course unnecessary to implement the xFree
function.
It is easy to trace the need for dynamic memory. As all memory allocation is carried out through the xAlloc
function and this function is available in source code (in sctos.c
), it is only necessary to introduce whatever count statements or printout statements that are appropriate.
In this section we describe how it is possible to re-implement the memory handling functions to avoid the standard avail lists and how to customize memory reuse.
The basic tools are the sctos.c
functions:
xGetPId xReleasePId xGetSignal xReleaseSignal xGetPrd xReleasePrd xAlloc_SDL_Charstring xFree_SDL_Charstring xAlloc_SDL_Bit_String xFree_SDL_Bit_String
and the compilation switch XFREEVARS
. The functions for Bit_String are also used for Octet_String.
The xGet
functions mentioned above will be called each time a process, signal, or procedure is to be created. The functions first look in their avail lists and only if this is empty a new data area is created. The Get
functions then initializes the data area. By changing the implementation of these functions it is possible to allocate memory in any suitable way. The xAlloc_SDL_Charstring
is called each time a new data area for a charstring is needed. The corresponding is true for Bit_String and Octet_String.
The xRelease
and xFree functions listed will be called when a process, signal, procedure, Charstring, Bit_String, or Octet_String data area is to be returned to the pool of free memory. In the standard implementation the memory is returned to the avail list for the object (except for charstring). By changing these functions, memory may be returned to the pool of available memory in any way. (By not entering the memory in the avail list, the xAlloc
function above will always be called when memory is needed.)
The remaining problem now is the dynamic data values among the variables and parameters in the processes, signals and procedures.
Free
function for the signal. A reference to the Free
function is stored in the xSignalIdNode
for the signal. Free functions are of course only generated if the signal contains any dynamic memory. The call of the Free
function is best placed in the sctos.c
function xFreeSignal
.XFREEVARS
. If this switch is defined, code to release all charstring values will be executed before a stop and before a return. The memory release is implemented as calls to Free functions generated for each SDL sort containing a Charstring, Bit_String, or Octet_String value.Everything that is said above about Charstring, Bit_String, or Octet_String values is also true for user defined data types implemented using dynamic memory. If a Free
function is introduced in the #ADT directive, the type is treated in the same way as charstring in relation to memory handling.
The compilation switches are used to decide the properties of the Master Library and the generated C code. Both in the library and in generated code #ifdefs
are used to include or exclude parts of the code.
The switches that are used can be grouped into five groups.
The first two groups will be discussed in Adaptation to Compilers.
The following switches define the library version:
The definition of the properties of these libraries can be found in scttypes.h and will be discussed below. Each library version is specified by the switches in the group property switches that it defines.
New library versions, containing other combinations of property switches, can easily be defined by introducing new library definitions in the scttypes.h file.
The property switches discussed below can be used to form library versions. If not stated otherwise for a certain property, all code, variables, struct components, and so on, are either included or excluded using conditional compiling (#ifdef
), depending on whether the property is used or not.
This means, for example, that all code for the monitor interface will be removed in an application not using the monitor, which makes the application both smaller and faster.
If this compilation switch is not defined then simulated time is used, otherwise the system time is connected to a real clock, via the sctos.c function SDL_Clock
.
This is the same as XCLOCK
(it will actually define XCLOCK
), except that if XCLOCK
is used, time will be zero at system start up, while if XCALENDERCLOCK
is used, time will be whatever the clock returns at system start up.
Define this compilation switch if the application should be able to communicate with signals via the SDT communication mechanism. This facility is used to accomplish communicating simulations and simulations communicating with, for example, user interfaces.
This switch should be defined if a generated simulator should be able to communicate with an ITEX simulator.
If this compilation switch is defined the environment functions xInitEnv
, xCloseEnv
, xInEnv
, and xOutEnv
will be called at appropriate places.
This is the same as XENV
(it will actually define XENV
), except that xInEnv should return a time value which is the next time it should be called (a value of type SDL_Time
). The main loop will call xInEnv
at the first possible occasion after the specified time has expired, or when the SDL system becomes idle.
This switch make signals using a compatible data structure as in SDT 2.3. This means that an extra and unnecessary component yVarP
is inserted in each signal.
This facility makes it possible for a user to implement his own log of the major events in the system. This compilation switch is normally not defined. By defining this switch, each output of a signal, i.e. each call of the function SDL_Output
, will result in a call of the function xSignalLog
. Each time a transition is started, the function xProcessLog
will be called.
These functions have the following prototypes:
extern void xSignalLog (xSignalNode Signal, int NrOfReceivers, xIdNode * Path, int PathLength); extern void xProcessLog (xPrsNode P);
which are included in scttypes.h if XSIGLOG
is defined.
Signal
will be a pointer to the data area representing the signal instance.
NrOfReceivers
will indicate the success of the output according to the following table:
The third parameter, Path
, is an array of pointer to IdNodes
, where Path[0]
refers to the IdNode
for the sending process, Path[1]
refers to the first signal route (or channel) in the path between the sender and the receiver, and so on, until Path[PathLength]
which refers to the IdNode
for the receiving process.
The parameter P
in the xProcessLog
function will refer to the process just about to start executing.
The fourth parameter, PathLength
, represents thus the number of components in the Path
array that are used to represent the path for the signal sent in the output. If the signal is sent to or from the environment, either Path[0]
or Path[PathLength]
will refer to xEnvId
, that is to the IdNode
for the environment process.
In the implementation of the xSignalLog
and xProcessLog
functions which should be provided by the user, the user has full freedom to use the information provided by the parameters in any suitable way, except that it is not possible to change the contents of the signal instance. The functions are provided to make it possible for a user to implement a simple log facility in environments where standard IO is not provided, or where the monitor system is too slow or too large to fit. A suitable implementation can be found in the file sctenv.c
If this compilation switch is defined, traces of the execution can be printed.
This facility is normally used together with the monitor, but can also be used without the monitor. The file stdout
must of course be available for printing.
Setting trace values must, without the monitor, be performed in included C code, as the monitor interface is excluded. The trace components are called Trace_Default
and can be found in IdNodes
representing system, blocks, and processes, and in the struct xPrsRec
used to represent a process instance. The values stored in these components are the values given in the Set-Trace command in the monitor. The value undefined is represented by -1.
When the monitor is excluded all trace values will be undefined at startup, except for the system which has trace value 0. This means that no trace is active at start up.
Suitable statements to set trace values in C code:
xSystemId->Trace_Default = value; /* System trace */ xPrsN_ProcessName->Trace_Default = value; /* Process type trace */ PId_Var.LocalPId->PrsP->NameNode->Trace_Default = value /* Process type trace */ PId_Var.LocalPId->PrsP->Trace_Default = value; /* Process instance trace */
PId_Var
is assumed to be a variable of type PId
.
If this compilation switch is defined it is possible for a simulation to communicate with the Organizer and the SDL Editor to highlight SDL symbols in the graphical representation.
This feature is used together with the monitor to implement graphical trace and commands like Show-Next-Symbol and Show-Previous-Symbol. It is possible to use graphical trace without the monitor in the same way as the ordinary trace (substitute Trace_Default
with GRTrace
in the description above). However the graphical trace is synchronized which means that the speed of the application is dramatically reduced.
Defining this compilation switch makes information available to the monitor about where in the source C code the execution is currently suspended. This facility, which is used together with the monitor, makes it possible to implement the monitor command Show-C-Line-Number.
If this compilation switch is defined, the monitor system is included in the generated application.
This compilation switch makes it possible to generate coverage tables. It should be used together with XMONITOR
.
These macros control the length of the char *
buffers used to read and write values of SDL sorts. A typical usage is when the monitor commands Assign-Value and Examine-Variable are entered. If large data types are used, it is possible to redefine the sizes of the buffers from their default size (5000 bytes) to something more appropriate.
This compilation switch should be defined if the generated simulator is to be executed from the Graphical User Interface to the simulator monitor.
This compilation switch should be defined if the generated simulator should be able to generate Message Sequence Charts.
This compilation switch should be defined if it should be possible to start and communicate with a user interface (or another application) from the simulation. This feature should be used together with the monitor and will define the switch XPMCOMM
(see also this switch).
When this compilation switch is defined the functions main and xMainLoop
are removed using conditional compiling. This feature is intended to be used when a generated SDL application should be part of an already existing application, that is when the SDL system implements a new function in an existing environment. The following functions are available for the user to implement scheduling of SDL actions:
extern void xMainInit( void (*Init_System) (void) #ifdef XCONNECTPM ,int argc, char *argv[] #endif ); #ifdef XNOMAIN extern void SDL_Execute (void); extern int SDL_Transition_Prio (void); extern void SDL_OutputTimer (void); extern int SDL_Timer_Prio (void); extern SDL_Time SDL_Timer_Time (void); #endif
The behavior of these functions are as follows:
xMainInit
: This function should be called to initialize the SDL system before any other function in the runtime library is called. An appropriate way to call xMainInit
is:
#ifdef XCONNECTPM xMainInit(yInit, argc, argv); #else xMainInit(yInit); #endif
The compilation switch XCONNECTPM
will be defined if the any switch that requires communication via SDT communication mechanism is defined (XPMCOMM
or XGRTRACE
).
SDL_Execute
: This function will execute one transition by the process instance first in the ready queue.
Before calling this function it must be checked that there really is at least one process instance in the ready queue. This test can be performed using the function SDL_Transition_Prio
discussed below.
SDL_Transition_Prio
: This function returns the priority of the process first in the ready queue (if signal priorities are used it is the priority of the signal that has caused the transition by the actual process instance). If the ready queue is empty, -1 is returned.
SDL_OutputTimer
: This function will execute one timer output and may only be called if there is a timer ready to perform a timer output. This test can be performed with either SDL_Timer_Prio
or SDL_Timer_Time
described below.
SDL_Timer_Prio
: This function returns the priority of the timer first in the timer queue if the timer time has expired for this timer. That is, if Nowis greater than or equal to the time given in the Set
statement for the timer.
If the timer queue is empty or the timer time for the first timer has not expired, -1 will be returned.
If signal priorities are used, the priority returned is the priority assigned to the timer type (in the timer definition) or the default timer priority; while if process priorities are used the priority returned is the priority of the process that has set the timer.
SDL_Timer_Time
: This function returns the time given in the set statement for the first timer in the timer queue. If the timer queue is empty, the largest possible time value (xSysD.xMaxTime
) is returned.
Depending on how the SDL system is integrated in an existing environment it might be possible to also use the monitor system. In that case the function xCheckMonitors
should be called to execute monitor commands.
extern void xCheckMonitors (void);
To give some idea of how to use the functions discussed above, an example reflecting the way the internal scheduler in the runtime library works is given below:
while (1) { #ifdef XMONITOR xCheckMonitors(); #endif if ( SDL_Timer_Prio() >= 0 ) SDL_OutputTimer(); else if ( SDL_Transition_Prio() >= 0 ) SDL_Execute(); }
Sometimes when integrating generated application or simulations in larger environments the main function can be useful but cannot have the name main. This name can be changed to something else by defining the macro XMAIN_NAME
. The main function came be found in the file sctsdl.c
.
The XSIGPRIO
compilation switch defines that priorities on signals (set in Output statements) should be used. This switch and the three other switches for priorities given below are, of course, mutually exclusive.
A signal priority is specified with a priority directive (see Assigning Priorities -- Directive #PRIO, that is by a comment with the following outline:
/*#PRIO 5 */.
A priority can be assigned to a signal instance in an output statement by putting a #PRIO directive last in the output symbol. In SDL-PR it is possible to put the #PRIO directive both immediately before and immediately after the semicolon ending the output statement. The Cadvanced/Cbasic Code Generator will first look for #PRIO directives in the output statement. If no directive is found there it will look in the signal definition for the signal for a priority directive. A #PRIO directive should be placed directly before the comma or semicolon ending the definition of the signal.
SIGNAL S1 /*#PRIO 3 */, S2 (Integer) /*#PRIO 5 */;
If no priority directive is found in the output symbol or in the definition of the signal, the default value for signal priority is used. This value is 100. Timers can be assigned priorities in timer definitions in the same way as signals in signal definitions.
The signal priorities will be used to sort the input port of process instances in priority order, so that the signal with highest priority (lowest priority value) is at the first position. Two signals with same priority are placed in the order they arrive. The priority of the signal that can cause the next transition by a process instance is used to sort the ready queue in priority order, so that the process with a signal of highest priority is first. With equal priority, the processes are placed in the order they are inserted into the ready queue. If a continuous signal caused a processes to be inserted into the ready queue, it is the priority of the continuous signal that will be used as signal priority for this "signal".
This compilation switch defines that process priorities should be used. For more information see The Cadvanced/Cbasic Code Generator, section Assigning Priorities - Directive #PRIO.
This compilation switch defines that priorities on signals should be used as first key for sorting in priority order, and process priorities should be used as second key.
This compilation switch defines that process priorities should be used as first key for sorting in priority order, and priorities on signals should be used as second key.
It is possible to redefine the default priorities for processes, signals, timer signals, continuous signals and start-up signals by defining the symbols below to appropriate values. The default value for these defaults are 100.
xDefaultPrioProcess xDefaultPrioSignal xDefaultPrioTimerSignal xDefaultPrioContSignal xDefaultPrioCreate
This compilation switch will turn on full optimization (except XOPTCHAN
), that is, it will define the following switches:
|
|
|
|
|
|
For more information, see these switches below. The XOPT
switches should not be used together with the monitor.
In the symbol table tree (see section Symbol Table Tree Structure) there will be one node for each parameter to a signal. These nodes are not necessary in an application and can be removed by defining the compilation switch XOPTSIGPARA
.
There will be a VarIdNode
in the symbol table tree for each variable declared in processes, procedures, or operator diagram. These nodes are not used in an application (without the monitor) and can be removed by defining the compilation switch XOPTDCL
.
There will be a VarIdNode
in the symbol table tree for each formal parameter in a processes, procedures, or operator diagram. These node are not used in an application and may be removed by defining the compilation switch XOPTFPAR
.
For each component in an SDL struct there will be one VarIdNode
defining the properties of this component. These VarIdNodes
are not used in an application and can be removed by defining the compilation switch XOPTSTRUCT
.
For each literal in a newtype that will be translated to an enum type, there will be an LitIdNode
representing the literal. These nodes will not be used in an application and can be removed by defining the compilation switch XOPTLIT
.
Each newtype and syntype, including the SDL standard types, will be represented by an SortIdNode
. These nodes are not used in an application if all the other XOPT...
mentioned above are defined.
Defining this compilation switch will remove all occurrences of C float
and double
types, and means for example that the SDL type Real is no longer available.
This switch is intended to be used in situations when it is important to save space, to see to that the library functions for floating type operations are not necessary to load. This switch cannot handle situations when the user includes floating type operations in C code in, for example, #CODE directives.
Defining this switch will remove all code for the SDL predefined sort Object_Identifier.
Defining this switch will remove all code for the SDL predefined sorts Bit_String, Octet, and Octet_String.
By defining this switch the user states that he is not going to use the export - import concept in SDL.
An attempt to perform an import operation when |
This compilation switch can be defined to save space, both in data and in the size of the kernel, if the SDL concept service is not used. If services are used and this switch is defined, there will be compilation errors (probably many!), when the generated code is compiled.
Section Create and Stop Operations describes how xLocalPIdRec
structs are allocated for each created process instance, and how these structs are used to represent process instances even after they have performed stop actions. This method for handling xLocalPIdRecs
is required to be able to detect when a signal is sent to a process instance that has performed a stop operation.
In an application that is going to run for a "long" period of time and that uses dynamic processes instances, this way of handling xLocalPIdRecs
will eventually lead to no memory being available.
By defining the compilation switch XPRSOPT
, the memory for the xLocalPIdRecs
will be reused together the yVDef_ProcessName
structs. This has two consequences:
More precisely, if we have a PId variable that refers to a process instance which performs a stop operation and after that a create operation (on the same process instance set) is performed where the same data area is reused, then the PId variable will now refer to the new process instance.
This means, for example, that signals intended for the old instance will be sent to the new instance. Note that it is still possible to detect signal sending to processes in the avail list even if XPRSOPT
is defined.
This switch can be used to remove all information about the paths of channels and signal routes in the system. The following memory optimization will take place:
ChannelIdNodes
for each channel, signal route, and gate are removed.ToId
component in the xPrsIdNodes
representing processes is removed.sctsdl.c
) are no longer needed and are removed.When the information about channels, signal routes, and gates is not present two types of calculations can no longer be performed:
In an ordinary SDL system OUTPUTs without TO must be used to start up the communication between different parts of the system, as there is no other way in SDL to distribute the PId values needed for OUTPUTs with TO.
This problem is solved if the Cadvanced/Cbasic Code Generator can calculate the receiver. Otherwise the data type PIdList
in the library of abstract data types is intended to solve this problem. It is described in The ADT Library. When this data type is used, global PId literals my be introduced, implemented as SDL synonyms. These literals can then be used to utilize OUTPUT statements with TO clauses from the very beginning.
The SDL sort Integer is translated to int in C. To translate the Integer sort to long int instead, just define the compilation switch X_LONG_INT
.
If this switch is defined, only a limited number of signals will be stored in the input port of the Env function. The limit is equal to the value defined for XENVSIGNALLIMIT
and is normally set to 20.
This switch will define all error handling switches (XE...) and XASSERT
given below.
This switch will report if the initial number of instances of a process type is greater than the maximum number.
This switch will report error situations in ADT operator.
This switch will report if no path out from a Decision is found.
This switch will report errors during Import actions.
This switch will report overflow when an SDL Real value is converted to an SDL Integer value using the operator Fix.
This switch will report value out of range for array index.
This switch will report division by zero in an integer division.
This switch will report errors during Output operations.
This switch will report range errors when a value is assigned to a variable of a sort containing range conditions.
This switch will report division by zero in a real division.
This switch will report errors in View operations
This switch will turn on error reports when accessing non-active choice components.
This switch will turn on error reports when accessing non-present optional struct components.
This switch will turn on error reports when accessing non-active union components.
These switches turn on error checking on pointers (generator Ref and Own).
By defining this switch the possibility to define user assertions which is described in Assertions.
This switch is used when the default memory handling algorithm is to be changed. By defining XFRREVARS
each Stop and Return statement in processes and procedures is preceded by statements to deallocate dynamic memory stored in variables and parameters. See also Changing the Standard Memory Allocation Procedure.
The variables defined at process level in a process containing services are not de-allocated when the last service stops. |
When using partitioning of a system a problem during the redirection of channels is that the number of channels going to the environment is not known at code generation time, which means that the size of the data area used for the connections is not known. This problem is solved in two ways.
Either the function handling redirections allocates more memory, which is the default, or the user specifies how many channels that will be redirected (which could be difficult to compute, but will lead to less need of memory).
In the first case (allocation of more memory) the macros:
#define XTRACHANNELSTOENV 0 #define XTRACHANNELLIST
should be defined like above. This is the standard in scttypes.h. If the user wants to specify the number of channels himself then
#define XTRACHANNELSTOENV 10 #define XTRACHANNELLIST ,0,0,0,0,0,0,0,0,0,0
i.e. XTRACHANNELSTOENV
should be the number of channels, while XTRACHANNELLIST
should be a list of that many zeros.
It is for debugging purposes sometimes of interest to introduce extra labels. The macro XDEBUG_LABEL
is inserted in the code for each input symbol. As macro parameter it has a name which is the name of the state concatenated with an underscore concatenated with the signal name.
state State1; input Sig1; state State2; input *; state *; input Sig2;
In the generated code for these input statements the following macros will be found:
XDEBUG_LABEL(State1_Sig1) XDEBUG_LABEL(State2_ASTERISK) XDEBUG_LABEL(ASTERISK_Sig2)
A suitable macro definition to introduce label would be:
#define XDEBUG_LABEL(L) L: ;
To use these label the usage of SDL must be restricted in one area. The same state may not receive two different signals with the same name! This is allowed and handled by SDT. The signal have to be defined at different block or system level and the outermost signal must be referenced with a qualifier.
Using these compilation switches most of the memory used for the IdStructs
can be moved from RAM to ROM. This depends of course on the compiler and what properties it has.
The following macro definitions can be inserted:
#define XCONST const #define XCONST_COMP const
This will introduce const in the declaration of most of the IdStructs
. It is then up to the compiler to handle const.
The XCONST_COMP macro is used to introduce const on components within a struct definition. This is necessary for some compilers to accept const on the struct as such.
If const
is successfully introduced, there is a lot of RAM memory that will be saved, as probably 90% of the data area for IdStructs
can be made const
.
The property switches are in principle independent, except for the relations given in the descriptions above, and it should always be possible to any combination.
The number of combinations is, however, so huge that it is impossible for us to even compile all combinations. If you happen to form a combination that does not work, please let us know, so that we either can correct the code, or, if that is not possible, publish a warning against that combination.
The switches defining a standard library version will define the following property switches:
The lowest layer of switches (that handle the implementation details) are set up using the three layers above. These switches will not be discussed here. Please refer to the source code files scttypes.h and sctsdl.c for more details.
This section describes how to generate a new library. The following topics are covered:
sdtsct.knl
file, which determines what libraries the Analyzer knows about, that is, what libraries that will be shown when Generate-Options is selected.comp.opt
file and the makeoptions
(make.opt
in Windows) file, which determines the properties of an object code library.Makefile
, which includes the makeoptions (make.opt)
file and generates a new object code library with the properties given by the included makeoptions (make.opt)
file.The structure of files and directories used for the Cadvanced/Cbasic Code Generator libraries is shown in Figure 499 The directory sdtdir
is in the installation:
<SDT installation directory>/sdt/sdtdir/<machine dependent dir>
where <machine dependent dir> is for example sunos5sdtdir
on SunOS 5, hppasdtdir
on HP, and wini386
in Windows. (In Windows, /
should be replaced by \
in the path above.)
This directory is here called sdtdir and is in UNIX normally referred to by the environment variable sdtdir
.
Figure 499 : Directory structure
|
"sctdir" is a reference to an object library and is usually setup as a parameter in the call to make. It can also be an environment variable.
In the sdtdir
directory three important files are found:
predef.sdl
contains the definition of the predefined sorts in SDL.
sdtsct.knl
contains a list of the available libraries that can be used together with code generated by the Cadvanced/Cbasic Code Generator.
help_sct.hlp
contains the help information that can be obtained using the monitor command help. The file predef.sdl
is read by the SDT Analyzer during analysis, while the file sdtsct.knl
is used to present the available libraries in the Make dialog in the Organizer (see Make).
In the INCLUDE
directory, there are two important groups of files:
The source code files for the runtime library: scttypes.h, sctlocal.h, sctsdl.c, sctutil.c, sctpost.c, sctpred.h, sctpred.c, sctmon.c, sctos.c
and sctenv.c
itex.h
.
In parallel with the INCLUDE
directory there are a number of directories for libraries in object form. The SCTDEBCOM
directory in Figure 499 is an example of such a directory. Each of these directories will contain three files: comp.opt
, makeoptions
(make.opt
in Windows) and sctworld.o (sctworld.lib
in Windows).
The comp.opt
file determines the contents of the generated makefile and how make is called. For more details see below.
The makeoptions (make.opt)
file describes the properties of the library, such as the compiler used, compiler options, linker options, and so on, while sctworld.o (sctworld.lib)
is a pre-linked library file.
To guarantee the consistency of, for example, compilation flags between the SDL system and the kernel, the makeoptions (make.opt)
file is used both by the make file compiling the library (Makefile
) and by the generated make files used to compile the generated SDL system. Non-consistency in this sense between the library and the SDL system will make the result unpredictable.
The sdtsct.knl
file describes for SDT which libraries that are available. This is presented by the Organizer in the Make dialog see Make). The sdtsct.knl
file has the following structure. Each available library is described on a line of its own. Such a line should first contain the name of the library (the name presented in the dialog), then the path to the directory containing the library, and last a comment up to end of line.
The path to the library can either be the complete path or a relative path. A relative path is relative to the environment variable sdtdir
(on UNIX) or SDTDIR
(in Windows), if that variable is defined. Otherwise it is relative to the SDT installation.
Simulation SCTDEBCOM RealTimeSimulation SCTDEBCLCOM Application SCTAPPLCLENV ApplicationDebug /util/sct/SCTDEBCLENVCOM ApplicationDebug x:\sdt\sdtdir\dbclecom MyTestLibrary ..\testlib\dbcom
Not that the two last lines is examples for Windows, while the fourth line is for UNIX.
The Organizer will look for an sdtsct.knl
file first in the directory SDT is started from, then in the home directory for the user, and then in the directory referenced by the environment variable sdtdir
(SDTDIR
) if it is defined, and last in the SDT installation.
In each directory that contains a library version there is a Makefile
that can "make" the library. To create a new library after an update of the source code, change directory to the directory for the library and execute the Makefile
. The Makefile
uses the makeoptions (make.opt)
file in the directory to get the correct compilation switches and other relevant information.
Do not generate and test libraries in the installation directory structure. Create an appropriate copy. |
This file determines the details of the generated make files, and the command issued to execute the makefile. A comp.opt file contains zero, one or more initial line starting with a #. These lines are treated as comments. After that it contains four lines of essential data.
On each of these lines % codes can be used to insert specific information.
On all four lines:
%n : newline %t : tab %d : target directory %s : source directory %k : kernel directory %f : base name of generated executable (no path, no file extension). NOT on line 2.
On line 2, the compile script:
%c : c file in compile script %C : c file in compile script, without extension %o : resulting object file in compile script
On line 3, the link script:
%o : list of all object files in link script %O : list of all object files in link script, with \ followed by newline between files %e : executable file in link script
On line 4, the make command:
%m : name of generated makefile
# makefile for unix make include $(sctdir)/makeoptions %t$(sctCC) $(sctCPPFLAGS) $(sctCCFLAGS) $(sctIFDEF) %c -o %o %t$(sctLD) $(sctLDFLAGS) %o -o %e make -f %m sctdir=%k
This file has the following structure:
Example on UNIX:
# makeoption to build SDT R-T library 'Application'. sctLIBNAME = Application sctIFDEF = -DSCTAPPLCLENV -DSUN4_1_1CC sctEXTENSION = _apa.sct sctOEXTENSION = _apa.o sctuseinclude = $(sctdir)/../INCLUDE sctlinkdir = $(sctdir) sctLINKKERNEL = $(sctlinkdir)/sctworld.o sctCC = cc sctCPPFLAGS = -I$(sctuseinclude) sctCCFLAGS = -c sctLD = cc sctLDFLAGS = sctLIBFLAGS = -x
Example in Windows:
# makeoption to build SDT R-T library `Application'. sctLIBNAME = Application sctIFDEF = -DSCTAPPLCLENV -DIC86 sctuseinclude = $(SCTDIR)\..\include sctlinkdir = $(SCTDIR) sctLINKMODULES = $(sctlinkdir)\sctsdl.obj \ $(sctlinkdir)\sctpred.obj \ $(sctlinkdir)\sctos.obj sctLINKKERNEL = $(sctlinkdir)\sctworld.lib sctCC = bcc32 sctCPPFLAGS = -I$(sctuseinclude) sctCCFLAGS = -c -W sctLD = bcc32 sctLDFLAGS = -W sctOEXTENSION = _apb.obj sctEXTENSION = ._apb.exe
The information to the right of the equal signs should be seen as an example. These environment variables set in the makeoptions
(make.opt
) file should specify:
sctLIBNAME
. This is only used by the Makefile
to report what it is doing.sctIFDEF
. This variable should specify what compilation switches, among those defined by the Cadvanced/Cbasic Code Generator system, that should be used. Usually there is one switch defining the library version and one defining the compiler.sctEXTENSION
. This is used to determine the file extension of the executable files.sctOEXTENSION
. This is used to determine the file extension of the object files.sctuseinclude
. This variable should specify where the include files scttypes.h, sctlocal.h, sctpred.h, sdt.h, post.h, and the library file post.o
(post.lib
) can be found.sctlinkdir
. This should be the directory where the object files of the particular library selected by sctdir
(SCTDIR
) can be found. Usually this is equal to sctdir
(SCTDIR
).sctLINKKERNEL
. This variable should specify the library of .o
files and will be used in generated make files for SDL systems to include the library in the link command. Normally, this is only a reference to sctworld.o
(sctworld.lib
).sctCC
. This defines the compiler to be used.sctCPPFLAGS
. This variable should give the compilation flag necessary to specify where the C preprocessor can find the include files scttypes.h, sctlocal.h, sctpred.h, sdt.h, and post.h.sctCCFLAGS
. This should specify other compiler flags that should be used, as for example -g (Sun cc) or -v (Borland bcc32) for debug information, -O for optimization.sctLD
. This defines the linker to be used.sctLDFLAGS
. This should specify other flags that should be used in the link operation.sctLIBFLAGS
. This is used to specify the particular flags that should be used during pre-linking, to form sctworld.o
from the .o
files for sctsdl.c
, sctutil.c
, sctpost.c
, sctmon.c
, sctpred.o, and sctos.c
. This is normally not used in Windows.The generated make files for an SDL system will as first action include the makeoptions
(make.opt
) file in the directory referenced by the environment variable sctdir
. It will then use the variables sctIFDEF
, sctLINKKERNEL
, sctCC
, sctCPPFLAGS
, sctCCFLAGS
, sctLD
, and sctLDFLAGS
to compile and link the SDL system with the selected library.
The make file is generated and executed by the Cadvanced/Cbasic Code Generator.
Below a UNIX make file generated for the SDT system example is shown.
# makefile for System: example include $(sctdir)/makeoptions default: example$(sctEXTENSION) example$(sctEXTENSION): \ example$(sctOEXTENSION) $(sctLD) $(sctLDFLAGS) \ example$(sctOEXTENSION) $(sctLINKKERNEL) \ -o example$(sctEXTENSION) example$(sctOEXTENSION): \ example.c $(sctCC) $(sctCPPFLAGS) $(sctCCFLAGS) \ $(sctIFDEF) example.c -o example$(sctOEXTENSION)
In this section the necessary changes to the source code to adapt it to a new environment are discussed. Adapting to a new environment could mean moving the code to new hardware or using a new compiler.
There are two parts of the source code that might need changes:
In Compiler Definition Section in scttypes.h the compiler definition section in scttypes.h is discussed in detail, while sctos.c is treated in The sctos.c File.
The following switches defining the compiler are currently handled in scttypes.h:
These are the compilers we have used to compile and link generated code and different versions of the library. To introduce a new compiler, a new section in scttypes.h should be introduced in which the properties of the compiler would be defined.
Some changes might also be necessary in sctos.c, where the generated application is connected to OS or hardware. The remaining part of the code ought to be possible to compile without changes, especially if the compiler follows the ANSI C standard.
The list of compilers will of course be extended with new compilers as we obtain the necessary information on how to handle each particular compiler.
The file scttypes.h contains one section for each compiler where the properties of the compiler are defined. The following information is given in the section:
cpp
?calloc
, free
, exit
, and getenv
FILE
, EOF
, and the I/O functionsXNOSIGNALS
getpid
, select
, and usleep
, or their equivalent are included.Below a typical section defining the properties of a compiler following ANSI-C is given.
#ifdef COMPILER_NAME #include <string.h> #include <stdlib.h> #ifdef XREADANDWRITEF #include <stdio.h> #endif #ifdef XCLOCK #include <time.h> #endif #if defined(XMONITOR) && !defined(XNOOSSIGNALS) #include <signal.h> #endif #if defined(XPMCOMM) && !defined(XENV) /* handle getpid, SUN/UNIX: */ extern int getpid XPP((void)); #endif #if defined(XMONITOR) && !defined(XNOSELECT) /* handle xCheckForKeyboardInput, SUN/UNIX: */ #include <sys/types.h> #include <sys/time.h> extern int select XPP((int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)); #endif #if defined(XCLOCK) && !defined(XENV) /* handle xSleepUntil, SUN/UNIX: */ extern void usleep XPP((unsigned useconds)); #endif #endif
Before the compiler definition sections the following macros are defined:
#define XPP(x) x #define XCAT(P1,P2) P1##P2 #define GETINTRAND random() #define GETINTRAND_MAX 2147483647 #define xptrint unsigned #define xint32 long int
which defines the most common setting for a number of properties. These might need to be changed for a particular compiler. If the compiler cannot use function prototypes then the following should be included:
#undef XPP #define XPP(x) () #define XNOPROTO
which defines that prototypes are not to be used. The XPP macro is used to include or exclude the parameters of functions in mainly extern declarations.
If token concatenation cannot be performed using the ANSI C standard, i.e. ##, one of the following examples is worth trying:
#undef XCAT #define XCAT(P1,P2) P1/**/P2
or
#undef XCAT #define XCAT(P1,P2) XCAT1(P1)P2 #define XCAT1(P) P
Next are the two types xptrint,
which defines an int
type with the same size as an address, and xint32,
which defines a 32-bits int type. The type xptrint
is by default defined as unsigned but might need to be changed unsigned long and xint32
is by default defined as long int but might need to be changed to some other int type. If xint32
is defined as int, the macro X_XINT32_INT should also be defined to get the printout correct as well.
Next in the section defining the compiler properties a number of .h files are included. Below a table is given, that for each .h file lists the functions and types used by the Cadvanced/Cbasic Code Generator system in either the library or in generated code:
string.h memcpy memset strlen strcmp strcpy strncpy strcat strncat only if XREADANDWRITEF stdlib.h calloc free exit getenv only if XMONITOR stdio.h only if XREADANDWRITEF FILE EOF fgetc ungetc printf sprintf fprintf sscanf fscanf fopen fclose time.h only if XCLOCK time_t time signal.h only if XMONITOR and not XNOOSSIGNALS signal
If the compiler does not handle C signals then the statement
#include <signal.h>
should be replaced by:
#define XNOOSSIGNALS
The last three parts in this section handle the utility functions needed by sctos.c to implement some of the operating system dependent functions. Please see below where sctos.c is discussed in detail.
The following functions are defined in sctos.c
#ifdef XCLOCK extern SDL_Time SDL_Clock (void); #endif #if defined(XCLOCK) && !defined(XENV) extern void xSleepUntil (SDL_Time WakeUpTime); #endif extern void * xAlloc (xptrint Size); extern void xFree (void **P); extern void xHalt (void); #if defined(XPMCOMM) && !defined(XENV) extern int xGlobalNodeNumber (void); #endif #if defined(XMONITOR) && !defined(XNOSELECT) extern xbool xCheckForKeyboardInput ( long xKeyboardTimeout); #endif extern SDL_PId xGetPId ( xPrsIdNode PType, SDL_PId Parent ); extern void xReleasePId (SDL_PId *P); extern xSignalNode xGetSignal ( XSIGTYPE SType, SDL_PId Receiver, SDL_PId Sender ); extern void xReleaseSignal (xSignalNode *S); extern xPrdNode xGetPrd (xPrdIdNode PrdId); extern void xReleasePrd (xPrsNode VarP); extern SDL_Charstring xAlloc_SDL_Charstring ( xptrint Size ); extern void xFree_SDL_Charstring (void **C); extern SDL_Bit_String xAlloc_SDL_Bit_String ( xptrint Size ); extern void xFree_SDL_Bit_String (void **C);
The function SDL_Clock
should return the current time, read from a clock somewhere in the OS or hardware. The return value is of type SDL_Time
, that is a struct with two 32-bits integer components, representing seconds and nanoseconds in the time value.
typedef struct { xint32 s; /* for seconds */ xint32 ns; /* for nanoseconds */ } SDL_Time;
The standard implementation of SDL_Clock
uses the C function time
, which returns the number of seconds since some defined date.
Note: Note that the C function |
In an embedded system or any other application that requires better time resolution, or when the C function time is not available, SDL_Clock
should be implemented by the user.
A typical implementation in an embedded system is to have hardware generating interrupts at a predefined rate. At each such interrupt a variable containing the current time is updated. This variable can then be read by SDL_Clock
to return the current time.
The function xSleep_Until
is given a time value, as a value of type SDL_Time
(see above) and should suspend the executing until this time is reached, when it should return.
This function is used only when real time is used (the switch XCLOCK
is defined) and when there is no environment functions (XENV
is not defined). The xSleep_Until
function is used to wait until the next event is scheduled when there is no environment that can generate events.
The function xAlloc
is used to allocate dynamic memory and is used throughout the runtime library and in generated code. The function is given a size in bytes and should return a pointer to a data area of the requested size. All bytes in this data area are set to zero. The standard implementation of this function uses the C function calloc
.
A user who wants to estimate the need for dynamic memory can introduce statements in xAlloc
to record the number of calls of xAlloc
and the total requested size of dynamic memory. Please note two things. A program using the monitor requires more dynamic memory than a program not using the monitor, so estimates should be made with the appropriate compilation switches. A call of calloc
will actually allocate more memory than is requested to make it possible for the C runtime system to deallocate and reuse memory. The size of this additional memory is compiler-dependent.
A user who wants to handle the case when no more memory is available at an allocation request can implement that in xAlloc
. In the standard implementation for xAlloc
a test if calloc returns 0 can be introduced, at which the program can be terminated with an appropriate message.
The function xFree
is used for the opposite operation, that is to return memory to the list of free memory so it can be reused by subsequent calls of xAlloc
. The standard implementation of this function uses the C function free
. The xFree
function is only used to free the memory allocated for Charstring values using the standard implementation. This means that if the user does not introduce any use of xFree
and the SDL type Charstring is not used, then there is no need to implement xFree
.
The function xHalt
is used to exit from a program and is in the standard implementation using the C function exit
to perform its task.
The function xGlobalNodeNumber
is used to assign unique numbers to each SDL system which is part of an application.
If environment functions are used for an SDL system this function should be implemented there. If, however, we have communicating simulations, there are no env
functions and the xGlobalNodeNumber
function is defined in sctos.c instead.
So the xGlobalNodeNumber
function is only used if XPMCOMM
is defined and XENV
is not defined. As this function is only used in the case of a communicating simulation, it is only necessary to implement it for computers/compilers handling SDT, which means that it is not interesting for a user to change the standard implementation of this function. The implementation calls the function getpid
, and uses thus the OS process number as global node number.
The function xCheckForKeyboardInput
is used to determine if there is a line typed on the keyboard (stdin
) or not. If this is difficult to implement it can instead determine if there are any characters typed on the keyboard or not. This function is only used by the monitor system, (when XMONITOR
is defined). If the function is not implemented the compilation switch XNOSELECT
should be defined to prohibit calls of the function.
The xCheckForKeyboardInput
function is used to implement the possibility to interrupt the execution of SDL transitions by typing <Return>
and to handle polling of the environment (xInEnv
or its equivalent when communicating simulations is used) when the program is waiting at the "Command
:" prompt in the monitor.
The functions
xGetPId xReleasePId xGetSignal xReleaseSignal xGetPrd xReleasePrd xAlloc_SDL_Charstring xFree_SDL_Charstring xAlloc_SDL_Bit_String xFree_SDL_Bit_String
are called to allocate and release memory used for processes, signals, procedures, charstrings, Bit_Strings, and Octet_Strings. By changing the implementation of these function a user can change the strategy for reuse of memory. For more information see the section Changing the Standard Memory Allocation Procedure.
This section is a reference to the macros that are used together with the generated C code from the Cadvanced/Cbasic Code Generator. Here the macros are just enumerated and explained. The section is divided in a number of subsection, each treating one major aspect of the code. Within the subsections the macros are enumerated in alphabetic order.
The context in which macros are used and the sequence of generated macro calls are not discussed here. Such information can be found in the section Structure of Generated Code.
Information about some of the macros ( Library Version Macros, Compiler Definition Section Macros, and General Properties) can also be found in the section Compilation Switches.
To fully understand the descriptions of the macros in this section it is also necessary to know the basic data structures used, especially for the static structures, i.e. the xIdNodes. This information can be found in the section The Symbol Table.
The information about the data types used for the dynamic structure of the system, i.e. about process instances, signal, timers, and so on, are also of interest. This can be found in The SDL Model.
Application.
Application without clock.
Application with a CORBA environment.
Stand-alone simulator for any environment. Should be executed from OS.
A simulator with a CORBA environment.
Stand-alone simulator with real time for any environment. Should be executed from OS.
Simulator with real time for host. Can be executed from SDT or from OS.
Stand-alone simulator, with real time and env functions, for any environment. Should be executed from OS.
Simulator, with real time and env functions, for any environment. May be executed from OS or from simulator GUI.
Simulator for host. Can be executed from SDT or from OS.
Application with minimal memory requirements. Real cannot be used. No channel information
Application with minimal memory requirements. Real cannot be used. Const for all channel information.
Suitable for execution of performance simulations.
cc for the ALPHA architecture.
cc for the IBM RS/6000 architecture.
gcc for Windows NT4 (Windows 95).
ANSI cc for HP-300 and HP-400 (Motorola architecture).
ANSI cc for HP-700 (PA-RISC architecture).
cc for HP-300 and HP-400 (Motorola architecture).
cc for HP-700 (PA-RISC architecture).
PC compilers.
The IAR compiler.
GNU cc for linux.
The OASYS cross compiler.
QNX4 Watcom 10.6
ANSI cc for SunOS 4.
cc for SunOS 4.
GNU cc for SunOS 4.
ANSI cc for SunOS 5.
cc for SunOS 5.
GNU cc for SunOS 5.
C++ for SUN.
cc for ULTRIX.
cc for VAX/VMS.
Should be defined as:
#define COMMENT(P)
The macro is used to insert comments in included C code.
A random generation function. Usually rand()
or random()
.
The max int value generated by function mentioned in GETINTRAND. Usually 2147483647 (32-bit integers).
Defined in generated code if the Cadvanced/Cbasic Code Generator version 3.5 was used.
Should concatenate token P1 and P2. Possibilities:
#define XCAT(P1,P2) P1##P2
or
#define XCAT(P1,P2) P1/**/P2
or
#define XCAT(P1,P2) XCAT2(P1)P2 #define XCAT2(P2) P2
Should be defined if there is no support for C signals. This feature is only of interest in simulations.
Should be defined if prototypes are not to be used.
Should be defined if there is no support for the select function found in UNIX operating systems. This is used to implement "user defined interrupt" by typing the return key while simulating.
If this macro is defined there will be no version check between the generated code and the scttypes.h
file.
Should be defined as nothing if XNOPROTO is defined, otherwise as x. The macro is used to produce function prototypes with or without parameters (ANSI-C or old style).
Defined in generated code if Cbasic was used.
Defined in generated code if Cadvanced was used.
Defined in scttypes.h
in a way that it possible to include the scttypes.h
file several times without any problems.
Should be defined if xint32
is int
.
Should be defined if xptrint
is unsigned long
.
Can be used to connect an application with a monitor on a target system with SDT running on a host computer.
Detect and report user defined assertions that are not valid.
Use the clock function in sctos.c
(not simulated time). Time is whatever the clock function returns.
Use the clock function in sctos.c
(not simulated time). Time is zero at system start up.
Compile with code to store information about the current coverage of the SDL system. This information can also be printed in the monitor.
Compile preserving the possibility to report the current C line number during simulations.
Defines XEOUTPUT, XEINTDIV, XEREALDIV, XECSOP, XEFIXOF, XERANGE, XEINDEX, XECREATE, XEDECISION, XEEXPORT, XEVIEW, XEERROR, XEUNION, XECHOICE, XEOPTIONAL, XEREF, XEOWN, and XASSERT.
For more information, see these macros.
Detect and report attempts to access non-active components in Choice variables.
Detect and report if more static instances are created at start up, than the maximum number of concurrent instances.
Detect and report errors in ADT operators.
Detect and report when there is no possible path out from a decision.
Detect and report the usage of the error term in an SDL expression.
Detect and report errors in import actions.
Detect and report integer overflow in the operator fix.
Detect and report index out of bounds in arrays.
Detect and report integer division with 0.
Call the env functions.
Insert the VarP
pointer in the xSignalNode
so that signals conform with their implementation in SDT 2.3.
Detect and report attempts to access optional struct components that are not present.
Detect and report warnings in outputs (mainly outputs where signal is immediately discarded).
Detect and report illegal usage of Own and ORef pointers.
Detect and report subrange errors.
Detect and report real division with 0.0.
Detect and report attempts to dereference null pointer.
Detect and report attempts to access non-active components in a #UNION.
Detect and report errors in view actions.
Compile with the trace in source SDL graphs enabled.
Enable the possibility for an executable to communicate with ITEX via the Postmaster.
If this macro is defined the main function in sctsdl.c
will be renamed to the name given by the macro.
Compile with the monitor system. This macro will implicitly set up a number of other macros as well.
Compile with the MSC trace enabled.
If this macro is defined the main function in sctsdl.c
will be removed.
Enable the possibility for an executable to communicate via the Postmaster.
Use priorities on process instance sets.
Use first priorities on process instance sets and then priorities on signal instances.
Enable the possibility to communicate with a user-defined UI.
Call the xSignalLog
and xProcessLog
functions.
Use priorities on signal instances.
Use first priorities on signal instances and then priorities on process instance sets.
Enable the possibility to communicate with the simulator UI.
As XENV but call xInEnv
at specified times (next event time is out parameter from function xInEnv
).
Compile with the textual trace enabled.
The majority of the xIdNode
structs can be made const by defining CONST
as const
. This is only possible in applications (not simulations).
This should normally be defined as const if XCONST is const. It is used to introduce const in the component declarations within the xIdNode
structs.
Do not include functions to calculate the expressions in continuous signals. This saves also one function pointer in the xIdNode
for the states. If this switch is defined, continuous signals cannot be used.
Do not include functions to calculate the expressions in enabling conditions. This saves also one function pointer in the xIdNode
for the states. If this switch is defined, enabling conditions cannot be used.
Do not include function to compare the parameters of two timers. This saves also one function pointer in the xIdNode
for the signals. If this switch is defined, timers with parameters cannot be used.
Do not include xIdNodes for remote variable definitions.
Do not include xSignalIdNodes
for signals and timers.
Do not include xSignalIdNodes
for start up signals.
The type Object_Identifier and all operations on that type is removed.
The types Bit_String, Octet, Octet_String and all operations on these types are removed.
All data and code needed to handle services are removed.
The type real and all operations on real are removed.
Defines XOPTSIGPARA, XOPTDCL, XOPTFPAR, XOPTSTRUCT, XOPTLIT, and XOPTSORT.
For more information see these macros.
Do not include xIdNodes for channels, signal routes, and gates. Information in services and processes about connections to signal routes and gates are also removed.
Note: If this compilation switch is defined all outputs must either be sent TO a process or the receiver must be possible to calculate during code generation. |
Do not include xIdNodes for variables.
Do not include xIdNodes for formal parameters.
Do not include xIdNodes for literals.
Do not include xIdNodes for signal parameters.
Do not include xIdNodes for newtypes and syntypes.
Do not include xIdNodes for struct components.
Optimize memory for process instances. All memory for a process instance can be reused, but signal sending to a stopped process, who's memory has been reused by a new process, cannot be detected. The new process will in this case receive the signal.
If this compilation switch is defined, xVarIdNodes are inserted for the Present components for optional struct components. This feature is only needed by the Validator and by LINK. It should not be defined otherwise.
Should be used mainly if the MONITOR or GRTRACE switches are defined. It will make the functions and struct components for SDT references available and is also used to expand the macros XAT_FIRST_SYMBOL, XBETWEEN_SYMBOLS, XBETWEEN_SYMBOLS_PRD, XBETWEEN_STMTS, XBETWEEN_STMTS_PRD, XAFTER_VALUE_RET_PRDCALL, and XAT_LAST_SYMBOL to suitable function calls. These functions are used to interrupt a transition between symbols during simulation.
See XCASELABELS below. The SDL symbols just after an SDL procedure call have to be treated specially, as the symbol number (=case label) for these symbols are used as the restart address for the calling graph. Normally this macro should be defined. If SDL procedure calls are transformed to proper C function calls, and SDL return is translated to a C return, and nextstate in a procedure is NOT translated to a C return (i.e. the process will be hanging in the C function representing the SDL procedure) then it is not necessary to define XCASEAFTERPRDLABELS.
The function implementing the behavior of a process, procedure, or service contains one large switch statement with a case label for each SDL symbol in the graph. This switch is used to be able to restart the execution of a process, procedure, or service at any symbol. In an application most of these label can be removed (all except for those symbols that start a transition, i.e. start, input, continuous signal). The macro XCASELABELS should be defined to introduce the case labels for all SDL symbol. This means that XCASELABELS should be defined in a simulation but not in an application.
If XCONNECTPM is defined the SDL simulation will try to connect itself to the postmaster. This is necessary if GR trace ( XGRTRACE), communicating simulations ( XPMCOMM), or communication with ITEX ( XITEXCOMM) is to be used. The XCONNECTPM feature is normally only used in simulations.
Count the number of timers that are removed at a reset operation. This information is used by the textual trace system ( XTRACE) to present this information. The information is really only of interest at a stop action when more then one timer might be (implicitly) reset. XCOUNTRESETS should not be defined in an application.
This macro is used to determine the number of signals sent to the environment that, during simulation, should be saved in the input port of the env process instance. Such signals can be inspected with the normal monitor commands for viewing of signals. This macro is only of interest in a simulation and has the default value 20.
Insert the data structure to represent an "error" state that can be used if no path is found out from a decision. This should normally be defined if XEDECISION is defined.
Insert free functions for each signal, timer, or startup signal that contains a parameter of a type having a free function. These signal free functions can the be used to free allocated data within a signal.
Insert free function calls for all variables of a type with free function, just before stop or return actions. This means that free actions are performed on allocated data referred to from variables is before the object ceases to exist.
This macro is used to determine if the SDL name of an SDL object should be stored in the xIdNode
for the object. This character string is used for communication with the user in for example the monitor. Normally this macro should not be used in an application. Sometime it might be useful for target debugging to define XIDNAMES, as it is then fairly easy to identify objects by just printing the SDL name from a debugger. On average this seems to cost approximately 5% more memory.
This macro should be defined if process instance numbers are to be maintained. The instance number is the number in the monitor printout Test:2
, identifying the individual instances of the process instance set Test in this case. XNRINST is normally only used in a simulation.
Include the function xSDLOpError
in sctsdl.c
. This function is used to print run-time errors in ADT operators.
Store the value of sender also in the xPrsNode
. The normal place is in the latest received signal. This is only needed in a simulation as sender might be accessed from the monitor system after the transition is completed and the signal has been returned to the pool of available memory.
Include the functions for basic Read and Write. This is needed mainly in simulations.
Allow the removal of timer signals for not-executing PIds. This is needed only in simulations to implement the monitor commands set-timer and reset-timer.
If this macro is defined then the functions xIsPath
and xFindReceiver
will return the path of signal routes, channels, and gates from the sender to the receiver, as out parameters. This information can then be used in the monitor system, for example, to produce signal logs. This macro should normally not be defined in an application.
The XSYMBTLINK macro is used to determine if a complete tree should be built from the xIdNodes of the system. If XSYMBTLINK is defined then all xIdNodes contains a Parent
, a Suc
, and a First
pointer. The value of the Parent
pointer is generated directly into the xIdNodes. Suc
and First
, however, are calculated in the yInit
function by calling the xInsertIdNode
function. The Suc
and First
pointers are needed by the monitor system, but not in an application, i.e. XSYMBTLINK should be defined in a simulation but not in an application.
This macro is used to include or remove test functions for syntype (or newtypes) with range conditions. The yTest
function is used by the monitor system and by the functions to test index out of bounds in arrays and to test subranges. This means that XTESTF should be defined if the monitor is used or if XERANGE or XEINDEX is defined.
When using partitioning of a system a problem during the redirection of channels is that the number of channels going to the environment is not known at code generation time, which means that the size of the data area used for the connections is not known. This problem is solved in two ways.
Either the function handling redirections allocates more memory, which is the default, or the user specifies how many channels that will be redirected (which could be difficult to compute, but will lead to less need of memory).
In the first case (allocation of more memory) the macros:
#define XTRACHANNELSTOENV 0 #define XTRACHANNELLIST
should be defined like above. This is the standard in scttypes.h. If the user wants to specify the number of channels himself then
#define XTRACHANNELSTOENV 10 #define XTRACHANNELLIST ,0,0,0,0,0,0,0,0,0,0
i.e. XTRACHANNELSTOENV
should be the number of channels, while XTRACHANNELLIST
should be a list of that many zeros.
See XTRACHANNELSTOENV
just above.
All generated struct values for block, block type, and block instance structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xBlockIdStruct
must be updated as well. Normally this macro should be empty.
#define XBLO_EXTRAS ,0
All generated struct values for block substructure structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xBlockSubstIdStruct
must be updated as well. Normally this macro should be empty.
#define XBLS_EXTRAS ,0
All generated struct values for xIdNode
structs contain this macro after the common components. This means that it is possible to insert new components in all xIdNodes by defining this macro. Normally this macro should be empty.
To insert a new int component with value 0 the following definition can be used:
#define XCOMMON_EXTRAS ,0
All generated struct values for literal structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xLiteralIdStruct
must be updated as well. Normally this macro should be empty.
#define XLIT_EXTRAS ,0
All generated struct values for package structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xPackageIdStruct
must be updated as well. Normally this macro should be empty.
#define XSYS_EXTRAS ,0
All generated struct values for procedure structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xPrdIdStruct
must be updated as well. Normally this macro should be empty.
#define XSYS_EXTRAS ,0
(PREFIX_PROC_NAME)
All generated struct values for process, process type, and process instance structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xPrsIdStruct
must be updated as well.
#define XPRS_EXTRAS(PREFIX_PROC_NAME) \ ,XCAT(PREFIX_PROC_NAME,_STACKSIZE)
All generated struct values for signal, timer, RPC_signal, startup signal structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xSignalIdStruct
must be updated as well. Normally this macro should be empty.
#define XSIG_EXTRAS ,0
All generated struct values for signal parameter structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xVarIdStruct
must be updated as well (Note that variables, formal parameters, signal parameters, and struct components are all handled in xVarIdStruct
.) Normally this macro should be empty.
#define XSPA_EXTRAS ,0
All generated struct values for newtype and syntype structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xSortIdStruct
must be updated as well. Normally this macro should be empty.
#define XSRT_EXTRAS ,0
All generated struct values for service, service type, and service instance structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xSrvIdStruct
must be updated as well. Normally this macro should be empty.
#define XSRV_EXTRAS ,0
All generated struct values for state structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xStateIdStruct
must be updated as well. Normally this macro should be empty.
#define XSTA_EXTRAS ,0
All generated struct values for system, system type, and system instance structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xSystemIdStruct
must be updated as well. Normally this macro should be empty.
#define XSYS_EXTRAS ,0
This macro gives the possibility to introduce global variables declared in the beginning of the C file containing the implementation of the SDL system unit.
If extern definitions are needed for the data declared in XSYSTEMVARS, this is the place to introduce it. These definitions will be present in the .h
file for the system unit (if separate generation is used).
All generated struct values for variables, formal parameters, and struct components structs contain this macro last in the struct. By defining this macro new components can be inserted. Note that the type xVarIdStruct
must be updated as well (Note that signal parameters also uses the type xVarIdStruct
). Normally this macro should be empty.
#define XVAR_EXTRAS ,0
The struct components that are needed for each procedure instance. Example: state.
The struct components that are needed for each process instance. Example: state, parent, offspring, self, sender, inputport.
The struct components that are needed for each service instance. Example: state
This macro is used to declare the yVarP
pointer (which is a pointer to the yVDef
struct for the process) in a procedure defined outside of a process. As a global procedure never can access process local data, it is suitable to let yVarP
be a pointer to a struct only containing the components defined in the macro PROCESS_VARS.
This macro is used to declare the yVarP
pointer (which is a pointer to the yVDef
struct for the process) in a service type defined outside of a process. As a global service type never can access process local data, it is suitable to let yVarP
be a pointer to a struct only containing the components defined in the macro PROCESS_VARS.
Local variables in the PAD function for a process or service. Example: temporary variables needed for outputs, create actions.
Declaration of the ySVarP
pointer used to refer to the received signal. Normally ySVarP
is void *
.
(VDEF_TYPE)
This macro is used within a process and in a service defined within a process. It should be expanded to a declaration of yVarP
, which is the pointer that is used to access SDL variables in the process. yVarP
should be of type VDEF_TYPE *
, where VDEF_TYPE
is the type of the yVDef
struct for the process. If the pointer to the yVDef
struct is passed as parameter to the PAD function, yVarP
can be assigned its correct value already in the declaration.
Local variables in the function implementing the behavior of an SDL procedure.
(VDEF_TYPE)
This macro is used within a procedure defined in a process. It should be expanded to a declaration of yVarP
, which is the pointer that is used to access SDL variables in the process. yVarP
should be of type VDEF_TYPE *
, where VDEF_TYPE
is the type of the yVDef
struct for the process. If the pointer to the yVDef
struct is passed as parameter to the procedure function, yVarP
can be assigned its correct value already in the declaration.
(VDEF_TYPE)
BEGIN_PAD is a macro that can be used to insert code that is executed in the beginning of the PAD functions. VDEF_TYPE
is the yVDef type
for the process.
(STARTUP_PAR_TYPE)
This macro can be used to introduce code that is executed at the beginning of the start transition. STARTUP_PAR_TYPE
is the yPDef
struct for the startup signal for this process.
This macro is used in the PAD function of a process that contains services. It should be expanded to a call to PAD function for the service that should execute the next transition (ActiveSrv
).
(PAD)
During the start transition of a process all inherited PAD functions up to and including the PAD function containing the START symbol have to be called. The reason is to initialize all variables defined in the process. This macro is used to perform a call to the inherited PAD function (the macro parameter PAD). Usually this macro is expanded to something like:
yVarP->RestartPAD = PAD; PAD(VarP);
followed by either a return
or a goto NewTransition
depending on execution model.
(PRD, THISPRD)
This macro is used in the same way as CALL_SUPER_PAD_START (see above) but for the start transition in a procedure. THISPRD
is the executing procedure function, while PRD
is the inherited procedure function.
(PAD)
This macro is used in the same way as CALL_SUPER_PAD_START (see above) but for the start transition in a service. PAD
is the inherited PAD function.
The LOOP_LABEL macro should be used to form the loop from a nextstate operation to the next input operation necessary in the OS where OS tasks does not perform return at end of transition (most commercial OS). This macro is also suitable to handle free on received signals and the treatment of the save queue. In an OS where SDL nextstate is implemented using a C return
(the Master Library for example) the LOOP_LABEL macro is usually empty.
Similar to LOOP_LABEL but used in procedures with states.
Similar to LOOP_LABEL but used in procedures without states. This macro is in many circumstances expanded to nothing.
Similar to LOOP_LABEL but used in the PAD function for a process containing services.
Should return the value of offspring.
Should return the value of parent.
Should return the value of self.
Should return the value of sender.
This macro is used in the PAD function of a process that contains services. It should be expanded in such a way that the start transitions for all of the services are executed.
This is a macro generated at the end of a function that represents the behavior of a procedure. It needs not to be expanded to anything. To define it as
return (xbool)0;
might remove a compiler warning that the end of a value returning function might be reached.
Should usually be expanded to the type xPrsNode
.
How to reach the xPrsIdNode
from a PAD function. Normally this is yVarP->NameNode
.
How to reach the xPrdIdNode
from a PRD function. Normally this is yPrdVarP->NameNode
.
How to reach the xSrvIdNode
from a PAD function. Normally this is ySrvVarP->NameNode
.
(PAD)
The function heading of the PAD function given as parameter.
(PAD)
The function prototype of the PAD function given as parameter.
(PRD)
The function heading of the PRD function given as parameter.
(PRD)
The function prototype of the PRD function given as parameter.
This macro is placed in the beginning of the yInit
function in the file containing code for the system. It can be expanded to variable declarations and initialization code.
(PROC_NAME, PROC_NAME_STRING, PREFIX_PROC_NAME, PAD_FUNCTION, VDEF_TYPE)
This macro can be used to introduce code for each process instance set in the system.
PROC_NAME
the name of the process without prefix.PROC_NAME_STRING
PREFIX_PROC_NAME
the name of the process with prefix.PAD_FUNCTION
the PAD function for this process instance set.VDEF_TYPE
the yVDef
struct for this process.(PROC_NAME, PROC_NAME_STRING, PREFIX_PROC_NAME, PAD_FUNCTION, VDEF_TYPE)
This macro can be used to introduce extern declaration (placed in the proper .h
file) for each process instance set in the system.
PROC_NAME
the name of the process without prefix.PROC_NAME_STRING
the name of the process as a character string.PREFIX_PROC_NAME
the name of the process with prefix.PAD_FUNCTION
the PAD function for this process instance set.VDEF_TYPE
the yVDef
struct for this process.In the yInit
function the function xInsertIdNode
is called for each IdNode
. In an application this is not necessary, and xInsertIdNode
can be defined as
#define xInsertIdNode(Node)
The function xInsertIdNode
is needed if XSYMBTLINK, XCOVERAGE, or XMONITOR is defined.
This macro is placed in all yInit functions and can be expanded to local variables needed within the yInit
function.
(SIG_NAME, SIG_IDNODE, RECEIVER, SIG_PAR_TYPE)
These macros are used to allocate a data area for a signal to be sent. ALLOC_SIGNAL is used if the signal has no parameters, while ALLOC_SIGNAL_PAR is used if the signal has parameters. The resulting data area should be reference by the variable mentioned by the macro OUTSIGNAL_DATA_PTR (see below).
SIG_NAME
the name of the signal without prefix.SIG_IDNODE
the xSignalIdNode
of the signal.RECEIVER
the receiver given in the TO clause, or calculated. In a NO_TO output, RECEIVER
is xNotDefPId
.SIG_PAR_TYPE
the yPDef type
of the signal. If the signal has no parameters this macro parameter is XSIGNALHEADERTYPE (see below).This macro should be expanded to the identification of the currently received signal. It is used to distinguish between signals when several signal is enumerated in the same input symbol.
This should be the pointer referring to be signal data area while building the signal during an output. It should be assigned its value in ALLOC_SIGNAL or ALLOC_SIGNAL_PAR, and will then be used during assignment of signal parameters and in the SDL_2OUTPUT macro just below.
(PRIO, VIA, SIG_NAME, SIG_IDNODE, RECEIVER, SIG_PAR_SIZE, SIG_NAME_STRING)
These six macros are used to send the signal created in ALLOC_SIGNAL or ALLOC_SIGNAL_PAR. The SDL_ALT versions of the macros are used if the directive /*#ALT*/
has been given in the output. The version without suffix is used for an output TO, while the suffix _COMPUTED_TO is used for an output without to but it was possible to compute the receiver during code generation time. The suffix _NO_TO indicates an output without to, where the receiver cannot be calculated during code generation time.
PRIO
the priority of the signal specified in a #PRIO directive.VIA
the via list given in the output.SIG_NAME
the name of the signal without prefixSIG_IDNODE
the xSignalIdNode
for the signal.RECEIVER
the receiver given in the TO clause, or calculated. In a NO_TO output, RECEIVER
is xNotDefPId
.SIG_PAR_SIZE
the size of the yPDef
struct of the signal. If signal without parameters SIG_PAR_SIZE
is 0.SIG_NAME_STRING
the name of the signal as a character string.In an output TO THIS in SDL, the RECEIVER parameter in the ALLOC_SIGNAL and SDL_2OUTPUT macros discussed above will become SDL_THIS.
(P)
This macro makes it possible to store a signal code (signal number) in the xSignalIdNode
for a signal. The macro parameter P
is the signal name without prefix.
This macro is inserted after the ALLOC_SIGNAL macro and the assignment of parameter values to the signal. It can be used to test if the alloc was successful or not.
This macro is inserted after the SDL_2OUTPUT macro.
(SIG_NAME, SIG_IDNODE)
This macro should be expanded to an identification of the signal given as parameter. Normally the identification is either the xSignalIdNode
for the signal or an int
value. If the id is an int
value it is suitable to insert defines of type #define signal_name number. A file containing such defines can be generated using the Generate Signal Numbers feature in Cadvanced/Cbasic.
SIG_NAME
the name of the signal without parametersSIG_IDNODE
the xSignalIdNode
for the signal.The struct components that are needed for each signal instance. Example: sender, receiver, signal type.
(PROC_NAME, PROC_IDNODE)
This macro is used as RECEIVER in the ALLOC_SIGNAL and SDL_2OUTPUT macros if the signal is sent to a process instance set in SDL.
PROC_NAME
the name of the receiving process without prefix.PROC_IDNODE
the xPrsIdNode
of the receiving process.(SIG_NAME, SIG_IDNODE, RECEIVER, SIG_PAR_TYPE)
These macros are used as alternative for the ALLOC_SIGNAL macros (see these macros above) if the directive #TRANSFER
if given in the output.
SIG_NAME
the name of the signal without prefix.SIG_IDNODE
the xSignalIdNode
of the signal.RECEIVER
the receiver given in the TO clause, or calculated. In a NO_TO output, RECEIVER
is xNotDefPId
.SIG_PAR_TYPE
the yPDef
type of the signal. If the signal has no parameters this macro parameter is XSIGNALHEADERTYPE (see below).The representation for a none signal.
This macro is used to indicate a yPDef
struct for a signal without parameters. Such a signal has no generated yPDef
struct. It is suitable to let XSIGNALHEADERTYPE be the name of a struct just containing the components in SIGNAL_VARS.
Depending on the representation of the signal type that is used (xSignalIdNode
or int
) this macro should either be xSignalIdNode
or int
.
(SIG_NAME, SIG_IDNODE, RECEIVER, SIG_PAR_TYPE)
These macros are used to allocate the Reply signal in the signal exchange in an RPC. The suffix _PAR is used if the reply signal contains parameters. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
SIG_NAME
the reply signal name without prefix.SIG_IDNODE
the xSignalIdNode
for the reply signal.RECEIVER
the receiver of the reply signal. The macro XRPC_SENDER_IN_ALLOC or XRPC_SENDER_IN_ALLOC_PRD are used as actual parameter. The suffix _PRD is used if the implicit RPC transition is part of a procedure.SIG_PAR_TYPE
the yPDef
type for the reply signal. If the reply signal does not contain any parameters the macro name XSIGNALHEADERTYPE is generated as actual parameter.This should be a reference to the data area for the reply signal that is allocated in the ALLOC_REPLY_SIGNAL macro. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
(PREPLY_IDNODE, PREPLY_NAME, RESTARTADDR)
These macros are used to implement the implicit nextstate operation in the caller of an RPC. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
PREPLY_IDNODE
the xSignalIdNode
for the reply signal.PREPLY_NAME
the name without prefix for the reply signal.RESTARTADDR
the restart address (symbol number) for the implicit input of the reply signal.(PRIO, VIA, SIG_NAME, SIG_IDNODE, RECEIVER, SIG_PAR_SIZE, SIG_NAME_STRING)
Send the call signal of an RPC.
PRIO
priority of signal.VIA
the via list, which in this case always is (xIdNode *)0
, i.e. no via list.SIG_NAME
the RPC call signal name without prefix.SIG_IDNODE
the xSignalIdNode
for the RPC call signal.RECEIVER
the receiver of the call signal. This is either expressed as an ordinary TO-expression or using the macro XGETEXPORTINGPRS (see below) in case of no explicit receiver specified in the call.SIG_PAR_SIZE
the size of the yPDef
struct for the call signal. If the call signal has no parameters this parameter will be 0.SIG_NAME_STRING
the name of the RPC call signal as a character string.(PRIO, VIA, SIG_NAME, SIG_IDNODE, RECEIVER, SIG_PAR_SIZE, SIG_NAME_STRING)
These macros are used to send the RPC reply signal. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
PRIO
priority of signal.VIA
the via list, which in this case always is (xIdNode *)0
, i.e. no via list.SIG_NAME
the RPC reply signal name without prefix.SIG_IDNODE
the xSignalIdNode
for the RPC reply signal.RECEIVER
the receiver of the reply signal. This is expressed using the macro XRPC_SENDER_IN_OUTPUT or XRPC_SENDER_IN_OUTPUT_PRD.SIG_PAR_SIZE
the size of the yPDef struct for the reply signal. If the reply signal has no parameters this parameter will be 0.SIG_NAME_STRING
the name of the RPC reply signal as a character string.(REMOTENODE)
This macro should be expanded to an expression that given the remote procedure given as actual macro parameter (more exactly the IdNode
for the remote procedure), returns one possible exporter of this remote procedure. Usually this macro is expanded to a call of the library function xGetExportingPrs
.
Macros that can be used for special processing needed to receive an RPC reply signal. The macros are usually expanded to nothing.
These macros can be used to save the sender of a received RPC call signal, for further use when the reply signal is to be sent. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
These macros are used to obtain the receiver of the reply signal (from the sender of the call signal) in the ALLOC_REPLY_SIGNAL macros. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
These macros are used to obtain the receiver of the reply signal (from the sender of the call signal) in the SDL_2OUTPUT_RPC_REPLY macros. The suffix _PRD is used if the implicit RPC transition is part of a procedure.
The state number used for a RPC wait state. XRPC_WAIT_STATE is usually defined as -3.
(REMOTENODE, EXPORTER, IS_DEF_EXPORTER)
This macro should be expanded to an expression that returns the address of the exported variable. Usually the function xGetExportAddr
is called.
REMOTENODE
the IdNode for the remote variable.EXPORTER
the value of the optional PId expression. If no PId expression is given this parameter is SDL_NULL.IS_DEF_EXPORTER
has the value (xbool)1
if a PId expression was found in the import statement, otherwise it is (xbool)0
.(PID_EXPR, HAS_EXPR, VAR_NAME_STRING, REVEALED_LIST, SORT_SIZE)
This macro should be expanded to an expression that returns the address of the viewed variable. Usually the function SDL_View is called.
PID_EXPR
the value of the optional PId expression. If no PId expression is given this parameter is SDL_NULL.HAS_EXPR
has the value (xbool)1
if a PId expression was found in the view statement, otherwise it is (xbool)0
.VAR_NAME_STRING
the name of the viewed variable as a character string.REVEALED_LIST
the list of the revealed variables.SORT_SIZE
the size of the sort of the revealed variable.(PROC_NAME, STARTUP_IDNODE, STARTUP_PAR_TYPE)
Allocate the data area for a startup signal and let the pointer mentioned in the macro STARTUP_DATA_PTR refer to this data area. The suffix _PAR is used if the startup signal contains parameters.
PROC_NAME
the name without prefix for the created process.STARTUP_IDNODE
the xSignalIdNode
for the startup signal of the created process.STARTUP_PAR_TYPE
the yPDef
for the startup signal of the created process.Allocate the data area for a startup signal and let the pointer mentioned in the macro STARTUP_DATA_PTR refer to this data area. This macro is used in a create THIS operation.
(PROC_NAME, PREFIX_PROC_NAME, PROC_IDNODE, PROC_NAME_STRING, MAX_NO_OF_INST, STATIC_INST, VDEF_TYPE, PRIO, PAD_FUNCTION)
This macro will be call once for each process instance set in the yInit function. It should be used to initiated common features for all instances of a process instance set.
PROC_NAME
the name without prefix for the process instance set.PREFIX_PROC_NAME
the name with prefix for the process instance set.PROC_IDNODE
the xPrsIdNode
for the process instance set.PROC_NAME_STRING
the name as character string for the process instance set.MAX_NO_OF_INST
the maximum number of instances of this process instance set.STATIC_INST
the number of static instances of this process instance set.VDEF_TYPE
the yVDef
type for this process instance set.PRIO
the priority for process instance set.PAD_FUNCTION
the PAD for this process instance set.(PROC_NAME, PROC_IDNODE, PROC_NAME_STRING)
This macro is used to create (a create action) a process instance.
PROC_NAME
the name without prefix for the process instance set.PROC_IDNODE
the xPrsIdNode
for the process instance set.PROC_NAME_STRING
he name as character string for the process instance set.This macro is used to implement create this.
(PROC_NAME, PREFIX_PROC_NAME, PROC_IDNODE, PROC_NAME_STRING, STARTUP_IDNODE, STARTUP_PAR_TYPE, VDEF_TYPE, PRIO, PAD_FUNCTION, BLOCK_INST_NUMBER)
This macro is called in the yInit function once for each static process instances that should be created of a process instance set.
PROC_NAME
the name without prefix for the process instance set.PREFIX_PROC_NAME
the name with prefix for the process instance set.PROC_IDNODE
the xPrsIdNode
for the process instance set.PROC_NAME_STRING
the name as character string for the process instance set.STARTUP_IDNODE
the xSignalIdNode
for the startup signal for the process instance set.STARTUP_PAR_TYPE
the yPDef
type for the startup signal for the process instance set.VDEF_TYPE
the yVDef
type for the process instance set.PRIO
the priority for the process instance set.PAD_FUNCTION
the PAD function for the process instance set.BLOCK_INST_NUMBER
if this process instance set is part if a block instance set then this macro is the block instance number for the block instance set that this process belongs to. Otherwise this macro parameter is 1.This macro is used to implement the SDL operation stop (both in processes and in services).
This macro is inserted after the ALLOC_STARTUP macro and the assignment of parameter values to the signal. It can be used to test if the alloc was successful or not.
This macro is inserted after the SDL_CREATE macro.
This macro should be expanded to a temporary variable used to store a reference to the startup signal data area. It should be assigned in the ALLOC_STARTUP macro and will be used to assign the actual signal parameters (the fpar values) to the startup signal.
This macro can be used to insert additional general components in the startup signals. In all startup signal yPDef structs SIGNAL_VARS will be followed by STARTUP_VARS.
(TIMER_NAME, TIMER_IDNODE, TIMER_PAR_TYPE)
Allocate a data area for the timer signal with parameters.
TIMER_NAME
the name without prefix of the timer.TIMER_IDNODE
the xSignalIdNode
for the timer.TIMER_PAR_TYPE
the yPDef
for the timer.(TIMER_VAR)
There will be one application of this macro in the yVDef
type for the process for each timer declaration the process contains. These declarations can be used to introduce components (timer variables) in the yVDef
struct to track timers. The parameter TIMER_VAR
is a suitable name for such a variable. The suffix _PARA is used if the timer has parameters.
(TIMER_VAR)
These macros will be inserted in start transitions, during initialization of process variables. This makes it possible to initialize the timer variables that might be inserted in the DEF_TIMER_VAR macro. The parameter TIMER_VAR
is the name for such a variable. The suffix _PARA is used if the timer has parameters.
(TIMER_VAR)
These macros will be inserted at an input operation on a timer signal. This makes it possible to update the timer variables that might be inserted in the DEF_TIMER_VAR macro. The parameter TIMER_VAR
is the name for such a variable. The suffix _PARA is used if the timer has parameters. Note that if a timer signal is received in an input * statement, no INPUT_TIMER_VAR will be present in this case.
(TIMER_VAR)
These macros will be inserted at a stop. This makes it possible to perform cleaning up of the timer variables that might be inserted in the DEF_TIMER_VAR macro. The parameter TIMER_VAR
is the name for such a variable. The suffix _PARA is used if the timer has parameters.
(TIMER_NAME, TIMER_IDNODE, TIMER_VAR)
This macro is used to implement the SDL operation active on a timer. Note that active on timers with parameters is not implemented in the Cadvanced/Cbasic Code Generator.
TIMER_NAME
the name without prefix of the timer.TIMER_IDNODE
the xSignalIdNode for the timer.TIMER_VAR
the timer variable that might be inserted in the macro DEF_TIMER_VAR.This is the implementation of now in SDL.
(TIMER_NAME, TIMER_IDNODE, TIMER_VAR, TIMER_NAME_STRING)
This macro is used to implement the SDL operation reset on a timer without parameters.
TIMER_NAME
the name without prefix of the timer.TIMER_IDNODE
the xSignalIdNode
for the timer.TIMER_VAR
the timer variable that might be inserted in the macro DEF_TIMER_VAR.TIMER_NAME_STRING
the name of the timer as a character string.(EQ_FUNC, TIMER_VAR, TIMER_NAME_STRING)
This macro is used to implement the SDL operation reset on a timer with parameters. Before this macro a timer signal with the timer parameters in the reset operation is created.
EQ_FUNC
the name of the generated equal function that can test if two timer instance are equal or not.TIMER_VAR
the timer variable that might be inserted in the macro DEF_TIMER_VAR.TIMER_NAME_STRING
the name of the timer as a character string.(TIME_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_VAR, TIMER_NAME_STRING)
(TIME_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_PAR_TYPE, EQ_FUNC, TIMER_VAR, TIMER_NAME_STRING)
(TIME_EXPR, DUR_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_VAR, TIMER_NAME_STRING)
(TIME_EXPR, DUR_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_PAR_TYPE, EQ_FUNC, TIMER_VAR, TIMER_NAME_STRING)
(TIME_EXPR, DUR_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_VAR, TIMER_NAME_STRING)
(TIME_EXPR, DUR_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_PAR_TYPE, EQ_FUNC, TIMER_VAR, TIMER_NAME_STRING)
These six SDL_SET macros are used to implement the SDL operation set on a timer. The suffix _WITH_PARA indicates the set of a timer with parameters. In this case the SDL_SET macro is preceded by an ALLOC_TIMER_SIGNAL_PAR macro call, plus the assignment of the timer parameters. The suffix _DUR is used if the time value in the set operation is expressed as:
now + expression
In this case both the time value and the duration value (the expression above) is available as macro parameter. The suffix _TICKS is used if the time value in the set operation is expressed as:
now + TICKS(...)
where TICKS is an operator returning a duration value. In this case both the time value and the duration value (the TICKS expression above) is available as macro parameter.
TIME_EXPR
the time expression.DUR_EXPR
the duration expression (only in _DUR and _TICKS).TIMER_NAME
the timer name without prefix.TIMER_IDNODE
the xSignalIdNode
for the timer.TIMER_PAR_TYPE
the yPDef
struct for the timer (only in _WITH_PARA)EQ_FUNC
the function that can be used to test if two timers have the same parameter values (only in _WITH_PARA).TIMER_VAR
the name of the timer variable that might be introduced in the macro DEF_TIMER_VAR.TIMER_NAME_STRING
the name of the timer as a character string.This should be the pointer referring to be timer data area while building
the timer. It should be assigned its value in ALLOC_TIMER_SIGNAL_PAR, and will then be used during assignment of signal parameters and in the SDL_SET macro
This macro is inserted after the ALLOC_TIMER_SIGNAL_PAR macro and the assignment of parameter values to the timer. It can be used to test if the alloc was successful or not.
This macro is inserted after the SDL_SET macro.
The struct components that are needed for each timer instance. Example: sender, receiver, timer type.
As timers are signals as well, after the timer signal has been sent, TIMER_VARS has to be identical to SIGNAL_VARS, except that new component may be add last in TIMER_VARS.
This macro is used to indicate a yPDef
struct for a timer without parameters. Such a timer has no generated yPDef
struct. It is suitable to let XTIMERHEADERTYPE be the name of a struct just containing the components in TIMER_VARS.
(PROC_NAME, PROC_IDNODE, VAR_SIZE)
Allocate a data area (yVDef
) for the called procedure.
PROC_NAME
the name of procedure with prefix.PROC_IDNODE
the xPrdIdNode
of the called procedureVAR_SIZE
the size of the yVDef
struct for the procedure.Allocate a data area (yVDef
) for a procedure when call THIS is used.
(PROC_IDNODE)
Allocate a data area (yVDef
) for the called procedure when calling a virtual procedure. The PROC_IDNODE
parameter is the xPrdIdNode
for the call procedure.
(PROC_NAME, PROC_IDNODE, LEVELS, RESTARTADDR)
These macros are used to implement a call operation in SDL. The yVDef
struct has been allocated earlier (in ALLOC_PROCEDURE) and the actual parameters have been assigned to components in this struct. The suffix _IN_PRD indicates that the procedure call is made in a procedure.
PROC_NAME
the name of procedure with prefix, which is the same as the name of the C function representing the behavior of the procedure.PROC_IDNODE
the xPrdIdNode
of the called procedure.LEVELS
the scope level between the caller and the called procedure.RESTARTADDR
the restart address the symbol number for the symbol after the procedure call.These two macros are only of interest if the PAD functions are left via a return at the end of transitions. In that case any outstanding procedure must be restarted when the process becomes active again.
(RESTARTADDR)
This macro is used to implement a call THIS operation in SDL. RESTARTADDR
is the restart address the symbol number for the symbol after the procedure call.
(PROC_IDNODE, LEVELS, RESTARTADDR)
These macros are used to implement a call operation on a virtual procedure in SDL. The yVDef
struct has been allocated earlier (in ALLOC_VIRT_PROCEDURE) and the actual parameters have been assigned to components in this struct. The suffix _IN_PRD indicates that the procedure call is made in a procedure.
PROC_IDNODE
the xPrdIdNode of the called procedure.LEVELS
the scope level between the caller and the called procedure.RESTARTADDR
the restart address the symbol number for the symbol after the procedure call.This macro is inserted after the ALLOC_PROCEDURE macro and the assignment of parameter values to the procedure parameters. It can be used to test if the alloc was successful or not.
This macro is inserted after the CALL_PROCEDURE macro.
This macro should be expanded to a temporary variable used to store a reference to the procedure data area. It should be assigned in the ALLOC_PROCEDURE macro and will be used to assign the actual procedure parameters (the fpar values).
The implementation of return in SDL.
If this macro is defined then all the code discussed for the macro CALL_PROCEDURE_STARTUP (just above) is removed.
Joins in SDL are normally implemented as goto:s in C, but in one case a more complex implementation is needed. This is when the label, mentioned in the join, is in a super type.
(RESTARTADDR,RESTARTPAD)
(RESTARTADDR,RESTARTPRD)
(RESTARTADDR,RESTARTSRV)
These macros represent join to super type in processes, procedures, and services, in that order.
RESTARTADDR
The restart address in the super type.RESTARTPAD, RESTARTPRD, RESTARTSRV
The PAD function for the super type.
Note: Implicit nextstate operations in RPC calls are treated in the RPC section. |
The state number for an asterisk state. ASTERISK_STATE is usually defined as -1.
The state number used for the error state. ERROR_STATE is usually defined as -2.
The state number for the start state. START_STATE should be defined as 0.
The state number for the start state in a procedure. START_STATE_PRD should be defined as 0.
(STATE_NAME, PREFIX_STATE_NAME, STATE_NAME_STRING)
Nextstate operation (in process or service) of the given state.
STATE_NAME
the name without prefix of the state.PREFIX_STATE_NAME
the name with prefix for the state. This identifier is defined as a suitable state number in generated code and is usually used as the representation of the state.STATE_NAME_STRING
the name of the state as a character string.Dash nextstate operation in a process.
Dash nextstate operation in a service.
(STATE_NAME, PREFIX_STATE_NAME, STATE_NAME_STRING)
Nextstate operation (in procedure) of the given state.
STATE_NAME
the representation of the state.PREFIX_STATE_NAME
the name with prefix for the state. This identifier is defined as a suitable state number in generated code and is usually used as the representation of the state.STATE_NAME_STRING
the name of the state as a character string.Dash nextstate operation in a procedure.
An any decision with two paths are generated according to the following structure:
BEGIN_ANY_DECISION(2) DEF_ANY_PATH(1, 2) DEF_ANY_PATH(2, 0) END_DEFS_ANY_PATH(2) BEGIN_FIRST_ANY_PATH(1) statements END_ANY_PATH BEGIN_ANY_PATH(2) statements END_ANY_PATH END_ANY_DECISION
(NO_OF_PATHS)
Start of the any decision. NO_OF_PATHS
is the number of paths in the decision.
(PATH_NO)
A path (not the first) in implementation part of the any decision. PATH_NO
is the path number.
(PATH_NO)
The first possible path in implementation part of the any decision. PATH_NO
is the path number.
(PATH_NO, SYMBOLNUMBER)
Definition of a path in the decision.
PATH_NO
the path number.SYMBOLNUMBER
the symbol number for the first symbol in this path.The end of the any decision.
End of one of the paths in the implementation section.
(NO_OF_PATHS)
End of the definition part of the any decision. NO_OF_PATHS
is the number of paths in the decision.
The implementation of informal decisions are similar to any decisions.
(PATH_NO)
The first possible path in implementation part of the informal decision. PATH_NO
is the path number.
(NO_OF_PATHS, QUESTION)
Start of the any decision.
NO_OF_PATHS
the number of paths in the decision.QUESTION
the question charstring.(PATH_NO)
The else path in implementation part of the any decision. PATH_NO
is the path number.
(PATH_NO)
A path in implementation part of the any decision. PATH_NO
is the path number.
(PATH_NO, ANSWER, SYMBOLNUMBER)
Definition of a path in the decision.
PATH_NO
the path number.ANSWER
the answer string.SYMBOLNUMBER
the symbol number for the first symbol in this path.(PATH_NO, SYMBOLNUMBER)
Definition of the else path in the decision.
PATH_NO
the path number.SYMBOLNUMBER
the symbol number for the first symbol in this path.(NO_OF_PATHS)
End of the definition part of the informal decision. NO_OF_PATHS
is the number of paths in the decision.
End of the else paths in the implementation section.
The end of the informal decision.
End of one of the paths in the implementation section.
The macros in this section handles testing the validity of for example a component selection of a choice or #UNION variable. Also tests for optional components in structs and for de-referencing of pointers is treated here.
(TAG,VALUE,NEQTAG,COMPNAME,CURR_VALUE)
(TAG,VALUE,ASSTAG,NEQTAG,COMPNAME,CURR_VALUE)
(TAG,VALUE,ASSTAG,NEQTAG,FREEFUNC,COMPNAME, CURR_VALUE)
The CHOICE macros are used to test and to set the implicit tag in a choice variable. The XSET_CHOICE_TAG and XSET_CHOICE_TAG_FREE set the tag when some component of the choice is assigned a value. The FREE version of the macro is used if the choice contains some component that has a Free function. The XCHECK_CHOICE_USAGE is used to test if an accessed component is active or not.
(PRESENT_VAR,COMPNAME)
This macro is used to check that a selected optional component is present. The PRESENT_VAR parameter is the present variable for this component, while COMPNAME is the selected components name as a char string.
(TAG,VALUE,NEQTAG,COMPNAME,CURR_VALUE)
(TAG,VALUE,ASSTAG,NEQTAG,COMPNAME,CURR_VALUE)
(TAG,VALUE,ASSTAG,NEQTAG,FREEFUNC,COMPNAME, CURR_VALUE)
The UNION macros are used to test tag in a union variable. The XCHECK_UNION_TAG and XCHECK_UNION_TAG_FREE check the tag when some component of the union is assigned a value. The FREE version of the macro is used if the union contains some component that has a Free function. The XCHECK_UNION_USAGE is used to test if an accessed component is active or not.
(VALUE,REF_SORTIDNODE,REF_SORT)
These macros are used to implement a test that Null pointers (using the Ref, Own, or ORef generator) are not de-referenced. These macros are inserted before each statement containing a Ref/Own/ORef pointer de-referencing. In case of an ORef pointer it is also checked that the ORef is valid, i.e. that it refers to an object owned by the current process.
(VALUE)
Checks that a ORef pointer is a valid pointer, i.e. NULL, or that it refers to an object owned by the current process.
(SYMB_NO)
A macro generated between the implementation of a value returning procedure call (implicit call symbol) and the symbol containing the value returning procedure call. SYMB_NO
is the symbol number of the symbol containing the value returning procedure call.
(SYMB_NO)
A macro generated between an input or start symbol and the first symbol in the transition. SYMB_NO
is the symbol number of the first symbol in the transition.
A macro generated immediately before a nextstate or stop operation.
(SYMB_NO, C_LINE_NO)
A macro generated between statements in a task. The suffix _PRD indicates that these statements are part of a procedure.
SYMB_NO
the symbol number of the next statement.C_LINE_NO
line number in C of this statement.(SYMB_NO, C_LINE_NO)
A macro generated between symbols in a transition. The suffix _PRD indicates that these symbols are part of a procedure.
SYMB_NO
the symbol number of the next symbol.C_LINE_NO
line number in C of this statement.(LABEL_NAME)
This macro gives the possibility to insert label at the beginning of transitions. Such labels can be useful during debugging. The LABEL_NAME
parameter is a concatenation of state name and the signal name. The * in state *; and input *; will cause the name ASTERISK to appear.
(SIG_NAME_STRING)
This macro is generated at input statements and can, for example, be used to generated trace information about inputs. The SIG_NAME_STRING
parameter is the name of the signal in the input.
(PRS_NAME_STRING)
This macro is generated among the declarations of variables in the PAD function for a process. It can, for example, be used to declare a char *
variable containing the name of the process. Such a variable can be useful during debugging. The PRS_NAME_STRING
parameter is the name of the process as a character string.
(PRD_NAME_STRING)
This macro is generated among the declarations of variables in the PRD function for a procedure. It can, for example, be used to declare a char*
variable containing the name of the procedure. Such a variable can be useful during debugging. The PRD_NAME_STRING
parameter is the name of the procedure as a character string.
The following sequence of macros should be inserted. Most of them concern removal of struct components (in IdNodes) that are not used due to the combination of other switches used.
#define NIL 0 #define XXFREE xFree #define XSYSD xSysD. #if defined(XPRSPRIO) || defined(XSIGPRSPRIO) || defined(XPRSSIGPRIO) #define xPrsPrioPar(p) , p #else #define xPrsPrioPar(p) #endif #if defined(XSIGPRIO) || defined(XSIGPRSPRIO) || defined(XPRSSIGPRIO) #define xSigPrioPar(p) , p #define xSigPrioParS(p) p; #else #define xSigPrioPar(p) #define xSigPrioParS(p) #endif #ifdef XTESTF #define xTestF(p) , p #else #define xTestF(p) #endif #ifdef XREADANDWRITEF #define xRaWF(p) , p #else #define xRaWF(p) #endif #ifdef XFREEFUNCS #define xFreF(p) , p #else #define xFreF(p) #endif #ifdef XFREESIGNALFUNCS #define xFreS(p) , p #else #define xFreS(p) #endif #define xAssF(p) #define xEqF(p) #ifdef XIDNAMES #define xIdNames(p) , p #else #define xIdNames(p) #endif #ifndef XOPTCHAN #define xOptChan(p) , p #else #define xOptChan(p) #endif #ifdef XBREAKBEFORE #define xBreakB(p) , p #else #define xBreakB(p) #endif #ifdef XGRTRACE #define xGRTrace(p) , p #else #define xGRTrace(p) #endif #ifdef XMSCE #define xMSCETrace(p) , p #else #define xMSCETrace(p) #endif #ifdef XTRACE #define xTrace(p) , p #else #define xTrace(p) #endif #ifdef XCOVERAGE #define xCoverage(p) , p #else #define xCoverage(p) #endif #ifdef XNRINST #define xNrInst(p) , p #else #define xNrInst(p) #endif #ifdef XSYMBTLINK #define xSymbTLink(p1, p2) , p1, p2 #else #define xSymbTLink(p1, p2) #endif #ifdef XEVIEW #define xeView(p) p, #define xeViewS(p) p; #else #define xeView(p) #define xeViewS(p) #endif #ifdef XCTRACE #define xCTrace(p) p, #define xCTraceS(p) p; #else #define xCTrace(p) #define xCTraceS(p) #endif #ifndef XNOUSEOFSERVICE #define xService(p) , p #else #define xService(p) #endif #if !defined(XPMCOMM) && !defined(XENV) #define xGlobalNodeNumber() 1 #endif #define xSizeOfPathStack 50 #ifndef xOffsetOf #define xOffsetOf(type, field) \ ((xptrint) &((type *) 0)->field) #endif #define xToLower(C) \ ((C >= `A' && C <= `Z') ? \ (char)((int)C - (int)'A' + (int)'a') : C) #ifndef xDefaultPrioProcess #define xDefaultPrioProcess 100 #endif #ifndef xDefaultPrioSignal #define xDefaultPrioSignal 100 #endif #ifndef xDefaultPrioTimerSignal #define xDefaultPrioTimerSignal 100 #endif #ifndef xDefaultPrioContSignal #define xDefaultPrioContSignal 100 #endif #ifndef xDefaultPrioCreate #define xDefaultPrioCreate 100 #endif #define xbool int #ifndef MAX_READ_LENGTH #define MAX_READ_LENGTH 5000 /* max length of input line */ #endif #ifndef MAX_WRI_LENGTH #define MAX_WRI_LENGTH 5000 #endif #ifndef MAX_M_WRI_LENGTH #define MAX_M_WRI_LENGTH 2*MAX_WRI_LENGTH #endif #define XASS 0 #define XASSMAKE 1 #define XASS2MAKE 2
The xDefaultPrio macros above should, of course, be defined to the suitable default values.
Other macros that should be defined are.
a null value for the type PId.
which is used as RECEIVER parameter in the SDL_2OUTPUT macros. Please see also the section were signals are treated.