EE 345S Real-Time DSP Lab: Grading of C Programs

Below is an approximate grading criteria for the "Code" portion of the lab grade.

Offense Penalty
No code submission -5
Use of global variables -3
Use of the pow function -3
Use of the "%" operator -3
Not using functions (e.g. for convolution)-3
Non-use of "for" loops -3
Use of "if" or "case" statements instead of a lookup table -3
The purpose of these is NOT to overly-constrain on how your write your programs... The intent is to encourage good programming techniques. Here is a little discussion about some of the points above:

Code Submission

You are expected to submit your code on the submission webpage before you turn in your lab report for each lab. Failure to do so will result in the above penalty.

Global Variables

In previous semesters, many students have used global variables inappropriately. For example, making the index in a "for" loop a global variable is generally bad programming practice, and it impedes readability, debugging, and verification.

It is generally accepted that variables should have the least scope necessary for their function. Furthermore, in some circumstances, it is necessary to use global variables. In Lab#2, an interrupt-based method using a lookup table would generally require that the lookup table be declared as global, since the interrupt handler function cannot take arguments. This usage would not be penalized.

Use of "pow" function and "%" operator

It is tempting to use the pow function to implement gray coding for the PAM lab. However, the pow function is designed for doing floating-point exponentiation. This is horribly inefficient since the gray-code mapping only uses exponentiation to make something positive or negative. In these labs, this mapping can be compactly and efficiently represented using a lookup table. When implementing convolution it is tempting to use the modulus (%) operator for automatically handling wrap-around. However, the modulus operator is usually compiles to an integer division. That is, whether one is interested in the quotient or the remainder, a division is performed. You may recall, that of the 4 basic arithmetic operations (+,-,*,/) division is by far the slowest. Also, for indexing the for-loop in the convolution routine, wrap-around will only happen once per execution (regardless of the number of taps). A simple conditional subtraction can have the same effect as the modulus operator while using far fewer clock cycles.

Use of Functions

In some of the labs, the same action is executed at multiple places in the program. For example, in the QAM lab, 16 convolutions are performed after each symbol is generated. In the past, several groups implemented this by copying-and-pasting the 5-6 lines of code that make up the convolution routine 16 times (and then making the appropriate modifications to each)...

There are several problems with such a technique. It makes the code unreadable and extremely difficult to maintain. Often is the case where after the 16 copies are made, a design mistake is discovered. Then the programmer must go back and change all 16 copies (which is highly error-prone). Had the convolution routine been written as a function, only one change would need to be made. Furthermore the use of a function reduces code size.

Non-use of "for" loops

Like the usage of functions, simple for-loops are highly useful. In the PAM & QAM labs, there is a need to convolve the same set of symbol values with different sub-filters. In the QAM lab, the number of sub-filters is 16. Sadly, some groups have implemented this by copying and pasting 16 lines (and hard-code each to a different filter). The same groups also tended to spend huge amounts of time debugging their code (a typo in one of the copies is often hard to spot). Even worse, some of these didn't use function calls for the convolution either... (which resulted in a very long and difficult-to-read program).

Non-use of look-up tables

In the PAM & QAM labs, there is a need to translate a group of bits to a group of values. For example, in the QAM lab, 4 bits are mapped onto 16 possible symbol values. One way of implementing this is by writing 16 "if" statements to test every possible combination of bits. A slightly better method would involve the use of a case statement. However, both of these approaches result in highly inefficient code. Remember that an IF statement often translates to the use of BRANCH instructions, which can be highly inefficient on pipelined processors.

For small tables, it often makes sense to implement such translation by a lookup table. In the QAM example, the 4 bit values could be converted to an integer (0-15) and this integer used as the index into an array. Not only is this simpler to write, but it results in highly efficient execution. (No branch penalty).

Summary

These coding guidelines are mainly intended to do the following: