Chapter 7 Example 7: source code associated with an operation
7.1 Definition of the source code into the code editor window
In the previous Example 6,
we have learnt that a m4 file,
called with the name of the application plus the m4x extension,
must be manually written.
It contains all the source code associated
with all operations present in a SynDEx application.
For example, in the example6 application,
the conv function increments
of one the value of the input and stores the result in the output.
Thus example6.m4x file contains:
define(‘conv’,‘ifelse(
MGC,‘INIT’,‘dnl’,
MGC,‘LOOP’,‘$2[0] = $1[0] + 1;’,
MGC,‘END’,‘dnl’)’)
where $1 and $2 correspond respectively
to the input port named i
and the output port named o
of the conv function.
You may read the comments written in the example6.m4x.
Handwriting this kind of code is not very easy, for several reasons:
- the port number may change
by inserting or removing a port or parameter,
following the SynDEx’s rule of port numeration.
For example, after inserting a parameter P
in the function conv,
$1 will not refer to the input port i
but it will refer to the new added parameter P.
All numbers are now brought, thus we must modify the code
and replace the arguments $1 by $2
and $2 by $3;
- this task is quite repetitive
when an application contains many operations;
- it is easy to make a mistake in the m4 syntax.
Great knowledges in m4 syntax
(two different kind of quotes, ifelse ...)
and SynDEx m4 macros (MGC) are required.
It should be more convenient to write
@OUT(o)[0] = @IN(i)[0] + @PARAM(P)
and let SynDEx interpret it and generate the associated m4x file
than to write the specification with the m4 syntax.
SynDEx (version ≥ 7.0.0) is able to do that
thanks the code editor
which is a tool integrated in the graphical user interface (GUI).
In the following example, we will show how to use this tool.
7.1.1 To add parameters to an already defined operation
In this example, we show how to modify some functions
by adding parameters in order to expand parameters
into m4 arguments.
Open the example6.sdx application:
- from the principal window;
choose
File / Open
→ Open the example6.sdx file;
- save it as example7.sdx
under a new folder of your tutorial folder
(e.g. my_example7).
Add parameters to the conv function:
- open the conv definition and add parameter names
P;T in the Parameters field,
- open the main algorithm definition, select the conv
reference, add parameters values 2;3 in the
Parameters field and modify the name reference from
conv to conv_ref in the Name
field.
Verify that the parameters have been stored in the function
We have two solutions:
- from the algorithm window on the main algorithm:
put the mouse on the conv box
→ Read the printed informations in the principal window;
- or right click on the algorithm window background and select
Activate Info Bubbles → Point the mouse cursor on the
conv operation box.
7.1.2 To edit the code associated with an operation
In the case of a generic processor
- Open the code editor
We need to launch the code editor of the selected operation.
Let us consider the case of the conv_ref function.
We have to do the following operations:
- in the main algorithm:
double left click on the conv_ref blue box.
It opens the conv definition window.
In its contextual menu,
select Edition of the associated source code.
It opens the code editor.
It looks like a window with three push-buttons and an editable text area.
Each push-button corresponds to one specific phase of three
(init, loop, and end phases).
When one of the three buttons is pressed,
the text area shows the associated source code;
- in the conv definition window,
right click on the background and select Edit code phases
→ Select init and end
→ OK;
- in the code editor window:
init phase
→ write in the text area (which is empty)
the following C language code.
This code is understood as a generic code:
printf("Init phase of function $0 for default processor.\n");
- do the same thing for the loop phase
1:
@OUT(o)[0]=@IN(i)[0]*@PARAM(T)+@PARAM(P);
printf("Loop phase of function $0 for default processor = %i.\n", @OUT(o)[0]);
- and for the end phase:
printf("End phase of function $0 for default processor.\n");
- in the code editor window:
Edit / Apply changes to all phases.
It saves all buffers of all edited phases;
- in the code editor window:
OK (it also saves all buffers).
Notice the following points:
- you can not launch the code editor from a main algorithm
or a read-only operation (definition coming from libraries);
- you can write a code associated with a herarchical operation meanwhile
it will not be present in the applicationName_sdc.m4x
file (the background color of the text area of the code editor is grey);
- it is important to recall that the code is common
to all the references of the same operator.
Only the values of parameters are specific of each instance.
- Verify that code is common to all references
We create a new reference to the conv function:
- in the main algorithm:
create a reference conv_ref_bis<8;9>
to the definition conv;
- in the main algorithm:
first remove the link between the conv_ref
and mul boxes.
Second, link the conv_ref output
to the conv_ref_bis input.
Third, link the conv_ref_bis output
to the mul b input;
- in the algorithm window left click on the Auto-position
button and write 120 in the two entry texts → OK,
- open the code editor of this new box:
the code is the same.
To show that values of parameters are specific
to the reference (and not to the definition),
we must generate the m4 code like shown
in the previous example
(Menu Adequation / Launch Adequation
then Code / Generate Executive(s))
and look in P1.m4
(Menu Code / Display Executive(s)).
The file contains
conv(2,3,_algo_cste2_P1_o,_algo_conv_ref_o)
conv(8,9,_algo_conv_ref_o,_algo_conv_ref_bis_o)
In the case of an architecture with heterogeneous processors
Sometimes it is interesting for an operation
to have different source codes depending on the type of processor.
For example, a given processor type X
may only offer assembly language as a programming interface.
In such case, we must be able to provide (for example)
C code for processors that support it,
and assembly language for the X processor type.
To support heterogeneous architecture,
the code editor associates code to a triplet (phase, processor, operation).
A special processor type Default is provided for processors that
have not been associated with dedicated code.
Its use allows to share a code between different processor types.
- Include a new processor type
From the principal window,
choose File / Included Libraries
→ Select c40
- Define an new architecture:
- from the principal window,
choose Architecture / Define Architecture
(cf. figure 1.14)
→ In the entry text, write archi2.
→ OK;
- in the archi2 window:
create a reference root
to the operator C40
and define it as the main operator.
- Replace the conv_ref_bis box by a reference to bar_ref:
- create a new function definition named bar
with one input port called in
and one output port called out;
- in the algo definition select all the references using
the mouse and copy them (right click → Copy),
- create a new main algorithm named algo2;
- paste in the algo2 definition window;
- in the algo2 definition window:
first delete the conv_ref_bis reference,
then create a reference bar_ref box
to the definition bar,
finally create the missing dependences.
- Insert code for C40 processor type to the bar function:
- in the algo2 definition window:
double left click on the bar_ref blue box.
It opens the bar definition window.
In its contextual menu,
select Edition of the associated source code.
It opens the code editor;
- in the bar definition window,
right click on the background and select Edit code phases
→ Select init and end
→ OK;
- in the code editor window:
Type of Processor: Select C40;
- in the code editor window:
click on the init phase button
and write in the text area the following code:
/* Hi, I am $0 function, in init phase for C40 processor */
- in the code editor window:
click on the loop phase button
and write in the text area the following code:
@OUT(out)[0] = @IN(in)[0];
/* Hi, I am $0 function, in loop phase for C40 processor */
- in the code editor window:
click on the end phase button
and write in the text area the following code:
/* Bye, I am $0 function, in end phase for C40 processor */
- Insert code for U processor type to the bar function:
- in the code editor window:
Type of Processor: Select U;
- in the code editor window:
click on the init phase button
and write in the text area the following code:
/* Hi, I am $0 function, in init phase for U processor */
- in the code editor window:
click on the loop phase button
and write in the text area the following code:
@OUT(out)[0] = @IN(in)[0] * 42;
/* Hi, I am $0 function, in loop phase for U processor */
- in the code editor window:
click on the end phase button
and write in the text area the following code:
/* Bye, I am $0 function, in end phase for U processor */
- Modify the durations
Add c40/C40 = 1
at the end of the Durations text area
for each definition referenced in algo2
Learn the macros of the code editor
The code editor comes with a set of predefined macros
that alleviate the user from knowing the black magic
of m4 processing.
The more useful ones are names translation macros.
These macros translate port and parameter names
to their internal representation as m4 parameters.
We have already encountered such macros in what we have just done:
@IN
, @OUT
and @INOUT
are port name translation macros,
and @PARAM
is the parameter name translation macro.
As a rule of thumb, you should use @PARAM(x)
when you want to refer to a parameter x
and @IN(i)
(resp. @OUT(o)
/ @INOUT(io)
)
when you refer to an input port i
(resp. output port o
/ input-output port io
).
The code editor recognizes three more macros:
@NAME
, @QUOTE
and @TEXT
.
These advanced macros are not used in this tutorial
and the reader is refered to SynDEx user manual
to learn more about it.
7.1.3 To generate m4x files
Before performing the code generation we have to perform the adequation:
- from the principal window,
choose Adequation / Launch Adequation;
- from the principal window,
choose Code: Select Generate m4x Files;
- from the principal window,
choose Code / Generate Executive(s);
- from the principal window,
choose Code / Display Executive(s).
Two cases are possible:
- the check box Generate m4x Files has not been checked.
For each operator of the main architecture
a processor_name.m4 file
containing m4 macro-code is produced.
As previously explained an architecture description file
(named example7.m4) is also produced;
- the check box Generate m4x Files has been checked.
Then, two new files (example7.m4x
and example7_sdc.m4x)
are generated in the same folder as the application.
These two files constitute the Applicative kernel:
- the MyApplication_sdc.m4x file
contains all m4 macro code associated with operations
used in the SynDEx application.
Each time code generation is triggered, this file is overwritten;
- the MyApplication.m4x file is an user editable file
whose goal is to allow the user
to complete the applicative kernel if needed.
At code generation time, if this file does not exist
then SynDEx creates a generic file
(including the MyApplication_sdc.m4x file
plus some other features), otherwise the existing file is kept.
The example7_sdc.m4x file contains the following code:
divert(-1)
# (c)INRIA 2001-2009
divert(0)
define(‘example7_bar’,‘bar’)
define(‘bar’,‘ifelse(
processorType_,‘C40’,‘ifelse(
MGC,‘INIT’, “/* Hi, I am $0 function, in init phase for C40 processor */”,
MGC,‘LOOP’,“$2[0] = $1[0]; /* Hi, I am $0 function, in loop phase for C40 processor */”,
MGC,‘END’, “/* Bye, I am $0 function, in end phase for C40 processor */”)’)’)
processorType_,‘U’,‘ifelse(
MGC,‘INIT’,“/* Hi, I am $0 function, in init phase for U processor */”,
MGC,‘LOOP’,“$2[0] = $1[0] * 42; /* Hi, I am $0 function, in loop phase for U processor */”,
MGC,‘END’, “/* Bye, I am $0 function, in end phase for U processor */”)’,
define(‘example7_conv’,‘conv’)
define(‘conv’,‘ifelse(
processorType_,processorType_,‘ifelse(
MGC,‘INIT’,“printf("Init phase of function $0 for default processor.\n");”,
MGC,‘LOOP’,“$5[0]=$3[0]*$2+$1;
printf("Loop phase of function $0 for default processor =%i.\n", $4[0]);”,
MGC,‘END’,“printf("End phase of function $0 for default processor.\n");”)’)’)
If the example7.m4x file did not exist
at code generation time then it will contain the following code:
divert(-1)
# (c)INRIA 2001-2009
divert(0)
define(‘dnldnl’,“// ”)
define(‘NOTRACEDEF’)
define(‘NBITERATIONS’,“5”)
include(‘example7_sdc.m4x’)
divert
#include <stdio.h> /* for printf */
divert(-1)
divert‘’dnl
Deeper insights about the m4 macro language can be found
in SynDEx user manual and GNU M4 manual.
- See the difference between executable codes:
Notice that:
- adequation modifies original files
example7.sdx and example7.sdc;
- code generation produces these files:
example7.m4, root.m4,
pc1.m4 (if archi is defined as main),
example7_sdc.m4x
(if Generate m4x files is set)
and example7.m4x
(if Generate m4x files is set and the file did not exist before);
- compilation produces these files:
example7.mk,
root, root.c,
and root.root.o,
and (if archi is defined as main)
pc1, pc1.c,
and pc1.pc1.o.
From the principal window,
choose File / Close.
In the dialog window, click on the Save button.