Program Structure
It is essential to understand how program structure is defined in order to use the tool correctly.
Programs are written in multiple source files, each containing functions. Each function will contain sequences of statements, loops (e.g. for / while / do), conditionals (e.g. if / switch) and function calls.
Compiling For XTA
The compiler outputs information which allows the XTA tool to make associations between source and instructions. This information is on by default but can be disabled by adding the following flag to the compiler options:
-fno-xta-info
The compiler also supports adding debug information without affecting optimizations. Debug information is not required in order for the XTA tool to analyze code, but the mapping between instructions and source code is not available without the debug information. In order to add debug information compile with:
-g
Structural Nodes
The compiler tools create a binary file with one program per xCORE tile. The XTA tool uses the binary file in order to produce accurate timing results.
When a route is created, the tool analyzes the binary to create a structure which closely represents the high-level program structure. It decomposes the program into structural nodes which can be displayed as a tree.
The worst and best case time is then calculated for each of the structural nodes. The way this is calculated depends on the type of structural node. The worst and best case times for the overall route is built up from the worst and best case times of the sub nodes.
The structural nodes can be of the following types:
- Instruction: the most basic building block of the program is the instruction.
- Block: a list of instruction nodes with no conditional branching which is therefore executed in sequence. The worst/best case time for a block is the sum of its component instructions.
- Sequence: a list of structural nodes which are executed in order. The worst/best case time for a sequence is the sum of the worst/best case times of its sub nodes.
- Conditional: a set of structural nodes out of which at most one node is executed. If this is within a loop then on each iteration a different node might be chosen. In some cases the entire conditional is optional. In those cases the best case time is for none of the options to be taken. The worst/best case time for a conditional is determined by the worst/best case time of each of its sub nodes.
- Loop: consists of a header and a body (both of which are structural nodes). The header corresponds to the conditional test part of the loop, and the body corresponds to the code that is executed if the loop is taken. This roughly corresponds to high level code structures such as while or for loops.
The body is executed once per iteration. The header always executes once more than the number of iterations. The worst/best case times for a loop is the worst/best case time of its header multiplied by (number of iterations + 1) plus the worst/best case time of the body multiplied by the number of iterations.
- Self-loop: a loop where the header and body are the same. It is therefore considered to have a minimum loop count of 1. This roughly corresponds to high level code structures such as do loops. The worst/best case time for a self-loop is determined by the worst/best case time of its body multiplied up buy the number of iterations.
- Function: is the high-level construct of the function and consists of a list of other structural nodes. The worst/best case time for a function is calculated in the same way as that of a sequence.
Identifying Nodes: Code References
A code reference is the way to specify a particular location in an application. A code reference is made up of a base and an optional backtrail. The base consists of a reference type and the backtrail consists of a comma separated list of reference types.
There are a number of different reference types, all of which map to one or more instruction program counters (PCs). This will usually be one PC, but can be more than one due to compiler optimizations or because the user has explicitly named multiple instructions with the same reference. Compiler optimizations such as inlining or unrolling will result in the same reference mapping to multiple PCs. For the grammar of specifying a code reference see Code Reference Grammar.
The different reference types are detailed below. The commands to list the instances of them for the currently loaded executable in the console are detailed with each type. In xTIMEcomopser Studio the available references are shown in the Info View.
Source File-Line
Source file-line references are valid for source lines which the compiler has defined as belonging to a source-level basic block. The valid lines can be listed in the console with:
list allsrclabels
Source Label
Source labels are added to source code using the #pragma xta label. To list the source labels in the console type:
list srclabels
Call File-Line
Call file-line references are valid for source lines which map to function calls. To list the valid source lines in the console type:
list allcalls
Call Label
Call labels are added to source code using the #pragma xta call. To list the source labels in the console type:
list calls
Endpoint File-Line
Endpoint file-line references are available for source lines which map to a valid endpoint. To list the endpoints in the console type:
list allendpoints
Endpoint Label
Endpoint labels are added to the source using #pragma xta endpoint. They must be on the line before an input/output operation. To list the labeled endpoints in the console type:
list endpoints
Label
Labels are arbitrary text strings referring to any source or assembly label. To list the labels in the console type:
list labels
Labels in assembly must be within an executable section.
Function
Functions are the functions contained within the binary. To list the labels in the console type:
list functions
Functions in assembler must be labeled as functions with the .type directive in order to be correctly detected by the tool (see xTIMEcomposer Studio User Guide). They must also be within an executable section.
Program Counter (PC)
Program counters are the lowest-level reference, giving a hexadecimal program counter value starting with 0x. They must map to the PC of an instruction within the executable section of the program.
Reference Classes
Particular console commands and GUI actions only work on particular types of references. The sets of reference types that are defined for a particular command is know as a reference classes.
ENDPOINT
These are references which can be used for timing. This means any reference in assembler (PC/label) and only source references which map to lines which can be reliably used for timing. Compiler optimizations cannot remove them or re-order them with respect to each other. In XC code these correspond to source lines with I/O operations. The following console command lists the types available in the class:
help ENDPOINT
CALL
These references map to function calls. These are used in back trails to identify unique instances of a code reference. The following console command lists the types available in the class:
help CALL
FUNCTION
These references map to functions. The following console command lists the types available in the class:
help FUNCTION
LABEL
The following console command lists the types available in the class:
help LABEL
PC
The following console command lists the types available in the class:
help PC
Forcing a Specific Type
It is possible to have a code reference which could map to multiple types. For example there could be an endpoint which has been given the same name as a function in the program. The way a reference in a backtrail is matched can depend upon the type of the reference. In order to resolve this potential ambiguity, it is possible to force the code reference to a certain type by prefixing with its type. See Code Reference Grammar for details.
Back Trails
A code reference’s base may occur multiple times within a program. For example, a function can be called from multiple places. The back trail for a reference is a way of restricting a reference to specific instances. Consider the example file shown in Using backtrails..
1 void delay_n_seconds ( int j) { 2 for ( unsigned int i = 0; i < j; ++i) { 3 # pragma xta label " delay_loop " 4 delay_1_second (); 5 } 6 } 7 8 int test () { 9 # pragma xta call " delay_1 " 10 delay_n_seconds (10); 11 # pragma xta call " delay_2 " 12 delay_n_seconds (20); 13 return 0; 14 }
The following commands could be used to time the test function:
- analyze function test
- set loop - delay_loop 10
That would have the effect of setting the number of loop iterations for the loop in both instances of the delay_n_seconds to 10. However, as the number of iterations are passed as a parameter to delay_n_seconds, the value is different for each call.
To time test correctly the loop iterations for each instance needs to be specified differently. This can be achieved by the use of the call references and backtrails. For example:
- analyze function test
- set loop - delay_1,delay_loop 10
- set loop - delay_2,delay_loop 20
This tells the tool to set delay_loop to 10 iterations when called from delay_1, and to 20 iterations when called from delay_2. The references used in the above case are composed of a base reference of type source label, and a backtrial or size one, of type call label. The above can also be achieved using the file-line equivalents. For example:
- analyze function test
- set loop - source.xc:10,source.xc:3 10
- set loop - source.xc:12,source.xc:3 20
However, this would not have resulted in a portable and robust script implementation, so using file-line references in this way from a script is not encouraged.
Inlining
When the compiler inlines some code (for example the delay_n_seconds function above) then some references will no longer be valid. In this case the following reference would not exist because the call no longer exists:
source.xc:10,source.xc:3
However, if the call has been labeled with a call label, the compiler ensures that the reference is still valid even if the code is inlined. So, in the above case, the following reference will still be valid;
delay_1,delay_loop
Scope of References
References can have either global or local scope. Globally scoped references are those which apply to (or get resolved on) the global tree. The global tree is the notional structural representation of the whole program, prior to any route analysis taking place. Locally scoped references are those which apply to (or get resolved on) a user created route tree. Whether a particular reference is globally of locally scoped depends on the command being executed. The following commands used globally scoped references:
- analyze path
- analyze function
- analyze loop
- add exclusion
- add branch
The following commands used locally scoped references:
- set/add loop
- set/add looppath
- set/add loopscope
- set/add instructiontime
- set/add pathtime
- set/add functiontime
In general, globally scoped references can lead to multiple route creation.