This example is in the directory: /home/lbesnard/Polychrony/V4.19/Linux/Examples/elementary/exochronous
The directory contains the description of a simple SIGNAL example (demand for using some information).
This example illustrates the compilation in a simple case where the initial program is not endochronous.
We also describe on this example how to find simple clock constraints.
Let A an input signal of type integer (for example). A user is supposed to need, at some instants, the value of A. For that purpose, he sends a demand D, of type event. Then, the first value of A following D is provided as output (A is emitted only once if there are several consecutive D).
Here is the program, defined in the file demand_next.SIG:
process demand_next = ( ? integer A; event D; ! integer X; ) (| B := D default (not ^A) | ZB := B$ init false | X := A when ZB |) where boolean B, ZB; end;
Compile it, and generate C code (for example), as follows:
signal -lis -tra -c demand_next.SIG
Observe the messages of the compiler:
===> Program analysis ===> Reduction to the kernel language ===> Graph generation (Process demand_next) # Annotated source program generation: demand_next_LIS.SIG ===> Clock calculus (Process: demand_next) # Hierarchized program generation: demand_next_TRA.SIG ------------ Events -> Booleans .... BEGIN -------------- Creating Tick : Extended specification => modifying Interface. ===> Clock calculus (Process: demand_next) ...
The message "Creating Tick : Extended specification => modifying Interface" (which appears here only because code generation is required) expresses the fact that the program is not endochronous. The clock calculus has not been able to synthesize one single master clock in the program. Instead, the clock hierarchy contains several trees. The different roots of these trees appear at the first level in the file demand_next_TRA.SIG (generated in the directory demand_next):
process demand_next_TRA = ( ? integer A; event D; ! integer X; ) pragmas main "" end pragmas (| (| D ^= D |) | (| CLK_A := ^A | CLK_A ^= A |) | (| CLK := CLK_A ^- D |) | (| CLK_B := D ^+ CLK_A | ACT_CLK_B{} |) | (| CLK_X := CLK_A ^* CLK_12 | CLK_X ^= X | X := A when CLK_X |) |) where event CLK_X, CLK_12, CLK_B, CLK, CLK_A; process ACT_CLK_B = ( ) (| CLK_B ^= B ^= ZB | (| CLK_12 := when ZB | CLK_13 := when (not ZB) |) | (| B := D default (not CLK) | ZB := B$1 init false |) |) where event CLK_13; boolean B, ZB; end %ACT_CLK_B%; end
(this file represents a rewriting of the program after the clock calculus). The different roots are: D, CLK_A, CLK, CLK_B, CLK_X (note that CLK_B has two subclocks: CLK_12 and CLK_13).
In order to be able to generate autonomous code, the compiler tries to transform the initial program into an endochronous one. Its strategy for doing that is, in particular, to add new Boolean inputs that will represent the clocks of "free" signals. These clocks will be defined themselves at the "Tick", which represents the master clock of the program (single root of the clock hierarchy).
It appears in the file demand_next_bDC_TRA.SIG (which is computed after the booleanization which is always made before code generation, and after the "endochronization" such as described above):
process demand_next_bDC_TRA = ( ? integer A; boolean C_A, C_D; ! integer X; ) pragmas main "" end pragmas (| (| Tick := true | when Tick ^= C_A ^= C_D | ACT_Tick{} |) |) where boolean Tick; ...
Now, only the Tick appears at the top level. It also appears that a Boolean input signal, C_A, has been added to represent the clock of A (when C_A ^= A) and that the input event D has been transformed into a Boolean input, C_D (booleanization). Both C_A and C_D are defined at Tick.
To produce an executable for the program, run the following commands:
cd demand_next genMake C demand_next make
Remind that, by default, read and write of external signals (inputs and outputs) are done via input and output files (communication functions are defined in the file demand_next_io.c).
For a given input X, you have to define a file RX.dat that contains the sequence of input values for X. For a given output Y, the execution of the program will produce a file WY.dat.
Be careful that when the compiler has added new inputs to make the program endochronous, you have also to provide values for these inputs. For example, if the compiler has added a Boolean input C_X representing the clock of an input X, its "true" values ("1" in C) correspond to the instants at which X is present, and its "false" values ("0" in C) correspond to the instants at which X is absent. For instance, if a simulation is done on 5 instants of the most frequent clock (Tick), and that X has a value at the 2nd and 4th instants, the input file corresponding to C_X (i.e., RC_X.dat for C) will contain the following values:
RC_X.dat:
0 1 0 1 0
and that corresponding to X will contain 2 values, for example:
RX.dat :
33 -7
For the demand_next example, you have to provide input values for C_A, A and C_D (note that this appears also in the file demand_next_io.c, which contains the declaration of the default communication functions). For example:
RC_A.dat RA.dat RD.dat | WX.dat 1 1 0 1 2 1 1 3 0 3 1 4 0 0 1 1 5 0 5 1 6 0 1 7 0 0 1 0 1 1 8 0 8 1 9 0 1 10 0
Note that for a simulation on files, it would be useful to produce also as output the Boolean clock of X.
Remark: We have left the compiler make the program endochronous. It is also possible to define explicitly a context which provides this property. For example:
process context_demand_next = ( ? boolean C_A; integer A; boolean C_D; ! integer X; ) (| C_A ^= C_D | A ^= when C_A | X := demand_next(A, when C_D) |) where process demand_next = ( ? integer A; event D; ! integer X; ) (| B := D default (not ^A) | ZB := B$ init false | X := A when ZB |) where boolean B, ZB; end; end ;
Suppose that, for some reason (?), we have written the previous program as follows:
process context_demand_next_constraint = ( ? boolean C_A; integer A; boolean C_D; ! integer X; ) (| C_A ^= C_D | A ^= when C_A | X := demand_next(A, when C_D) |) where process demand_next = ( ? integer A; event D; ! integer X; ) (| B := D default (not ^A) | ZB := B$ init false | X := (A when ZB) default (-1 when not ZB) | X ^= A |) where boolean B, ZB; end; end ;
We compile it as follows:
signal -lis -tra -c context_demand_next_constraint.SIG
The following trace appears:
===> Program analysis ===> Reduction to the kernel language ===> Graph generation (Process context_demand_next_constraint) # Annotated source program generation: context_demand_next_constraint_LIS.SIG ===> Clock calculus (Process: context_demand_next_constraint) # WARNING : clocks constraints (Process : context_demand_next_constraint) # Hierarchized program generation: context_demand_next_constraint_TRA.SIG ...
The message "clocks constraints" means that the clock calculus cannot prove some equalities between clocks.
The clock constraints appear in the file context_demand_next_constraint_TRA.SIG (generated in the directory context_demand_next_constraint):
process context_demand_next_constraint_TRA = ( ? boolean C_A; integer A; boolean C_D; ! integer X; ) pragmas main "" end pragmas (| (| CLK_C_A := ^C_A | CLK_C_A ^= C_A ^= C_D | ACT_CLK_C_A{} |) | (| (| CLK_A ^= CLK_37 |) | (| CLK_18 ^= CLK_32 |) |)%**war: Clocks constraints % |) where event CLK_37, CLK_32, CLK_18, CLK_A, CLK_C_A; process ACT_CLK_C_A = ( ) (| (| CLK_A := when C_A | CLK_A ^= A ^= X | X := (A when CLK_35) default ((-1) when CLK_18) | CLK := when (not C_A) |) | (| CLK_10 := when C_D | CLK_11 := when (not C_D) |) | (| CLK_B := CLK_A ^+ CLK_10 | ACT_CLK_B{} |) | (| CLK_32 := CLK_A ^* CLK_18 |) | (| CLK_35 := CLK_A ^* CLK_17 |) | (| CLK_37 := CLK_35 ^+ CLK_18 |) | (| CLK_40 := CLK_A ^- CLK_10 |) |) where event CLK_40, CLK_35, CLK_B, CLK_17, CLK_11, CLK_10, CLK; process ACT_CLK_B = ( ) (| CLK_B ^= B ^= ZB | (| CLK_17 := when ZB | CLK_18 := when (not ZB) |) | (| B := CLK_10 default (not CLK_40) | ZB := B$1 init false |) |) where boolean B, ZB; end %ACT_CLK_B%; end %ACT_CLK_C_A%; end %context_demand_next_constraint_TRA%;
The clock constraints appear at the top level:
(| (| CLK_A ^= CLK_37 |) | (| CLK_18 ^= CLK_32 |) |)%**war: Clocks constraints %
Let us analyze, for instance, the first one:
CLK_A ^= CLK_37
We have:
CLK_A := when C_A (thus CLK_A := CLK_C_A when C_A) and CLK_37 := CLK_35 ^+ CLK_18 with CLK_35 := CLK_A ^* CLK_17 CLK_17 := CLK_B when ZB CLK_18 := CLK_B when (not ZB) Thus CLK_37 := (CLK_A ^* (CLK_B when ZB)) ^+ (CLK_B when (not ZB)) (with CLK_B := CLK_A ^+ CLK_10)
This expression cannot be proved equal to CLK_A.
This constraint come from the definition of X in the program:
X := (A when ZB) default (-1 when not ZB)
and the clock equality:
X ^= A
Suppose we want (as it appears here), that a special value, for instance -1, is provided as output at the instants at which the last value of A has not to be provided, this can be specified as follows:
| X := (A when ZB) default -1 | X ^= A ^+ D
Remark: When we compile context_demand_next_constraint.SIG, the clock constraints appear even if we don't ask code generation:
signal -lis -tra context_demand_next_constraint.SIG
It may be the case that clock constraints appear only when code generation is required. Consider for example the program:
process demand_next_constraint = ( ? integer A; event D; ! integer X; ) (| B := D default (not ^A) | ZB := B$ init false | X := (A when ZB) default (-1 when not ZB) | X ^= A |) where boolean B, ZB; end;
(which is not endochronous).
When it is compiled by
signal -lis -tra demand_next_constraint.SIG
the file demand_next_constraint_TRA.SIG shows that there are several roots but does not mention any clock constraint that cannot be proved by the clock calculus.
However, when code generation is required:
signal -lis -tra demand_next_constraint.SIG
a master clock, Tick, is added, and clock constraints appear:
===> Program analysis ===> Reduction to the kernel language ===> Graph generation (Process demand_next_constraint) # Annotated source program generation: demand_next_constraint_LIS.SIG ===> Clock calculus (Process: demand_next_constraint) # Hierarchized program generation: demand_next_constraint_TRA.SIG ------------ Events -> Booleans .... BEGIN -------------- Creating Tick : Extended specification => modifying Interface. ===> Clock calculus (Process: demand_next_constraint) ===> Clock calculus (Process: demand_next_constraint) ===> Clock calculus (Process: demand_next_constraint) ===> Clock calculus (Process: demand_next_constraint) ....BEGIN for node : demand_next_constraint ....DONE.... ------------ Events -> Booleans .......END -------------- # WARNING : clocks constraints (Process : demand_next_constraint) # Hierarchized program generation: demand_next_constraint_bDC_TRA.SIG
But these clock constraints are not constraints of the program itself (thus they do not appear in demand_next_constraint_TRA.SIG), but they are constraints of the "extended specification" obtained by "endochronization". In this case, they appear in demand_next_constraint_bDC_TRA.SIG (which is obtained after the booleanization) and can be analysed through this file (which may be more difficult).