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 |
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 |
For C++, if conditional statements should look like this:
1 2 3 4 5 6 7 8 9 | if ( conditional_expression0 ) { |
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.