The Image Computing Systems Laboratory (ICSL) at the University of Washington is developing an image computing library (UWICL) for the Texas Instruments (TI) TMS320C80 Multimedia Video Processor (MVP). The purpose of the UWICL is to provide a portable and efficient library of core image computing algorithms to the MVP user community. This library will be made available as source code and or executables to companies who wish to purchase the library for product development with the MVP.
The purpose of this document is to establish common parallel processor (PP) assembly coding styles to be used for developing and maintaining the UWICL. There are many reasons for needing a standardized PP coding style. First, the style guide will ensure that all PP code has a consistent layout. Many different programmers will be viewing and maintaining the source code, both in-house and in industry. Second, code that is in a consistent layout will be easier to understand and maintain. Because many different programmers will be manipulating the UWICL source code, minimizing the learning curve to develop, maintain and understand the library will improve efficiency. Finally, enforcement of the PP coding style will help reduce the number of coding errors that otherwise might be introduced.
The PP style outlined in this document comes from the experience of UW MVP programmers and various published C style guides. The style issues that will be addressed in this guide include:
The style guidelines in this document are required to be used by all programmers developing and maintaining code for the UWICL. A style guide committee will review all code written for the UWICL on a regular basis to ensure that it conforms to the style rules.
A UWICL module consists of a set of files, documentation and source code, that implements one image computing library function. The files that make up the module include master processor (MP) and PP C source code, PP assembly source code, include files, macro libraries and a README file. Please note that all the source files are Big Endian dependent.
The MP source files will be written in C and will be used primarily to set up the packet request and initiate the PP assembly library functions. More details about the MP C source files can be found in the C Style Guide.
The PP assembly source files will include functions implementing the image computing functions. The image computing function for a given module should be logically grouped into different source files as opposed to putting all functions into one source file or one function into one source file.
Wherever possible, include files should be used to encapsulate data declarations, especially those to be used by multiple files. Acceptable declarations to be found in the PP include files include constant declarations, structure definitions, and external variable declarations. Note that PP macros are not to be declared in an include file (unlike C macros) but rather, grouped together into a PP macro library. Include files located in a module are to be used only by that module. Global include files that contain useful constants and structures to be used by all UWICL modules should be located in a public directory.
In the grand tradition of other assemblers, the TI assembler for PP assembly language supports the use of macros. In some instances the use of macros can be a powerful tool. Like all good tools however, disaster can befall upon the unwary user. The style used for macros then shall make them easy to use as well as flag their potential dangers.
A README file should accompany each image computing (IC) module. The README file should be a global description of the IC module and include details about the source files and functions, such as the algorithms used, input/output specifications, timings and calling conventions. If relevant, information about compilation and installation should also be included.
All function source file names will follow the 8.3 format. The first eight characters serve to describe the function. An optional bits per pixel field is included in the descriptive name if multiple data formats are supported. The three character extension designates the file type.
[c_ or c]<Function description>[bits/pixel]<".pps">
where:
examples:
[c_ or c]<Function description>[bits/pixel]<".i">
where:
examples:
<"ppmac_"><description><".s">
where:
ppmac_min.s --> PP macro to determine the min of two numbers
Each source file will contain a number of sections. These sections should be easily identifiable and separated by blank lines. The sections are ordered is as follows:
A global comment block must be listed first which includes the file name, global purpose of the file, contents of file (e.g. list of functions), author, and modification history. The format of this comment prologue is detailed later.
If there are any include files for the source file, they will be listed next. It is suggested that a comment be appended to the include if the file is included for non-obvious reasons.
Any external variable declarations using the .global assembler directive are listed next. The external declaration allows the compiler to resolve references to it in other source files at link time.
Any global variable declarations to be use only within the source file are declared next, again using the .global assembler directive.
Any global labels that identify PP functions or branch locations are listed next.
PP functions, identified by a global label, follow last. Each function should be preceded by a function comment prologue which includes all the details about the function. All functions in one source file should be declared in a breadth-first fashion, with functions of similar level of abstraction grouped together. Also, logically similar functions should be included in the same source file and distinctly different functions should be broken up into different source files (if possible).
A block comment prologue must be included at beginning of EACH source file. This prologue should contain information such as the file name, global purpose of the file, contents of file (e.g. list of functions), author, and modification history. An example file comment prologue is as follows:
Any code that implements a specific computation and that is repeatedly used by other functions should be encapsulated into a function, unless the code is in a tight loop. The PP assembler currently does not support the concept of functions. Instead, a logical grouping of assembly instructions that is be considered a function is preceded with a global label. The end of the function is identified with the comment string:
;* END: function_name
which may be followed by the .end assembler directive if it is the last function in the source file.
The name of the global label identifying the function must follow the naming convention:
<"$pp_"><description>
where:
$pp_add_tight_loop_8
Note that although the function name is a global label, the label must be in ALL lower case. Global labels used for other purposes other that to identify a function must be ALL capitalized. This naming procedure will aid programmers in differentiating branching labels from function labels.
Each PP function will be preceded by a block comment prologue. This prologue not only provides useful information about the function, but it helps delineate the functions within a source file (particularly useful for PP assembly). The function prologue will contain information such as the name of the function, a general description of the function, parameters expected, return values, details of the algorithm , side-effects of the function, global variable modifications, author, and modification history (detailing the changes). The information contained within the comment prologue must be completed thoroughly as much of the information is vital to the understanding of the function. For example, although a PP function accepts no formal parameter list, it does access parameters through the parameter RAM. These parameters and descriptions should be included in the function prologue as it will NOT be obvious to a maintenance programmer reviewing the code. An example function prologue is as follows:
All first level assembly statements within the function should be indented 4 spaces (Note: spaces are used instead of tabs to ensure consistent indentation with different editors and editor setups). Statements within loops should be indented additional spaces to identify those regions easily (details about loop indenting is detailed in the loop section). There must be one blank line above the function prologue and one blank line after it. There should also be one blank after the end comment that identifies the end of the function.
In the grand tradition of other assemblers, the TI assembler for PP assembly language supports the use of macros. In some instances the use of macros can be a powerful tool. Like all good tools however, disaster can befall upon the unwary user. The style used for macros then shall make them easy to use as well as flag their potential dangers.
Macros must not be written directly into a piece of PP assembly code. Instead, all macros must be encapsulated into their own .s files and archived into a macro library file using the mvpar archiver. Each file shall contain only one macro and shall begin with the prefix mac_ (see the file name convention section). All related macros (files) should be archived into one .lib library file and the file name should be representative of the macros contained within it. If macros can be used by other UWICL routines, they should be archived into a public macro libraries so that they then be used by all ICL team members, thus promoting the creation of reusable code. The macro libraries are included into PP assembly code by using the .mlib assembler directive.
The name of the global label identifying the macro must follow the naming convention:
<description><"_m">
where:
inc4_m
The names of the parameters of a macro must follow the naming convention:
<description><"_p#">
where:
Each PP macro must begin with a comment prologue. The comment prologue must include three mandatory sub-blocks denoting macro name, macro usage, and macro restrictions. The macro restrictions section must carefully describe the limits of type on each of the parameters as well as the dangers in using the macro. For example, the use of macros in tight loops may make the loop look compact but the compiler will insert the macro code inserting all the macro instructions which may lead to cache misses. For example:
The macro comment prologue should have at least one full line of white space above it. and one below it. One blank line must follow the .ENDM label of the macro.
Careful attention must be paid to the last instruction of a macro. For example, if the last instruction in a macro is an assignment to an address register from a data unit operation, then there will be a delay slot of instruction after the execution of the macro. Such ending considerations must be well documented in the RESTRICTIONS sub-block of the macro comment prologue.
As with function declarations, the PP assembler currently does not support the concept of variable declarations. Instead, variables are defined by assigning a name to a PP register using the .set assembler directive:
All PP variable declarations should precede the assembly code that makes up the PP functions and not be embedded within the function. Global variable declarations should be listed after the include statements and before the first function comment prologue. Local variable declarations should be listed after the function prologue but before the function code.
Labels can be assigned to address registers, index registers and data registers. Strict naming conventions will make the code much more readable/understandable and make the formidable task of maintaining PP assembler much easier for future programmers of the UWICL. For that reason, we will adopt a specific variable naming convention:
<descriptive name>< register>[data type]
where:
.
Note that all variable names should be alphanumerically unique and should not rely on capitalization for delineation. For example, there should never be two variables named foo and Foo in the UWICL code. Also, all descriptor parts of a register name must be lower case. For example use reg_d1_w, not Reg_d1_w. Also, is a name is to consist of more than one word, it is important to delineate the words with an underscore instead of using capitals. For example, use top_of_the_world instead of TopOfTheWorld.
All declared variables must have a side-bar comment describing the purpose of the variable. This procedure will also help the programmer understand the variable naming if the name is too cryptic.
The section of variable declarations should have one blank line above it and one blank line below it. Logical groupings of variables can also be delineated from the other variables by a blank line above and below.
The PP assembly language allows for the use of up to three simultaneous hardware controlled loops (see the PP Users Guide for detailed instructions on how to properly set up hardware controlled loops). Each of the three hardware loops has a set of registers associated with them. Thus, instead of using a compound loop statement (as in the while and for statements of C), loops are set up by assigning appropriate values into the loop registers. Because finding and understanding a PP assembly programs loops are usually essential to understanding the code, the PP style shall be used to ensure that all UWICL loops are readable and easily understandable.
As noted above, the proper loop registers must be set for each loop. In all UWICL defined loops, the initialization of the loop registers must be as close to the corresponding loop as possible. In setting the loop start and loop end registers, labels corresponding the start and stop addresses of the loop must be used (never use absolute addressing (e.g. 0x12345678) as it doesn't allow for dynamic linking). In setting the loop option bits of the loop control register, predefined labels should be used instead of absolute numbers. The predefined labels must be defined in a header file called pp_icl_names.i. For example:
The naming convention for the labels denoting the starting and ending address of the loop must consist of some lower case descriptive name following the convention:
<prefix><"_ls#" | "_le#">
where:
All loops must be preceded with a loop comment prologue. The comment prologue must appear after the loop register initialization, but before the actual loop code begins. The comment prologue will be broken down into four mandatory sub-blocks (which must be separated by a blank line) denoting loop name, number of iterations, function, and exiting conditions. For example:
The executable statements within the outermost loop must be indented four spaces from the indentation of the statements outside the loop. Note that the starting and ending loop labels must be in column 1. The executable statements within the inner loop must be indented four spaces from the indentation of the code in the loop above it. There should be no blank line between the loop start label and the fist loop instruction. Likewise no blank line should be found between the last loop instruction and the loop end label. Also, each label should be on it's own line. Finally, to give the loop a more cohesive look, there should be no whitespace between executable statements within the loop unless the statement is preceded by a comment line.
The comment prologue will be must have at least one full line of white space above it. If the comment block corresponds to an inner loop, it must have the same indentation as the code in the loop above it.
To keep each loop readable and manageable, all innermost hardware loops must contain less than 64 instructions. This will also ensure that there are no cache misses during inner loop execution (see the Design Guide for more details).
Use if the .loop assembler directive can lead to inefficient and unreadable code. Because the .loop instruction can be replaced by real hardware loops, the .loop assembler directive shall not be used in any UWICL code (see the Design Guide for more details).
The MVP allows a number of parallel operations to occur. In particular, a multiplication, an arithmetic operation, a global addressing operation and a local operation. To make parallel PP assembly statements more readable, one of two different formats must be used. Each format is acceptable but a given function should be consistent and use only one or the other. In the first format, each parallel statement will be on its own line, indented 4 space from the first parallel statement (Note: spaces are used instead of tabs to ensure consistent indentation with different editors and editor setups). Additionally, each parallel statement may contain aligned side-bar comments indicating whether it is a multiplication, arithmetic, global addressing, or local addressing operation. For example:
In the second format, each parallel statement will be on its own line, directly below the first parallel statement For example:
In most cases PP assembly code is used to do the serious number crunching and/or data movement. Because of this, one is likely to find many operators binding PP data and address registers. To ensure that UW ICL PP assembly code is highly readable and maintainable, all UWICL code will be subject to style rules regarding the use of operators.
All binary operators must be preceded and succeeded by one space. All equal signs must me preceded and succeeded by one space. Finally, all unary operators should have no spaces between the operator and the operand. For example:
reg_d1_w = reg_d0_w + reg_d1_w
reg_d0_w = ~reg_d1_w
Structures/unions can be defined in PP assembly, similar to the C structure/union format. However, unlike C, the .struct/.endstruct and .union/.endunion constructs does not allocated memory but instead creates a symbolic template that can be used to symbolically reference memory. This feature is useful in producing readable code and should be used for any memory which has a complex structure.
In using the .struct/.endstruct and .union/.endunion, each element of a structure/union must be declared on it's own line, indented 4 spaces and be complete with a side-bar comment (Note: spaces are used instead of tabs to ensure consistent indentation with different editors and editor setups). An example of a complex PP assembly structure is as follows:
All modules developed for the UWICL MUST use makefiles. Makefiles can be used for more than the standard automatic conditional compilation. Makefiles can be used to execute standard UNIX commands along with the make process. These UNIX commands can be tied to a convenient and easy to understand label. For example, many makefiles include a section that will remove all of the object files in the current directory. The command then to clean out the directory of object files would be:
%> make clean
All makefiles used by the UWCL team shall have the make labels shown in Table 1.
-----------------------------------------------------------------------------
Makefile targets Functionality
-----------------------------------------------------------------------------
clean Remove all object files in current directory.
all or <function name>.out Compile the function to work with the hard
ware.
test.out Compile the function to work with the simula
tor.
cpimg / delimg Create / delete link to images to test with this
function, for use with simulator.
cplut / dellut Create / delete link to look-up table (LUT)
used to test the function with the simulator.
-----------------------------------------------------------------------------
As mentioned above, the compilation and linking process requires the use of certain parameters. Each UWICL makefile specifies the object file names and additional makefile paramaters, which are passed to a common UWICL makefile. This step insures a uniform compiling process.
As in C, the PP assembler allows for conditional compilation. In this way code can be complied in or out of the executable by changing the predefined value of a symbol without having to comment statements out or un-commenting statements in. One ideal use for conditional compilation is for debug type code. During development, debug and error checking code can be compiled into the executable. After debugging is complete, the debug code can be made as harmless as comments by switching the value on the conditional compilation. The advantage of keeping the debug code in the source files is that later modifications can be debugged using the statements used previously and thus, cutting down on the maintenance programmers time and effort.
Conditional compilation is set by using the assembler directives .if, .elseif, and .endif. For readability, the conditional compilation statements should begin in the left most column of the row they are intended for. Below is an example:
The PP assembly programmer should be very careful when using these options as it can lead to unforeseen errors. For example, the programmer may think some lines of code may be in the executable but they may not be their due to conditional compilation.
The PP assembler allows for the use of header or include files. Unfortunately, the PP assembler defines two different assembler directives to accomplish the same task: .copy and .include. To avoid confusion, all UWICL team members must use only the .include assembler directive when they want to include header/include files.
The PP assembler allows for different types of global labels. To support these types, different assembler directives are supported: .def, .ref, and .global. If .global is used, the assembler itself will determine whether the variable needs to be of type .def or of type .ref. To avoid confusion then all UWICL team members must use only the .global assembler directive when defining global variables.
The PP assembler allows for the PP assembly code programmer to set well defined assembly time expressions to a symbol (label). Unfortunately the PP assembler supports two different directives to accomplish this task: .equ and .set. To avoid confusion, all UWICL team members must only use the .set assembler directive when equating an expression to a label.
Note: these restrictions will be updated and revised as we get more experience with PP assembly.
As with life, there are times when the rules just must be broken. Before a UWICL team member shall be allowed to violate a rule set forth in this document, he/she should consult with at least one member of the style guide committee. The programmer must justify why the style guidelines are to be violated and to explain why it is necessary to violate the rules.
This PP style guide has presented a style that is intend to make PP assembly well structure, easy to read, less errors and easily maintainable. It is important to ensure there are sufficient comments, there is adequate use of whitespace, file organizations are adhered to and name conventions are followed. In order for this style to be useful, it must be followed. If anyone has conflict with the style imposed, do NOT ignore them. Instead, approach the style guide committee and the style will be explained or modified. This document is intended to be updated on a frequent basis as better and additional style guidelines are defined.
;*