Standardized coding conventions enable code written by multiple developers to be consistent and improve readability, maintainability, and extensibility. We have developed a simple set of coding conventions for ECE 2400 that we would like you to use in all programming assignments. Keep in mind that these are just guidelines, and there may be situations where it is appropriate to defy a convention if this ultimately improves the overall code quality.

Most of the below contents are adapted from Prof. Batten's ECE 2400 Coding Conventions. Other references include Google's C++ Style Guide and lowRISC Verilog Coding Style Guide.

1. Directories and Files

This section discusses the physical structure of how files should be organized in a project.

1.1. Directories

All coding files should be in a single  sim  directory. All tests should be in a single tests directory within the sim directory. Anything other than ad-hoc testing should always be done in a build directory within the sim directory.

1.2. File Names

C/C++ files should be named in all lowercase and should use an underscore (_) to separate words. Verilog files should be named in CamelCase style (capitalize the first letter of each word).

C source files should use the  .c  filename extension, C++ source files should use the  .cc  filename extension, Verilog source files should use the .v filename extension, and Python source files should use the .py filename extension. For C/C++, header files should use the  .h  filename extension, and inline files should use the  .inl  filename extension. Data files that contain C/C++ code and are meant to be included using the C preprocessor should use the  .dat  filename extension. All test programs should end in  -test.c. All evaluation programs should end in  -eval.c.

All Verilog PyMTL wrapper files should have postfix RTL, all Verilog source files could have postfix VRTL. e.g. "CombinationalFFTRTL.py," "CombinationalFFTVRTL.v."

1.3. Header and Inline Files

All header files should be self-contained. A header should include all other headers it needs. The definitions for template and inline functions should be placed in a separate  .inl  file and included at the end of the header. Every header should use include guards where the name of the include guard preprocessor macro is derived directly from the filename. For example, a header file named  foo-bar.h  would use the following include guards:

1
2
3
4
#ifndef FOO_BAR_H
#define FOO_BAR_H

#endif      

2. Formatting

This section discusses general formatting that is common across all kinds of files. The focus is on readability.

2.1. Line Length

Lines in all files should in general be less than 80 characters. Using less than 74 characters is ideal since this is a natural width that enables reasonable font sizes to be used when using side-by-side code development with two listings on modern laptops and side-by-side code development with three to four listings on 24" to 27" monitors. Lines longer than 80 characters should be avoided unless there is a compelling reason to use longer lines to increase code quality.

2.2. Indentation

Absolutely no tabs are allowed. Only spaces are allowed for the purposes of indentation. The standard number of spaces per level of indentation is two. Here is an example in C++:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int gcd( int x, int y )
{
  while ( y != 0 ) {
    if ( x < y ) {
      int temp = x;
      y = temp;
      x = y;
    }
    else {
      x = x - y;
    }
  }
  return x;
}

2.3. Variable Declarations

There should be a whitespace around the assignment operator. Here is an example:

1
2
int a=3;   // incorrect
int a = 3; // correct

If possible, consider vertically aligning the variable names and assignment operators for related variables:

1
2
unsigned int a     = 32;
int*         a_ptr = &a;

Never declare multiple variables in a single statement. Always use multiple statements. Here is an example:

1
2
3
int a, b; // incorrect
int a;    // correct
int b;    // correct

Never use one letter variable names except in the case of iterations. Never use abbreviations in variable names. Use underscores to separate variables with multiple words in their names.

int a;          // incorrect
int appl;       // incorrect
int applepies; // incorrect
int apples; // correct
int apple_pies; // correct

2.4. Conditional Statements

For Verilog, use begin and end unless the whole statement fits on a single line. An example is provided below:

1
2
// correct
if
(condition) foo = bar;
else foo = bum;

// incorrect
if (condition) begin
foo = bar;
end else begin
foo = bum;
end


For C++, if conditional statements should look like this:

1
2
3
4
5
6
7
8
9
if ( conditional_expression0 ) {
statement0; } else if ( conditional_expression1 ) { statement1; } else { statement2; }

Avoid single-line if statements:

1
2
3
if ( conditional_expression0 ) return 1; // incorrect
if ( conditional_expression0 )           // correct
  return 0;                              // correct

2.5. Iteration Statements

for  loops should look like this:

1
2
3
for ( int i = 0; i < size; i++ ) {
  loop_body;
}

We really want the open curly brace should be on the same line as the  for  statement.

2.6. Function Definitions

Function definitions should look like this:

1
2
3
4
int foo_bar( int a, int b )
{
  function_body;
}

Notice that for functions the open curly brace goes on its own line.

3. Naming

3.1. Type Names

For C programs, the names of user-defined types should usually be all lowercase, use underscores (_) to separate words, and use a  _t  suffix.

1
typedef unsigned int uint_t;

For C++ and Verilog programs, the names of user-defined types should usually use CamelCase.

1
2
3
4
class FooBar
{
  ...
};

When specifying pointer types, the  *  should be placed with the type without whitespace:

1
2
3
int * a_ptr; // incorrect
int *a_ptr;  // incorrect
int* a_ptr;  // correct

As a reminder, never declare multiple variables in a single statement. This is never allowed:

1
int *a_ptr, *b_ptr; // not allowed!

3.2. Variable Names

The names of variables should always be all lowercase with underscores (_) to separate words. Do not use CamelCase for variable names. For pointers, use a  _ptr  or  _p  suffix. For data member fields, use a  m_  prefix. Never use one letter variable names except in the case of iterations. Never use abbreviations in variable names.

3.3. Function/Method Names

The names of free functions and methods should always be all lowercase with underscores (_) to separate words. Do not use CamelCase for function or method names.

4. Comments

Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment on and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments. When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous - the next one may be you!

Do not state the obvious. In particular, don't literally describe what code does, unless the behavior is nonobvious to a reader who understands C/C++ well. Instead, provide higher-level comments that describe why the code does what it does, or make the code self-describing.

4.1. Comment Style

Use  //  comments. Do not use the older  /* */  comments. Include a space after // before starting your comment:

1
2
//without space, incorrect formatting
// with space, correct formatting

4.2. Comment Location

Avoid trailing comments. They make lines too long and are hard to read. Prefer placing each comment on its own line whenever possible. So avoid this:

1
2
3
4
5
6
if ( a > b ) { // if a is greater, subtract b
  c = a - b;
}
else { // if b is greater, subtract a
  c = b - a;
}

Prefer this instead:

1
2
3
4
5
6
7
8
9
// if a is greater, subtract b
if ( a > b ) {
  c = a - b;
}

// if b is greater, subtract a
else {
  c = b - a;
}

4.3. File Comments

All files should include a "title block". This is a comment at the very beginning of the file which gives the name of the file and a brief description of the purpose and contents of the file. Title blocks should use the following format:

1
2
3
4
//=========================================================================
// foo-bar.h
//=========================================================================
// Description of the purpose and contents of this file.

The horizontal lines used in the title block should extend exactly 74 characters (i.e., two '/' characters and 72  =  characters). You do not need to duplicate comments between the  .h  and  .cc. Often the header will have a description of the interface, and the source file will discuss the broad implementation approach.

4.4. Function Comments

Almost every function declaration in the header should have comments immediately preceding it that describe what the function does and how to use it. These comments may be omitted only if the function is simple and obvious. These comments should be descriptive ("Opens the file") rather than imperative ("Open the file"); the comment describes the function, it does not tell the function what to do. In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.

Every function definition in the source file should have a comment like this:

1
2
3
4
//------------------------------------------------------------------------
// foo_bar()
//------------------------------------------------------------------------
// optional high-level discussion of implementation approach

4.5. Old Comments

Do not leave old comments in the source file. So you must remove comments that were provided by previous people working on the code if they are no longer relevant.

5. Scoping

This section discusses the use of local and global variables.

5.1. Local Variables

Place a function's variables in the narrowest scope possible. Declare variables close to where they are initialized.

5.2. Static and Global Variables

For C/C++, do not use non-const static or global variables unless there is a very good reason to do so. Const global variables are allowed and should definitely be used instead of preprocessor definitions.

6. C Pre-processor

Using the C pre-processor should be avoided. Use of the C pre-processor should usually be limited to include guards and the  UTST  macros. When the C pre-processor must be used, pre-processor macro names should be in all capital letters and use an underscore (_) to separate words.

Do not use the C pre-processor to declare global constants. Use const global variables instead.

  • No labels