[Previous] [Next] [Contents] [Index]


    The Cadvanced/Cbasic Code Generator

The Cadvanced/Cbasic Code Generator translates your SDL system into a C program that you can compile and link together with a runtime library to form an executable program such as a simulator, a validator or, in the case of Cadvanced, an application.

This chapter is a reference manual to the Cadvanced/Cbasic Code Generator. There are also a number of other chapters related to code generation:

Table of Contents 

Introduction

Application Areas for the Cadvanced/Cbasic Code Generator

There are a number of application areas for the Cadvanced/Cbasic Code Generator, for example:

In this part of the manual, the general behavior of the code generator and its application for simulation and debugging are discussed. The possibility to generate simulators is described in The SDT Simulator.

Functional Simulation and Debugging

During the validation of a specification or design of an application expressed in ITU SDL, you can use the Cadvanced/Cbasic Code Generator as a tool for simulation to help you understand and debug the behavior of a system description. (See The SDT Simulator.)

Errors arising from two different areas have to be considered in the validation process. In the language domain, errors due to illegal or illogical usage of the language concepts might be introduced into the specification; while in the problem domain, logical errors might be introduced.

With traditional computer program development, most illegal uses of language concepts are found by compilers or by run-time systems. Examples are syntax errors, missing declarations, division by zero, or indexing an array out of its bounds.

In the problem domain, however, the only feasible ways of detecting logical errors in non-trivial programs are testing and proofreading. When it comes to specifications in SDL, language domain errors can be detected by using the SDT Analyzer, which can be seen as a compiler without a code generation facility (see The SDT Analyzer). To detect problem domain errors, testing by simulating the specification is the main procedure available. Please see also The SDT Validator.

The specification of a protocol in SDL, for instance, specifies a signal interface by giving a hypothetical implementation of the components in the protocol. This strategy immediately brings up two different purposes for simulating the behavior of a system specification: to understand the external view and to understand the internal view.

In the external view, the signal interface is of concern, while the internal behavior of the system specification (the behavior of the processes in the system) is of little or no interest. In the internal view, the internal behavior of the system specification is of concern, while the external signal interface is simply seen as part of the internal behavior.

A simulation of the internal behavior of a system specification constitutes an important part of the validation of the specification, both as a debugging tool and as a means to increase the understanding of the dynamic behavior of the specification. A designer of a system might use this kind of simulation to understand the specification better.

The ability to simulate and debug applications generated by the code generator at an SDL level is a very important feature towards achieving the correct overall behavior of the application. The debugging facilities provided by SDT have much in common with interactive debuggers for ordinary programming languages. The debugging is performed on a host computer.

Another application of the code generator as a simulator generator, is of course in SDL education, where simulation, especially of the internal behavior of a system specification, can serve as a powerful way of clarifying the semantics of SDL concepts.

Performance Simulation

SDT and the Cadvanced/Cbasic Code Generator in SDT can be used for performance simulations. You describe the performance model of the actual system using SDL. This model can be translated to a simulation and executed. By introducing measurements of interesting data, such as queue lengths, delays, and so on, into the SDL model, it is possible to gather statistical data during the execution of the simulation. In The Performance Library, you can find a description of the performance simulation facilities.

To simplify this kind of simulation, a number of SDL abstract data types and their implementations have been developed, where, for example, random number generation and handling of queues are supported. Please see The ADT Library.

Validation

The SDT Validator uses the code produced by the Cadvanced/Cbasic Code Generator to form a program suitable for validation of an SDL system. The Validator uses state space exploration and can be used to:

The Validator is described in The SDT Validator.

Communicating Simulations

You can specify that a generated C program should be able to communicate over the PostMaster, which is the mechanism used for communication between the SDT tools. Signals sent from the SDL system (the generated program) to the environment and signals coming to the SDL system from the environment can be handled. This facility makes it, for example, possible to develop simulation programs for two communicating systems, execute them under SDT and obtain communication between the systems.

As a generated C program does not know what it communicates with, it can of course communicate with any type of application, as long as the application is connected to the PostMaster (the communication medium) and sends signals according to the defined format. How to achieve this is described in Using the Telelogic Tau Public Interface.

A very interesting group of such applications are user interfaces. By connecting a user interface and an SDL simulation you can achieve several things: You can, for example, build well-designed application oriented user interfaces that present what is going on in a simulation, or you can in a simple way define the logic behavior behind a user interface during its prototyping phase.

Overview of the Cadvanced/Cbasic Code Generator

To facilitate the validation of SDL specifications or descriptions, the SDT Analyzer contains an SDL parser, an SDL semantic checker, and the Cadvanced/Cbasic Code Generator.

Creating a C Program

To obtain an executable program that behaves according to an SDL description, you enter the SDL description into the SDT Analyzer, which contains the Cadvanced/Cbasic Code Generator. If the SDL description is syntactically and semantically correct, a C program is generated. You then compile this program using an ordinary C compiler and link it with a precompiled SDL run-time library to form an executable program. See Figure 482.

As indicated above, the C code generation facility contains two components:

Figure 482  : The production of an executable program

Extracted pic [1]

Executing a C Program as a Simulator

The generated C program uses an internal data representation of the SDL objects in the system, for example process instances and signal instances. The process instances will execute transitions in a quasi-parallel manner. During a transition, SDL actions such as tasks, decisions and signal outputs are executed according to the semantics of SDL.

You interact, using SDL terminology, with the simulator through a monitor system, which contains a number of commands to:

Contents of This Chapter

You can find more details on creating and executing a C program in the following sections:

Generating a C Program

A generated C program can be used for several things, for example as a simulator, as a validator, or as an application with the behavior defined by the translated SDL system. The process of generation of simulators, validators, or applications, see below, is started in the Organizer, in the make dialog (see Make) or by the quick buttons for simulation and validation (see Quick Buttons).

The SDT Analyzer, which contains the C code generation facility, can also be started as a stand-alone tool. For more information about this possibility please see The Analyzer Command-Line UI.

Process of Generating a C Program

There are four steps that must be performed to start the execution of, for example, a simulator:

  1. The SDT Analyzer and its built-in Cadvanced/Cbasic Code Generator creates a program expressed in C source code.
  2. The generated C file (or files) is compiled.
  3. The compiled file (or files) is linked together with a pre-compiled library.
  4. The executable program that is created in the link operation is started.

This process has been automated in SDT and requires no user knowledge about compiling or linking of programs. The process is initiated in the Organizer using the quick buttons for simulation and validation, or by using the Make dialog.

A C program can only be generated for an SDL system. The C code that constitutes the program can, however, be generated on multiple files, which means that a local change in, for example, a block diagram only requires a regeneration and recompilation of the code for that unit. The object files, (the compiled versions of the C files) for the other unchanged units can then be used in the link operation to form a new executable program. This feature in automatically used by the make facility and the quick buttons, to minimize the amount of work and thus the turn-around time, for the process from a change in the SDL system to a new simulator (for example).

The separation of the C code for an SDL system can be decided by the user. The Edit Separation command from the Generate menu is used for that purpose, see Edit Separation. The effect on the generated file structure and some guidelines of how to use separation can be found in the section Selecting File Structure for Generated Code -- Directive #SEPARATE.

Executing a C Program

The generated C programs can in principle be compiled as either a simulator, a validator, or an application. Generated applications have no further connection with SDT and are executed as any other application.

A generated simulator or validator can however be started in two different ways:

The SDL Unit for Which Code is Generated

The first time a C program is generated for a system, the complete system will be selected for analysis and C code generation. After that only the unit (system or block) that is changed will be selected. Note that the lowest level of possible regeneration object is a block. That block may not be a block type, or be part of a block type or system type. The reason that a process cannot be generated without regenerating the enclosing block, is that internal process information about, for example, formal parameters are used to generate code for other processes within the same block.

Errors During Code Generation

Errors that may occur during code generation are internal errors. That is, errors due to not yet implemented features of SDL, and errors related to problems with opened or closed operations of files.

An error message starts with the line:

and is followed by a description of the error. The error:

will also contain a trace showing where in the SDL source the feature was found.

All the error messages are listed below, together with a short explanation. Some messages contain a"#" followed by a number which is used to indicate where information, specific of the error situation, can be found.

The translator cannot yet handle this SDL feature. See the list of restrictions in the section Restrictions.

A .c, .h, or .m file cannot be opened. Probably the you have no write permission in the target directory.

These four last errors are internal errors in the Cadvanced/Cbasic Code Generator, which we would be pleased if you could report to us. The contact information for Telelogic Customer Support can be found in How to Contact Customer Support. To be able to investigate such an error, we would very much appreciate it if you could quote the SDL system that triggered the error. Otherwise it is almost impossible to find the problem.

Features

Partitioning

General Ideas

The partitioning concept is a way to divide one SDL system into several applications. As a special case this means also that it is possible to simulate and validate selected parts of a system. You should note the difference between partitioning and separation. The partitioning feature is a way to select the parts of an SDL system which should be handled, while the separation feature is a way to select the file structure for the generated files.

To select a partition (or a program) it is, in simple cases, possible to use selections in the Organizer, and in more general cases possible to work with build scripts, i.e. text files containing commands to the Analyzer (the syntax used when running the Analyzer stand-alone). The restriction in the Organizer view is that only one selection can be handled and that instantiation of OO types cannot be selected. In a build script on the other hand, several component commands can be used to select several parts of a system. As the component command takes an SDL qualifier as a parameter, instantiations can also easily be selected.

Using Selections in the Organizer

To start with the simple case when one block or process should be simulated, this is easy to perform directly from the Organizer: Select the proper block or process and press the Simulate button (or go via Make dialog).

Note: 

If you already have generated a simulation from the Organizer, and want to generate a new one with other options or with another selection, you should perform a Full Make, as changes in options or selection is not handled by the build process. Otherwise compilation or link errors might be the result of the build process.

Only the system, a block or a process can be selected for simulation. Types, including procedures, are only definitions and are not executable objects, while services depend on its enclosing process and cannot be simulated on its own because of these dependencies. Block and process instantiations can be simulated, but only using build scripts, as such objects cannot be selected in the Organizer. The discussion above is of course also valid for generation of validators and applications.

Unconnected Diagrams

As another special case, there might be unconnected diagrams in the Organizer, i.e. objects not bound to a file. If such an object is a block, process, or a procedure, C code can anyhow be generated resulting in, for example, a Simulator or Validator.

Build Scripts

In general cases, build scripts should be used to specify the build process. Using such a file there are a number of features that can be used.

There are two Analyzer commands, see Description of Analyzer Commands, that are of major interest for specifying a partition. First we have the Program command, which takes a name as parameter. Second we have the Component command, which takes a qualifier as a parameter. The Program command gives the start of a partitioning specification, while the Component command is used to select an SDL component that should be part of the partitioning. A partition specification can of course contain a number of components. The Component command is very similar to a selection in the Organizer when running directly from the Organizer.

A program section in a build script typically starts with a Program command and ends with a Generate command.


Example 177 : Build script      

In Windows, the target-directory command could, for example, be:


The example above means that a program containing the implementation of the complete block b1 and the process p22 in block b2 is generated with the Cadvanced code generator. The modularity is user defined and a system header file (.ifc file) will also be generated.

Code from the code generators will be placed in a subdirectory with the name given in the Program command, to the directory given by target directory. If this subdirectory does not exist it will be created.

Note: 

You should always include a target-directory command in a build script, as otherwise the target directory will depend on where SDT is started!

In the example above the generated C code can be found in the directory /home/jk/example/target/MyExample
(In Windows c:\example\target\MyExample).

In the example below three programs are generated for three different partitionings, also using different code generators.


Example 178 : Build script with several programs      

Analyzer commands that are of the type "set up an option" can be placed outside of the Program commands. The options actually used at the generate commands, are the options set up after executing all the commands up to the generate command. All the possibilities in the Make dialog and the Analyze dialog in the Organizer are also provided as commands in the Analyzer. Please see The Analyzer Command-Line UI, for a list of all commands.

Note: 

When build scripts are used, all features in the Analyzer will have its hard coded defaults, if it is not set in the build script. Preferences and your settings in the Organizer are ignored. The default values are given in The Analyzer Command-Line UI, or you can start a stand-alone analyzer (sdtsan) directly in an OS shell and issue the commands Show-Analyze-Options and Show-Generate-Options.

See also SDL Make, for handling of build scripts in the Organizer.

Behavior of Generated Partitioning

The basic idea is to redirect all channel going to objects not part of the current partitioning to the environment. This operation is performed by the code generator at code generation time. This means that all signals sent between objects in the partitioning and objects outside the partitioning, will be seen as signals to or from the environment. This is true everywhere, in simulations, validations, applications, in generated environment header files (.ifc files), and in generated environment functions.

Generation of Support Files

The Cadvanced/Cbasic Code Generator can generate a number of support files, together with the ordinary .c, .h, and makefiles. These files are

The generation of these files can be selected in the Organizer Make dialog, or as an Analyzer command, depending on which interface is used. The details on the system header files and the environment function can be found in The Environment Functions, while the signal number file can be used to assign numbers to all signals in the system. Signal number files are most used in connection with OS integrations.

Implementation

Note: 

If this is the first time you read this manual, we recommend that you temporarily skip the remaining part of this chapter and instead proceed with The SDT Simulator. When you have acquainted yourself with the monitor system, please continue here again.

In this section some implementation details are presented, that can be useful for understanding how a generated simulation or application behaves. Abstract data types are treated in the next section.

Time

A generated C program can be executed in two modes with respect to the treatment of time:

Simulated Time

Using simulated time, which is the most useful mode for simulations, means that the time in the simulation has no connection with the wall clock. Instead the discrete event simulation technique is used. This technique is based on the idea that the current value for the simulation time (Now in SDL) is equal to the time at which the currently executing event is scheduled. After one event is finished, the simulation time is increased to the time when the next event is scheduled and this event is started. Events in SDL will be process transitions, timer outputs, and signals sent to the system from the environment. As an example, the use of the discrete event simulation technique means that if the next event is a timer output scheduled one hour from now, and the next transition is allowed to execute, the timer output will occur immediately. The simulation time will be increased by one hour, but the user does not have to wait one hour.

Real Time

If real time is used, then there will be a connection between the clock in the executing program and the wall clock. In the example above the user would have to wait one hour until the timer output took place. To implement real time a clock function provided by the operating system is used. Not all systems are suitable to simulate in this way. The time scale in the system ought to be seconds or maybe minutes, not milliseconds and not hours.

At program start up the system time, SDL Now, is zero. The system clock is stopped during the time the program spends in the monitor system.

Note: 

The C standard function time used as real time clock returns the time in seconds. It does not handle parts of seconds. The implementation of the clock can be changed by re-implementing the function SDL_Clock in sctos.c.

Scheduling

The process instances in the simulated system will execute transitions that consist of actions like tasks, decisions, outputs, procedure calls, etc., according to the rules of SDL. It is assumed that a transition takes no time and that a signal instance is immediately placed in the input port of the receiver when an output operation occurs.

A transition is always executed without any interrupts, if the user does not manually rearrange the ready queue using an appropriate command provided by the monitor system ( Rearrange-Ready-Queue). It is possible to execute a few SDL symbols in one transition and then to re-arrange the ready queue and execute another transition. The interrupted transition can afterwards be executed to its end.

A quasi-parallel strategy for selecting transitions to be executed is thus the basic scheduling mechanism. SDL does not in itself define an execution strategy and does not have concepts such as priorities for processes, which require concurrent execution of transitions (or interrupts within transitions). The selected strategy is therefore an allowed, but not the only, possible strategy for execution.

As a consequence of the execution strategy, a generated simulator is not directly suited for simulation of "timing effects", that is, situations where the time or order of actions in different process instances is of vital importance.


Example 179 : Scheduling      

An example of such a situation is: Suppose a process instance A outputs two signal instances during the same transition, one to process instance B and one to process instance C. During the corresponding transitions of B and C, a signal instance is sent to process instance D.

If the behavior of the system is dependent on the order in which the signal instances are received in the input port of D, this is a hazard situation where the execution speed of process instances and the delay of signals in channels will determine the behavior. The way to handle such a situation would be to manually decide the order in which transitions should be executed.


As the Cadvanced Code Generator is also intended to generate applications, process priority has been introduced as an additional feature. For more information about how to assign priorities to processes see sub-section Assigning Priorities -- Directive #PRIO.

The Ready Queue

The ready queue is a queue containing all process instances which have received a signal that can cause a transition, but which have not yet completed that transition. The ready queue is ordered firstly according to the priority and secondly according to insert time, that is a process which will be inserted last among the processes with the same priority, but before all processes with lower priority (high priority value = low priority). A process will never be inserted before the process currently executing, as pre-emptive scheduling is not used. In more detail:

Enabling Conditions and Continuous Signals

Enabling conditions and continuous signals are additional concepts in SDL. The model for these concepts use repetitive signal sending, to have the expressions recalculated repeatedly. This model is not suitable during simulation, and definitely not acceptable in an application. We have therefore used an implementation strategy closer to the described behavior of the concepts, rather than the model used to define the concepts.

Implementation Strategy

First we distinguish between those enabling conditions and continuous signals that are dynamic and those that are static, that is containing expressions that can or cannot change their value when the corresponding process is waiting in the state. The expression in a dynamic enabling condition or continuous signal contains some part that can change its value, even though the process does not execute any statements. Or, put more precisely, it contains at least one import, view, or reference to Now.

Static enabling conditions or continuous signals do not provide any problems or any execution overhead, except that the corresponding expressions have to be calculated at nextstate operations. Dynamic enabling conditions or continuous signals, however, have to repeatedly be recalculated. The strategy selected for these expressions is to recalculate them after each transition or timer output performed by any process (and additionally also before the monitor is entered within a transition). In other words, each process waiting in a state containing a dynamic enabling condition or continuous signal executes an implicit nextstate operation between each transition or timer output performed by other processes.

Synonyms

Synonyms

An SDL synonym is implemented either as a C macro (#define) or as a C variable. To be translated to a macro the expression defining the value of the synonym must be:

All other synonyms are implemented as variables given their values at program start up.

The reason for raising this question is because it is relevant to the implementation of arrays and powersets. There are two different implementations for each of these concepts, see Array and Powerset. An array in SDL can either be translated to an array in C or a linked list in C. A powerset can either be translated to a bit array in C or a linked list. The translation method is selected by looking at the index type. If the index type is a syntype with one limited range, the array and bit array scheme is used, otherwise the linked list is used.

If a synonym translated to a variable is used in a range condition of a syntype and the syntype is used as an index sort in an array or powerset instantiation, the linked list scheme is used to implement the array or powerset. The reason for this is that the length of the array cannot depend on a variable in C.

External Synonyms

External synonyms can be used to parameterize an SDL system and thereby also a generated program. The values that should be used for the external synonyms can either be read by the generated program during start up, or included as macro definitions into the generated code. The Cadvanced/Cbasic Code Generator can handle both these cases -- it is not necessary to select which way should be used for each synonym until the program is compiled.

Using a Macro Definition

To use a macro definition in C to specify the value of an external synonym, perform the following steps:

  1. Write the macro definitions on a file.


  2. Example 180 : Macro Definition      

    #define synonym1 value1
    #define synonym2 value2
    


    The synonym names are the SDL names (without any prefixes) and with any character not in letters, digits or underscore removed.
  3. Introduce the following #CODE directive at the system level among the SDL definitions of, for example, synonyms, sorts, and signals but before any use of the synonyms.


  4. Example 181 : #CODE Directive      

    /*#CODE
    #TYPE
    #include "filename"
    */
    


    If this structure is used, the value of an external synonym can be changed merely by changing the corresponding macro definition and recompiling the system.

Note: 

When an application is created, macro definitions should be used for all external synonyms, as the function for reading synonym values stored on file is not available. (See below.)

Reading Values at Program Start up

The other way to supply the values of the external synonyms is to read the values at program start up. If there are any external synonyms that do not have a corresponding macro definition, it is possible to choose between supplying the values of the remaining external synonyms from the keyboard or to use a file containing the values.

When the application is started, the following prompt appears:

If the user chooses to read the values from the terminal, he will be prompted for each value. In the other case the user should have created a file containing the external synonym names and their corresponding value according the following example:


Example 182 : Values at Program Startup      

The synonyms may be defined in any order.

Import -- Export

These concepts are not implemented with the full semantics according to the model in the SDL recommendation. The model says that an imported value should be obtained using a signal interchange between the importer and exporter.

In the Cadvanced/Cbasic Code Generator we use a model where the imported value is directly obtained from the exporter, which of course make the import operation much faster. However, the scheduling effect of the signal interchange is lost, as well as the change of SENDER in the involved processes. If these effects are important for an application, remote procedure calls can be used instead, see below.

Remote Procedure Calls

Remote procedure calls (RPC) have much in common with import/export, except that instead of obtaining one value, RPCs give the opportunity to execute a procedure in the exporting process. In the Cadvanced/Cbasic Code Generator the model described in the SDL recommendation is used in detail to implement RPCs.

This means that a remote procedure call is translated to:

In the exporting process there will be implicit transitions where the pCALL signal can be handled.

For more details about this model, please see the SDL recommendation.

Procedure Calls and Operator Calls

In SDL-92 value returning procedures (and remote procedures) are introduced. This means that an SDL procedure can be called within an expression. In the Cadvanced/Cbasic Code Generator such procedure calls are implemented according to the model in the SDL recommendation, that is by inserting an extra CALL just before the statement containing the value returning procedure call. The result from the call is stored in an anonymous variable, which is then used in the expression.


Example 183 : Procedure Call      

Note: 

The value returning procedure calls are transformed to ordinary calls, by adding a new IN/OUT parameter for the procedure result, last in the call.

Operators which are defined using operator diagrams, are according the models in the SDL recommendation, treated exactly as value returning procedure.

External Procedures

External procedures is a extension to SDL introduced in SDL-96. An external procedure is defined in a text symbol as a procedure heading:

Instead of giving an implementation for the procedure the keyword external is inserted. The purpose of external procedures in SDL are to specify the existence of procedures without giving their implementation.

The Cadvanced/Cbasic Code Generator will generate no code for an external procedure declaration and will translate a call to such a procedure to an ordinary C function call. It is then up to the user to provide the C implementation of this function. Note that the code generator will in the generated function call use the name of the external procedure as it is. No prefix is inserted in this case, just as for external synonyms.

Any

There are two different applications of any. It is possible to write

within an expression, or to write just

in a decision. The second case, with any in a decision, is implemented in the following way:

The first case, the any(SortName) within an expression, is implemented using a random number generator to draw a random number of the given type.

Note: 

any(Sort) where Sort is a syntype is only implemented if the syntype contains at most one range condition which is of the form a:b, that is one limited range.

If any(SortName) is used for a sort violating the note above, there will be a C compilation error on the symbol ANY_SortNameWithPrefix. This means that a user can implement any for such sorts himself by defining a C macro with this name, that implements any for the given sort. Such a macro should be inserted in the #TYPE section of a #ADT directive in the syntype.

Calculation of Receiver in Outputs

The Cadvanced/Cbasic Code Generator contains an algorithm that calculates the receiving process instance set, for outputs without TO, considering channels, signal routes, connection points, and via list. There are however a few restrictions for the algorithm:

This algorithm means that for an ordinary SDL-88 system, that is not generated using separate units, no information about the channels and signal routes are needed to direct signal to the correct receiver. For more information about the possible optimizations in applications, please see the compilation switch XOPTCHAN and the ADT PidLit ( The Data Type PIdLit).

Abstract Data Types

In this section we will discuss the following topics:

SDL Predefined Types

Mapping Table

Below is a table which summarizes the mapping rules between SDL and C, concerning the predefined types in SDL and their operators. Note that many of the operators are in C defined as macros, and expanded by the C preprocessor to simple operators in C.

SDL name/operator C name/expression/operator
Boolean
SDL_Boolean
False, True
SDL_False, SDL_True
not
xNot_SDL_Boolean
and
xAnd_SDL_Boolean 
or
xOr_SDL_Boolean
xor
xXor_SDL_Boolean
=>
xImpl_SDL_Boolean
=, /=
yEqF_SDL_Boolean, yNEqF_SDL_Boolean
Character
SDL_Character
NUL
SOH

...

SDL_NUL 
SDL_SOH 

... (for all unprintable characters)

'a'
'b'

...

'a' 
'b'

... (for all printable characters except ' and \)

'''', '\'
'\'',  '\\'
chr
xChr_SDL_Character
num
xNum_SDL_Character
<, <=, >, >=
xLT_SDL_Character, xLE_SDL_Character, xGT_SDL_Character, xGE_SDL_Character
=, /=
xEqF_SDL_Character, xNEqF_SDL_Character
Charstring
SDL_Charstring
'aa'
SDL_CHARSTRING_LIT("Laa","aa")
MkString
xMkString_SDL_Charstring
Length
xLength_SDL_Charstring
First
xFirst_SDL_Charstring
Last
xLast_SDL_Charstring
//
xConcat_SDL_Charstring
SubString
xSubString_SDL_Charstring
=, /=
yEqF_SDL_Charstring, yNEqF_SDL_Charstring
Integer
SDL_Integer

0, 1 etc.

SDL_INTEGER_LIT(0),

SDL_INTEGER_LIT(1) etc.

+
xPlus_SDL_Integer

- (monodic, dyadic)

xMonMinus_SDL_Integer, xMinus_SDL_Integer
*
xMult_SDL_Integer
/
xDiv_SDL_Integer
mod
xMod_SDL_Integer
rem
xRem_SDL_Integer
Float
xFloat_SDL_Integer
Fix
xFix_SDL_Integer
<, <=, >, >=
xLT_SDL_Integer, xLE_SDL_Integer, xGT_SDL_Integer, xGE_SDL_Integer
=, /=
yEqF_SDL_Integer, yNEqF_SDL_Integer
Natural
SDL_Natural
Real
SDL_Real
12.45, ...
SDL_REAL_LIT(12.45, 12, 450000000)

- (monodic, dyadic)

xMonMinus_SDL_Real, xMinus_SDL_Real
+
xPlus_SDL_Real
*
xMult_SDL_Real
/
xDiv_SDL_Real
<, <=, >, >=
xLT_SDL_Real, xLE_SDL_Real, xGT_SDL_Real, xGE_SDL_Real
=, /=
yEqF_SDL_Real, yNEqF_SDL_Real
PId
SDL_PId
NULL
SDL_NULL
=, /=
yEqF_SDL_PId, yNEqF_SDL_PId
Duration
SDL_Duration
23.45
SDL_DURATION_LIT(23.45, 23, 450000000)
+
xPlus_SDL_Duration

- (monodic)

xMonMinus_SDL_Duration

- (dyadic)

xMinus_SDL_Duration
* (Duration * Real)
* (Real * Duration)
xMult_SDL_Duration, xMultRD_SDL_Duration
/
xDiv_SDL_Duration
<, <=, >, >=
xLT_SDL_Duration, xLE_SDL_Duration, xGT_SDL_Duration, xGE_SDL_Duration
=, /=
yEqF_SDL_Duration, 
yNEqF_SDL_Duration
Time
SDL_Time
23.45
SDL_TIME_LIT(23.45, 23, 450000000)
+ (Time + Duration)
+ (Duration + Time)
xPlus_SDL_Time,
xPlusDT_SDL_Time

- (result: Time)

xMinusT_SDL_Time

- (result: Duration)

xMinusD_SDL_Time
<, <=, >, >=
xLT_SDL_Time, xLE_SDL_Time,
xGT_SDL_Time, xGE_SDL_Time
=, /=
yEqF_SDL_Time, 
yNEqF_SDL_Time
IA5String
SDL_IA5String
NumericString
SDL_NumericString
VisibleString
SDL_VisibleString
PrintableString
SDL_PrintableString
Bit
SDL_Bit
not
xNot_SDL_Bit
and
xAnd_SDL_Bit
or
xOr_SDL_Bit
xor
xXor_SDL_Bit
=>
xImpl_SDL_Bit
=, /=
yEq_SDL_Bit, yNEq_SDL_Bit
Bit_String
SDL_Bit_String
not
xNot_SDL_Bit_String
and
xAnd_SDL_Bit_String
or
xOr_SDL_Bit_String
xor
xXor_SDL_Bit_String
=>
xImpl_SDL_Bit_String
MkString
xMkString_SDL_Bit_String
Length
xLength_SDL_Bit_String
First
xFirst_SDL_Bit_String
Last
xLast_SDL_Bit_String
//
xConcat_SDL_Bit_String
SubString
xSubString_SDL_Bit_String
BitStr
xBitStr_SDL_Bit_String
HexStr
xHexStr_SDL_Bit_String
=, /=
yEq_SDL_Bit_String, yNEq_SDL_Bit_String
Octet
SDL_Octet
not
xNot_SDL_Octet
and
xAnd_SDL_Octet
or
xOr_SDL_Octet
xor
xXor_SDL_Octet
=>
xImpl_SDL_Octet
ShiftL
xShiftL_SDL_Octet
ShiftR
xShiftR_SDL_Octet
Plus
xPlus_SDL_Octet
Minus
xMinus_SDL_Octet
Mult
xMult_SDL_Octet
I2O
xI2O_SDL_Octet
O2I
xO2I_SDL_Octet
Div
xDiv_SDL_Octet
Mod
xMod_SDL_Octet
Rem
xRem_SDL_Octet
BitStr
xBitStr_SDL_Octet
HexStr
xHexStr_SDL_Octet
<, <=, >, >=
yLT_SDL_Octet, yLE_SDL_Octet, yGT_SDL_Octet, yGE_SDL_Octet
=, /=
yEq_SDL_Octet, yNEq_SDL_Octet
Octet_String
SDL_Octet_String
MkString
xMkString_SDL_Octet_String
Length
xLength_SDL_Octet_String
First
xFirst_SDL_Octet_String
Last
xLast_SDL_Octet_String
Concat
xConcat_SDL_Octet_String
SubString
xSubString_SDL_Octet_String
BitStr
xBitStr_SDL_Octet_String
HexStr
xHexStr_SDL_Octet_String
Bit_String
xBit_String_SDL_Octet_String
Hex_String
xHex_String_SDL_Octet_String
=, /=
yEq_SDL_Octet_String, yNEq_SDL_Octet_String
Object_Identifier
SDL_Object_Identifier
MkString
xMkString_SDL_Object_Identifier
Length
xLength_SDL_Object_Identifier
First
xFirst_SDL_Object_Identifier
Last
xLast_SDL_Object_Identifier
Concat
xConcat_SDL_Object_Identifier
SubString
xSubString_SDL_Object_Identifier
=, /=
yEq_SDL_Object_Identifier, yNEq_SDL_Object_Identifier
Null (sort)
SDL_Null
Null (literal)
SDL_NullValue
=, /=
yEq_SDL_Null, yNEq_SDL_Null

C Definitions

We will here discuss the types and macros supplied by the runtime library in the Cadvanced/Cbasic Code Generator for the predefined types in SDL. These macros and extern definitions for functions can be found in the file sctpred.h, except for the PId sort which is handled in the file scttypes.h.

Note: 

For more information about the Charstring sort, see the section Handling of the Charstring Sort.

Translation of Sorts

The following data types are handled by the Cadvanced/Cbasic Code Generator:

Predefined Types

All the predefined data types (Integer, Natural, Boolean, Character, Charstring, Real, Time, Duration, PId, Bit, Bit_String, Octet, Octet_String, Object_Identifier, IA5String, Numeric_String, Printable_String, and Visible_String) are completely handled. The name of these types in the generated C code will be SDL_Integer, SDL_Natural, SDL_Boolean, and so on. The translation rules for these types and their operators are discussed in more detail in the SDL Predefined Types.

Enumeration Type

A sort which is not a struct and does not contain any inheritance or generator instantiation, but which contains a literal list, is seen as an enumeration type. See the example below. Such a type is translated to int, together with a list of defines where the literals are defined as 0, 1, 2, and so on. As in all examples in this sub-section, the prefixes, which are added to names when they are translated to C, are not shown. The prefixes are added to make sure that no name conflicts occur in the generated program. For more information about prefixes see Names and Prefixes in Generated Code.


Example 184 : Enumeration Type                        

is translated to:


Struct

An SDL struct is translated to a struct in C, as can be seen in the example below.


Example 185 : Struct                                    

is translated to:


All the properties of a struct in SDL are preserved in the C code.

The predefined operators Extract! and Modify! are implemented as component selections in the struct in the same way as in SDL, that is, if S is a variable of type Str, then S!a in SDL is translated to S.a in C.

The predefined operator Make!, which is a constructor of a struct value, is implemented by generating a Make function in C. This means that the expression "(. 12, true, 0.22 .)" in SDL is translated to the C function call Make(12, true, 0.22). The Make function will, of course, be given a name that makes it unique.

The components of a struct may be of any sort that the code generator can handle. A component may, however, not directly or indirectly refer to the struct sort itself. As an example the sort Str above may not have a component of sort Str. In such a case the translation to a C struct would not any longer be valid.

There are some extensions to SDL that are handled by the code generator. It is possible to define bit fields, i.e, to define the size of components (as in C) and to have optional components and components with initial values (as in ASN.1). Examples are shown below.


Example 186 : Struct with bit fields                                    

is translated to:


Note that only Integer and UnsignedInt should be used in bit field components.


Example 187 : Struct                                    

is translated to:


Both optional components and components with initial values have a Present flag. This is according to ASN.1 and the translation of ASN.1 to SDL defined in Z.105. The present flag for a component with initial value is always true, while the present flag for an optional component is false until the component is assigned a value. In SDL the present flags can only be accessed through operators and cannot be changed.

Union

Please see also the CHOICE concept presented below, as it usually provides a better and more secure solution to the same kind of problems.

Using the directive #UNION (see example below) it is possible to tell the Cadvanced/Cbasic Code Generator to generate a union according to the following example:


Example 188 : Union                                    

is translated to:


The first component in the struct is assumed to be a tag value indicating which of the union components that are active. The tag should either be integer or an enumeration type. Tag value 0 or first enumeration literal is used to indicate that the first of the remaining components are active, and so on. On the SDL level a #UNION struct should be handled just like any other struct. It is up to the code generator to generate the correct code for operations on the struct, like assignment, test for equality, component selection, and so on.

Note: 

It is completely up to the user to make certain that only valid components in a #UNION struct are accessed. During simulation, however, the test ensures that only valid components are accessed.

UnionC

By using the directive #UNIONC according to the example below, it is possible to tell the Cadvanced/Cbasic Code Generator to generate a true C union.


Example 189 : UnionC                                    

is translated to:


.

Note: 

The #UNIONC directive is not recommended for use as the Cadvanced/Cbasic Code Generator cannot give any support for checking the validity of component selection. Both the #UNION directive and the CHOICE concept discussed below are much better.

Note also that pointer types, including Charstrings are not allowed in #UNIONC structs, as it is not possible to know when to allocate and de-allocate memory for such components.

Choice

Choice, which is an SDL extension originating from the needs when translating ASN.1 to SDL, can be used to express a union with implicit tag.


Example 190 : Choice                                    

is translated to:


The component Present, which is the tag field, and its type (StrPresent in the example above) are both available in SDL. The Present component can in SDL be accessed, but not changed, through:

The Present component is automatically set by the code generator when a component in the choice is given a value.

Note that during simulations and validations, it is automatically tested that a component "is present" when an attempt is made to access the component. A run-time error is issued if this is not the case.

Array

Instantiations of the predefined generator array can be handled by the code generator with the following restriction: The component and index sort may be any sorts that the code generator can handle, but may not directly or indirectly refer to the array type itself (see also the previous paragraph on struct).

If the index sort is a discrete sort, with one closed interval of values, that is of the following sorts:

then the SDL array is translated to a struct containing an element which is an array in C. The reason for this translation strategy is that an array may not be result type of a C function (only the address of an array may be result type), which is necessary as SDL operators may have arrays as result. By embedding the C array in a C struct this problem is avoided.

If the index sort is not one of the sort in the enumeration above, the SDL array is translated to a linked list. The list head contains the default value for all possible indexes, while the list elements contain value pairs, (index_value, component_value), for each index having a component value not equal to the default value.


Example 191 : Array      

is translated to:


All the properties of an array in SDL are preserved in the C code.

The predefined operators Extract! and Modify! are implemented as component selection of the array in C in the same way as in SDL, so if AVar is a variable of type Arr, and Index is a valid index expression, then AVar(Index) in SDL is translated to AVar.A[Index] in C. In the case of a link list implementation of the array, component selection is made through function calls.

The predefined operator Make!, which is a constructor of an array value, is implemented by generating a Make function in C. This means that the expression "(. 0.22 .)" in SDL is translated to the C function call Make(0.22). The Make function will of course be given a name that makes it unique.

String

Instantiations of the predefined generator string can be handled by the code generator with the following restriction: The component sort may be any sorts that the code generator can handle, but may not directly or indirectly refer to the string type itself. Strings are translated to linked list containing one element for each element in the string value. Operations and component selection in string sorts are fully supported.

Powerset

Instantiations of the predefined generator powerset can be handled by the code generator with the following restriction: The component sort may be any sorts that the code generator can handle, but may not directly or indirectly refer to the powerset type itself.

There are two translation schemes for powersets. If the component sort fulfills the conditions for index sorts mentioned in the subsection about arrays above ( Array), an array of 32-bit integers are used. Each bit will be used to represent a certain element whether it is a member of the powerset or not. If this is not the case, a linked list of all elements that are member of the set, is used to represent the powerset. All the available operations defined for Powersets in SDL are supported.

Bag

The Bag generator, which is introduced in SDL in Z.105, i.e. in the mapping from ASN.1 to SDL, is similar to powerset. However, it is possible to have several elements with the same value in a bag. A bag is always translated into a linked list, with one element for each value that is a member of the bag. Each element contains the value and the number of occurrences of this value.

Syntypes

Syntypes may be defined for any sort that the code generator can handle, giving a new name for the sort and possibly a new default value for variables of the sort. Range conditions that restrict the allowed range of values are also allowed.

The syntype is translated to a type equal to the parent type using typedef. The check that a variable of a syntype is only assigned legal values is implemented in a test function that is generated together with the type definition. An attempt to assign an illegal value to such a variable will be reported as an SDL dynamic error. If the syntype is can be used as index sort in an array and the generated type in C would become an array, there will also be a test function that can be used to check that an index value is within its range in an array component selection.


Example 192 : Syntypes       

is translated to:


Default Values

Default values are fully supported for all sorts that the code generator can handle, both if a default value is given in a sort definition and if an initial value is given in a variable definition (DCL).

Default values will also be assigned to all variables and components which do not have a default value specified in SDL. The reason for this is to avoid handling undefined variables in C, which might give serious problems and unexpected behavior of an executing program. The values selected by the code generator in such a case can be found below.

Note: 

This is a deviation from SDL-92. It means that the generated program does not handle the value undefined for any type.

The following start values are used if no default value is given in the sort and no start value is given in the data definition (DCL):

Sort Start Value

Integer, Natural, Bit, Octet

0

Bit_String, Octet_String

empty string

Real

0.0

Time

0.0

Duration

0.0

Character

NUL

Charstring

'' (empty string)

Boolean

false

PId

null

Enumeration

First value in enumeration

Struct and Array

  1. Components initialized according to their defaults
  2. Components initialized according to this table.

String, Powerset, and Bag

Empty string, empty powerset, empty bag

Syntypes

Default value of father type (newtype)

According to this table for father type.

Operators

In SDL-92, it is possible to define the behavior of operators in ADTs directly in SDL, using operator diagrams. Such operators are translated to C by the Cadvanced/Cbasic Code Generator, and none of what is said below is valid for such an operator.

A user defined operator in an SDL sort definition, which is not defined by an operator diagram, is translated to a C function which asks the user for the result of the operation. At a call of an operator, the user is supplied with information describing what the operator and the sort are called, and given information about the parameter values. You are then requested to answer with the result value. If you press <Return> at the prompt for the result, the default value of the actual result type is returned.


Example 193 : Operator      

assuming that newtype S contains an operator

Literals

In sorts that are translated to enumeration types in C, literals are obviously handled by the code generator. In sorts that are not enumeration types, literals are treated as operators without parameters and are handled in exactly the same way as user defined operators.

Note: 

The Cadvanced/Cbasic Code Generator does not permit naming of literals using name class literals or character strings.

Axioms and Literal Mappings

Axioms and literal mapping are allowed by the code generator in sorts, but are completely ignored.

Implementation of User Defined Operators

Including Implementations of Operators

In the previous subsection, the default behavior of the Cadvanced/Cbasic Code Generator concerning operators (not defined in operator diagrams) and literals were described. If you do not specify otherwise interactive functions are generated, which, in each case, will ask you for the operator result or literal value. This is a fast way of getting started, but you will probably find it tedious in the long run, especially if you are using abstract data types extensively. To cope with this problem and to make it possible to generate applications, the code generator offers a possibility to include implementations written in C of the operator and literal functions. This possibility can be used as an alternative to operator diagrams, where the operator is defined indirectly in SDL.

It is possible to choose between two alternatives to implement operators and literal functions:

Note: 

The C functions are divided into a function heading (extern or static declaration) and a function body.

An example of a function heading (extern declaration) is:


Example 194 : Implementing an Operator      

while the corresponding function body is:


Note: 

In all examples of this manual, prototypes as defined in ANSI-C, are used. If the compiler does not support prototypes, the examples should be transformed to the old C-style for functions.

The main reason for this division of functions into heading and body is the separate compilation scheme used in C. If, for example, an abstract data type is defined in a system and used in a process in the system, and the process is generated on a separate file, then there has to be a module interface file (a .h file) for the system containing the external interface (types, extern declarations of functions and so on). The interface file should then be included in the file generated for the process.

Even if separate compilation is not used, the division of functions into heading and body is useful. By having static declarations of the functions, the order in which functions must be defined is relaxed. If static declarations were not used, a function could only call the functions that are defined textually before the actual function.

To select the way the Cadvanced/Cbasic Code Generator should generate code for operators and literals, code generator directives are used. A code generator directive is an SDL comment with the first characters equal to `#', followed by a sequence of letters identifying the directive. In this case the letters are ADT (for Abstract Data Type) and OP (for operator). An ADT directive and a OP directive should thus look like:

The text is not case sensitive.

OP directives are recognized at two different positions in an abstract data type:

ADT directives are recognized immediately before the reserved word ENDNEWTYPE (or ENDSYNTYPE).


Example 195 : Implementing an Operator (#ADT)       

At each of the positions after a literal name or operator definition, there is a possibility to specify how this literal or operator should be implemented. In the directive immediately before ENDNEWTYPE the default implementation technique can be given. When the code generator determines how to generate code for a literal or an operator, it first looks for an OP directive after the literal name or operator definition. If no such directive is found it looks for a directive immediately before ENDNEWTYPE. If no ADT directive is found here, the generation technique Q (question) is assumed.

An OP or ADT directive specifying a generation technique should have the following structure:

The letter between the parentheses should be either Q (question) or B (body). The interpretation of Q and B was explained earlier. If B has been specified for any operators or literals, then the C code for these functions must be supplied by the user. This code should be placed in the #BODY section in the ADT directive, according to the following example:


Example 196 : Implementing an Operator (#ADT)       

The section name, i.e #BODY, must be given on a line of its own and must have the # character in the first position of the line. Upper case and lower case letters are as usual considered to be equal. If the section is empty, the section name can also be removed.

Note: 

The Cadvanced/Cbasic Code Generator will not check the consistency between the specification of implementation techniques and the actual code included in the body section. This check is, together with checking the C code for syntactic and semantic errors, left to the C compiler.

Unfortunately it is not possible to have C comments within the code that is included in a #ADT directive, as SDL and C use the same symbols for start and end of comments. If a C comment is included, the SDT Analyzer will consider the end of the C comment as the end of the SDL comment. Instead a C macro called COMMENT can be used according to the examples below. Note that there might be some compiler dependent restriction of the character set allowed within the COMMENT macro. For example, the character `;' might not be allowed.


Example 197  : Comment in ADT      

The function headings representing literals and operators are determined by their corresponding definition in SDL. The number of parameters, their types, the result type of the function and function name are all defined in SDL. In the example above, where the struct Str is defined, there are two literals (Lit1 and Lit2) and two operators
(Op1: Str, integer --> Str; and Op2: Str, Boolean --> Str;). The function heading of the corresponding C functions should be:


Example 198 : Implementing an Operator      

The function bodies, which should be supplied by the user if B is specified in the OP or ADT directive, are ordinary C functions.


Example 199 : Implementing an Operator      

Before it is possible to give a complete example of an abstract data type with implementation of its operators supplied as C functions, it is necessary to look at the problem of names. When a name of some object in SDL is translated to C, a suitable sequence of characters, a prefix, is added to the SDL name, to make the name unique in the C program, see also Names and Prefixes in Generated Code. This strategy is selected in the Cadvanced/Cbasic Code Generator to avoid name conflicts in the generated code, but it makes it also impossible to predict the full name of, for example, a type or a function, in the generated program. To handle this problem the user can tell the code generator to translate a name in the C code in the same way as SDL names are otherwise translated. This is specified by enclosing the SDL name between `#(' and `)' in the C code. The two functions in the previous example and their headings would then become:


Example 200 : Including SDL name in C Code      

This facility to access an SDL name in C code is described in more detail in the section Accessing SDL Names in C Code -- Directive #SDL. A few observations concerning the example above might be appropriate:

  1. The predefined sorts in SDL, that is for example integer, natural, Boolean have the names SDL_Integer, SDL_Natural, SDL_Boolean, and so on in the generated code. These types should not be enclosed between `#(' and `)'.
  2. The component names of a struct are unchanged in the struct implementation in C, which means that struct components should not be enclosed between `#(' and `)' either.

Two Examples of ADTs

We now give two complete examples of abstract data types.


Example 201 : ADT Example      

The only thing in this example which has not been discussed earlier is the use of the make function in the function for literal Lit1. Each struct in SDL has an implicit operator Make!, which is a constructor for struct values from the struct components. The code generator generates a function that corresponds to this operator with the name yMake_ followed by the sort name with prefix. The function has one parameter for each struct component and returns a value of the struct sort. The returned value is the struct value that is obtained when each component is assigned the value of the corresponding parameter. This make function is often very useful when literal functions in struct sorts are to be implemented. In C code the name of the make function in easiest to obtain by writing yMake_#(SortName), substituting the name of the actual struct sort for SortName.


Example 202 : ADT Example      

Note that no body is supplied for the operator Sum as the default implementation strategy for operators, which should be used for Sum, is Q (question).

For more information about the functions and types (supplied by the runtime library in the Cadvanced/Cbasic Code Generator and contained in generated code) that can be useful when implementing operators in C, see SDL Predefined Types, and last in More about Abstract Data Types.

Error Situations in Operators

In the C function used to implement operators (and literals) it is possible to define error situations and handle them as ordinary SDL run-time errors. The C library function xSDLOpError, with the following prototype:

can be used for this purpose.


Example 203 : Error Handler in Operator      

Example of use:


This is a simplified version of the test in the function for the operator First in the sort Charstring. Here the error situation is when we try to access the first character in a charstring of length 0. In this case the xSDLOpError is called and a default value is returned (NULL). By including the xSDLOpError call between #ifdef XECSOP - #endif the function is only called to report the error if error checks are turned on. The first parameter to xSDLOpError should identify the operator and the sort, while the second parameter should describe the error.

Handling of the Charstring Sort

The SDL sort Charstring is implemented as char * in C.

Note: 

This means that the value NUL (ASCII character 0) cannot be part of a Charstring, as this value is used as string terminator in C (this is checked by the library functions for Charstring).

The code generator and the library functions for the Charstring operators use the first character (index 0) in the C string to indicate the status of the string. If the first character is:

All the library functions for Charstrings handle memory in an appropriate way. A user only has to take the extra character in to account, when Charstrings are handled in C. Any Charstring function parameters having a `T' as first character must be handled according to the discussion above. A function that returns a Charstring and that creates new temporary memory to store the result, should assign the value `T' to the first character in the Charstring.

As pointers and dynamic memory are used to implement Charstrings, it is necessary to be careful when Charstrings are handled in C code, which we show in two examples.


Example 204 : Equal Test on Charstring Sort      

If the C operator == is used to check if two charstrings are equal, then the actual test that is performed is to see if the two pointer values to the data areas representing the characters in the string are equal.

To check if the characters in the charstrings are equal the equal function should be used:



Example 205 : Assignment on Charstring Sort            

If the C assignment operator, =, is used to assign the value of one charstring variable (C1) to another charstring variable (C2), then two things will go wrong:

  1. The memory used to represent the old value of C1 is lost and can never be reused.
  2. C1 and C2 now refer to the same memory area, which means that if one of the variables is changed the other will also be changed. This leads to unpredictable behavior of the program.

The correct way to handle assignment of charstrings is to use the routine:


The problems mentioned above can of course also occur if a struct or array containing charstring components (or subcomponents) is handled carelessly. It is, for example, necessary to use the generated equal and assign routines to perform equal test and assignment (see subsection Help Functions).

To avoid problems one should be aware that Charstring is implemented as char * in C and take the consequences thereof. There are a number of help functions (that implement the operators for the Charstring sort) supplied in the runtime library that might be helpful when handling Charstrings. See SDL Predefined Types).

It is usually necessary to allocate dynamic memory when an operator returning a charstring value is implemented. There are two help functions that should be used in connection with allocation and de-allocation of dynamic memory:

  1. SDL_Charstring xAlloc_SDL_Charstring (xptrint Size)
    The function xAllocSDL_Charstring takes a requested size in bytes and returns a pointer to a data area of this size. The new area will contain 0 in all bytes.
  2. void xFree_SDL_Charstring (void * * C)
    The function xFree_SDL_Charstring should be given an address to a pointer and will return this data area to the pool of available memory. It will also give the pointer the value 0 (NULL).

These functions are, of course, implemented using the C standard functions for allocation and de-allocation of memory, and are described in detail in Allocating Dynamic Memory.

External Properties

As an alternative to the #ADT directive, which is a comment, the external properties clause in a newtype can be used as container for this information. See the following example:


Example 206 : External Properties in a Newtype      

The #ADT directive, without the /* */ can be placed between ALTERNATIVE C; and ENDALTERNATIVE.

Note: 

According to the syntax of SDL, if you have an external properties clause (i.e. alternative - endalternative), you cannot, in the same newtype, have operator diagrams, axioms, or literal mappings.

More about Abstract Data Types

More about Operators

Previously under Implementation of User Defined Operators, the possibility to include implementations of operators in abstract data types was described. Here we will first discuss some additional features concerning operators and then describe how to replace the generated type definition for an abstract data type with a user defined type definition.

For an operator in an abstract data type not only B (body) or Q (question) may be specified. The following choices are available:

Note: 

As C does not include the possibility to have user defined operators, I (infix) is only adequate together with S (standard).

For each operator one of the letters B, Q, H, S and one of the letters P, I should be supplied, either in a #OP directive, or in a #ADT directive, or as the defaults Q and P; for literals P and I have no meaning.

The purpose of S is straight forward and easy to understand, but H might require some explanation. H means that the code generator will not generate any code for the operator, which leaves the user with a number of possibilities:


Example 207 : Using S (Standard Function or Operator)      

Example of usage of S (standard)

An SDL expression using these operators:

sin(a + 7.0) will be translated to: sin(zh723_a + 7.0)


These examples show how standard functions in the target language can be directly utilized in abstract data types. In C it is often easiest to use #OP(HP) for such special cases, and implement the operator in the #HEADING section as a C macro transforming the call to the appropriate syntax.

Including Type Definitions

In the remaining part of this subsection, the inclusion of a type definition in the target language for an abstract data type will be described. When this facility is used, it is necessary to specify how to perform assignment, test for equal, assign default values, and so on, as it is not possible to generate when the type definition is not known (not generated). All this information is given in the #ADT directive, which has the following structure:

where each x on the first line should be replaced by one of the characters B, H, Q, S, or G. Replace y by P or I. The interpretation of these characters is similar to the their interpretation for operators.

B

Body

H

Heading

Q

Question

S

Standard

G

Generate

P

Prefix

I

Infix

The reason why G (generate) is not allowed for operators or literals is of course that it would mean to generate the implementation of the operators from the axioms, which is, at least in the general case, an impossible task. For an operator defined in an operator diagram, G is assumed independently of what the user specifies.

The specifications, given in ADT directives, of how to generate code for type definition, assignment, test for equal, default values, and free function should be interpreted according to the table below. More details about the functions can be found in the subsection Help Functions.

Type Definition

First the actual type definition. The entry - should be interpreted as if no specification is given for T.

Type Interpretation

T(G)

Generate type definition from SDL sort

T(B)

Do not generate type definition

T

Same as T(B)

-

Same as T(G)

Assignment

It is possible to select how assignments should be performed for values of the type. Note that all generated assignments will be of the form:

The yAssF_#(SortName) is a macro either implemented as assignment or as a call to the yAss_#(SortName) function (if such function is to be used), i.e as:

Type Interpretation

A(B)

Use and generate heading, but not body, of yAss_#(S)

A(H)

Use, but generate no code for yAss_#(S)

A(G)

  • If the type definition is generated:
    • Use = if possible.
    • Otherwise use and generate heading and body of the assign function yAss_#(SortName)
  • If type definition is not generated ( T, T(B) ):
    • Use =

A(S)

Use =

A

Same as A(B)

-

Same as A(G)

Equal Test

It is possible to select how test for equality should be performed for values of the type. Note that all generated equal tests will be of the form:

The yEqF_#(SortName) is a macro either implemented as C equal or as a call to the yEq_#(SortName) function (if such function is to be used), i.e as:

The /= operator is represented by the macro

Type Method

E(B)

Use and generate heading, but not body, of yEq_#(S)

E(H)

Use but generate no code for yEq_#(S)

E(G)

  • If the type definition is generated:
    • Use == if possible
    • Otherwise generate heading and body yEq_#(S).
  • If the type definition is not generated
    • Use ==

E(S)

Use ==

E(Q)

Use and generate an equal function that asks for the result of the test (same as Q for operators).

E

Same as E(B)

-

Same as E(G)

Initialization

The default value that variables of the given type will be initialized to.

Type Interpretation

D(B)

Generate heading, but no body of default function
yDef_#(SortName).

D(H)

Generate neither heading nor body of default function.

D(G)

Generate both heading and body of default function, or use macro if adequate.

D(S)

Same as D(G)

D

Same as D(B)

-

Same as D(G)

Free of Dynamic Memory

How dynamic memory (if used for the type) will be released for reuse when it is no longer needed.

Type Interpretation

F(B)

Generate heading, but no body of the free function yFree_#(SortName).

F(H)

Generate neither heading nor body of the free function.

F(S)

Use the function xFree

F

Same as F(B)

-

Do not use free function.

Make!

In the case of array or struct sorts there will be a make function to implement the constructor Make! defined in SDL.

Type Interpretation

K(B)

Generate heading, but no body of the make function yMake_#(SortName).

K(H)

Generate neither heading nor body of the make function.

K(G)

Generate the make function

K

Same as K(B)

-

Same as K(G)

Extract! and Modify!

This entry specifies how component selection (struct components, array components for example) should be performed. In SDL a component can be selected in two ways:

An Extract operation can be generated in four ways:

The last version, the Extract function, is used for all other cases.

Type Interpretation

X(B)

Use Extract function

X(G)

Use component selection according to table above.

X

Same as X(B)

-

Same as X(G)

A Modify operation can in the same way be generated in four ways:

The last version, the Addr function, is used for all other cases.

Type Interpretation

M(B)

Use Addr function

M(G)

Use component selection according to table above.

M

Same as M(B)

-

Same as M(G)

Write Function

The write function is used by the monitor system to write values of the type.

Type Interpretation

W(B)

Generate heading but not the body of a write function.

W(H)

Generate neither heading nor body of a write function, but assume that the user has provided such a function.

W(S)

Values of this type are to be printed as a HEX string. No write function is assumed to be present.

W

Same as W(B)

-

Same as W(S)

Read Function

The read function is used by the monitor system to read values of the type.

Type Interpretation

R(B)

Generate heading but not the body of a read function.

R(H)

Generate neither heading nor body of a read function, but assume that the user has provided such a function.

R(S)

Values of this type are to be read as a HEX string. No read function is assumed to be present.

R

Same as R(B)

-

Same as R(S)

When generate is specified for a function, the code generator might decide not to generate the heading of the function, as in some cases it is not needed.

All code that is not generated is, of course, assumed to be included by the user in the #TYPE, #HEADING and #BODY sections in the #ADT directive.

Another name for an assign function, equal function and so on may be used, by including the desired name within quotes together with the generation options in the #ADT directive.

If for example the name of a certain assign function should be AssX, this can be obtained by specifying: A(B 'AssX') for the assign function. This name will then be used throughout the generated code, both in generated declaration and at the places where the function is called. Note that the name should be last in the specification for the actual function.

An include statement may be generated together with or replacing the type definition by giving a file name within quotes last in the specification part of the #ADT directive, immediately before the first section with code.


Example 208 : Including a File in ADT      

If the directive

is used, the following include statement will be generated:


Note: 

Turning off the generation of the objects contained in the include file must be performed by the user.

Directive #REF

The directive #REF can be used to specify that the address, not the value, of a variable should be passed as parameter to an ADT operator, as it is defined in SDL. This feature cannot be used for operators defined in operator diagrams (the directive will be ignored for such operators).

The #REF directive is used as shown in the example below.


Example 209 : Including a File in ADT      

The headings for these two operator will become in ANSI-C syntax (ignoring prefixes)


This feature can be used to optimize parameter passing to operators. The directive, however, also imposes the restriction that the actual parameters must be a variable or a formal parameter (see example Example 210 below). This is checked by the code generator. A #REF directive does not in any way effect the way a operator call should be implemented in SDL. It is the responsibility of the code generator to generate the proper actual parameters in C.


Example 210  : Including a File in ADT      

With the ADT in the previous example the following operator call is valid:

The same call of eq2 would not be valid as the second parameter is not a variable or a formal parameter.


Help Functions

Assign Function

An assign function has the following heading:

It should assign the value passed as second parameter to the variable passed as first parameter. The SDL predefined sorts Charstring (including syntypes of charstring), Bit_String, Octet_String, and Object_Identifier have assign functions

while all the other predefined types can use =. Sorts translated as enum types can always use =, while arrays (translated to arrays), powersets (translated to bit arrays), and struct sorts can use = if all components can use =. The remaining generators array (translated to linked list), powerset (translated to linked list), and string need assignment functions. A syntype in SDL uses = if its father type can use =, otherwise it uses the assign function of the father type.

The extra third parameter in Assign functions should be given a value according to the ideas given below, preferably using the macros indicated.

Equal Function

An equal function has the following heading:

It should return true or false depending on if the two values passed as parameters are equal or not. The SDL predefined sorts Charstring (including syntypes of charstring), Time, Duration, PId, Bit_String, Octet_String, and Object_Identifier have equal functions:

while all the other predefined types can use ==. Sorts translated as enum types can always use ==, while arrays, stings, powersets, and struct sorts always have equal functions. A syntype in SDL uses == if its father type can use ==, otherwise it uses the equal function of the father type.

Default Function

All sorts defined in an SDL system, including the predefined sorts, will have a default function which is used to initialize variables of the actual sort. A default function has the following heading:

It should assign the default value to the variable passes as parameter. The default function will in most cases be implemented using a macro (all predefined sorts, all sort with explicit default value, all sorts translated to enum type, all Syntypes). Only structured sorts without a default value will have a function as default function.

Free Function

For a sort which uses allocated memory and pointers, a user should specify how to return such memory to the pool of free memory, using a free function.

The function should take the address to a pointer, return the allocated memory to the pool of available memory and assign 0 to the pointer. The SDL predefined sort Charstring has a free function:

Each structured type (struct or array) which has a component that has a free function, will have a free function. The usage of the free functions will be discussed in detail in the section Allocating Dynamic Memory.

Make Function

For each array and struct sort (not Syntypes) there will be a generated make function, which is used as a constructor of array or struct values out of their respective components, according to the rules for the Make! operator in SDL.

The number of parameters in a struct make function is the same as the number of components in the struct.

Write Function

In order to examine variable values for variables that are of a sort that is implemented in C, i.e. has #ADT(T) in its ADT directive, it is necessary to implement a write function. Otherwise the value can only be presented as a HEX string. Note that the run-time kernel can automatically handle all SDL sorts for which the code generator generates the C type definition. A write function should look like:

Given the address of a value of the type SortName, this function should return a char *, i.e. a character string, containing the value represented in a printable form. This character string is the string that will be printed by the monitor, when it needs to print a value of this type. To implement the write function it is not uncommon that a static char array is needed.

Note: 

The following two considerations when it comes to write and read functions:

The function xWriteSort, which is part of the run-time kernel can be useful when implementing Write functions.

The xWriteSort function takes the address of a value to be printed, and a pointer to a xSortIdNode and returns the given value as a string. This function is typically useful if the sort we are implementing a write function for contains one or several components of sorts defined in SDL.

Read Function

In order to assign new values to variables that are of a sort that is implemented in C, i.e. has #ADT(T) in its ADT directive, it is necessary to implement a read function. Otherwise the value can only be read as a HEX string. Note that the run-time kernel can automatically handle all SDL sorts for which the code generator generates the C type definition. A read function should look like:

A read function is given an address to store the value that is read. It should return 1 if the read operation was successful. Otherwise, 0 should be returned and Result should be unchanged.

There are some suitable functions in the run-time kernel which can help you when you are implementing a read function. Basically the function xScanToken described below is a tokenizer that transforms sequences of characters to tokens. This function returns tokens according to the following enum type:

Function xScanToken

The function xScanToken:

reads the next token from input (stdin or Simulator UI) and returns the type of the next token as function result. If the token is xxId or xxString the strVar parameter will contain the identifier, number, or string. The size of the char[] parameter passed as actual parameter should be large enough to store the possible values. If some other token was found, no information is stored in strVar.

xUngetToken

Sometimes it is necessary to look-ahead to determine how to handle the current token. Using the function xUngetToken below it is possible return one token to the input. Note that both parameters must have the values obtained from xScanToken.

The functions below can also be useful while implementing Read function. xPromptQuestionMark is suitable to obtain prompt in a similar way as for SDL defined sorts, while xReadOneParameter can be used to read element for element in a list, separated by commas and terminated either by ".)" or "]". The function xReadSort is similar to xWriteSort and can be used to read a component in the treated sort.

xPromptQuestionMark

The function result and the parameter strVar behave in the same way as for the function xScanToken (see above). The parameter Prompt is the prompt that should be used. This string has to start with a ` `, i.e. a space. To conform with other built-in read function, the Prompt parameter should be: " (SortName) : " (note the ending space colon space). The QuestionPrompt parameter should either be identical to the Prompt parameter, or be null, i.e. (char *)0. If QuestionPrompt is null, the xPromptQuestionMark function will return xxEoln if a end-of-line is found. If QuestionPrompt is not null, the xPromptQuestionMark function will print the QuestionPrompt, and continue to read. Normally QuestionPrompt should be equal to Prompt in a simple data type, while it should be null in a structured data type.

xReadOneParameter

The xReadOneParameter can be used to read one component in a structured data type. It is required that components are separated by commas or just white spaces, and that the component list ends with either ".)" or "]". For an example of how to use this function, please see the second example below. xReadOneParameter returns 0 if an error was found, 1 if the read operation was successful and there are more components to read, and 2 if the read operation was successful and the next token is the ending ".)" or "]".

xReadSort

This function reads a value of the sort referenced by the SortNode parameter. If the read operation was successful, 1 is returned and the read value is stored at the address given as parameter. If an error was detected 0 is returned.


Example 211 : ADT Example, Byte Type      

This example is taken from the ADT byte (see Abstract Data Type for Byte). The byte type should be read and printed using HEX format.



Example 212 : ADT Example, Struct Read and Write Functions      

This is an example of how the read and write functions for a struct with two components can look. The monitor system can handle reading of writing of struct values automatically, so please see this just as an example.


Generators

The Cadvanced/Cbasic Code Generator handles all the predefined generators in SDL, i.e. Array, String, Powerset, and Bag. It is also possible for a user to write his own generators and instantiate them in newtypes. However, the behavior of a user defined generator has to be specified completely by the user. This is performed in a somewhat extended #ADT directive placed just before the endgenerator keyword. These extensions are described below.

There are three additional sections in the directive, apart from #TYPE, #HEADING, and #BODY. These are #INSTTYPE, #INSTHEADING, and #INSTBODY. The inline C code in #TYPE, #HEADING, and #BODY is placed at the point of the generator, i.e. it is generated once. The contents of #INSTTYPE, #INSTHEADING, and #INSTBODY is inserted at each instantiation of the generator, i.e. in each newtype defined using the generator.

In the #INSTTYPE, #INSTHEADING, and #INSTBODY it is possible to use # followed by a number to access the information given in the generator instantiation:

#1 and ##1 are equal, except when the corresponding actual generator parameter is a struct (or union). In that case, assuming the SDL struct:

which will be generated as

#1 will become struct aaa_s (or union aaa_s if a union), while ##1 will become aaa.


Example 213 : Example of User Defined Generator      

Note the usage of #INSTTYPE and #INSTHEADING in the example above. The code in these section will be inserted in each newtype defined with this generator. For example, in a newtype:

The #INSTTYPE section will become:

The example above is a stripped version of the Ref generator which is part of the ctypes package used to simplify the integration of generated code and existing code in an application.

Directives to the Cadvanced/Cbasic Code Generator

Syntax of Directives

The Cadvanced/Cbasic Code Generator recognizes a number of directives given mainly in SDL comments. The #ADT, #OP, #UNION, and #REF directives used in abstract data types are examples of such directives. The directives #ADT and #OP were described in the section Implementation of User Defined Operators, Union, and Directive #REF, in connection with abstract data types and are not further discussed here.

A directive has the general structure:

  1. The start of comment character: /*
  2. A `#' character.
  3. The directive name.
  4. Possible directive parameters given in free syntax. That is, spaces and carriage returns are allowed here.
  5. The end of comment characters */.

Upper and lower case letters are considered to be equal in directive names.


Example 214 : #OP Directive      

Take as an example the directive:

This comment will be recognized as a directive only if no other character is inserted in the sequence /*#OP. After this part, spaces and carriage returns may be inserted freely.


Selecting File Structure for Generated Code
-- Directive #SEPARATE

The purpose of the separate generation feature is to specify the file structure of the generated program. Both the division of the system into a number of files and the actual file names can be specified. There are two ways this information can be given.

The Cadvanced/Cbasic Code Generator can generate a separate file for:

Note: 

Instantiations cannot be separated, i.e. an instance of a block type cannot be generated on a file of its own.

If #SEPARATE directives are used, they should be placed directly after the first semicolon in the system, block, process, or procedure heading; see the following example.


Example 215 : #SEPARATE Directive      

In the example above the two versions of separate directive, with or without file name, are shown. As can be seen a file name should be enclosed between quotes. The code generator will append appropriate extensions to this name when it generates code.

If no file name is given in the directive, the name of the system, block, process, or procedure will be used to obtain a file name. In such case the file name becomes the name of the unit with the appropriate extension (.c .h) depending on contents. The file name is stripped from characters that are not letters, digits or underscores.

The possibility to set up full, user-defined, or no separation in the Organizer's Make dialog and in the user interface of a stand-alone Analyzer (see The Analyzer Command-Line UI), can be used, in simple manner, to select certain default separation schemes. This setting will be interpreted in the following way:

Independently if No, User defined, or Full separation has be selected, the code generator will use the file name specified in the Edit Separation dialog or the #SEPARATE directive, for a file that is to be generated.

An Example of the Usage of the Separate Feature

In the following example a system structure and the #SEPARATE directives are given. The same information can easily be set up in the Organizer as well. This example is then used to show the generated file structure depending on selected generation option.


Example 216 : #SEPARATE Directive      

Applying Full Separate Generation

If Full separate generation is selected then the following files will be generated:

Sfile.c
Sfile.h 
B1.c
B1.h 
P11file.c 

P12.c 

B2.c
B2.h
P21.c 

P22.c 

The .c files contain the C code for the corresponding SDL unit and the .h files contain the module interfaces.

Applying Separate Generation

If User defined separate generation is selected then the following files will be generated:

Sfile.c
Sfile.h

Contains code for units S, B2, P21

B1.c
B1.h

Contains code for units B1, P12

P11file.c

Contains code for unit P11

P22.c

Contains code for unit P22

The user defined separate generation option thus makes it possible for a user to completely decide the file structure for the generated code. The comments on files and extensions given above are, of course, also valid in this case.

Applying No Separate Generation

If the separation option No is selected, only the following file will be generated:

Sfile.c

Contains code for all units

The comments on files and extensions earlier are valid even here.

Guidelines

Generally a system should be divided into manageable pieces of code. That is, for a large system full separate generation should be used, while for a small system no separate generation ought to be used. The possibility to regenerate and recompile only parts of a system usually compensate for the overhead in generating and compiling several files for a large system.

Note: 

A file name has to be specified, in the Organizer Edit Separation command, see Edit Separation, or in the #SEPARATE directive, if two units in the system have the same name in SDL and should both be generated on separate files, otherwise the same file name will be used for both units.

Accessing SDL Names in C Code
-- Directive #SDL

When writing C code that is to be included in a generated program it is often necessary to refer to names of objects defined in SDL. The name of an SDL object is, however, transformed when it is translated to C. A prefix, which is a sequence of characters, is added to the SDL name to make the C name unique in the C program. Furthermore, all characters in SDL name which are not allowed in a C name are removed. The prefixes are calculated by looking at the structure of definitions in the actual scope and in all scopes above. This means that adding a declaration at the system level might change all prefixes in blocks and processes contained in the system. As a consequence it is almost impossible to know the prefix of an object in advance.

To be able to write C code and use the name of SDL objects in that code, the Cadvanced/Cbasic Code Generator provides the directive #SDL which is used in C code to translate an SDL name to the corresponding C name.

The syntax of the #SDL directive is as follows:

or

There is also a short form for the directive. No characters are allowed between the # character and the left parentheses in this form:

or

Replace SDL name with the name of an object in the SDL definition and entity class name by any of the following identifiers (upper and lower case letters are considered to be equal):

block
blockinst
blocksubst
blocktype
channel
channelsubst
connect
formalpar
gate
generator
label
literal
newtype
operator
package
predef
procedure
process
processinst
processtype
remoteprd
remotevar
service
serviceinst
servicetype
signal
signallist
signalroute
sort (= newtype)
state
synonym
syntype
system
systemtype
timer
variable
view

This list contains all entity classes, which means that not all of the entries are relevant for practical use. When a #SDL directive is found in included C code, the code generator first identifies what SDL object is referred to and then replaces the directive by the C name for that object. The search for the SDL object starts in the current scope (the scope where the C code is included), and follows the scope hierarchy outward to the system definition, until an appropriate SDL object is found. An appropriate SDL name is considered to be found if it has the specified name and is in the specified entity class. If no entity class name is given the search is performed for all entity classes.

Note: 

In types, especially in block types, #SDL should be used with care. The reason is that some of the objects in a block type are generated for each instantiation of the block. A #SDL directive on such an object might lead to overloading of names in C. Sensitive objects are processes, process instantiations, signal routes, channels, remote definitions.

The table in the subsection SDL Predefined Types gives the direct translation between an SDL name and the corresponding C name or expression. For these names the #SDL directive should not be used.

Including C Code in Task -- Directive #CODE

The user's own C code may be included in tasks by using the #CODE directive. This directive has the following syntax:

Type the directive name on the first line and the C code on the following lines up to the end of comment symbol. Note that text on the same line as the #CODE directive are not handled.

A #CODE directive can be placed:

The C code in the directives is textually included in the generated code at the position of the directive. If, for example, a code directive is placed between two assignment statements, the code in the directive is inserted between the translated version of the assignment statements.

Note: 

The Cadvanced/Cbasic Code Generator handles the C code in directives as text and performs no check that the code is valid C code.

The code directive is included as a facility in the code generator to provide experienced users an escape possibility to the target language C. This increases the application range of the code generator.

An example of a possible use of the code directive is: An algorithm for some computation, which in the SDL description is only indicated as a task with an informal text, could be implemented in C. In this case the directive #SDL described in the previous subsection will probably become useful to access variables and formal parameters defined in SDL.

Some general hints on how to write C code that can be included into a simulation program, especially when charstrings or sorts containing charstrings as components are used, can be found in the last part of the section Implementation of User Defined Operators.

Unfortunately it is not possible to have C comments within the code that is included in any directive, as SDL and C use the same symbols for start and end of comments. See also Example 197 which illustrates the possibility to use the C macro COMMENT.

Including C Declarations -- Directive #CODE

The #CODE directive can also be used to include C declarations; for example types, variables, functions, #define, and #include in the declaration parts of the C program. This version of the code directive has the following structure:

The separation of functions into HEADING and BODY sections serves the same purpose as in the #ADT directive, see Implementation of User Defined Operators.

Code directives to include C declarations may, generally speaking, be placed immediately after a semicolon that ends a declaration in SDL. More precisely it is allowed to place a #CODE directive after the semicolon that ends:

In the following small PR example the allowed positions are marked with an * followed by a number.


Example 217 : #CODE Directive      

A code directive is considered to belong to the unit where it is defined and the declarations within the directive are thus placed among the other C declaration for that unit. In the example above, directives at positions 1, 2, 3, 4, 12 belong to system s, directives at positions 5,6,7,11 belong to block b1, while directives at positions 8, 9, 10 belong to process p1. Only one code directive may be placed at each available position.

Note: 

A variable declared in a #CODE directive that belongs to a process will be shared between the process instances of the process instance set. Such a variable should only be used to represent some common property of all the process instances. To have a variable that is local to a process instance, the variable should be defined in SDL using DCL.

In the generated code the type sections are included in the order of appearance in SDL. However, the type sections are also sensitive for their relative position comparing with SDL sort definitions. This means that the order of the type definitions in the system in the example above will be as follows:

  1. Type sections in 1, 2, 3
  2. Type generated for newtype n
  3. Type sections in 4, 12

As the Cadvanced/Cbasic Code Generator will generate the SDL sorts in the correct order, definition before usage, in C, the full algorithm is as follows.

The heading sections are placed in the order of their appearance in SDL. This applies to the body sections as well. All body sections will be placed after the sequence of heading sections and the heading section will be placed after all the type definitions. The SDL declarations made in the corresponding unit are available in the code directives and can as usual be reached using the #SDL directive. All declarations made in code directives are of course available in code directives in tasks in the corresponding unit or in its subunits.

The general hints on how to write C code that fits into a generated C program given in the section Implementation of User Defined Operators and in the section Accessing SDL Names in C Code -- Directive #SDL are also applicable here.

Including C Code in SDL Expressions
-- Operator #CODE

For each sort defined in an SDL system, both predefined and user defined, the Cadvanced/Cbasic Code Generator includes an operator #CODE with the following signature:

where S is replaced by the sort name. This operator or rather these operators make it possible to access variables and functions defined in C using the #CODE directive in SDL expressions and still have syntactically and semantically correct SDL expressions.

During code generation, the code generator will just copy the Charstring parameter at the place of the #CODE operator.


Example 218 : #CODE Directive      

Suppose that x and y are SDL variables, which are translated to z72_x and z73_y, that a and b are C variables, and f is a C function defined in #CODE directives.

SDL expression C expression
x + #CODE('a')
z72_x + a
x + #CODE('a*b')
z72_x + a*b 
x*#CODE('(a+b)')*y
z72_x*(a+b)* z73_y
#CODE('f(a,#SDL(x))')
f(a, z72_x) 


Within the Charstring parameter of a #CODE operator the #SDL directive is available in the same way as in other included C code. This is also shown in the last of the examples above.

As there is one #CODE operator for each sort in the system, it is sometimes necessary to qualify the operator with a sort name to make it possible for the SDL Analyzer to resolve which operator that has been used. If, for example, the question and all answers in a decision are given as applications of #CODE operators, then it is not possible to determine the type for the decision. One of the #CODE operators should then be qualified with a sort name to resolve the conflict.


Example 219 : Code Directive      

In this case the sort of the decision cannot be resolved. To overcome this problem the question could be written as


Names and Prefixes in Generated Code

When an SDL name is translated to an identifier in C, a prefix is normally added to the name given in SDL. This prefix is used to prevent name conflicts in the generated code, as SDL has other scope rules than C and also allow different objects defined in the same scope to have the same name, if the objects are of different entity classes. It is, for example, allowed in SDL to have a sort, a variable and a procedure with the same name defined in a process. So the purpose of the prefixes is to make each translated SDL name to a unique name in the C program.

A generated name for an SDL object contains four parts in the following order:

  1. The character "z"
  2. A sequence of characters that make the name unique. If the object is part of a package, the package name will appear in this sequence.
  3. An underscore "_"
  4. The SDL name stripped from characters not allowed in C identifiers

Sequence of Characters

A C identifier may contain letters, digits, and underscore "_" and must start with a letter.

The sequence of characters that make the name unique is determined by the position of the declaration in structure of declarations in the system:

The total sequence making a name unique is now constructed from the "declaration numbers" for the unit and its parents, that is the units in which it is defined, starting from the top.

If, for example, a sort is defined as the 5th declaration in a block that in turn is the 12th declaration in the system, then the total sequence will be b4 (if not more than 36 declarations are present on any of the two levels).


Example 220 : Generated Names in Code      

Examples of generated names:

SDL Name Position of the Declaration Generated Name

S1

10th declaration in the system

z9_S1

Var2

3rd declaration in the process, which is the 5th declaration in the block,

which is the 15th declaration is system

ze42_Var2


There will also be other generated names using the prefixes. If, for example, a sort MySort is translated to za2c_MySort, then the equal function connected to this type (if it exists) will be called yEq_za2c_MySort.

Prefixes

Note: 

If the OO diagram types in SDL-92 are used (system type, block type, process type), full prefix should always be used, as the OO concepts in itself most likely mean the name conflicts will be introduced in C.

This strategy for naming objects in the generated code should be used in all normal situations, as it guarantees that no name conflicts occur. The Cadvanced/Cbasic Code Generator offers, however, possibilities to change this strategy. In the Make dialog in the Organizer (see Make) and in the user interface an Analyzer running stand-alone (see The Analyzer Command-Line UI), it is possible to select one of the following strategies: full prefix, entity class prefix, no prefix, or special prefix. Full prefix is default and is the strategy described above.

Entity Class Prefix

If entity class prefix is selected, then the prefix that is concatenated with the SDL name will be in accordance with the table below and depends only of the entity class of the object.

Entity class Prefix Entity class Prefix

Block, block type, block instance

blo

Process, Process type, Process instance

prs

Block substructure

bls

Remote procedure

rpc

Channel

cha

Remote variable

imp

Channel substructure

chs

Service, Service type, Service instance

ser

Connection

con

Signal

sig

Formal parameter

for

Signal list

sil

Gate

gat

Signal route

sir

Generator

gen

Sort = Newtype

sor

Import

imp

State

sta

Label

lab

Syntype

syt

Literal

lit

Synonym

syo

Operator

ope

System, System type

sys

Package

pac

Timer

tim

Predef

pre

Variable

var

Procedure

prd

View

vie

Using entity class prefix means that the user must guarantee that no name conflict occurs. It also means, however, that the generated names are predictable and thus simplifies writing C code where the SDL names are used. It is only necessary to look for name conflicts within entity classes, for example not having two sorts with the same name. The entity class prefixes handle the case when two objects of different entity class have the same name. Note that the table above contains all entity classes. Not all of the items are actually used by the code generator.

No Prefix

The third alternative, no prefix, means of course that no prefixes are added to the SDL name. The name in the C program will then be the SDL name stripped form characters that are not allowed in C identifiers (everything except letters, digits, and underscore). In this case, the user must guarantee that no name conflict occurs and that the stripped name is allowed as a C identifier, that is, that it begins with a letter.

Special Prefix

In the fourth alternative, special prefix, full prefixes are used for all entity classes except variable, formal parameter, sort, and syntype. For these entity classes no prefix is used.

Conclusion

As was said in the beginning of this subsection, the user should have a good reason for selecting anything but the full prefix, as it could be very difficult to spot name conflicts. The C compiler will in some cases find a conflict, but may in other cases consider the program as legal and generate an executable program with a possibly unwanted behavior. The note above about OO concepts is also a strong argument for full prefix.

Case Sensitivity

Another aspect concerning identifiers is that SDL is case insensitive, while C is case sensitive. The Cadvanced/Cbasic Code Generator has two translation schemes for identifiers, one is to use the capitalization used in the declaration of the object of concern (default), and one is to use lower case identifiers. The translation scheme is selected in the Make dialog in the Organizer (see Make) or in the user interface of the Analyzer, when it is executed stand-alone ( The Analyzer Command-Line UI).

Specifying Names in Generated Code
-- Directive #NAME

If you wish to decide the name of an object in generated code yourself you can use the #NAME directive. Place the directive directly after the name in the declaration of the object. It should contain the desired name to be used in the generated code within quotes.


Example 221 : #NAME Directive      

The name defined in a #NAME directive will be used everywhere that the SDL name is used in the generated code, with two exceptions:

There are, however, some restrictions on where #NAME directives can be placed. Some objects in, for example, a block type are generated in each instantiation of the block type. If a name directive is placed at such an object, the name will probably be overloaded in C, resulting in a C compilation error. The sensitive objects are processes, process instantiations, signal routes, channels, remote variables, and remote procedures.

Assigning Priorities -- Directive #PRIO

Priorities can be assigned to processes and process instantiations using the directive #PRIO. The process priorities will affect the scheduling of processes in the ready queue, see Time. A priority is a positive integer, where low value means high priority. #PRIO directives can be placed directly after the process heading in the definition of the actual process or last in the reference symbol (in SDL-GR).


Example 222 : #PRIO Directive in process headings      .

Processes that do not contain any priority directive will have the default priority 100.

Initialization -- Directive #MAIN

The #MAIN directive is used to include initialization code that should be executed before any process transitions are started. The directive should be placed in the system definition directly after the system heading.


Example 223 : #MAIN Directive      

The #MAIN directive has exactly the same structure as the #CODE directive for including code in tasks. The included code will, however, be placed last in the yInit function, after the initialization of the internal structure, but before any transitions are executed.

Modifying Outputs -- Directive #EXTSIG, #ALT, #TRANSFER

The purpose of these directives is to modify the standard behavior of an SDL output. The #EXTSIG directive can be used to build applications with the SDT run-time library. The directives #ALT and #TRANSFER are only useful together with other real-time operating systems.

The directive #EXTSIG is used to replace the code for an SDL output with any appropriate in-line C code. This is an optimization and an alternative to the OutEnv function (see Building an Application). The #EXTSIG directive can be specified:

In the first case the #EXTSIG is valid for the signal(s) sent in the output symbol, and in the second case for all outputs of the defined signal.


Example 224 : #EXTSIG Directive      

For each output of a signal with a #EXTSIG directive (in either way described above) the following code is generated:

where SignalName is the name of the signal in SDL. The parameter ToExpression is a translated version of the SDL expression after TO in the output. If no TO expression is given in the output, this parameter will be xNotDefPId. The entry SignalParameters will be replaced by the list of signal parameter values given in the output.

The intention of this code is to give the user the possibility of introducing a macro with the same name as the signal, where the implementation of the output is expanded to in-line code. By just having a compilation switch which selects if this macro is visible or not, the same generated code can be used both for simulation and for highly optimized applications. An appropriate switch is probably XENV, which governs the normal way of connecting an SDL system to the environment.


Example 225 : #CODE Directive      

The following #CODE directive can be included in a text symbol in the system diagram (assuming a signal called SigName with one parameter).


The other two directives, #ALT and #TRANSFER, can be used together with appropriate real-time operating systems, to have two different interpretations of an output (internal or external output for example) and to specify that a received signal should be immediately retransmitted (#TRANSFER). These kinds of features are not uncommon in real-time operating systems, and can be modeled and simulated by the Cadvanced/Cbasic Code Generator using these directives. Both these directives should be placed last in the output symbol.

The presence of an #ALT directive will be reflected in the generated code in the way described below.

In the Master Library, the macros with and without ALT are expanded identically. In an OS integration they might be handled differently to implement two classes of signal sending.

The presence of a #TRANSFER directive indicates that a signal should be directly retransmitted to some other receiver. This can of course be performed in SDL by an input-output, but then it is necessary to create a new signal and copy the contents of the received signal to the new signal. Using the #TRANSFER directive this copying can be avoided.

In generated code the macros

are generated to allocate the data area for a new signal. If a #TRANSFER directive is present in the output statement, these macros are replaced by:

Note: 

In the master library #TRANSFER will still be implemented as a signal copy. It may be possible in an OS integration to avoid the copying if the OS supports such actions.

Normally the #TRANSFER directive should be used in the following way:

That is, receive none of the signal parameters in the input and retransmit the signal unchanged. If you want to receive, for example, the second parameter (in variable Var1) and retransmit the signal unchanged except for parameter 3, that should have a new value (73), the following code can be used:

Linking with Other Object Files
-- Directive #WITH

Note: 

This feature is only valid for SDL-PR as input, when the Analyzer is executed stand-alone. Similar features are available in the Organizer's Make dialog among the Generate makefile options, see Generate makefile.

You can tell the Cadvanced/Cbasic Code Generator that a number of user defined and precompiled units should be linked together with a generated simulation program. You do this in a #WITH directive that should be placed in the system definition directly after the system heading.


Example 226 : #WITH Directive      

Within the #WITH directive the object files that are to be part in the link operation should be given between quotes, as in the example above. These files will be included in the definition of the link operation in the generated .m file.

The make file will, however, not include any definition of how to compile the corresponding source files, as it is impossible for the code generator to know the compilation options or even what compiler the user wants. A user that knows how to interface routines in other languages in a C program, can with this knowledge and the #WITH directive link modules written in another language together with the generated program.

Note: 

The #WITH directive will only affect the generated make file. There will be no change in the generated C code when a #WITH directive is introduced.

Naming Tasks in Trace Output -- Directive #ID

To simplify the identification of a TASK in a trace printout, the Cadvanced/Cbasic Code Generator uses the variable on the left hand side of the first assignment statement or the first informal text in the task symbol. A user that is not satisfied with this can name the tasks using #ID directives. An ID directive should contain the character string that is to be used as identification in trace printouts.


Example 227 : #ID Directive      

The code generator will use the first ID directive it finds in a TASK (if any). An ID directive may be placed:

Directive #C, #SYNT, #SYNTNN, #ASN1

These directives are used to pass information from the tools generating SDL from other languages, for example for C (a .h file) or ASN.1.

The #C and the #ASN.1 directive will be inserted after the semicolon ending the package definition or after the package name.

The #ASN.1 directive contains the ASN.1 module name and the #C directive contains the name of the originating .h file.

The #SYNT directive and its special form the #SYNTNN directive are used in a package generated from C, to indicate which SDL sorts that are synthesized, i.e. which sorts that where needed in SDL but do not have a name and definition in C. As the originating .h file is included in the generated code (with a #include), no typedefs should be generated for the non-#SYNT sorts, while typedefs have to be generated for the synthesized sorts. The #SYNT directive is inserted directly after the name of the newtype or syntype.

Using the Cadvanced/Cbasic Code Generator to Generate C++

Note: 

This section is only applicable if you have compiled versions of the runtime library that can be linked together with the object files produced by the C++ compiler. In most cases this means that you must have the SDT Master Library (which is provided as a part of the SDT Cadvanced product), i.e. have source code for the runtime library.

General

The C code in the Master Library and the C code generated by the SDT Cadvanced/Cbasic Code Generator is in the common subset of C and C++, and will thus compile both as a C program and as a C++ program. There is one special feature in the code generator concerning C++ when it comes to abstract data types and the possibility to match a C++ class and an SDL data type. Otherwise all the features for including C code, directive #ADT, directive #CODE (see Abstract Data Types and Accessing SDL Names in C Code -- Directive #SDL), and so on, are directly applicable for C++ as well. The #CODE directives make it possible to include class definitions as C++ code and the utilization of the classes as C++ code in SDL tasks.


Example 228 : Using C++ Classes      

CODE directive containing declarations (see Including C Declarations -- Directive #CODE) which should be placed among the SDL declarations:

Example of usage of the class in a CODE directive in a TASK (see Including C Code in Task -- Directive #CODE).

Example of usage of the class in a CODE operators in expressions (see Including C Code in SDL Expressions -- Operator #CODE).


Connection Between C++ Classes and SDL

To obtain a close connection between a C++ class and SDL, an abstract data type in SDL can be used; see below.

If you have a C++ class (from a class library or developed specifically for the project), then the following correspondence rules can be used to map the class on an abstract data type.

C++ SDL

Class definition

Abstract data type definition

Class instance pointer

Process variable

Member functions

Operators

new, delete

Operators


Example 229 : SDL and C++ Class      

Suppose we have a C++ class with the following interface (.h file):

then the following abstract data type can be used to represent the class (in the example the NAME directive, see Specifying Names in Generated Code -- Directive #NAME, is used to instruct the Cadvanced/Cbasic Code Generator which name to use in C for particular SDL objects):

Note that the #ADT specification means that no code will be generated for the abstract data type. The abstract data type can utilized in the following way:


The only feature that is not described before is the C option in the #OP directive. C (class) is an alternative to I (infix) and P (prefix), and specifies that the operator call should be translated to a member function call of a C++ member function. #OP(C) means that an operator call

is translated to

Note: 

This means that each operator mapped on class member function should have the class instance pointer as first parameter.

Restrictions

SDL Restrictions

The SDT Cadvanced/Cbasic Code Generator handles the majority of SDL concepts according to the definition of SDL-92. There are however a number of restrictions that are discussed in this section.

Analyzer Restrictions

The restrictions in the SDT Analyzer are, of course, also valid in the Cadvanced/Cbasic Code Generator. For more information see Analyzer.

Cadvanced/Cbasic Code Generator Restrictions

The Cadvanced/Cbasic Code Generator introduces more severe restrictions on the allowed set of SDL concepts than the Analyzer. For more information, see C Code Generator.


[Previous] [Next] [Contents] [Index]