Table of Contents Previous Chapter 36 The Master Library

36 The Master Library

Structure of Generated Code

This section gives an overview of the C code that is generated by the SDT C 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 chapter 41, References in SDT-3. 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 562 on page 2201. 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:

--------------------------------------------------------------------
Unit            Compilation unit                                      
--------------------------------------------------------------------
System unit     The top element of the SDL unit subtree for this      
                compilation unit is the system or a system type.      
Package unit    The top element of the SDL unit subtree for this      
                compilation unit is a package.                        
Block unit      The top element of the SDL unit subtree for this      
                compilation unit is a block or a block type.          
Process unit    The top element of the SDL unit subtree for this      
                compilation unit is a process or a process type.      
Service unit    The top element of the SDL unit subtree for this      
                compilation unit is a service or a service type.      
Procedure unit  The top element of the SDL unit subtree for this      
                compilation unit is a procedure.                      
Father unit     The compilation unit which is father to the current   
                compilation unit.                                     
Child unit      A compilation unit which is direct child to the       
                current compilation unit.                             
Sub unit        A compilation unit which is part of the subtree       
                with the current compilation unit as root.            
Unit name       The name of the SDL object that is top element in     
                the subtree for the compilation unit.                 
--------------------------------------------------------------------
Figure 562 : Block Structure and Compilation Unit Structure (Example). 
-----
(fig)  
       
-----

Structure of a Generated File

A generated file will consist of a number of sections, which will be discussed in below:

  1. Include file section
  2. Section with types and forward references
  3. CODE directives, #HEADING section
  4. CODE directives, #BODY section
  5. Section with variables and functions
  6. Initialization section
  7. Function main (only if unit for system or system instance).
If this unit has any subunits or it is a unit for 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

Include files

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 Code Generator, version 
2.02 */
/* C-Advanced option */
#define XSCT_CADVANCED
#define SCT_VERSION_2_02
#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
extern void yInit XPP((void));
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.

Types and Forward References

General Structure

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:

  1. B
  2. P1
  3. entities within P1
  4. P2
  5. entities within P2
  6. S.
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)" on page 2214:

  1. Synonyms translated to macros
  2. Type definitions, #TYPE sections in #ADT and #CODE directives
  3. #HEADING sections in #ADT directives
  4. Synonyms translated to variables.
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, node 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.

Example 137   
/*****
* 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 chapter 41, References in SDT-3).

The following entity classes are treated in subsections below:

System, System Instance, System Type

The following are generated for a system:

/*****
* SYSTEM mall and ENV
* #SDTREF(TEXT,mall.pr,1)
******/
extern XCONST struct xSystemIdStruct ySysR_mall;
extern XCONST struct xPrsIdStruct yEnvR_env;
That is extern definitions of the IdStructs for the system and for the process representing the environment.

The following is generated for a system instance:

/*****
* SYSTEM INST systype1 and ENV
* #SDTREF(TEXT,systype1.pr,2)
******/
extern XCONST struct xSystemIdStruct ySysR_z_systype1;
extern XCONST xIdNode ySysP_z_systype1[];
extern XCONST struct xPrsIdStruct yEnvR_env;
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 its is possible to refer to channels generated in the system instance from C code generated for the system type, for example in an OUTPUT VIA.

Package

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 .c and a .h file   
are generated.                                                         
---------------------------------------------------------------------

Block Substructure

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;

Block, Block Instance, Block Type

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.

Process, Process Instance, Process 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.

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.

The Type yVDef_ProcessName

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.

Service, Service Instantiation, Service Type

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.

Procedure, Operator Diagram

A procedure 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;

The Type yVDef_ProcedureName

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;
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

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 the all the inherited procedures are included in the struct above, starting from the top of the inheritance chain.

Signal, Timer

For a signal or a timer an 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;

Remote Procedure

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.

Variable, Formal Parameter

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

Remote Variable

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

State

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

Channel, Signal Route, Gate

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_CHAN struct xChannelIdStruct yChaR_z1_C1;
extern XCONST_CHAN struct xChannelIdStruct yChaRR_z1_C1;
#define yChaN_z1_C1  (&yChaR_z1_C1)
#endif

Newtype, Syntype, Synonym and #CODE (#TYPE section)

Synonyms Translated to Macros

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

Type Definitions from Abstract Data Types and #CODE
(#TYPE Section)

The unit is scanned for Newtypes, Syntypes, and #CODE directives (among declarations). The search algorithm is described in "Implementation" on page 1966 chapter 34, The C Code Generator.

If newtype or syntype is found:

  1. A typedef which is the translation of the SDL type to C, if the user has not turned off the generation of the type in the #ADT directive.
  2. An extern definition of a xSortIdStruct:
#ifndef XOPTSORT
extern XCONST struct xSortIdStruct ySrtR_sort1;
#define ySrtN_sort1  (&ySrtR_sort1)
#endif
  1. The #TYPE section in the #ADT directive is copied.
  2. If the #ADT directive contains a name of a file that should be included then:
#include "filename"
If #CODE directive is found:

  1. The #TYPE section in the directive is copied.
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

Function Headings from Abstract Data Types

The unit is scanned for Newtypes and Syntypes. In the order the types are found the following is generated:

  1. The #HEADING section in the #ADT directive is copied.
  2. Help function headings and some macros. Examples:
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 ))
#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
#endif
The 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 XERANGE and XEINDEX determine if      
there should be test functions or if the macro versions, which only   
are substituted by the expression, should be used.                    
--------------------------------------------------------------------
The code for the yTstA_TypeName and yTstI_TypeName functions are shown in the section "Newtype, Syntype" on page 2233 and their use is discussed in "Translation of SDL Expressions" on page 2257.
  1. If the type is a newtype, then operator and literal function headings are generated.
    For each literal that should have a generated heading (type not translated to enum type):
extern TypeName  LiteralName XPP(( void ));
For each operator that should have a generated heading:
extern ResultTypeName  OperatorName
  XPP(( appropriate parameters ));

Synonym Variables

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.

Example 138   
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" on page 2238.

CODE directives, #HEADING section

All #CODE directives (among declarations) are scanned in their relative order and the #HEADING sections are copied.

CODE directives, #BODY section

All #CODE directives (among declarations) are scanned in their relative order and the #BODY sections are copied.

Variables and Functions

Here the declaration of the variables and functions, defined in section "Types and Forward References" on page 2203, can be found. The symbol table tree discussed in this previous section is here once more traversed in the same order.

System, System Instance, System Type

The following declarations are generated for a system (including for the symbol table root and the process representing the environment):

/*****
* SYMBOL TABLE ROOT
******/
static XCONST struct xIdStruct ySymbRootVar =
  {xSystemEC xSymbTLink((xIdNode)0, (xIdNode)0),
   (xIdNode)0 xIdNames("SymbolTableRoot")
   XCOMMON_EXTRAS};
/*****
* 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
static XPRSNODE yEnvA_env = (XPRSNODE)0;
static XPRSNODE yEnvB_env = (XPRSNODE)0;
XCONST struct xPrsIdStruct yEnvR_env =
  {xProcessEC xSymbTLink((xIdNode)0,
   (xIdNode)0), (xIdNode)&ySymbRootVar
   xIdNames("env") XCOMMON_EXTRAS, 0, 0 xService(0)
   xOptChan(yEnvO_env), 1 xNrInst(1) xNrInst(1),
   &yEnvB_env,(xptrint)sizeof(xPrsRec)
   xPrsPrioPar(xDefaultPrioProcess), &yEnvA_env
   xTrace(-1) xGRTrace(-1) xBreakB(0) xBreakB(-1)
   xBreakB(0) xMSCETrace(-1) xCoverage(0)
   xCoverage(0) xCoverage(0), 0, (xPrsIdNode)0,
   (xPrdIdNode *)0, (xBlockIdNode)0 xBreakB("")
   XPRS_EXTRAS(env)};
#ifndef XNOSTARTUPIDNODE
static XCONST struct xSignalIdStruct ySigR_Env =
  {xStartUpSignalEC xSymbTLink((xIdNode)0,
   (xIdNode)0), (xIdNode)&ySymbRootVar xIdNames("-")
    XCOMMON_EXTRAS, (xptrint)sizeof(XSIGNALHEADERTYPE),
   0, 0 xFreS(0) SIGCODE(STARTUPSIGNAL) xBreakB("")
   XSIG_EXTRAS};
#endif
In this code four IdStructs are declared, the symbol table root node, the node for the system, the node for the env process, and the startup signal for the env process. To look at each individual component, this code should be compared with the type definitions for the IdStructs, see section "Type Definitions from Abstract Data Types and #CODE (#TYPE Section)" on page 2214.

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" on page 2203. 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};

Package

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
   XPAC_EXTRAS};

Block Substructure

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};

Block, Block Instance, Block Type

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:

Block or block instance with substructure

Block or block instance with processes

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,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};

Process, Process Instance, Process Type

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

/*****
* 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" on page 2241.

/*+++++
* Function for process P1
* #SDTREF(TEXT,mall.pr,30)
++++++*/
YPAD_FUNCTION(yPAD_z00_P1)
{
}

Process Instantiation

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.   
-----------------------------------------------------------------------

Service, Service Instantiation, Service Type

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.

Procedure, Operator Diagram

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" on page 2241.

/*+++++
* Function for procedure Proc2
* #SDTREF(TEXT,mall.pr,89)
++++++*/
#ifndef XNOPROTO
YPRD_FUNCTION(z011_Proc2)
{
}

Signal, Timer

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

Remote Procedure

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

Variable, Formal Parameter

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

Remote Variable

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);

State

For a state a StateIdStruct, together with some list, are generated. The list are used as follows:

/*****
* STATE State3
* <<SYSTEM mall/BLOCK B1/PROCESS P1>>
* #SDTREF(TEXT,mall.pr,72)
******/
static XCONST xInputAction yStaH_z002_State3[] =
 {xDiscard, xDiscard, xInput, xInput,
  xNotInSignalSet};
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};

Enabling Conditions

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

Continuous Signals

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

Channel, Signal Route, Gate

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_CHAN 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_CHAN 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 list with names:

yChaI1_Name, yChaI2_Name, yChaO1_Name, yChaO1_Name

are generated and used for such connections.

Newtype, Syntype

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))
  1. First the SortIdNode for this newtype or syntype is declared. This is followed by the IdNodes for struct components or literals (if valid).
  2. After that the help function bodies are generated.
#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
}
#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
  1. The #BODY section in the #ADT directive is copied.
  2. If the type is a newtype then the operator and literal function bodies of the operators and literals that should have question functions are generated. Code is generated for each operator or literal according to the example below, where the code for an operator with two parameters is shown. A literal is treated as an operator without parameters.
Example 139 :   
#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;
}
  

Initialization

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 Code Generator, version 2.0";
#endif
  xSymbolTableRoot = &ySymbRootVar;
  xInsertIdNode((xIdNode)&ySymbRootVar);
  XINIT_SYSTEMIDNODE(ySysR_z_mall)
  xInsertIdNode((xIdNode)&ySysR_z_mall);
  xEnvId = &yEnvR_env;
  xInsertIdNode((xIdNode)&yEnvR_env);
  XCREATE_ENV_PROCESS
#ifndef XNOSTARTUPIDNODE
  xInsertIdNode((xIdNode)&ySigR_Env);
#endif
#ifndef XOPTSORT
  yInit_Predefined();
#endif
  code to build the symbol table and create
  static process instances
  code 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.

Building the Symbol Table Tree

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.

Creation of Static Process Instances

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:

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);

Initialization of Synonyms

Example 140 : Initialization of synonyms:  
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 ExtSynonymName is an external synonym.

Function main

If it is a unit for a system or a system instance, the main function will be generated last in the .c file.

#ifndef XNOMAIN
#ifndef XMAIN_NAME
#define XMAIN_NAME main
#endif
#ifdef XCONNECTPM
#ifndef XNOPROTO
int XMAIN_NAME( int argc, char * argv[] )
#else
int XMAIN_NAME( argc, argv )
  int    argc;
  char * argv[];
#endif
#else
int XMAIN_NAME XPP(( void ))
#endif
{
#ifdef XCONNECTPM
  xMainInit(yInit, argc, argv);
#else
  xMainInit(yInit);
#endif
  xMainLoop();
  return 0;
}
#endif

Process Behavior

This section will treat the yPAD_ProcessName and yProcedureName functions, that are used to implement the actions performed by processes.

Structure of Process and Procedure Functions

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.

Example 141   
/*---------- 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
}
  
Example 142   
/*-------- Procedure ProcedureName ---------*/
#ifndef XNOPROTO
xbool ProcedureName( xPrsNode P )
#else
xbool ProcedureName( P )
  xPrsNode  P;
#endif
{
  local variable section
  switch statement
  translation of symbols
}
  

Local Variables Section

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];

Section Handling Procedure Calls

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;

Switch Statement

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

Translation of Symbols

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.

Translation of Start

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.

Translation of Input and Continuous Signal

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)

Translation of Task

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" on page 2257.

Translation of Output

OUTPUT statements are translated to the following basic structure:

Example 143   
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.

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 144   
Example of via list containing two channels:

    yViaList[0] = yChaN_ChannelName1;
    yViaList[1] = yChaN_ChannelName2;
    yViaList[2] = (xIdNode)0;
  

Translation of Create

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 t the call of the function xGetSignal, will SDL_CREATE will be expanded to a call of the function SDL_Create.

There are a number of versions of the ALLOC macro:

The value xDefaultPrioProcess will be replaced by the process priority if a #PRIO directive is used.

Translation of Decision

We distinguish between three types of decision:

The informal decisions and the any decisions are treated last in this subsection about decisions.

  #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 {
      ....
    }
#ifdef XEDECISION
    else {
      xErrorDecision(xWriteSort(&yDcn_TypeName,
        ySrtN_TypeName));
      return;
    }
#endif
The transitions within a decision are included directly in the if statement.
An any decision according the following example in SDL-PR:
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)
    END_ANY_PATH
    BEGIN_ANY_PATH(2)
      XBETWEEN_SYMBOLS(7, 390)
    END_ANY_PATH
    BEGIN_ANY_PATH(3)
      XBETWEEN_SYMBOLS(8, 405)
    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)
    END_INFORMAL_PATH
    BEGIN_INFORMAL_PATH(2)
      XBETWEEN_SYMBOLS(21, 593)
    END_INFORMAL_PATH
    BEGIN_INFORMAL_PATH(3)
      XBETWEEN_SYMBOLS(22, 608)
    END_INFORMAL_PATH
    END_INFORMAL_DECISION
XBETWEEN_SYMBOL macros are used to represent the statements in that decision path.

Translation of Set

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:

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:

Translation of Reset

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);

Translation of Export

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);

Translation of Call

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. restartadd 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.

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.

Example 145   
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.

  

Translation of RPC

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 parameters);
   CALL rpc1(as in inupt above);
   OUTPUT pREPLY_rpc1(in/out parameters) TO SENDER;
   NEXTSTATE -;
This is implemented as:

  1. Receive signal pCALL.
  2. Create the pREPLY signal.
  3. Copy in/out parameter from pCALL to pREPLY.
  4. Call procedure with in parameters from the pCALL signal and in/out parameters as the address of the corresponding parameter in pREPLY.
  5. After the procedure has returned, output the pREPLY signal.
  6. Perform an ordinary nextstate operation.

Translation of Nextstate

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.

Translation of Join

A JOIN is translated to a goto in C.

    goto L_LabelName;

Translation of Stop

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.

Translation of Return

A RETURN is generated as an application of the macro
SDL_RETURN. After macro expansion this becomes:

xReleasePrd(VarP);
return (xbool)0;

Translation of Label

An SDL LABEL is simply translated to a C label. The C name will be L_SDLLabelName.

Translation of SDL Expressions

In this section some of the translation rules for expressions are described. For more information see chapter 34, The C Code Generator where, for example, the translation rules for literals and operators in the predefined abstract data types are given.

Subrange Variable

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.

Index in Array

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.

SDL Variable

An SDL variable is represented in C by:

yVarP->VariableName1
yPrdVarP->VariableName2
((yVDP_ProcedureName)VarP->ActivePrd
  ->StaticFather)->VariableName3
(*(yPrdVarP->VariableName4))
The examples are:

  1. Reference to a process variable from the process or from a called procedure.
  2. Reference to a local variable or formal IN parameter from the procedure.
  3. Reference to a variable declared one static level above the one accessing the variable. The number of
    ->StaticFather determines the difference in declaration levels.
  4. Reference to a formal IN/OUT parameter from the procedure.

View

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)))

Import

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

NOW is translated to the macro SDL_NOW which is expanded to SDL_Now().

Self, Parent, Offspring, Sender

SELF, PARENT, OFFSPRING, SENDER are translated to:

P->Self  
P->Parent  
P->Offspring  
P->Signal->Sender

Timer Active

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.

Allocating Dynamic Memory

Introduction

This section deals with the allocation and deallocation of dynamic memory in SDT. 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 C 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.

Processes

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 dependant on the declarations in the process. The translation rules for SDL types, both predefined and user defined, can be found in chapter 34, The C Code Generator.

For each process instance set in the system the following number of structs of a different kind will be allocated:

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.

Compilation switch XPRSOPT

If the compilation switch XPRSOPT is defined then:

Services

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

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 chapter 34, The C Code Generator.

For each signal type in the system the following number of data areas will be allocated:

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 can new memory be allocated.

------------------------------------------------------------------
Note:                                                               
There will be one common avail list for all signals without parame  
ters.                                                               
------------------------------------------------------------------

Timers

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

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 chapter 34, The C Code Generator.

For each type of procedure in the system the following number of data areas will be allocated:

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 can new memory be allocated.

Data types

The predefined SDL type charstring is implemented as char * in C and thus requires dynamic memory allocation.

There is one implementation of the SDL sort Charstring, which is both flexible in charstring length and can reuse all memory.

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" on page 2267.

Charstrings are also handled correctly if charstrings 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 charstrings there will also be a Free function that is utilized to release all dynamic memory in the structured variable. These Free functions are not used in the standard memory handling procedure, but may be utilized to implement alternatives.

Functions for Allocation and Deallocation

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" on page 2300.

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.

Changing the Standard Memory Allocation Procedure

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
xAllocCharstring   xFreeCharstring
and the compilation switch XFREEVARS.

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 xAllocCharstring is call each time a new data area for a charstring is needed.

The xRelease and xFree functions listed will be called when a process, signal, procedure, or charstring 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 charstring values among the variables and parameters in the processes, signals and procedures.

Everything that is said above about charstring values is also true for user defined data types implemented using dynamic memory. If a Free function is introtroduced in the #ADT directive, the type is treated in the same way as charstring in relation to memory handling.

Compilation Switches

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.

  1. Switches defining the compiler.
  2. Switches defining properties of the compiler.
  3. Switches defining a library version.
  4. Switches defining a property of a library version.
  5. Switches defining the implementation of a property.
The first two groups will be discussed in "Adaption to Compilers" on page 2295.

The following switches define the library version:

--------------------------------------------------------
Switch          Corresponds to Library                    
--------------------------------------------------------
SCTDEBCOM       Simulation                                
SCTDEBCLCOM     RealTimeSimulation                        
SCTAPPLCLENV    Application                               
SCTDEBCLENVCOM  ApplicationDebug
(Simulation with environment) SCTPERFSIM PerformanceSimulation
(Library with simulated time, no environ ment functions, no monitor.) --------------------------------------------------------
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.

Description of Compilation Switches

XCLOCK

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.

XCALENDERCLOCK

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.

XPMCOMM

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.

XITEXCOMM

This switch should be defined if a generated simulator should be able to communicate with an ITEX simulator.

XENV

If this compilation switch is defined the environment functions xInitEnv, xCloseEnv, xInEnv, and xOutEnv will be called at appropriate places.

XTENV

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.

XENV_CONFORM_2_3

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.

XSIGLOG

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:

----------------------------------------------------------------------
NrOfReceivers   Output Statement Contents                               
----------------------------------------------------------------------
-1:             A TO clause, but no path of channels and signal         
                routes were found between the sender and the            
                receiver.                                               
0:              No TO clause, and no possible receivers were            
                found in the search for receivers.                      
1:              If the output statement contains a TO clause, a         
                path of channels and signal routes was found            
                between the sender and the receiver.
If the output statement contains no TO clause, exactly one possible receiver was found in the search for receivers.
The output was thus successful. The only error situation that still might be present is if an output with a TO clause is directed to a process instance that is stopped. ----------------------------------------------------------------------
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

XTRACE

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 SetTrace 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.

Example 146 :  
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.

----------------------------------------------------------------------
Note:                                                                   
Note that the variable xPrsN_ProcessName is declared, and there         
fore only available, in the file containing the block where the pro     
cess is defined (and in files representing processes contained in the   
block).                                                                 
----------------------------------------------------------------------
  

XGRTRACE

If this compilation switch is defined it is possible for a simulation to communicate with the organizer and the editor to highlight SDL symbols in the graphical representation.

This feature is used together with the monitor to implement graphical trace and commands like ShowPreviousSymbol and ShowPreviousSymbol. 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.

XCTRACE

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 ShowCLineNumber.

XMONITOR

If this compilation switch is defined, the monitor system is included in the generated application.

XCOVERAGE

This compilation switch makes it possible to generate coverage tables. It should be used together with XMONITOR.

MAX_READ_LENGTH, MAX_WRITE_LENGTH

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 AssignValue and ExamineVariable are entered. If large data types are used, it is possible to redefine the sizes of the buffers from their default size (1000 bytes) to something more appropriate.

XSIMULATORUI

This compilation switch should be defined if the generated simulator is to be executed from the Graphical User Interface to the simulator monitor.

XMSCE

This compilation switch should be defined if the generated simulator should be able to generate Message Sequence Charts.

XSDLENVUI

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).

XNOMAIN

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 Now is 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 (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:

Example 147   
  while (1) {
#ifdef XMONITOR
    xCheckMonitors();
#endif
    if ( SDL_Timer_Prio() >= 0 )
      SDL_OutputTimer();
    else if ( SDL_Transition_Prio() >= 0 )
      SDL_Execute();
  }
  

XMAIN_NAME

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.

XSIGPRIO

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" on page 2042 in chapter 34, The C Code Generator, 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 C 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.

Example 148   
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".

-----------------------------------------------------------------------
Caution!                                                                 
Signal priority is not included in SDL according to ITU Recommen         
dation Z.100, and that sorting the signals in the input port of a pro    
cess instance according to priorities is a direct violation of the SDL   
standard. This feature is however included for users that need such      
a behavior to implement their applications.                              
-----------------------------------------------------------------------

XPRSPRIO

This compilation switch defines that process priorities should be used. For more information see chapter 34, The C Code Generator, section Assigning Priorities - Directive #PRIO.

XSIGPRSPRIO

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.

XPRSSIGPRIO

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.

xDefaultPrio...

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

XOPT

This compilation switch will turn on full optimization (except XOPTCHAN), that is, it will define the following switches:

-----------------------
XOPTSIGPARA  XOPTDCL     
XOPTFPAR     XOPTSTRUCT  
XOPTLIT      XOPTSORT    
-----------------------
For more information, see these switches below. The XOPT switches should not be used together with the monitor.

XOPTSIGPARA

In the symbol table tree (see section "Symbol Table Tree Structure" on page 2131) 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.

XOPTDCL

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.

XOPTFPAR

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.

XOPTSTRUCT

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.

XOPTLIT

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.

XOPTSORT

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.

XNOUSEOFREAL

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.

XNOUSEOFEXPORT

By defining this switch the user states that he is not going to use the export - import concept in SDL.

--------------------------------------------------------------
Caution!                                                        
An attempt to perform an import operation when 
XNOUSEOFEXPORT is defined will result in a compilation error, as the function xGetExportAddr is not defined. --------------------------------------------------------------

XNOUSEOFSERVICE

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.

XPRSOPT

Section "Create and Stop Operations" on page 2171 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:

  1. The need for memory will not increase due to the use of dynamic processes (the memory need depends on the maximum number of concurrent instances).
  2. It will no longer be possible to always find the situation when a signal is sent to a process instance that has performed a stop action.
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.

XOPTCHAN

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:

When the information about channels, signal routes, and gates is not present two types of calculations can no longer be performed:

  1. To check if there is a path of channels and signal routes between the sender and the receiver in an OUTPUT statement with a TO clause. This is no problem as this is just an error test that we probably do not want to be performed in an application.
  2. To calculate the receiver in an OUTPUT without TO clause, if the C Code Generator has not performed this calculation at generate time (see "Calculation of Receiver in Outputs" on page 1974 in chapter 34, The C Code Generator). This is more serious, as it means that OUTPUT without TO cannot always be used. The restrictions are:
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 C 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 chapter 35, 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.

X_LONG_INT

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.

XENVSIGNALLIMIT

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.

XEALL

This switch will define all error handling switches (XE...) and XASSERT given below.

XECREATE

This switch will report if the initial number of instances of a process type is greater than the maximum number.

XECSOP

This switch will report error situations in the Charstring operator.

XEDECISION

This switch will report if no path out from a Decision is found.

XEEXPORT

This switch will report errors during Import actions.

XEFIXOF

This switch will report overflow when an SDL Real value is converted to an SDL Integer value using the operator Fix.

XEINDEX

This switch will report value out of range for array index.

XEINTDIV

This switch will report division by zero in an integer division.

XEOUTPUT

This switch will report errors during Output operations.

XERANGE

This switch will report range errors when a value is assigned to a variable of a sort containing range conditions.

XEREALDIV

This switch will report division by zero in a real division.

XEVIEW

This switch will report errors in View operations

XASSERT

By defining this switch the possibility to define user assertions which is described in chapter 32, The Simulator in section "Assertions" on page 1837.

XFREEVARS, XFREEFUNCS

These switches are used when the default memory handling algorithm is to be changed. By defining the switch XFREEFUNCS the Free functions generated for sorts containing Charstrings will become available. 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" on page 2267.

XPARTITION, XTRACHANNELSTOENV

The compilation switch XPARTITION is used to select if the necessary functions for partitioning an SDL system into several C programs should be available or not. These functions handle the reconnection of channels and signal routes, so paths going to units not part of a partition are redirected to the environment.

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. We have solved this by introducing the macro XTRACHANNELSTOENV. The size of the data area will be the number of channels going to the environment in the system plus the value of XTRACHANNELSTOENV (which by default is 50).

A large system containing more that 50 paths that will be redirected to the environment requires XTRACHANNELSTOENV to be redefined to a larger value.

XDEBUG_LABEL

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.

Example 149   
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.

  

XCONST, XCONST_NOPART, XCONST_COMP

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_NOPART 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. With partitioning is used, XCONST_NOPART should not be defined.

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 memory that will be save in RAM, as probably 90% of the data area for IdStructs can be made const.

Compilation Switches - Summary

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:

------------------------------
SCTDEBCOM       SCTDEBCLCOM     
XPRSPRIO        XCLOCK          
XPARTITION      XPRSPRIO        
XEALL           XPARTITION      
XMONITOR        XEALL           
XTRACE          XMONITOR        
XCTRACE         XTRACE          
XMSCE           XCTRACE         
XCOVERAGE       XMSCE           
XGRTRACE        XCOVERAGE       
XPMCOMM         XGRTRACE        
XSDLENVUI       XPMCOMM         
XITEXCOMM       XSDLENVUI       
XSIMULATORUI    XSIMULATORUI    
SCTAPPLCLENV    SCTDEBCLENVCOM  
XCALENDERCLOCK  XCALENDERCLOCK  
XENV            XPRSPRIO        
XPRSPRIO        XPARTITION      
XOPT            XENV            
XPRSOPT         XPRSOPT         
                XEALL           
                XMONITOR        
                XTRACE          
                XCTRACE         
                XMSCE           
                XCOVERAGE       
                XGRTRACE        
                XPMCOMM         
                XSDLENVUI       
                XSIMULATORUI    
SCTPERFSIM                      
XEALL                           
XPRSPRIO                        
------------------------------
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.

Creating a New Library

----------------------------------------------------------------------
Caution!                                                                
If you create new versions of the library, make sure that the library   
and the generated code are compiled with the same compilation           
switches. If not, you might experience any type of strange behavior     
in the generated application!                                           
----------------------------------------------------------------------
This section describes how to generate a new library. The following topics are covered:

Directory Structure

The structure of files and directories used for the C Code Generator libraries is shown in Figure 563 on page 2290 The directory sdtdir is referenced by the environment variable sdtdir, which is set up in the SDT initialization file sdt.sou. The sdtdir directory is also a child directory to the directory referenced by the environment variable sdtrelease in the file structure that is delivered.

Figure 563 : Directory Structure. 
-----
(fig)  
       
-----
sctdir, as well as sdtdir, is an environment variable that is used by SDT to find the current directory.

In the sdtdir directory three important files are found:

  1. predef92.sdl contains the definition of the predefined sorts in SDL.
  2. sdtsct.knl contains a list of the available libraries that can be used together with code generated by the C Code Generator.
  3. help_sct.hlp contains the help information that can be obtained using the monitor command help.
The file predef92.sdl is read by the SDT Analyser during analysis, while the file sdtsct.knl is used to present the available libraries in the Make dialog in the Organizer (see "Make" on page 1112 in chapter 22, The SDT Organizer).

In the INCLUDE directory, a there are two important groups of files:

  1. 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
  2. The files necessary to include communication with other SCT applications: post.h, post.o, sdt.h.
In parallel with the INCLUDE directory there are a number of directories for libraries in object form. The SCTDEBCOM directory in Figure 563 on page 2290 is an example of such a directory. Each of these directories will contain two files: makeoptions and sctworld.o

The makeoptions file describes the properties of the library, such as the compiler used, compiler options, linker options, and so on, while sctworld.o is a pre-linked library file.

To guarantee the consistency of, for example, compilation flags between the SDL system and the kernel, the makeoptions 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.

File sdtsct.knl

The sdtsct.knl file describes for SDT which libraries that are available. This is presented by the Organizer in the Make dialog see "Make" on page 1112 in chapter 22, The SDT Organizer). 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, if the path starts with a `/', or a path relative to the environment variable sdtdir, if the path does not start with a `/'.

Example 150 :   
Simulation          SCTDEBCOM      
RealTimeSimulation  SCTDEBCLCOM
Application         SCTAPPLCLENV   
ApplicationDebug    /util/sct/SCTDEBCLENVCOM
  
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.

File Makefile

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 file in the directory to get the correct compilation switches and other relevant information.

----------------------------------------------------------------
Caution!                                                          
Do not generate and test libraries in the $sdtrelease directory   
structure. Create an appropriate copy.                            
----------------------------------------------------------------
----------------------------------------------------------------
Note:                                                             
The environment variables sdtdir and sctdir need not refer to di  
rectories in $sdtrelease. Any directory containing the relevant   
files may be used.                                                
----------------------------------------------------------------

File makeoptions

This file has the following structure:

# makeoptionfile to build 
#SDT R-T library 'Application'.
# Remember to set the environment variables
#sdtdir and sctdir to the appropriate values.
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
The information to the right of the equal signs should be seen as an example. These environment variables set in the makeoptions file should specify:

Generated Make Files

The generated make files for an SDL system will as first action include the makeoptions 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 usually generated and executed by the SDT Organizer. Running the Analyzer stand-alone, the make file is however generated and executed by the C Code Generator.

Example 151 :  
Below a make file generated for the SDT system Example is shown. This is generated by the C Code Generator. The make files generated by the SDT Organizer is similar but not exactly equal.

# 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) \
        Example.c $(sctIFDEF) \
            -o Example$(sctOEXTENSION)
  

Adaption to Compilers

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:

  1. In scttypes.h there is a section defining the properties of different compilers, where a new compiler can be added.
  2. In sctos.c the functions that depend on the operating system or hardware are collected. These might need to be changed due to a new compiler, a new OS, or a new hardware.
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" on page 2300.

Compiler Definition Section in scttypes.h

-------------------------------------------------------------------
Caution!                                                             
When compiling under SUN's Solaris 2.1 (or later versions), we       
recommend not to use the compiler /usr/ucb/cc                        
Our experience is that the unbundled compiler is subject to generat  
ing compilation errors.                                              
Instead, we recommend to run the unbundled compiler 
/opt/SUNWSpro/bin/cc -------------------------------------------------------------------
The following switches defining the compiler are currently handled in scttypes.h:

-------------------------------------------------------
SUN4_1_1CC       The Sun C compiler for Sun OS 4.1.3     
SUN4_1_1GCC      The Gnu C compiler for Sun OS 4.1.3     
SUN_CXX          The Sun C++ compiler                    
SUN4_ANSICC      The Sun ANSI C compiler for Sun OS 4.x  
SUN5_ANSICC      The Sun ANSI C compiler for Sun OS 5.x  
SUN5_CC          The Sun C compiler for Sun OS 5.x       
Linux            The Gnu C compiler for Linux            
HPUXANSICC       The HP ANSI C compiler                  
HPUXANSI_RISCCC                                          
HPUXCC           The HP C compiler                       
HPUX_RISCCC                                              
ULTRIXCC         The Digital C compiler for ULTRIX       
AIXV3CC          IBM RS/6000 C compiler for AIX 3.2      
OASYS_CXX        Green Hill/Oasys cross compiler 
C++-68000 and
Microtec C cross compiler for 68000 IC86 Borland C++, version 3.0 and later
Microsoft C, version 6.x IARC51 IAR C compiler for 8051 computers -------------------------------------------------------
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:

Below a typical section defining the properties of a compiler following ANSI-C is given.

Example 152   
#ifdef COMPILER_NAME
#include <string.h>
#include <stdlib.h>
#include <stdarg.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 C 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 dependant functions. Please see below where sctos.c is discussed in detail.

The sctos.c File

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);

SDL_Clock

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 time only handles full seconds.   
---------------------------------------------------------
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.

----------------------------------------------------------------------
Note:                                                                   
If an application does not require a connection with real time (for ex  
ample if it is not using timers and should run as fast as possible),    
there is no need for a clock function. In such a case it is probably    
suitable to use simulated time by not defining the compilation          
switch XCLOCK, whereby SDL_Clock is never called and does not           
need to be implemented. An alternative is to let SDL_Clock always       
return the time value 0.                                                
----------------------------------------------------------------------
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.

------------------------------------------------------------------
Caution!                                                            
The variable must be protected from updates during the period of    
time that the SDL_Clock reads the clock variable.                   
Calling the interrupt routine while the SDL_Clock reads the clock   
variable would cause a system disaster.                             
------------------------------------------------------------------

xSleep_Until

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.

xAlloc

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-dependant.

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.

xFree

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.

xHalt

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.

xGlobalNodeNumber

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 UNIX function getpid, and uses thus the UNIX process number as global node number.

xCheckForKeyboardInput

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.

xGet... and xRelease...

The functions

xGetPId
xReleasePId
xGetSignal
xReleaseSignal
xGetPrd
xReleasePrd
xAlloc_SDL_Charstring
xFree_SDL_Charstring
are called to allocate and release memory used for processes, signals, procedures and charstrings. 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" on page 2267.

 
Table of Contents Next Chapter