Escape to C Code¶
Because the SNL does not support the full C language, C code may be
escaped in the program. The escaped code is not compiled by
snc
, instead it is literally copied to the generated C code.
There are two escape methods:
Any code between
%%
and the next newline character is escaped. Example:%% for (i=0; i < NVAL; i++)
Any code between
%{
and}%
is escaped. Example:%{ extern float smooth(); extern LOGICAL accelerator_mode; }%
Note that text appearing on the same line after
%{
and before}%
also belongs to the literal code block.
A variable or preprocessor macro declared in escaped C code is foreign to the SNL, just as if it were declared in C code extern to the SNL program and its use will give a warning if no foreign declaration preceeds it.
Preprocessor Directives¶
A very common pitfall is the use of preprocessor directives, such as
#include
, in multi-line literal C code blocks in conjunction
with using the preprocessor on the unprocessed SNL code (which
happens by the default with the standard build rules if the name of
the source file has the .st
extension).
For instance with
%{
#include <abcLib.h>
/* ... */
}%
the header file will be included before the SNL compiler parses the program, which is most probably not what you wanted to happen here. For instance, if the header contains macro definitions, these will not be in effect when the rest of the C code block gets compiled.
You can defer interpretation of a preprocessor directive until after
snc
has compiled the code to C, by ensuring that some extra
non-blank characters appear in front of the #
sign, so
cpp
does not recognize the directive. For instance
%%#include <abcLib.h>
or
%{#include <abcLib.h>}%
Defining C Functions within the Program¶
Escaped C code can appear in many places in the SNL program. But only
code that appears at the top level, i.e. outside any SNL code block will
be placed at the C top level. So, C function definitions must be
placed there; this means either inside the definitions section
(initial_defns
) or at the end of the program. For example:
program example
...
/* last SNL statement */
%{
static float smooth (pArray, numElem)
{ ... }
}%
It matters where the function is defined: if it appears at the end of the program, it can see all the variable and type definitions generated by the SNL compiler, so if your C function accesses a global SNL variable, you must place its definition at the end. However, this means that the generated code for the SNL program does not see the C function definition, so you might want to place a separate prototype for the function in the definitions section (i.e. before the state sets).
Remember that you can call any C function from anywhere in the SNL program without escaping. You can also link the SNL program to C objects or libraries, so the only reason to put C function definitions inside the SNL program is if your function accesses global SNL variables.
Calling PV Functions from C¶
The built-in SNL functions such as pvGet
cannot be directly
used in user-supplied functions. However, most of the built-in
functions have a C language equivalent with the same name, except
that the prefix seq_
is added (e.g. pvGet
becomes
seq_pvGet
). These C functions expect an additional first argument
identifying the calling state set, which is available in action code
under the name ssId
. If a process variable name is required, the
index of that variable must be supplied. This index is obtained via
the pvIndex
function (which must be called from SNL code,
not C code, to work).
If the program is compiled with the +r
option, user
functions cannot directly access SNL variables, not even global ones.
Instead, variables (or their address) should be passed to user
functions as arguments. Alternatively, you can pass the pVar
variable of type USER_VAR*
and access SNL variables as structure
members of pVar
. See next section for details and an example.
The prototypes for the C functions corresponding to the built-in SNL
functions as well as additional supporting macros and type
definitions can be found in the header file seqCom.h
. This header
file is always included by the generated C code.
Variable Modification for Reentrant Option¶
If the reentrant option (+r
) is specified to snc
then all variables are made part of a structure. Suppose we have the
following top-level declarations in the SNL program:
int sw1;
float v5;
short wf2[1024];
The C file will contain the following declaration:
struct UserVar {
int sw1;
float v5;
short wf2[1024];
};
The sequencer allocates the structure area at run time and passes a pointer to this structure into the program. This structure has the following type:
struct UserVar *pVar;
Reference to variable sw1
is made as
pVar->sw1
This conversion is automatically performed by snc
for all SNL
statements, but you will have to handle escaped C code yourself.
Note
Safe Mode (enabled with the +s
option) implies
reentrant mode. In safe mode, each state set has its own copy of the
UserVar
struct. You can operate on its members in any way you like,
including taking the address of variables and passing them to C
functions.
Here is a stupid example of a C function that does a pvGet, increments the variable, and then does a pvPut:
program userfunc
option +r;
%{
static void incr(SS_ID ssId, int *pv, VAR_ID v)
{
seq_pvGet(ssId, v, SYNC);
*pv += 1;
seq_pvPut(ssId, v, SYNC);
}
}%
int i;
assign i to "counter";
foreign ssId;
ss myss {
state doit {
when (delay(1)) {
incr(ssId, &i, pvIndex(i));
} state doit
}
}