setup()
), run time (go()
), and termination (wrapup()
) methods generate code to be compiled and executed later. Additionally, code generation stars have two more methods called initCode()
and execTime()
. The
setup()
method is called before the schedule is generated and before any memory is allocated. In this method, we usually initialize local variables or states. Note that the setup method of a star may be called multiple times. This means that the user should be careful so that the behavior of the star does not change even though setup method is called multiple times. The initCode()
method of a star is called after the static schedule has been generated and before the schedule is fired. This method is used to generate the code outside of the main loop such as initialization code and procedure declaration code. To generate start-up code, use the initCode
method, NOT the setup method, since setup is called before scheduling and memory allocation. The main use of the setup method, as in SDF, is to tell the scheduler if more than one sample is to be accessed from a porthole with the setSDFParams
call.The
go()
function is used to generate the main loop code for the star. Finally, the wrapup()
function is used to generate the code after the main loop.The
execTime()
method returns an integer specifying the time needed to execute the main loop code of a code generation star in processor cycles or instruction steps. These numbers are used by the parallel schedulers. In the assembly code generation domains, the integer returned is the main loop code execution time in DSP instruction cycles. The better the execTime()
estimates are for each star, the more efficient the parallel schedule becomes.If a star is invoked more than once during an iteration period, the precedence relation between stars should be known to the parallel scheduler. If there is no precedence relation between invocations, the parallel scheduler will try to parallelize them. By default, there is a precedence relation between invocations for any star (this is equivalent to having a self-loop). To assert that there is no such self-loop for a star, we have to call the
noInternalState()
method in the constructor:
noInternalState()
if it is. The
CGStar
class is the base class for all code generation stars, such as high level language code generation stars and assembly language code generation stars. In this section, we will explain the common features that the CGStar
class provides for all derivative code generation stars. As a simple example to see how code generation stars are written, let's write an adder star for the C code generation domain. The
defstar
is almost the same as for a simulation star:
Codeblocks are implemented as protected static class members (e.g. there is one instance of a codeblock for the entire class). Since they are protected, codeblocks from one star class can be used from a derived star. The
codeblock
directive defines a block of code with an associated identifying name ("addCB
" in this case).
{ }
" should not be on the same lines with the code. Had addCB
been defined as follows:
}
" on the same line is discarded by the preprocessor (ptlang
). Secondly, the spaces and tabs between the opening "{
" and the first non-space character will be ignored.The first definition of the
addCB
codeblock is translated by ptlang
into a definition of a static public member in the .h
file:
The
myCode
stream (codestreams are explained later on). If a stream name is specified, addCode
looks up the stream using the getStream(
stream-name
)
method and then adds the code into that stream. Furthermore, if a unique name is provided for the code, the code will only be added if no other code has previously been added with the given unique name. The method addCode
will return TRUE
if the code-string has been added to the stream and otherwise will return FALSE
.The star just defined is a very simple star. Typical code generation stars will define many codeblocks. Conditional code generation is easily accomplished, as is done in the following example:
addCode()
method to generate the code inside the main loop body. In the assembly language domains, addCode
can be called in the initCode
and wrapup
methods, to place code before or after the main loop respectively. In all of the code generation domains, we can use the addProcedure()
method to generate declarations outside of the main body. Refer to
"Code streams" on page 13-16 for documentation on the addCode
and addProcedure
methods.
The next section describes the extended codeblock support. The previous discussion of simple codeblocks is still correct and supported by
ptlang
; the extensions below are upward compatible. These extensions are experimental. They may change in future version of Ptolemy, and may still contain bugs.13.2.2 Codeblocks with arguments
Simple codeblocks (as described above) have a name and are implemented as static member strings. Extended codeblocks have a name, optional arguments, and are implemented as non-static functions. They have an escape mechanism so that C++ expressions may be evaluated at run time and inserted into the generated code. However, in order to take advantage of this escape mechanism, a codeblock must be defined and called with arguments, even if those arguments are empty. An example:
codeblock(cbLoop,"int N, double x") {
for (i=0; i < @N; i++) {
$ref(output,i) = sin(i*@x);
}
}cbLoop
with two arguments: N
and x
. The variable i
will appear in the generated code, while the C++ expressions N
and x
are escaped by @
and will be evaluated at code-generation time. When this is called as
cbLoop(5, 0.1);
for (i=0; i < 5; i++) {
$ref(output,i) = sin(i*0.1);
}go()
method as:
go {
addCode(cbLoop(5, 0.1));
}addCode()
method will process the $ref()
macro as described elsewhere. More complicated expressions are allowable. In general, the @
clause may be delimited by parentheses "(
"and ")
", and must be operator <<
printable. The above codeblock could have been equivalently declared as:
for (i=0; i < @(N); i++) {
$ref(output,i) = sin(i*@(x));
}
}
codeblock(cbLoop2,"char *portname, int N, double x") {
for (i=0; i < @(int(length)); i++) {
$ref(@portname,i) = sin(i*@(x/N));
}
}length
is a data member of the star (typically a state). When called as:
cbLoop2("ina", 3, 0.2);length
is 20):
for (i=0; i < 20; i++) {
$ref(ina,i) = sin(i*0.6666666);
}@
-escapes in codeblocks which would otherwise have no arguments, add in a null argument list as in:
codeblock(cbLoop3,"") {
for (i=0; i < @(int(length)); i++) {
$ref(output,i) = sin(i*0.1);
}
}@(int(length))
will be replaced with the value of the class member length
. The above example would be called with an empty argument list as:
go {
addCode(cbLoop3());
}
@ATSIGN ==> @
@{ ==> {
@LBRACE ==> { (LBRACE is literal string)
@} ==> }
@RBRACE ==> } (RBRACE is literal string)
@\ ==> \
@BACKSLASH ==> \ (BACKSLASH is literal string)
@id ==> C++ token {id} (id is one or more alphanumerics)
@(expr) ==> C++ expr {expr}(expr is arbitrary with balanced
parens)
@(white_space) ==> nothing
@anything_else is passed through unchanged (including the @)\
" will omit the following newline in the generated code. This special meaning of trailing "\
" may be prevented by using "@\
" or
"@BACKSLASH
".13.2.3 In-line codeblocks
Code blocks may be specified in the body of a method. Inside the definition of a method (such as go()
), all contiguous blocks of lines with a leading @
will be translated into an in-line codeblock (i.e., an addCode()
statement). The @
escape mechanism for C++ expressions works as described above for codeblocks with arguments. Within @
-escaped expressions, in-line codeblocks may reference local method variables as well as member variables.@
will be ignored. Note that no override mechanism is provided to prevent the in-line codeblock interpretation. Note also that @
has dual meanings: the first @
on the line introduces in-line codeblock mode, while subsequent @
characters on the same line escape into C++ expressions. For example:
go() {
@ CMAM_wait( &$ref(ackFlag), 1);
}
go() {
addCode(" CMAM_wait( &$ref(ackFlag), 1);\n");
}
go {
@ $ref(output) = \
int ni = input.numberPorts();
for (int i = 1; i <= ni; i++) {
@$ref(input#@i) @(i < ni ? " + " : ";\n") \
}
}input.numberPorts()
" returns 3 when the above program is run, the generated code will be:" $ref(output) = $ref(input#1) + $ref(input#2) + $ref(input#3);\n"
start
, go
, exectime
etc.) are processed this way; not user-defined methods.13.2.4 Macros
In code generation stars, the inputs and outputs no longer hold values, but instead correspond to target resources where values will be stored (for example, memory locations/registers in assembler generation, or global variables in C-code generation). A star writer can also define states which can specify the need for global resources.CGStar
class.
$ref(name)
name%0
operator in the SDF simulation stars. If a star has a multi-porthole, say input, the first real porthole is input#1. To access the first porthole, we use $ref(input#1)
or $ref(input#internal_state)
where internal_state
is the name of a state that has the current value, 1.
$ref(name,offset)
name%offset
in SDF simulation stars.
$val(state-name)
$ref
macro in place of $val
is that no additional target resources need to be allocated.
$size(name)
$starName()
$fullName()
$starSymbol(name)
CGCPrinter
star.
fp
for a star instance should be unique globally, and the $starSymbol
macro guarantees the uniqueness. Within the same star instance, the macro returns the same label.
$sharedSymbol(list,name)
sharedSymbol
does not provide the method to generate the code, but does provide the method to create a label for the code. To generate the code only once, refer to
"Code streams" on page 13-16. A example where a shared symbol is used is in CGCPCM
star.
offset
and belongs to the PCM
class. The conversion function is named mulaw
, and belongs to the same PCM class. Other stars can access that table or function by saying $sharedSymbol(PCM,offset)
or $sharedSymbol(PCM,mulaw)
. The initCode
method tries to put the sharedDeclarations
codeblock into the global scope (by addGlobal()
method in the CGC domain). That code block is given a unique label by $sharedSymbol(PCM,PCM)
. If the codeblock has not been previously defined, addGlobal
returns true, thus allowing addCode(sharedInit)
. If there is more than one instance of the PCM star, only one instance will succeed in adding the code.
$label(name), $codeblockSymbol(name)
addCode()
more than once on a particular codeblock, all codeblock instances will have unique symbols. A example of where this is used in the CG56HostOut
star.
$label
macro will assign unique strings for each codeblock. The base
CGStar
class provides the above 8 macros. In the derived classes, we can add more macros, or redefine the meaning of these macros. Refer to each domain document to see how these macros are actually expanded. There are three commonly used macros in the assembly code generation domains; these are:
$addr(name)
This returns the address of the allocated memory location for the given state or porthole name. The address does not include references to the memory bank the location is coming from; for instance, "x:2034" for location 2034 in the "x" memory bank for Motorola 56000 is output as 2034.
$addr(name,<offset>)
This macro returns the numeric address in memory of the named object, without (for the 56000) an "x:" or "y:" prefix. If the given quantity is allocated in a register (not yet supported) this function returns an error. It is also an error if the argument is undefined or is a state that is not assigned to memory (e.g. a parameter). $ref(name,<offset>)
This macro is much like $addr(name), only the full expression used to refer to this object is returned, e.g. "x:23" for a 56000 if "name" is in x memory. If "name" is assigned to a register, this expression will return the corresponding register. The error conditions are the same as for $addr
$mem(name)
Returns the name of the memory bank in which the given state or porthole has its memory allocated.
$
" appear in the output code, put "$$
" in the codeblock. For a domain where "$
" is a frequently used character in the target language, it is possible to use a different character instead by redefining the virtual function substChar
(defined in CGStar
) to return a different character. It is also possible to introduce processor-specific macros, by overriding the virtual function
processMacro
(rooted in CGStar
) to process any macros it recognizes and defer substitution on the rest by calling its parent's processMacro
method.
AsmPortHole
that might be useful in assembly code generation stars:
bufSize()
baseAddr()
bufPos()
bufSize()
-1.
A_
". Attributes that apply to portholes have the prefix "P_
". The following attributes are common to all code generation domains:
A_GLOBAL
If set, this state is declared global so that it is accessible everywhere. Currently, it is only supported in the CGC domain.
A_LOCAL
This is the opposite of A_GLOBAL
.
A_SHARED
A state that is shared among all stars that know its name, type, size.
A_PRIVATE
Opposite of A_SHARED
.
A_LOCAL|A_PRIVATE
. Right now, only A_SHARED|A_LOCAL
is supported in the assembly language domains. This combination means that all stars will share the particular state across a processor. For all stars to share it in a universe the bits A_SHARED|A_GLOBAL
need to be set; this combination is not implemented yet - the default method will probably restrict all the stars that share this state to the same processor.
A_CONSTANT
The state value is not changed by the star's execution.
A_NONCONSTANT
The state value is changed by the star's execution.
A_SETTABLE
The user may set the value of this state from a user interface.
A_NONSETTABLE
The user may not set the value of this state from a user interface (e.g. edit-parameters doesn't show it).
AB_
for states, and PB_
for portholes. The only two bits that exist in all states are AB_CONST
and AB_SETTABLE
. By default, they are on for states, which means that the default state works like a parameter (you can set it from the user interface, and the star's execution does not change it). For assembly language domains, the following attributes are defined:
A_CIRC
If set, the memory for this state is allocated as a circular buffer, whose address is aligned to the next power of two greater than or equal to its length.
A_CONSEC
If set, allocate the memory for the next state in this star consecutively, starting immediately after the memory for this star.
A_MEMORY
If set, memory is allocated for this state.
A_NOINIT
A_REVERSE
If set, write out the values for this state in reverse order.
A_SYMMETRIC
If set, and if the target has dual data memory banks (e.g. M56000, Analog Devices 2100, etc.), allocate a buffer for this object in both memories.
A_ROM
Allocate memory for this state in memory, and the value will not change -- A_MEMORY
and A_CONSTANT
set.
A_RAM
A_MEMORY
set, A_CONST
not set
P_CIRC
If set, then allocate the buffer for this porthole as a circular buffer, even if this is not required because of any other consideration.
P_SHARED
Equivalent to A_SHARED
, only for portholes.
P_SYMMETRIC
Similar to A_SYMMETRIC
, but for portholes.
P_NOINIT
Do not initialize this porthole.
|
" operator. For example, to allocate memory for a state but make it non-settable by the user, I can say
Fork
star. When a Fork
star has N outputs, the default behavior is to create N buffers for output connections and copy data from input buffer to N output buffers, which is a very expensive and silly approach. Therefore, we pay special attention to stars displaying this type of behavior. In the setup method of these stars, the forkInit()
method is invoked to indicate that the star is a Fork
-type star. For example, the CGCFork
star is defined as
Fork
-type stars by not allocating output buffers, but instead the stars reuse the input buffers. Unfortunately, in the current implementation, assembly language fork stars can not do their magic if the buffer size gets too large (specifically, if the size of the buffer that must be allocated is greater than the total number of tokens generated or read by some port during the entire execution of the schedule). Here, forks or delay stars that copy inputs to outputs must be used. Another example of a
Fork
-Type star is the Spread
star. The star receives N tokens and spreads them to more than one destination. Thus, each output buffer may share a subset of its input buffer. We call this relationship embedding: the outputs are embedded in the input. For example, in the CGCSpread
star:
A
Collect
star embeds its inputs in its output buffer:
UpSample
and DownSample
stars. One restriction of embedding, however, is that the embedded buffer must be static. Automatic insertion of Spread
and Collect
stars in multi-processor targets (refer to the target section) guarantees static buffering. If there is no delay (i.e., no initial token) in the embedded buffer, static buffering is enforced by default. A buffer is called static when a star instance consumes or produces data in the same buffer location in any schedule period. Static buffering requires a size that divides the least common multiple of the number of tokens consumed and produced; if such a size exists that equals or exceeds the maximum number of data values that will ever be in the buffer, static allocation is performed.