ec2c, poc - ansi C code generator |
ec2c, poc - ansi C code generator
ec2c file.ec [options]
poc file.oc [options]
These tools are Ansi-C code generators. They are presented together, since they share the same conventions for the generated user interface. Otherwise, they are completely separated tools, working in different formats, and performing different tasks:
poc takes as input a oc file, which is already a sequential imperative program; the work of poc is then a simple translation between similar formalisms.
ec2c compiles an ec program into Ansi-C code. Since ec (which is a subset of Lustre) is a declarative language, a lot of work remains to do for building sequential code. In this sense, ec2c is closer (but anyway simpler) to a tool like ec2oc. Roughly speaking, using ec2c is almost equivalent to using ec2oc with the -0 option, and then poc. The main characteristic of this compiler is that both the compilation time and the size of the code are linear with respect to the size of the source code. Moreover the compilation algorithm strictly follows the Lustre formal semantics, just like the simulator ecexe does.
The code generated by those compilers consists essentially in a procedure implementing a step of the reactive program described in the source file. In order to run the reactive program, the user must write a main loop around the "step" procedure.
In the following, we precise what it is automatically generated, and what the user has to write. Let us call foo.oc (resp. foo.ec) the source file, FOO the oc module (resp. the ec node) defined in the source file, foo.c and foo.h the generated files, and loop.c the main program written by the user.
The generated code allows multiple allocations of a reactive module. The memory needed for an instance of the reactive module is defined in foo.c, and declared in foo.h:
struct FOO_ctx;
The user does not have to know what this structure is made of, he is only allowed to manipulate pointers. The user can get a new context using a procedure declared in foo.h:
struct FOO_ctx * FOO_new_ctx(void* client_data);
The user can associate an extra information to a new execution context using the client_data
argument. This information is necessary if the user wants to run several instances of a same reactive module concurrently.
The procedure implementing a step of the reactive module is declared in foo.h:
void FOO_step(struct FOO_ctx * ctx);
This procedure is called with an execution context previously created by a call to FOO_new_ctx
. This step procedure has no input/output parameters, since communication between the main loop and the reactive module is made via input/output procedures. More precisely, the user must call input procedures to set the input values before he calls the step procedure. The step procedure calls output procedures to send its outputs to the environment.
Communications between foo.c and loop.c are made via input and output procedures. The input procedures are defined in foo.c and used in the main loop, the output procedures are defined by the user (in loop.c for instance), and used in foo.c.
For each input IN, of type TYP, foo.c contains the definition of the procedure:
void FOO_I_IN(struct FOO_ctx* context, TYP value);
Note that if IN is a pure signal (poc only), the value parameter is omitted:
void FOO_I_IN(struct FOO_ctx* context);
For each output OUT, of type TYP, the user must define a procedure:
void FOO_O_OUT(void* client_data, TYP value);
Note that if OUT is a pure signal (poc only), the value parameter is omitted:
void FOO_O_OUT(void* client_data);
Output procedures are called within FOO_step(FOO_ctx* ctx), using the client data which has been associated with ctx when it was created.
Here is a simple example of a main loop using a reactive module (whose name is sum
) with two real inputs (x
and y
) and a single real output s
; note that the client data is not necessary, since the loop uses only one instance of the reactive module:
#include <stdlib.h>
#include "sum.h"
void sum_O_s(void* cdata, float _V){
printf("result: %f\n", _V);
}
main(){
_float x;
_float y;
struct sum_ctx* prg = sum_new_ctx(NULL);
while(1){
printf("(float) x ?\n");
scanf("%f", &x);
sum_I_x(prg, x);
printf("(float) y ?\n");
scanf("%f", &y);
sum_I_y(prg, y);
sum_step(prg);
}
}
Each external object declared in the source file is supposed to be implemented by the user. Some information is necessary for the compilation of the c generated code, other is necessary only for linking.
Compiling The foo.c program generated by poc cannot be compiled unless the external types are defined. poc supposes that those definitions are in a file called foo_ext.h.
Linking External constants, functions and procedures are declared in foo.c as imported objects, so foo.c can be compiled separately. Indeed the user must define those objects somewhere, and link the corresponding code with the poc object code if he wants to build an executable program!
When called with the -loop option, poc (resp. ec2c) produces an extra c-file FOO_loop.c. This code contains a main procedure implementing a loop which reads inputs on stdin and write outputs to stdout. If foo.c does not need external object, it is a simple way to obtain executable code. For instance:
poc sum.oc -loop
gcc sum.c sum_loop.c -o sum
produces an interactive program sum, which allows the user to test his code.
If external objects are needed, the user must write all the necessary code plus two procedures for each external type TYP:
TYP _get_TYP(char* name)
reads a value of type TYP on stdin, and returns it. The argument "name
" is the name of the input, used to make the procedure more "interactive".
void _put_TYP(TYP val)
prints the value "val
" on stdout.
In order to allow automatic manipulation of the code generated, the compiler generates special comments (pragmas) in the header file. Pragmas are single line Ansi C comments, begining with the string poc:. Ther is a pragma which gives the name of the module, and a pragma for each input and each output, giving its type and its name. Here is an example of pragma section:
//poc:MODULE sum
//poc:IN _float x
//poc:IN _float y
//poc:OUT _float s
set the verbose mode.
define the prefix for the target files. The default is to use the name of the module (i.e. node), which is not necessarily the name of the source file.
generate an extra main file called name_loop.c. This main is sufficient to build a stand-alone application if the reactive module does not need any external definitions.
ec2c, poc - ansi C code generator |