![]() |
![]() |
![]() |
![]() |
![]() |
Environment Functions
General
Virtually all real applications have some physical environment, let it be software or hardware. The SDL model describes on a high abstraction level the interaction the environment (sending or receiving signals or remote procedure calls), but not what should really happen. A signal sent from the SDL system should for example cause a hardware register to be set or a TCP/IP packet to be sent over a network. The implementation of the interaction is provided in the environment functions.
Separating the decision that an interaction should occur from its implementation simplifies the understanding of the overall behavior of the model. It is also easier to simulate or validate the model, as in such a situation it is necessary to have full control over the environment. For example, it is usually not suitable to simulate on the target platform, which makes hardware unable to access.
The following environment functions are available for use:
extern void xInitEnv (void);extern void xCloseEnv (void);extern void xOutEnv (xSignal *);extern void xInEnv (void);
- xInitEnv and xCloseEnv are used to initialize and close down the environment in a controlled way
- xOutEnv is called by the run-time kernel when a signal is sent from the system to the environment and should implement the actions needed when signals are sent from the system.
- xInEnv provides the reverse support, i.e. to send a signal into the system due to events in the environment. The details depend somewhat on the situation and will be described below.
The usage of the environment functions is controlled in the Targeting Expert by specifying which environment function should be used. This results in the possible inclusion of the macro defines
#define USER_CFG_USE_xInitEnv#define USER_CFG_USE_xCloseEnv#define USER_CFG_USE_xOutEnv#define USER_CFG_USE_xInEnvin the file extreme_user_cfg.h generated by the targeting expert.
xInitEnv
This function is to be used to initialize the environment. The function is called once during the initialization of the application (in function xInit in uml_kern.c).
xCloseEnv
This function can be used to close down the environment. The function is called once in the main function in extreme_kern.c.
In an RTOS integration this might not be a suitable way to perform the closing of RTOS tasks - refer to the technical documentation of the RTOS how to properly close down OS threads.
xOutEnv
The xOutEnv function passes a pointer to a signal that is sent to the environment. It is called from the signal sending functions in extreme_kern.c (xOutput and xOutputSimple). The function should perform whatever action that is necessary when the signal passed as parameter is sent to the environment.
The function should free the memory of any signal parameters that is implemented using pointers. The handling of the signal itself is, however, performed by the calling functions. In the generated template for the xOutEnv function that is discussed below, the proper code for memory management is automatically inserted. An example of an xOutEnv is also shown in the section on the template environment functions.
xInEnv
The xInEnv function is a way to handle signals that are sent from the environment to the application. If it is a suitable way, or not, depends on the actual application and the integration mode. Independent of how and where signals to be sent into the system are handled, there are two functions in extreme_kern.c that should be used:
extern xSignal*xGetSignal (xSignalId, int, xSignal **);extern voidxENVOutput (xSignal *, xuint8, SDL_Pid);First, xGetSignal is used to get a pointer to a signal data area. Then the signal parameters should be assigned their values and last the signal is sent using the xENVOutput function.
- first parameter to xGetSignal: The signal identity (a number).
- second parameter to xGetSignal: The size of the data area for parameters. If the system contains no signal, no timer, and no part with parameters, then this parameter is not used at all.
- third parameter to xGetSignal: This is an optimization parameter that should always be 0.
- first parameter to xENVOutput: Reference to the signal obtained by xGetSignal
- second parameter to xENVOutput: Signal priority for the signal. If signal priorities are not enabled, then this parameter is excluded.
- third parameter to xENVOutput: The receiver of the signal.
More details on how to give these parameters can be found in the sections below.
Implementing signal sending to the application
There are three typical cases of how to implement signal sending into the application.
Sending without use of xInEnv
In the first case the xInEnv function is not used. Consider the situation of a bare integration (no underlying RTOS). In that case it is possible to send signals directly in interrupt routines into the system, using the functions described above. To protect the data structure for signals and signal queues it is then necessary to implement functions or macros to disable and enable interrupts.
Sending using xInEnv in bare integration
Case two is also a bare integration (no underlying RTOS). However the signals are not sent directly in the interrupt routines. Instead the interrupt routines are just used to set up some global data structure to remember information about external events. The xInEnv function is then used to actually send the signal. xInEnv will be repeatedly called from the scheduler (function xMainLoop in extreme_kern.c) between each transition executed by the system. In each call the xInEnv function should check for external events and send the appropriate signal(s) into the system. xInEnv should the return as fast as possible to the scheduler. It is up to you to protect the data structure used to remember external event. The data structures in the Cextreme Code Generator kernel need not to be protected in this case.
Send signals with xInEnv in a RTOS integration
Case three handles the situation of an RTOS integration. In that case it is suitable to run xInEnv in a thread of its own. In the predefined integrations the main thread runs xInEnv after it has created the other threads. The xInEnv function should in this case look something like:
while (1) {wait for an event;if (event corresponding to signal 1) {send signal 1;}if (event corresponding to signal 2) {send signal 2;}and so on;}The "wait for an event" should hang this thread when there is nothing to do. Otherwise this thread might run all the time. When something happens that should cause a signal to be sent to the environment, the code where this has been detected should store information in some global data area (protected!) and execute code to restart the xInEnv function. In simple cases just a semaphore can be used to handle xInEnv.
A polling solution can be obtained in the structure above by just implementing "wait for an event" as a sleep for an appropriate amount of time. During each turn the xInEnv will then check if some external event has occurred and send the corresponding signal.
Note: If a polling solution for xInEnv is choosen it is important that the thread is suspended for a long enough time. The minimal time to wait depends on the pace in which injected signals can be consumed by the application. This in turn can depend on various things, such as the design of the model, which thread deployment that is used, or even on how threads are allocated on different hardware artifacts. In general you must make sure that signals are not injected in the application at a faster pace than they can be consumed. Otherwise the application will eventually run out of resources.
Interface header file (.ifc)
The .ifc file is a system interface header file containing information about entities defined inside the system. For such entities code is generated. Some of these definitions can be useful in external code, like for example the envi\x7f ronment functions. In the generated code for the system, except the .ifc file, all SDL name are prefixed or suffixed to make the names unique in C. In the .ifc file the SDL names have a predefined prefix.
In the .ifc file names for the parts in the system can be found. These names can be used as receiver, when sending signals in the xInEnv function.
Generated environment functions
In the beginning of the systemname_env.c file the following statements can be found.
#include "extreme_kern.h"#ifdef XENV_INC#include XENV_INC#endif#include "exenv.ifc"By defining XENV_INC to a file name it is possible to include a user defined file. This macro is suitable to define in the extreme_user_cfg.h file generated by The Targeting Expert. Use the possibility to include your own defines and insert something like:
#define XENV_INC "my_defines.h"In this way the skeletons for the generated environment functions can be filled in using macro definitions from the included file. The environment functions can then be regenerated without the risk of overwriting manually inserted or modified code.
xInitEnv and xCloseEnv structure
The xInitEnv and the xCloseEnv functions have the following structure:
extern void xInitEnv(void){/* Code to initialize the environmentmay be inserted here */XENV_INIT}extern void xCloseEnv(void){/* Code to bring down the environment in a controlledmanner may be inserted here. */XENV_CLOSE}where XENV_INIT and XENV_CLOSE are empty macros if you have not defined them earlier. These macros should be the sequence of statements and function calls needed at the initialization and termination.
The function xInitEnv is surrounded by:
#ifdef USER_CFG_USE_xInitEnv#endif(similar for xCloseEnv as for xInEnv and xOutEnv discussed below) to compile the function only if you have specified that the function should be used. This is set in the extreme_user_cfg.h file by the Targeting Expert.
xOutEnv structure
The generated xOutEnv function will have the following structure.
extern void xOutEnv(xSignal *SignalOut){OUT_START_CODE/* Signal s1 */#ifndef OUT_SIGNAL_sig_s1#define OUT_SIGNAL_sig_s1#endifif (SignalOut->Sid == sig_s1) {OUT_SIGNAL_sig_s1return;}/* Signal s2 */#ifndef OUT_SIGNAL_sig_s2#define OUT_SIGNAL_sig_s2(P1, P2)#endifif (SignalOut->Sid == sig_s2) {OUT_SIGNAL_sig_s2(((ySignalPar_sig_s2 *)SignalOut)->Param1,((ySignalPar_sig_s2 *)SignalOut)->Param2)xFreeSignalPara(SignalOut);return;}}where OUT_START_CODE is an empty macro if you have not defined it. The code consists of a sequence of if statements, each handling one of the signal types that can be sent to the environment. For each signal the if expression tests the signal identity.
When the correct if statement is found the statements defined by the macro OUT_SIGNAL_signalname is executed. This macro has one parameter for each signal parameter and is empty if you have not defined it. This means that the code will compile, but do nothing if the OUT_SIGNAL macros are not defined. The structure makes incremental development possible, that is to say that you can implement the treatment of a few signals and then start testing, without bothering about the code for all the other signals.
The code in the example above assumes that the name for signals used in the .ifc file is sig_%n, where %n is the signal name in SDL. The xFreeSignalPara function call in the s2 section is necessary when any of the signal parameters is implemented using dynamic memory and a free operation is needed for the parameter value. The data area for the signal itself is handled in extreme_kern.c at the places where xOutEnv is called.
xInEnv structure
The generated xInEnv function has the following structure. The example below shows an example suitable in a bare integration.
extern void xInEnv (void){xSignal *SignalIn;IN_START_CODE/* Signal s1 */#ifdef IN_SIGNAL_sig_s1if (IN_SIGNAL_sig_s1) {SignalIn = xGetSignal(sig_s1, 0, 0);xENVOutput(SignalIn, IN_RECEIVER_s1);}#endif/* Signal s2 */#ifdef IN_SIGNAL_sig_s2if (IN_SIGNAL_sig_s2) {SignalIn = xGetSignal(sig_s2,sizeof(ySignalPar_sig_s2), 0);((ySignalPar_sig_s2 *)SignalOut)->Param1 =IN_PARA1_sig_s2;yAssF_s_7(((ySignalPar_sig_s2 *)SignalOut)->Param2,IN_PARA2_sig_s2,XASS_MR_ASS_NF);xENVOutput(SignalIn, IN_RECEIVER_s2);}#endifIN_END_CODE}where the macros IN_START_CODE and IN_END_CODE are empty if you have not defined them.
Each signal is treated in four steps.
- The enabling macro IN_SIGNAL_signalname. This is used in an if statement to determine if an external event has occurred that should cause the signal to be sent into the system.
- The SignalIn variable is assigned a new signal data area.
- The signal parameters are filled in, if any. The value for each parameter should be defined using the appropriate macro IN_PARA1_signalname, IN_PARA2_signalname and so on.
- The signal is sent by calling xENVOutput. Here the receiver of the signal must be given by the macro IN_RECEIVER_signalname. The appropriate values can be found in the .ifc file looking for defines of type:
#define xPartNo_Partname <integer number>The structure above also enables incremental development as the complete section for a certain signal is removed if the enabling macro is not defined.
For a threaded application the structure is very similar. A loop is included in the function according to the following.
extern void xInEnv (void) {xSignal *SignalIn;IN_START_CODEwhile (1) {IN_WAIT_FOR_ACTION/* To avoid that the thread running xInEnv takesall resources it should wait on for example asemaphore until something occurs that shouldcause a signal to be sent into the system *//* Here the code for the signals is placedin the same way as in the previous example.*/}IN_END_CODE}Note: IN_WAIT_FOR_ACTION must be implemented according to the previous discussion.
http://www.ibm.com/rational |
![]() |
![]() |
![]() |
![]() |