The Cmicro Code Generator translates your SDL system into a C program that you can compile together with the Cmicro Library and the Cmicro Tester target library. The Cmicro Library and the Cmicro Tester target library is not available as a pre-linked library but is delivered as source to enable scaling of the kernel. The scaling is dependent upon the SDL system characteristics. This chapter is a reference manual for the Cmicro Code Generator.
The application area for the Cmicro Code Generator is:
In this part of the chapter, the general behavior of the Cmicro Code Generator, as seen from the users point of view, is discussed.
The generated code in combination with the Cmicro Library is highly optimized, which is unavoidable for microcontrollers and real-time applications. Some optimizations have been possible only by introducing restrictions in the use of SDL. Other optimizations have been possible by generating more compact code. For the restrictions in the use of SDL please see SDL Restrictions. Details regarding the output of the Cmicro code generation can be found in Output of Code Generation.
With the generated code it is possible to debug the application on the target using the Cmicro Library and the Cmicro Tester target library. The parts of the Cmicro code generation which are used for the Cmicro Tester are also highly optimized. Please see The Cmicro Tester.
The SDT Analyzer, which can be invoked from the Organizer, contains an SDL parser, an SDL semantic checker, and -- among other code generators -- the Cmicro Code Generator.
Many options can be chosen from the user which affect the analysis of the SDL system. Furthermore, a lot of error checks are performed automatically before code generation starts. This makes it possible to improve written SDL specifications before any run-time testing must be done.
The options that the user may choose for analysis and the error checks that are performed by the analyzer are described in The SDT Analyzer.
At some places the Cmicro Code Generator can be used in exactly the same way as the SDT C Code Generator (Cbasic/Cadvanced) can be used. At some other places the use of this C Code Generator, or what this C Code Generator produce, is different.
The Cmicro Code Generator generally can process the same input as the SDT C Code Generator can. The differences are explained within this chapter.
The differences in the output of the both code generators are described within the subsection Output of Code Generation.
The overall differences of the both code generators are described in the section Differences between Cmicro and Cadvanced.
The following subsections describe how the Cmicro Code Generator might be used.
The C files, which are generated by the Cmicro Code Generator, can only be used in connection with the Cmicro Library and the Cmicro Tester. It is not possible to validate and simulate the SDL system with the C code generated by the Cmicro Code Generator as this code is only suitable for target applications. To simulate and validate the SDL system within SDT, the user has to choose the Cbasic Code Generator. In order to view the process of generating C applications see the Organizer's Make dialog in The Organizer.
The SDT Analyzer, which contains the Cmicro Code Generator can also be started as a stand-alone tool. For more information about this possibility please see The SDT Analyzer.
There are several steps that must be carried out before the generated C files can be compiled and linked together with the Cmicro Library. The user should follow the procedures that are documented in the section Targeting with the Cmicro Package.
In the following subsections the different files that are generated are explained.
The first file that is generated from the Cmicro Code Generator is called sdl_cfg.h
. It is used to scale the Cmicro Kernel depending on what characteristics the SDL system has. This is called automatic scaling and automatic dimensioning facility.
The file contains a header, process ID declarations, and then a #define
or a /*NOT define ... */
for each of the flags that the Cmicro Code Generator can generate automatically.
/* Program generated by SDT Cmicro <version> <date> */ #ifndef XSCT_CMICRO #define XSCT_CMICRO #endif /* "sdl_cfg.h" file generated for system <systemname> */ #define XMK_CFG_TIME <GenerationTime> #define XPTID_<auto-prefix>_<ProcessName> 0
The XMK_CFG_TIME
macro is used internally when compiling and executing with the Cmicro Tester takes place. With this macro, a rough consistency check for the generated files is done. The <GenerationTime> of the different files that are generated is compared in the Library, by the BodyBuilder and by the Cmicro Tester. If there is an inconsistency, compilation errors will occur.
The process ID declarations are generated as #define
values in C, like:
#define XPTID_<auto-prefix>_MyProcess 0
where the first process in the system is the value of 0 assigned, the second process gets the value 1, and so on. Due to the implementation of SDL'92 object orientation in the Cmicro Code Generator, there is also an automatic prefix generated. Using this prefix in the user's environment functions, it is possible to distinguish between several processes with the same name. Please refer to Generation of Identifiers for more information.
The rest of the file sdl_cfg.h
is about the automatic scaling and automatic dimensioning of the SDL system. It may look for example like:
#define MAX_SDL_PROCESS_TYPES <N> #define XMK_USED_ONLY_X_1 #define MAX_SDL_TIMER_TYPES <X> #define MAX_SDL_TIMER_INSTS <Z> #define XMK_HIGHEST_SIGNAL_NR 4 /* NOT #define XMK_USED_TIMER */ /* NOT #define XMK_USED_DYNAMIC_CREATE */ /* NOT #define XMK_USED_DYNAMIC_STOP */ /* NOT #define XMK_USED_SAVE */ #define XMK_USED_SIGNAL_WITH_PARAMS /* NOT #define XMK_USED_TIMER_WITH_PARAMS */ /* NOT #define XMK_USED_SENDER */ /* NOT #define XMK_USED_OFFSPRING */ /* NOT #define XMK_USED_PARENT */ /* NOT #define XMK_USED_SELF */ /* NOT #define XMK_USED_PWOS */ /* NOT #define XMK_USED_INITFUNC */
For a first rough understanding of the meaning of the different flags: The SDL system from above contains <N>
process types (using SDL'88 terminology), all the processes are declared in the form (0,1)
or (1,1)
. There are <X>
timers declared (in this case, <X>
must be 0, because XMK_USED_TIMER
is undefined, and the system uses an amount of <Z>
signals. The system does not use any create or stop (XMK_USED_DYNAMIC_CREATE and XMK_USED_DYNAMIC_STOP are undefined). In this way all the other flags have special meaning.
For explanations about the different flags the user should refer to Automatic Scaling Included in Cmicro.
Assumed, that the user selected "No separation" in the Organizer's Make Dialog, and no partitioning is used, then the Cmicro Code Generator will generate one C file per SDL system. This file contains all the characteristics of the SDL system including all the declarations that the SDL system itself needs. For an explanation of this file see Output of Code Generation.
There is one file generated from the Cmicro Code Generator that contains all the definitions and declarations that are necessary to implement the environment functions xInEnv
and xOutEnv
.
The file is generated only if the option Generate environment header file of the Organizer's Make dialog is switched on.
The file is called <systemname>.ifc
and it contains a header, the type definitions used on system level (newtypes, syntypes, synonyms), the signal IDs and the structure type definitions for the parameters of the signals.
#ifndef X_IFC_z_env01 #define X_IFC_z_env01 #define XMK_IFC_TIME <GenerationTime>
The XMK_IFC_TIME
macro is used internally when compiling and executing with the Cmicro Tester takes place. With this macro, a rough consistency check for the generated files is done. The <GenerationTime> of the different files that are generated is compared in the Library, by the BodyBuilder and by the Cmicro Tester. If there is an inconsistency, compilation errors will occur.
Followed by the header the section about sorts follows. The sorts are generated according to the documentation in The Cadvanced/Cbasic Code Generator.
Next, the definitions for signals follows, which consists of:
IN
or OUT
as seen from SDLFor easy interpretation: For an SDL signal without parameters, going from the environment to SDL, like:
signal SIn;
the following is generated into the .ifc
file:
/* SIn IN */ #define SIn <X>
For an SDL signal with parameters, going from SDL to the environment, like
signal SOut (integer, mystruct, boolean);
the following is generated into the .ifc
file:
/* SOut OUT */ typedef struct { SIGNAL_VARS SDL_Integer Param1; mystruct Param2; /* mystruct declared in the section */ /* declaring sorts */ SDL_Boolean Param3; } yPDef_<UniquePrefix>_SOut; typedef yPDef_<UniquePrefix>_SOut *yPDP_<UniquePrefix>_SOut; #define yPDP_SOut yPDP_<UniquePrefix>_SOut #define yPDef_SOut yPDef_<UniquePrefix>_SOut #define SOut <X>
At least the signal ID (here: SOut
) and the name of the structure (here: yPDef_SOut
) must be used in the xOutEnv
C function in this case.
The code generation of structure types and signal IDs is (except the C comment about IN
or OUT
) independent from the direction the signal goes.
In order to give some additional help for making C identifiers unique, the Cmicro Code Generator generates, in the section declaring sorts:
/***** * NEWTYPE mystruct (TYPE section) * <<SYSTEM systemname>> ******/ typedef struct <UniquePrefix>_mystruct_s { <typename> v1; <typename> v2; .......... ... <typename> vN; } <UniquePrefix>_mystruct; #ifdef XPREFIX_NAMES_IN_IFC #define pTYPE_mystruct <UniquePrefix>_mystruct #else #define mystruct <UniquePrefix>_mystruct #endif
If the user defines the flag XPREFIX_NAMES_IN_IFC
, the prefix pTYPE_
is used for all the names of the sorts that are generated into the .ifc
file, which will help to distinguish a sort name from a signal name for example.
More explanation about the environment header file is given in The Cmicro Library.
The Cmicro Code Generator generates a file that contains production rules for the C program. This file can be used together with "make" facility only. The make facility that the user wants to use must be conform to the UNIX make. The file is called <systemname>.m
and the use of it is described in The Cmicro Library.
The generated symbol file is used to store symbolic information about the SDL system. The file has meaning for the host part of the Cmicro Tester only and is called <systemname>.sym
. It is used for Cmicro Tester purposes only and is described within The Host Symbol Table.
The generated kernel group file contains information about process names. This file is especially used in integrations, when OO is used and processes are instantiated. Using the information from this file, it is possible to distinguish between several process instantiations with the same name.
In this section the implementation details are discussed. These details are meaningful for understanding how a generated Cmicro application does work.
For host simulation, with the predefined kernels, a time unit represents one second. In target applications, time is to be implemented by the user (see subsection Defining the SDL System Time Functions in mk_stim.c).
If real time is used, then there will be a connection between the clock in the executing program and the wall clock. For applications the user must provide the connection with the wall clock, normally the hardware timer.
Note: The C standard function |
The Cmicro Kernel does not use a process ready queue. It processes the signals in the order of their appearance. To do this, there is a signal queue which stores the signals sent to any process (either internally or externally). There are different ways to influence the scheduling when using the Cmicro Code Generator:
It is possible to assign priorities to process types (using SDL'88 terminology). The processes' priorities are assigned when designing the SDL system. They are assigned using the #PRIO directive.
There are some things to be kept in mind when using process priorities:
The Cmicro Kernel handles process priorities by collecting all signals sent to processes of the same priority in a separate queue. Thus, there is a queue for each priority level.
While the SDL system is running the kernel checks for signals in the queues with decreasing priority. This check takes place whenever an SDL output appears or a process performs an SDL nextstate operation. Because of the kernel checking for signals whenever an output takes place, it is possible to have preemptive scheduling.
Assume, there are two process types lowprio and highprio. Let process type lowprio have the priority one and process type highprio have the priority zero.
If an instance of process type lowprio performs an output to process type highprio, there appears a signal in a queue of a higher priority level (zero is the highest priority level available, process lowprio has priority one) which leads to the kernel immediately working on the signal sent to the process highprio. The transition of process lowprio will not end until process highprio has finished its transition invoked by the signal.
This way of scheduling is implemented using recursion.
Note: Process priorities are available only when using a compiler which can handle recursion. |
There is basically no restriction on the number of priority levels, but the target and compiler used will of course limit the depth of recursion.
As a general recommendation process priorities should not be assigned one per process type, but the process types should be grouped according to their purposes and these groups should then be assigned a priority level.
The signals in the queue(s) are normally ordered according to their appearance (FIFO-strategy). By assigning priorities to signals this ordering is user definable. The directive #PRIO is used to assign a priority to signals.
Priority increases with decreasing numbers, but there is no restriction to use consecutive numbering.
Whenever a signal is sent, it is inserted into the signal queue(s) according to its priority.
Assume, there is a process performing two signal outputs, first_sig and second_sig. Using the standard FIFO-strategy signal first_sig would be worked on before signal second_sig. But with signal priorities and signal first_sig assigned priority fifty and signal second_sig assigned priority twenty, signal second_sig would be in front of signal first_sig in the queue and thus would be worked on before signal first_sig.
For more details please refer to Assigning Priorities -- Directive #PRIO.
Every combination of signal and process priorities may be used. In this way it is possible to adapt the scheduling to the users' needs.
The Cmicro Code Generator only allows SDL synonyms to be implemented as C macros. To be translated to a macro the expression defining the value of the synonym must be:
Synonyms that cannot be calculated at analyze time are not supported by the Cmicro Code Generator. That is to say synonyms that would normally be implemented as variables in the generated code are not supported by the Cmicro Code Generator.
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 be included as macro definitions into the generated code, for instance by including another header file.
To use a macro definition in C to specify the value of an external synonym, the user should perform the following steps:
#define synonym1 value1 #define synonym2 value2
#CODE
directive at the system level among the SDL definitions of synonyms, sorts, and signals, for example, but before any use of the synonyms./*#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 re-compiling the system.
Note: When an application is created, the macro definitions should be used for all external synonyms, as the function for reading synonym values stored on file is not available. (See below.) |
This method of reading external synonyms is not supported for applications and is therefore not supported by the Cmicro Code Generator.
In SDL-92, value returning procedures and operator calls are introduced. This means, that an SDL procedure can be called within an expression. As the Cmicro Code Generator cannot handle procedures with states, it is not necessary to map such calls to a different scheme.
TASK i := (call p(1)) + (call Q(i,k));is translated to something like:
i = p(1) + Q(i,k);
Note: The value returning procedure calls are transformed to C functions which return values. |
Operators which are defined using operator diagrams, are as in the models in the SDL recommendation, treated exactly as value returning procedures.
The code generation for the PAD function is different compared with Cadvanced, in the way that code that is common in process types is copied into the PAD function for instantiated processes. This is implemented in contrast to Cadvanced, where for each process type definition there is a C function generated once, that is called by the instantiated PAD function, for common code. This makes a difference when system partitioning and/or file separation is used.
`Any' should not be used in applications using the Cmicro Code Generator, because it leads to an error message. Random is not implemented in the Cmicro Package.
The Cmicro Code Generator is a code generator using the semantics of SDL-92 with some restrictions. The behavior for output is according to the rules described in the following:
ErrorHandler
will be called.In this section the specialities and exceptions about abstract data types for Cmicro are discussed only. A complete documentation about the abstract data types is given in The Cadvanced/Cbasic Code Generator.
All the macros and external definitions for functions can be found in the files sctpred.h
and sctpredg.h
, except for the PId sort which is handled in the file ml_typ.h
.
The C functions for the handling of predefined sorts are defined in the file sctpred.c
.
On UNIX these files can be found in $sdtdir/cmicro/kernel
.
In Windows these files can be found in the Telelogic Tau installation under %SDTDIR%\cmicro\kernel
.
A general exception existing for all the predefined types is that the user must configure which predefined types are to be compiled into the target C program. This is necessary to hold the target C program as small as possible. The configuration is to be performed with the help of the BodyBuilder, which is explained in The Cmicro Library.
Problems will occur during compilation when the configuration is not according to what the SDL system needs. The user should refer to the explanations about manual scaling in The Cmicro Library. |
External synonyms are to be defined by the user in the following way.
For a synonym like
synonym xternal integer = EXTERNAL;
the Cmicro Code Generator expects to see xternal
as a #define
value that is to be defined by the user. This can be done for example in the following way:
synonym xternal integer = EXTERNAL; /*#CODE #TYPE #ifndef XSCT_CMICRO #define xternal 7 #endif */
This also means, that if xternal is not defined from the user, it will lead to compilation errors.
Charstrings can be used either in the usual way as they are when using Cadvanced, or they can be used in a restricted way. The decision is up to the user and is a question of configuration. The user should be aware that some of the predefined sorts from ASN.1 are based on the implementation of SDL charstrings. This is discussed in subsection Support of SDL Constructs.
The predefined data types Time and Duration are implemented in a more or less restrictive way. It is possible to specify a real value for Time and Duration on SDL level, like 23.45. The Cmicro Library uses only the integer part in front of the dot, 23 in this example. The mapping of SDL time units to time units in a target application is -- in any case -- up to the user.
The #UNIONC directive is not recommended when using the Cmicro Code Generator because there is no support for checking the validity of the component selection. Both the #UNION directive and the CHOICE concept are a better alternative.
These generators are implemented in Cmicro, but the user should be aware that the use of any of them requires that dynamic memory allocation is used in the target system. Generally, Cmicro tries to prevent the use of dynamic memory allocation whenever possible. The reasons for this are explained in The Cmicro Library.
This package can be used together with Cmicro with the following restriction.
There are two operators that are excluded when Cmicro C code is compiled. The operators are "CStar2CString
" and "CharStar
".
The reason for this is that with Cmicro it is possible to define an array of char in C instead of the predefined solution of Cadvanced (to use dynamic memory allocation). This is discussed in subsection Support of SDL Constructs.
This package cannot be used with Cmicro.
This ADT cannot be used together with Cmicro because it refers to Cadvanced code.
This ADT can be used together with Cmicro in the same way as described for Cadvanced.
This ADT is not useful for typical Cmicro applications (embedded systems usually do not provide a hard disk in Cmicro applications) and for that reason never has been tested. The ADT may however work with Cmicro.
This ADT cannot be used together with Cmicro.
This ADT cannot be used together with Cmicro because it refers to Cadvanced code.
This ADT can be used together with Cmicro.
This ADT can be used together with Cmicro.
This ADT cannot be used together with Cmicro, generally.
Instead of pidlist.pr
, the user may include the cm_pidlist.pr
file. The use of this ADT is however restricted, because Cmicro implements a different scheduling algorithm. This means, that systems that are successfully simulated first, may contain problems when a Cmicro target application is build and executed.
It is therefore recommended not to use neither pidlist.pr
nor cm_pidlist.pr
, in order to achieve the best possible SDL conformity.
This ADT can be used together with Cmicro but the user should be aware of it's own restrictions. For more explanations please see Pointer Types.
This ADT can be used together with Cmicro.
This ADT can be used together with Cmicro.
This ADT can be used together with Cmicro.
Default values are in principle generated in the same way as with the Cadvanced Code Generator. It is however possible to configure the default value setting, which is explained in The Cmicro Library. The right configuration is essential to prevent illegal behavior.
The Cmicro Code Generator does not provide read and write functions. The reason is, that the Cmicro Code Generator mainly is used to build target applications, and not simulations. This is also a consequence of optimizing the target program. If the user uses the Q (question) operator, the Cmicro Code Generator ignores this.
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 ErrorHandler
, with the following prototype
extern void ErrorHandler( xmk_OPT_INT errnum )
can be used for this purpose. xmk_OPT_INT
is defined in ml_typ.h
, normally as an ordinary C int. errnum
may be one of the free values of error numbers. Please inspect ml_err.h
in order to get a list of reserved values.
if ( strlen(C) <= 1 ) { #ifdef XMK_USE_ERR_CHECK ErrorHandler (ERR_N_InvalidStringLength); #endif return SDL_NUL; } else return C[1];
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 C function ErrorHandler
is called and a default value is returned (NULL). By including the call to ErrorHandler
between #ifdef XMK_USE_ERR_CHECK - #endif
the function is only called to report the error, if error checks are turned on. The one parameter to the C function ErrorHandler
should identify the error. The number must be given by the user.
Another possibility to route error messages to the host system is to use the C function xmk_PrintString
of the Cmicro Tester, defined as:
extern void xmk_PrintString( char * )
if ( strlen(C) <= 1 ) { #ifdef XMK_ADD_MICRO_TESTER xmk_PrintString ("ERR:Invalid Stringlength"); #endif return SDL_NUL; } else return C[1];
As already mentioned in earlier subsections, the user should be aware that some of the ASN.1 predefined sorts are based on the implementation of SDL charstrings. The user should also refer to subsection Support of SDL Constructs.
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 run-time library that might be helpful when handling Charstrings.
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. These are documented in Dynamic Memory Allocation.
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 Cmicro Code Generator can generate a separate file for:
system S; /*#SEPARATE 'filename' */ block B; /*#SEPARATE */ process type P1 inherits PType; /*#SEPARATE */ process P2 (1, ); /*#SEPARATE */ procedure Q; /*#SEPARATE */
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 Cmicro 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 a case the file name becomes the name of the unit with the appropriate extension (.c .h
) depending on contents. The file name is stripped of 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 Generated Files), can be used in a 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 been selected, the Cmicro 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.
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 options.
system S; /*#SEPARATE 'Sfile' */ block B1; /*#SEPARATE */ process P11; /*#SEPARATE 'P11file' */ process P12; block B2; process P21; process P22; /*#SEPARATE */
If Full separate generation is selected then the following files will be generated:
|
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.
If User defined separate generation is selected then the following files will be generated:
|
Sfile.h |
Contains code for units S, B2, P21 |
|
B1.h |
Contains code for units B1, P12 |
|
|
Contains code for unit P11 |
|
|
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.
If the separation option No is selected, only the following file will be generated:
|
|
Contains code for all units |
The comments on files and extensions earlier are valid even here.
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 re-compile 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, using the Organizer Edit Separation command or 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. |
Priorities can be assigned to processes using the directive #PRIO. The process priorities will affect the scheduling of processes, see Scheduling. A priority is a positive integer, where low value means high priority. #PRIO directives should be placed directly after the process heading in the definition of the current process.
Process P1; /*#PRIO 0 */ Process P2(1,1); /*#PRIO 1 */ Process P3 : P3Type; /*#PRIO 0 */ Process P4(1,1) : P4Type; /*#PRIO 1 */
Processes that do not contain any priority directive will have a user defined default priority with the name xDefaultPrioProcess
.
There are some things to be kept in mind when using process priorities:
Priorities can be assigned to signals using the directive #PRIO. The signal priorities will also effect the scheduling of processes, see Scheduling.
Signal priorities can be specified, either:
It is impossible to specify #PRIO in a SDL input. Cmicro will ignore any occurrence of #PRIO in SDL input.
Signal priorities do affect the SDL output and the SDL create actions only.
The following rules are to be considered here:
xDefaultPrioSignal
), and must in the range of 0 to 255. As a recommendation, this value should be set to 100, so that both higher, as well as lower priorities can be declared with #PRIO.xDefaultPrioSignal
for each occurrence of that signal in an output.xDefaultPrioSignal
will be used.XMK_CREATE_PRIO
. As a general recommendation, this priority should be higher than any other signal priority.The following example will give more explanations (note, that the values PA, PB are of sort pid):
Signal S1, /*#PRIO 11 */ S2, /*#PRIO 22 */ S3, S4; /*#PRIO 44 */ .... output S1 to PA output S1 to PB; /*#PRIO 55*/ .... output S3 to PC; output S3 to PD; /*#PRIO 66*/
Assuming the following C definition:
#define xDefaultPrioSignal 100
the following priorities will then be generated:
output S1 to PA --->use of prio 11 output S1 to PB--->use of prio 55 .... output S3 to PC--->use of prio 100 output S3 to PD--->use of prio 66
There is no difference for the #EXTSIG, #ALT and #TRANSFER directives for Cmicro compared with Cadvanced, except that the use of it will sometime lead to a better performance. This is because if #EXTSIG for example is used in the case of an output to the environment, the user can prevent the Cmicro Kernel to be called (and the xOutEnv
function to be executed).
This section gives an overview of the code generated by the Cmicro Code Generator. This is useful, to make it possible to interpret the generated code. To know how the code is generated makes it quite easy to understand the program which is necessary and useful when testing and debugging erroneous executable programs.
Figure 538 : Structure of the generated C code
|
Not all the intricate details of the generated code are described here. The depth of description is sufficient to give the reader a reasonable understanding of the code generation algorithms. Explanations will illustrate what the code looks like, but not why.
The generated code contains several places where prefixes are generated, which consists of a prefix and unique numbering. The following prefix is generated for all objects: "z<nnn>_
", where nnn is an incremental number.
Allowance for conditional compilation occurs in several places throughout the generated code. The generated C code is conditionally compiled, for example, for dynamic process creation (create symbol). A differentiation is made between conditional compilations generated by the Cmicro Code Generator (called automatic scaling, prefix XMK_USED_
) and conditional compilations which are dependent on header files, which are to be modified by the user (called manual scaling, prefix XMK_USE_
).
Note: Generally speaking, the ordering of the following subsections corresponds to the ordering in which the code is generated. |
Each compilation unit is compiled either in one a.c
file or into two files, a.c
and a.h
.
Only the differences are shown, when comparing the output of SDT C Code Generator with the Cmicro Code Generator. The overall differences of the both code generators are described in the section Differences between Cmicro and Cadvanced.
Code generation on the .c
file for the current unit is started by generating the following header:
/* Program generated by SDT Cmicro, version x.y */ #define XSCT_CMICRO #define C_MICRO_x_y #define XMK_C_TIME <GenerationTime> #include "ml_typ.h"
The XSCT_CMICRO
macro can be used by the user to distinguish between the different Code generators, for example within ADT bodies.
TheC_MICRO_x_y macro can be used by the user to distinguish between different versions of the Cmicro Code Generator. This is usually not but might become necessary if the output of the Cmicro Code Generator is different.
TheXMK_C_TIMEmacro is used internally when compiling and linking and executing with the Cmicro Tester takes place. With this macro, a rough consistency check for the generated files is done. The <GenerationTime> of the different files that are generated is compared in the Library, by the BodyBuilder and by the Cmicro Tester. If there is an inconsistency, compilation errors will occur.
The#include "ml_typ.h" is used to include all necessary declarations that the generated C code may use, including automatic scaling from sdl_cfg.h
and predefined sorts.
As a difference to SDT C Code Generator, this section contains the definitions for the process IDs and the forward declarations used in the generated C code.
Process IDs are generated as #define
values in C, like:
#define XPTID_<UniquePrefix>_MyProcess 0
where the first process in the system is the value of 0 assigned, the second process gets the value 1, and so on. Please refer to Generation of Identifiers for more information.
The following forward references are generated:
extern XCONST XPDTBL yPDTBL_<UniquePrefix>_MyProcess;
Following this, the usual declarations are generated as described in The Cadvanced/Cbasic Code Generator.
No synonym variables are generated when using Cmicro.
Symbol tables are only generated for the Cmicro Tester, and not into the generated C code. The symbol tables generated for the Cmicro Tester are described within The Cmicro Tester.
Tables are used to represent the behavior of SDL objects, like processes and timers. It is not absolutely necessary to understand how these tables are generated and how the Cmicro Kernel works with them. The following subsections are only for those readers interested in the nature of the table structure.
The root process table contains, for each of the defined SDL process types, a reference (i.e. a pointer) to the Process Description Table. The Cmicro Kernel is the main user of the root process table. Via this table, it can access all SDL process types and all SDL process instance data. The location of the generated root process table is directly before the yPAD-functions in the generated C file. The type definitions used in this table are located in the ml_typ.h
module.
Figure 539 : Root process table
|
C-Type definition (ml_typ.h
) :
extern xPDTBL yPDTBL [];/* for the Cmicro Kernel */ #define X_END_ROOT_TABLE/* Table-End Marker of yPDTBL*/
C constants (sdl_cfg.h
):
#define MAX_SDL_PROCESS_TYPES <N> /* <Process-type-id´s> Process Types are numbered */ /* from 0 to N-1(see chapter "Generating PID") */ #define XPTID_Process1Name 0 #define XPTID_Process2Name 1 #define XPTID_ProcessnName N-1
C code generation for the whole system:
XPDTBL yPDTBL [MAX_SDL_PROCESS_TYPES+1] = { yPDTBL_ Process1Name, yPDTBL_ Process2Name, ...... yPDTBL_ ProcessnName, X_END_ROOT_TABLE }
In order to reduce the use of dynamic memory allocation, there is a table generated in the code which is used by the Cmicro Tester to store and retrieve test options, like switches, which define the trace.
The table is conditionally compiled and only included if the Cmicro Tester is contained in the target- executable.
The symbol trace table looks like:
/************************************************************* ** Symbol trace table *************************************************************/ #ifdef XMK_ADD_TEST_OPTIONS XSYMTRACETBL *xSYMTRACETBL[MAX_SDL_PROCESS_TYPES+1] = { (XSYMTRACETBL_ENTRY *) NULL, /* for first Processtype */ (XSYMTRACETBL_ENTRY *) NULL, /* for second Processtype */ ........ ........ (XSYMTRACETBL_ENTRY *) NULL, /* for last Processtype */ X_END_SYMTRACE_TABLE /* table end marker */ }; #endif
More information can be obtained by reading The Cmicro Tester.
The struct is generated in the header-section of the generated C file.
typedef struct { PROCESS_VARS TypeName1 FPAR_var1; TypeName2 FPAR_var1; TypeName3 DCL_var1; TypeName4 DCL_var2; TypeName4 yExp_DCL_var2; TypeName5 FPAR_var1; } yVDef_ProcessName;
Instances of a given type are represented as a C array. The code generation of variables for each SDL process looks like:
#define X_MAX_INST_ProcessName upperlimitofprocessinstances1 static yVDef_ ProcessName yINSTD_ProcessName[X_MAX_INST_ProcessName];
A reference to this array is generated in the Process Description Table which is discussed in the subsection Process Description Table.
This table is generated for each process in the header-section of the generated C file. It contains information about the state of each process instance. The table contains ordinary SDL state values as well as the values XSTARTUP
and XDORMANT. XSTARTUP
is generated for each instance which is to be statically created (in (x, N) declarations, where x is > 0), XDORMANT
is the value which is used to tag a process instance as sleeping. In the case of creation this instance can be reused.
C typedef for the process state table (located in ml_typ.h
):
typedef u_char xSTATE; /* see defines below */ #define XSTARTUP 0xff /* valid only if xSTATE is */ /* u_char else 0xffff */ #define XDORMANT 0xfe /* valid only if xSTATE is */ /* u_char, else 0xfffe */
C code generation for each process:
static xSTATE yPSTATETBL_znn_ProcessName [X_MAX_INST_znn_ProcessName] = { <creation-tag> /* Instance 0 */ <creation-tag> /* Instance 1 */ <creation-tag> /* Instance M-1 */ };
where <creation-tag>
is either XSTARTUP
or XDORMANT
.
Code for a process type with 4 instances, 2 of which are to be created at SDL system start:
static xSTATE yPSTATETBL_znn_ProcessName [4] = { XSTARTUP, /* Create at SDL-system-start */ XSTARTUP, /* Create at SDL-system-start */ XDORMANT, /* Create later */ XDORMANT /* Create later */ };
A reference to this table is created in the Process Description Table, which is discussed in the subsection Process Description Table.
This is generated in the header-section of the generated C file. It contains all transitions of a process, including asterisk states, asterisk inputs and asterisk save.
The C typedef
for the transition table (located in ml_typ.h
) is as follows:
typedef struct { xINPUT SignalID; /* Input, Asterisk-Input. */ /* Input is Timer */ /* and/or ordinary Signal */ xSYMBOLNR SymbolNr; /* Symbolnumber to be used */ /* in yPAD-function */ } xTR_TABLE_ENTRY;
C code generation:
static XCONST xTR_TABLE_ENTRY yTRTBL_znn_ProcessName [XMAX_TRANS_znn_ProcessName]= { /* state_0-table */ input_1, SymbolNr, input_2, SymbolNr, XASTERISK,XSAVEID /* asterisk save */ input_N, SymbolNr, /* state_1-table */ ............... ............... /* state_j-table */ input_1, SymbolNr, input_2, SymbolNr, input_N, trans_jN, XASTERISK,XSAVEID /* asterisk save */ };
The SymbolNr
shown above is used to select the right transition in the switch generated in the yPAD function.
Where the C define
XASTERISK
is an ID defining all possible SDL Inputs (asterisk Inputs),
XSAVEID
is a simple ID defined in ml_typ.h
which can be compared by the SDL Kernel to detect signal-save.
And where:
#define XASTERISK -1 #define XSAVEID xSave
A reference to this table is created in the Process Description Table.
This is generated in the header section of the generated C file.
C typedef (ml_typ.h
):
typedef u_char xSTATE_INDEX;
C code generation (header of generated C file):
static xCONST xSITBL xSTATE_INDEX_znn_ProcessName [<count_transitions_of_ProcessName] = { 0, /* i.e.a process with 3 states, but no asterisk states */ /* state_0 has 2 transitions */ 2, /* state_1 has 5 transitions */ 7, /* state_2 has 3 transitions */ 10 /* table-end-index XI_TABLE_END */ };
The first value in the above table indicates the beginning of the first state in the Transition Table. If asterisk state definitions are not found in the process, this value is 0.
A reference to this table is created in the Process Description Table.
These tables are used to store the values parent and offspring for each process. The reason an extra table is used to store this information is to simplify initialization. The Cmicro Kernel updates the values in the table according to the SDL rules.
C-type definition (ml_typ.h
):
#ifdef XMK_USE_PID_ADDRESSING typedef struct { #ifdef XMK_USE_SDL_PARENT xPID Parent; #endif #ifdef XMK_USE_SDL_OFFSPRING xPID Offspring; #endif } xPIDTable; #endif
C code generation for each process:
/*-----------Process-PID-Values-------------*/ #ifdef XMK_USE_PID_ADDRESSING static xPIDTable yPID_TBL_z00_P1[X_MAX_INST_z00_P1]; #endif
A reference to this table is created in the Process Description Table, which is discussed in the subsection Process Description Table.
For each SDL process, an automatically initialized C structure is generated called process description table. This table is used in the Root Process Table to enable the Cmicro Kernel to access process type information as well as process instance data.
Inspect the following diagram to see which information is contained in the process description table:
Figure 540 : Process description table
|
Allocated to each SDL process type is one table yPDTBL_ProcessName.
The type definitions of this table are located in the ml_typ.h
module.
C typedef for the process description table (ml_typ.h
):
typedef struct { #ifdef XMK_USE_PID_ADDRESSING xPIDTable *pPIDTable; /* Table with */ /* Parent/OffspringValues */ #endif xINSTD *pInstanceData ; /* Pointer to Instancedata*/ /* Vector */ xINSTDLEN DataLength ; /* Length of Instancedata */ /* for 1 Instance */ /* (used by SDL-BS) */ unsigned char MaxInstances ; /* Max.Number of Instances*/ #ifdef XMK_USE_TIMESLICE /* Time-Slices can be individually specified by the user*/ /* The value stored in TimeSlice is measured in ticks */ /* The Cmicro Kernel has to be scaled to handle */ /* timeslicing */ xmk_T_TIMESLICE TimeSlice; #endif #ifdef XMK_USE_PREEMPTIVE /* Process-Priority can be specified with #PRIO on the */ /* SDL-Level. It is available only, if the Cmicro */ /* Kernel is scaled to handle preemption. */ xmk_T_PRIOLEVEL PrioLevel;/*Priority of this processtype*/ #endif xmk_T_TRANS_ADDRESS yPAD_Function ; /* Address of the */ /* yPADFunction */ xTRTBL TransitionTable ; /* Pointer to transition table */ xSITBL *StateIndexTable ; /* Pointer to state index table */ xSTATE *ProcessStateTable;/* Pointer to process state table */ } XPDTBL;
C code generation for each process:
#define X_MAX_INST_ProcessName 1 xPDTBL yPDTBL_ProcessName = { yPID_TBL_znn_<process:N>, (xINSTD*) yINSTD_znn_ProcessName, X_MAX_INST_znn_ProcessName, (xmk_T_TRANS_ADDRESS) yPAD_znn_ProcessName, yTRTBL_znn_ProcessName; xSTATE_INDEX_znn_ProcessName, yPSTATETBL_znn_ProcessName; };
For each generated process description table, a new entry in the Root Process Table is generated.
No code is generated to evaluate the graphical references during run-time of the SDL system. A large amount of memory is required to store and handle such information which normally proves too large for any real target system.
Alternatively, C comments are generated which make it possible to verify and debug the generated code as illustrated in the following example. The PR <position> indicates in which line number of the SDL-PR file the symbol can be found.
For processes : /************************************************************* ** PROCESS <process-name> ** <<SYSTEM <system-name>/BLOCK <block-name>> ** #SDTREF(<reference>) *************************************************************/ For signals : /************************************************************* ** SIGNAL S1 ** <<SYSTEM <system-name>/BLOCK <block-name>> ** #SDTREF(<reference>) *************************************************************/ For yPAD-function /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** Function for process <process-name> ** #SDTREF(<reference>) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ For output : /*----- ** OUTPUT <signal-name> ** #SDTREF(<reference>) ------*/ For nextstate : /*----- ** NEXTSTATE <state-name> ** #SDTREF(<reference>) ------*/
The basic structure of the generated C code for process and procedure definitions remains the same as for the SDT C Code Generator although some modifications are evident.
The code generation for the PAD function is different compared with Cadvanced, in the way that code that is common in process types is copied into the PAD function for instantiated processes.
Procedures follow the same code generation as processes, with some small exceptions in macro naming conventions for variable declarations.
Each SDL process is represented in C by a C function called yPAD_ProcessName
.
yPAD_ProcessName
/* Function for process ProcessName */ #ifndef XNOPROTO extern YPAD_RESULT_TYPE yPAD_ProcessName ( YPAD_ANSI_PARAM ) #else extern YPAD_RESULT_TYPE yPAD_ProcessName ( YPAD_KR_PARAM ) YPAD_KR_DEF #endif { local variable section State-input-selection { start-transition including nextstate transition-1 including nextstate transition-2 including nextstate ....... transition-n including nextstate } pad-end-section } /* Function for procedure ProcedureName */ #ifndef XNOPROTO extern YPRD_RESULT_TYPE yPAD_ProcedureName ( YPRD_ANSI_PARAM ) #else extern YPRD_RESULT_TYPE yPAD_ProcedureName ( YPRD_KR_PARAM ) YPRD_KR_DEF #endif { local variable section section representing procedure body }
The following defines are generated in the local variables section for processes.
YPAD_YSVARP /* used for signal variable pointers */ YPAD_YVARP(yVDef_z00_P1)/* used for process variables */ YPAD_TEMP_VARS /* used for temporary variables */ YPRSNAME_VAR("P1") /* can be used for printf */ BEGIN_PAD /* used for some preparations */ /* to handle signals, or Integration */ /* of any Realtime operating system */
After expansion by the C preprocessor:
yVDef_z00_ProcessName *yVarP =(yVDef_z00_ProcessName *)pRunData; unsigned char *yOutputSignal; unsigned char *ySVarP; (void) printf(("PROCESS:%s\n", "ProcessName")); if((P_MESSAGE != ((void *) 0)) && (P_MESSAGE->mess_length > 4)) { ySVarP = (unsigned char *) P_MESSAGE->mess_ud.pt_ud; } else { ySVarP = (unsigned char *) P_MESSAGE->mess_ud.ud; }
The following defines are generated in the local variables section for procedures:
YPRD_YVARP(yVDef_znnn_ProcedureName) /* used for procedure variables */ YPRD_TEMP_VARS /* used for temporary variables */ YPRDNAME_VAR("ProcedureName") /* can be used for printf */
The selection of the appropriate SDL transition which is to be executed in the current state with the current signal in the input port goes in principle over the transition table, described in previous chapters. With this table, the Cmicro Kernel can evaluate a symbol number, which is local to a process, a unique numbering of the different possible transitions. This numbering algorithm begins at 0 (which corresponds to the start symbol) and continues until all symbols for this particular process type have been numbered.
The appropriate transition is selected by the following switch:
switch (XSYMBOLNUMBER) { { case 0:. start-transition nextstate; case 1: transition-1 nextstate; }
After pre-compiling it:
switch (_xSymbolNumber_ ) { ..... }
The start transition is included into the body of the generated yPAD function and has the same layout as transitions, with the following exceptions:
Assignment of initialization values to all local variables in the processes and procedures (if any) is executed. All DCL variables are filled with their default-values.
The start transition is selected by the special case-value zero in the switch-statement of the yPAD function.
Note: FPARS in dynamic process creation are not contained in this version of the Cmicro Package. |
The transitions are translated in the order they are found and are only translated to the sequence of actions they consist of. The translation of actions are discussed in the subsection Translation of Actions following a few lines below.
Each yPAD function is finished with:
END_PAD (yPAD_ProcessName);
The main reason for this is to make it possible to integrate other real-time operating systems.
SDL output statements are translated to the following basic structure:
There are a lot of different output macros generated. The main reason for this is that for each output situation an optimized code is to be generated.
One differentiation is made for signals without parameters and signals with parameters. For a signal without parameters, suffix _NPAR
is used for the macro generated and for a signal with parameters, suffix _PAR
is used. The relevant output macro can then be expanded to a simpler output C function called xmk_SendSimple
, if no signal priority is used.
Another differentiation is made for signals which are sent to the system's environment or which are sent internally in the SDL system. The suffix _ENV
is appended to the macros which are shown here, if the signal should go to the system environment.
The different directives which can be used within SDT to modify outputs are discussed in subsection Modifying Outputs -- Directive #EXTSIG, #ALT, #TRANSFER.
The other different output situations which are handled, will be described in the next subsections.
If the user specifies output SignalName without TO and VIA in SDL, the Cmicro Code Generator calculates the receiver of the signal. It is also possible to have more than one receiver for the signal. During execution time, any possible receiver that are alive may be selected otherwise if no receiver can be found, the C function ErrorHandler
will be called. The following code is generated:
ALLOC_SIGNAL_ppp(SignalNamewithoutPrefix, SignalNamewithPrefix, SignalParameterTypeStructureName)ordinary assignment of Signal Parameters, if there are some...
SDL_OUTP_ppp(Priority, SignalNamewithoutPrefix, SignalNamewithPrefix, TO_PROCESS(ProcessNamewithoutPrefix, ProcessNamewithPrefix), SignalParameterTypeStructureName, "SignalNamewithoutPrefix")
Note: The |
After expansion, the user will find a C function call to the xmk_SendSimple
function or the xmk_Send
function.
Priority is generated as xDefaultPrioSignal
if no priority is specified for the signal with #PRIO
.
TO_PROCESS
is expanded to a function call if there is at minimum one (x, N) declaration in the system, where N is > 1. This function returns one of the possible receivers of the signal.
TO_PROCESS
selects an active instance of the given process type It does not check for different types as receivers.
TO_PROCESS
is expanded so that the pid is passed directly to one of the C functions xmk_Send*
, if there are only (x,1) declarations in the system.
If the environment is the receiver of the signal, then the following code is generated:
ALLOC_SIGNAL_ppp(SignalNamewithoutPrefix, SignalNamewithPrefix, SignalParameterTypeStructureName)ordinary assignment of Signal Parameters, if there are any...
SDL_OUTP_ppp_ENV(Priority, SignalNamewithoutPrefix, SignalNamewithPrefix, ENV, SignalParameterTypeStructureName, "SignalNamewithoutPrefix")
The |
After expansion, the user will find that ENV
is passed to one of the C functions xmk_SendSimple
or xmk_Send
. ENV
is a special value used inside the Cmicro Kernel to detect which signals are to be passed to the C function xOutEnv
.
If the user specifies the output SignalName to pid in SDL, the Cmicro Code Generator generates the following code:
ALLOC_SIGNAL_ppp(SignalNamewithoutPrefix, SignalNamewithPrefix, SignalParameterTypeStructureName)ordinary assignment of Signal Parameters, if there are some...
SDL_OUTP_ppp(Priority, SignalNamewithoutPrefix, SignalNamewithPrefix, pid-variable, SignalParameterTypeStructureName, "SignalNamewithoutPrefix")
Note: The |
Expansion reveals a C function call to the xmk_SendSimple
function or the xmk_Send
function.
Priority is generated as xDefaultPrioSignal
, if no priority is specified for the signal with #PRIO.
Possible generated values for pid variable are SDL_SENDER, SDL_PARENT, SDL_OFFSPRING
and SDL_SELF
or an SDL pid variable. These values are passed to the xmk_Send*
functions. The name of a process as specified in SDL may also be given.
The Cmicro Code Generator computes the possible receivers in an output with the VIA clause. If there are several possible receivers, an error message is produced.
If there is exactly one receiver, the same code is generated as for SDL output without to.
ALLOC_SIGNAL_NPAR
ALLOC_SIGNAL_PAR
same for signals with parametersTO_PROCESS
Macro used to evaluate a receiver process instance, if necessary in the case of (x, N) declarations, where N > 1.SDL_OUTP_NPAR
Output internally in the SDL system for signal without parametersSDL_OUTP_PAR
same for signal with parametersSDL_OUTP_NPAR_ENV
SDL_OUTP_PAR_ENV
same for signal with parametersSDL_ALTOUTP_NPAR
#ALT for an output internally in the SDL system for signal without parametersSDL_ALTOUTP_PAR
same for signal with parametersSDL_ALTOUTP_NPAR_ENV
#ALT for an output to the system environment for signal without parametersSDL_ALTOUTP_PAR_ENV
same for signal with parametersEXT_SignalName
if #EXTSIG is used in outputTRANSFER_SIGNAL
#TRANSFER is used in outputThe create action in SDL is translated to the following C code:
ALLOC_STARTUP_ppp(ProcessNamewithoutPrefix, ProcessNamewithPrefix, "ProcessNamewithoutPrefix, 0);....assignment of start-up values (cannot be used in this version of the Cmicro Package)
SDL_CREATE(ProcessNamewithoutPrefix, ProcessNamewithPrefix, "ProcessNamewithoutPrefix, 0, VariableofCreatedProcess, PriorityofCreatedProcess, yPAD-functionNameofCreatedProcess);
Note: The |
PriorityofCreatedProcess
is generated as
xDefaultPrioProcess
, if no priority is specified with #PRIO.
The translation of set is restricted in a few areas in order to produce efficient code for a micro controller. For example, the SDL duration expressed by a real value in the context of timers is not implemented. The reason for this is that controllers do not have floating point operations or floating point operations are not used in order to increase the performance. For timers, such a high resolution is not necessary in most applications. The Cmicro Package uses a long value in its standard implementation to represent absolute time.
In order to make the examples below more readable, it is assumed that at least one timer with parameter is used in the system (macro XMK_USED_TIMER_WITH_PARAMS
is defined in the generated file sdl_cfg.h
). If the macro is not defined, then the handling for timers with parameters is not included.
If the following is specified in SDL-PR:
Timer TimerName; ...... Set (now + durationvalue, TimerName) ;
or
Set (now + 22222, TimerName) ;
then the following code is generated:
SDL_SET_DUR(xPlus_SDL_Time(SDL_NOW,SDL_DURATION_LIT(22222.0,22222,0)), SDL_DURATION_LIT(22222.0, 22222, 0), TimerName, TimerNamewithPrefix, yTim_timer2, "TimerNamewithoutPrefix")
If the following is specified in SDL-PR:
Timer TimerName := TimerGroundValue ;---> see Note:!
then the following code is generated:
SDL_SET_TICKS(xPlus_SDL_Time(SDL_NOW, TICKS(SDL_INTEGER_LIT(22222))), TICKS(SDL_INTEGER_LIT(22222)), TimerName, TimerNamewithPrefix, yTim_timer2, "TimerNamewithoutPrefix")
The code after expansion then contains a function call to
xmk_TimerSet (TIMEEXPR,TimerNamewithPrefix,0).
TIMEEXPR
is the result of the evaluation of now plus duration value.
#define SDL_SET_DUR(TIME_EXPR, DUR_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_VAR, TIMER_NAME_STRING) \ xmk_TimerSet(TIME_EXPR, TIMER_IDNODE,0); #define SDL_SET_TICKS(TIME_EXPR, DUR_EXPR, TIMER_NAME, TIMER_IDNODE, TIMER_VAR, TIMER_NAME_STRING) \ xmk_TimerSet(TIME_EXPR,TIMER_IDNODE,0);
If a timer with parameter is defined in SDL-PR:
Timer TimerName (integer); ... set (now+1, TimerName (4711));
then the following code is generated:
SDL_SET_DUR_WITH_1IPARA(xPlus_SDL_Time(SDL_NOW, SDL_DURATION_LIT(1.0, 1, 0)), SDL_DURATION_LIT(1.0, 1, 0), TimerName, TimerNamewithPrefix, yPDef_z262_twp1, yTim_TimerName, "TimerName", SDL_INTEGER_LIT(4711))
The code after expansion then contains a function call to
xmk_TimerSet (TIMEEXPR,TimerNamewithPrefix,4711).
set (now + 5.5, TimerName)is not allowed (the real part is discarded i.e. 5.5 (= 5).
If the user specifies in SDL-PR:
Reset (TimerName) ;
then the following code is generated:
SDL_RESET(TimerNamewithoutPrefix, TimerNamewithPrefix, yTim_TimerName)
The code after expansion contains a function call to xmk_TimerReset
(TimerNamewithPrefix).
For a timer with one integer parameter, the following macro call is generated:
SDL_RESET_WITH_1IPARA(TimerNamewithoutPrefix, TimerNamewithPrefix, TimerParStruct, yTim_TimerName, TimerValue)
Timers with parameters are supported with the restriction that only one integer parameter is allowed. |
As SDL procedures are implemented with the restrictions explained within subsection SDL Restrictions, the following explanatory C code (to a procedure called ex_proc
) is generated:
ex_proc (....C parameters ...);
All necessary parameters are routed via the C function call stack.
Operator diagrams and procedures returning values are -- considering the call -- handled in the same way please see the following explanatory example:
TASK i := (call p(1)) + (call Q(i,k));is translated to something like:
i = p(1) + Q(i,k);
Note: The value of returning procedure calls are transformed to C functions returning values. |
The nextstate operation is generated at the end of each transition contained in the yPAD function, as follows:
SDL_NEXTSTATE(State1, z000_State1, "State1")after preprocessing:
return (z000_State1);
SDL_DASH_NEXTSTATEwhich is defined as:
return (XDASHSTATE);
A stop action is translated to:
SDL_STOP
which is defined as
return (XDORMANT);
which is good code saving. The Cmicro Kernel then enters the new state value into the Process State Table.
#ifdef XFREEVARS FREE_PROCESS_VARS () #endif SDL_RETURN
The macro definitions are:
#define SDL_RETURN \ if (_xxptr != (unsigned char*) NULL) \ { \ XMK_MEM_FREE ((unsigned char *)_xxptr); \ } \ return ;
where xxptr is the pointer to the procedure instance data, as given via the C function call parameter list. Note, that the memory previously allocated directly before the procedure call is freed at the end of the procedure, not outside of the procedure.
In this section some of the translation rules for expressions are described. For more information see Translation of Sorts where for example the translation rules for literals and operators in the predefined abstract data types are given.
SDL now is translated to the macro SDL_NOW
which is expanded to the C function xmk_NOW.
This function is exported by the module mk_stim.c.
The definitions for self, parent, offspring, sender are:
#ifdef XMK_USED_SELF #define SDL_SELF xRunPID #endif #ifdef XMK_USED_PARENT #define SDL_PARENT pRunPIDTable->Parent #endif #ifdef XMK_USED_OFFSPRING #define SDL_OFFSPRING pRunPIDTable->Offspring #endif #ifdef XMK_USED_SENDER #define SDL_SENDER P_MESSAGE->send #endif
All the variables above are of type xPID
. All variables are maintained by the Cmicro Kernel. xRunPID
is a global variable which contains the pid of the SDL process which is currently running. P_MESSAGE
is a pointer to the signal instance which is currently worked on.
An SDL timer active expression is translated to:
SDL_ACTIVE(TimerName, TimerName, yTim_TimerName)
which is expanded to:
xmk_TimerActive(TimerName)
A conditional expression in SDL is translated to a conditional expression in C.
An explicit initialization function is not generated by the Cmicro Code Generator in any case.
The structure of the SDL system is not generated into the C code. What is seen in the generated code, is the behavior of the SDL system. Variables of processes are initialized during the start transition of a process and no information about the structure of the SDL system is available during run-time in the generated code.
An initialization function is generated only in that case if synonyms are used within SDL, which require an initialized C variable.
All this results in a more compact executable.
For example, the following use of an SDL synonym results in a generated initialization function:
synonym a integer := /*#CODE anyUserFunction () */
The following C code is then generated within the C function yInit
:
yAssF_SDL_Integer(a, anyUserFunction (), XASS);
yInit
is called by the Cmicro Kernel if the define
XMK_USED_INITFUNC
is generated into the file sdl_cfg.h
, which is done in the case above.
The Cmicro Code Generator allows SDL synonyms to be implemented as C macros and C variables.
Initialization is implemented within the C function yInit
which is conditionally compiled.
The C function main
is not automatically generated by the Cmicro Code Generator. This is unnecessary because the main
function usually is provided from the user or the predefined main
function can be used. Instead of an automatically generated main
function, the user must supply the function body of main
, for target applications. Guidelines can be found in the subsection Implementation of Main Function.
The structure of an SDL system can be represented by a tree diagram. In SDL the root of the tree is represented by the SDL system followed by blocks, block substructures, processes and procedures1. Channels, channel substructures and signal routes are also represented in the tree. This tree is static, which means it cannot be modified during the run-time of an SDL system.
The SDT Code Generator generates code so that this static structure is present in the generated code. This is good for debugging purposes.
The Cmicro Code Generator generates code so that this static structure is not present in the generated code, in order to spare memory. To enable debugging of the generated code, C comments are generated. Please consult the subsection GR References.
A symbol table is necessary for the Cmicro Tester running on the host or the development system.
For more information consult The Cmicro Tester.
In order to implement the environment functions, it is important to notice that process IDs in Cmicro are generated into the sdl_cfg.h
file, which must be included by the user's environment C module. These IDs are coded like it is described in Generated Configuration File. More explanations are given in the following.
Since process IDs might become ambiguous, especially in block type and process type instantiations in SDL'92, the names of process IDs that are to be used in the environment functions are to be given a prefix. Using this prefix within the environment functions (xInEnv
), it can be guaranteed that different process IDs (equates to "instance sets" in SDL'92) with the same name can be distinguished, which is necessary in order to send signals to the right process instance within the SDL system. On the other hand, prefixes are not necessary when all the process instance sets within the system have a different name. The Cmicro Code Generator uses an algorithm to calculate the prefixes in the most convenient way.
For example, if a process named "myprocess" exists only once within the SDL system, there will be no automatic prefix generated, e.g. the full process ID is
#define XPTID_myprocess 0
If, as another example, the process "myprocess" exists twice, for example once within a block called "myfirstblock" and once more within a block called "mysecondblock", the Cmicro Code Generator then creates two definitions which guarantee that the processes can be distinguished:
#define XPTID_myfirstblock_myprocess 0 #define XPTID_mysecondblock_myprocess 1
In this way, by adding scope names (block names), prefixes are always generated in a way so that no naming conflicts occur. Of course, for process and block type instantiations, the name of the instance is being used to generate this unambiguous prefix.
SDL process types (process instance sets in SDL'92), as well as SDL process instances are numbered consecutively beginning with zero. The ordering of these numbers is the same as the ordering of the processes in the SDL-PR file.
The values 250 to 255 are reserved for internal purposes and must not be used for process type numbering. The BodyBuilder checks this rule automatically. For small systems this does not create any problems.
The Cmicro Kernel assumes the above definitions.
In the generated C code, the SDL values self, sender, parent and offspring, and variables of this type are represented by the typedef xPID. The intention is to have unique numbering of processes and their instances in the whole SDL system. This becomes necessary because of the Cmicro Code no longer containing the structure of the SDL system (system, block...). The typedef xPID
is defined as
unsigned char
, XMK_USED_ONLY_X_1
in the section Automatic Scaling Included in Cmicro.or
unsigned int
,There are a few macros defined to extract the process type number or the process instance number from a variable of the type xPID
and to build an xPID
variable from a process type number and a process instance number, the users do not have to think about the internal representation:
processtype = EPIDTYPE(xPID_variable) processinstance = EPIDINST(xPID_variable) xPID_variable = GLOBALPID(processtype, processinstance)
SDL signals and timers are numbered automatically by the Cmicro Code Generator so that they have a unique number over the complete system. Timers are represented by the values 1, 2, 3.... MT to the last timer of the MT timers in the system. After that follow ordinary SDL signal numbers beginning with MT+1, MT+2, MT+3... MT + MS.
When using the standard Cmicro Package, as delivered, then the values 0 and 251 to 255 are reserved for internal purposes. If the upper limit of 250 signals and timers is being reached, then the signal ID type has to be changed from unsigned char to unsigned int, thus allowing more than 60000 signals/timers to be handled. All these changes will be done if the flag XMK_USE_MORE_THAN_250_SIGNALS is set.
C code generated for signals and timers:
#define znnn_SignalName 1 #define znnn_SignalName 2
Where znnn_
is the automatically generated prefix which is required to cope with the SDL scope rules. Remember, that processes in SDL can have the same name as signals, states etc. Prefixing, however, ensures uniquely named SDL objects in the generated C Code.
A system with 2 signals S1 and S2, and a timer TIMER1:
#define z049_TIMER1 1 #define z050_S1 2 #define z051_S2 3
When it comes to connecting the environment to the SDL system, the automatic numbering of signal IDs and timer IDs may not be required. If the user wants to prevent the automatic numbering of signals, then it is possible to #include a file containing all the signal and timer numbers. The file may contain something like:
#undef SignalOrTimerName #define SignalOrTimerName AnyValueAccordingToKernelRules
SDL states are consecutively numbered from 1 through to N for each process type. The values 0, and 250 to 255 are reserved for internal purposes in the Cmicro Package. This restriction incurs no foreseeable difficulty as processes should never have more than 50 States as a recommendation.
If there are even more states per process the flag XMK_USE_HUGE_TRANSITIONTABLES must be set.
The following C code generation is supplied for the header-section of the generated C file(s).
For each SDL process:
#define znnn_State1Name 1 #define znnn_State2Name 2 .... #define znnn_State3Name 3
For a process with 2 states S1 and S2:
#define z020_S1 #define z021_S2
These values are used in the state-index-table and in the generated C functions, wherever a nextstate is referenced.
The SDT Cmicro Code Generator handles SDL concepts according to the definition of SDL-92. In addition to the restrictions of all the SDT C Code Generators, the following additional restrictions are introduced for the Cmicro Code Generator:
The following restrictions are additional regarding the packages that are delivered together with SDT.
It is impossible to read in header files created with Cadvanced and use them in Cmicro and the other way around. The reason is that it is impossible to mix up C code between Cadvanced/Cbasic and Cmicro.
Mixing C code from different C Code Generators is not possible as the different code generators use their own run-time model and run-time data structures. Trying to mix up the C code will lead to compilation errors.
There is no such interface available for Cmicro and the existing interfaces in SDT cannot be used.
The Light and Tight Integrations delivered with SDT are only available for Cadvanced but not applicable to Cmicro. There are light and tight integrations for Cmicro but these are not part of the product.
If the Cmicro Tester is to be used, then the scope rules of SDL are handled in a restrictive fashion. No information is generated for the system, block, block substructure, channel and signalroute. After applying the Cmicro Code Generator, all the structuring information is lost.
This means that it is impossible to address two different processes with the same name in different blocks. In order to avoid problems, give all processes, signals and timers in the system a different (unique) name.
The predefined sort charstring and all the predefined sort that are based on the implementation of charstring (like predefined sorts from ASN.1) cannot be handled, if the Cmicro Tester is to be used. All predefined sorts which are generated into pointers in C cannot be used. In order to get a detailed description, please see in The Cmicro Tester.
The restrictions in the SDT Analyzer, which also affects the Cmicro Code Generator, are summarized in The SDT Analyzer.