Copyright © 3S Technologies
Best viewed in Internet Explorer ver. 9.0 - 1024 X 768 resolution.
Coding standards and guidelines for programming in Embedded C
The reason for documenting these standards and guidelines is to ensure developed firmware meets minimum levels of readability and maintainability.
Source code has two equally important functions:
It must work, and it must clearly communicate how it works to a future programmer or the future version of yourself.
There are several reasons like:
1. Standardize the structure, coding style and logic of an application.
2. Create precise, readable and unambiguous source code.
3. Standards describe methods of best practice resulting in higher quality code.
4. Developers can focus on the coding activity at hand rather than spending time worrying about these issues.
5. Developers can go into any code and quickly determine the intent of the algorithm.
Generally speaking software applications that are developed according to standards and guidelines are correct and easy to maintain.
However, if client insists on a particular style of coding, his standards shall supersede.
1. Categories of Coding Guidelines
Standards: “Standards” are those items that are mandated by corporate policy, platform independence requirements, or they are generally accepted good or necessary programming standards that promote more concise code. All developers should follow standards.
Guidelines: “’Guidelines” are a collection of ‘recipes of what many experienced programmers consider good coding practices. These Guidelines are not concrete rules and exceptions may apply. All developers are encouraged to follow them. New programmers will find these Guidelines particularly useful in their projects. Guidelines are suggestions to improve the consistency and maintainability of the code. While the Guidelines in this document are strongly encouraged for logical reasons, they are not mandatory prescriptions and are left to the individual developer to follow as appropriate.
2. C Coding Convention
1. Code will not generate any errors or warnings.
2. The number of files included should be minimized.
3. Conditionally compiled constants and definitions will be controlled by a compilation option.
4. Pathnames for files should be located in operating system specific header include files. Only relative path names shall be used.
5. Pathnames for files should be located in operating system specific header include files.
6. Use symbolic values instead of numeric values
7. Use the directive #include <> to include files from libraries and the directive #include "filename.h" for user-prepared include file.
1. Use global variables sparingly, limiting their scope.
2. Organize header files by functionality.
3. It is not recommended to include a file in other include file.
4. Optimize code gradually. i.e. Optimization of logic development shall be done gradually.
5. Code optimization (optimization option provided by compiler) shall be avoided until it is necessary to meet project limitations. Code optimization sometimes exposes limitation on code debugging.
6. Use Internationalized strings for strings that are displayed to the user
7. All such strings should be in UNICODE format wherever there is tool support and placed in the project's resource file.
8. All code should be written to ANSI C (C99) standards.
9. Keep the code simple
10. Be explicit
11. Be consistent
12. Keep the spirit of the standards
13. Avoid complicated statements
14. ALWAYS use the standard acronyms, abbreviations or mnemonics
15. Avoid private header filenames that are the same as (standard) library header filenames
1. ALWAYS use a version control system
2. Check in the compilers, linkers, assemblers used by this project into the version control system. Sometimes older compilers doesn’t work with new environment so total project environment shall be documented and checked in to version control repository.
2.3 Version File
1. Each project shall maintain a version file. It shall provide the firmware version name, version date, and part number.
2.4 Startup Code
1. Check if any initializations made in the start-up code are overwritten elsewhere in the code.
2. Always initialize the stack on an even address.
2.5 Stack and Heap Issues
1. Always initialize the stack on an even address.
1. Verify stack requirements while debugging
2. Test the implementation of heap memory allocation/deallocation for a compiler
3. If you chose to use malloc(), write a wrapper around it that checks the return value and safely crashes (with diagnostic information) in case malloc()_ fails.
4. In case of RTOS/special library environment, write application specific wrappers around standard APIs.
1. Practice encapsulation wherever possible
2. Keep module sizes under 1000 lines
1. Encourage a uniform module look and feel
1. All data types MUST be declared using upper case characters.
2. All enumeration tags should be in uppercase
3. All local variables should be in lower case
4. Always use unsigned int for variables, which cannot have negative values.
1. All module level variables should be prefixed with the module name followed by underscore. Separate words starting with a capital letter.
2. Do not assign a variable and a TYPEDEF (or STRUCT) with the same name, even though C allows this. This type of redundancy can make the program.
3. All global variables should be prefixed with the module name. Separate works starting with a capital letter.
4. References to local objects shall not be returned to a calling procedure.
5. Input variables shall not be changed within functions.
6. Variables are to be declared with the smallest feasible scope.
7. If possible, every variable that is declared is to be given a value before it is used. Try to initialize the variable in the declaration.
1. Name separate words with an initial capital
2. Use acronyms, abbreviations and mnemonics consistently.
1. All standard C data types MUST be avoided because their size is not portable.
2. Structures and Unions MUST be typed.
1. Do not depend on character data behaving as signed or unsigned.
2. Do not assume that longs, floats, doubles, or long doubles may begin at arbitrary addresses.
3. Use parentheses to set precedence.
4. Do not assume that pointers and integers have the same size.
5. Pointer arithmetic must be used coherently and in its intended manner.
6. Try to avoid data typecasting.
7. If a set of declarations is likely to change, if code is ported from one platform to another, put those declarations in a separate header file.
1. Avoid converting a const to a non-const.
2. Constants should be defined using const or enum. Do not use #define.
2.12 Control Structures
1. In a switch each case statement body (group of statements associated with a case) shall end with a break before the next case label.
2. In those cases where it is desired that control "flow through" into the case statement body associated with the next case label a standard comment must appear in place of the break.
3. A switch statement must always contain a default branch that handles unexpected cases even if it does nothing.
4. Avoid the use of goto.
5. Always put the constant on the left hand side of an equality/inequality comparison.
1. Avoid the use of continue and break within a loop. When these instructions are used within a loop comments should be added to indicate why they are used and what the resulting effect on control flow is.
1. Keep functions small, unless there are real time constraints or stack limitations
2. Define a prototype for every called function, with the exception of those in the compiler’s runtime library.
2.14 Recursive Routines
1. Put all such code as close to the beginning of the ISR as possible, so you can then re-enable interrupts as soon as possible.
2.15 Interrupt Service Routines
1. Use the non-maskable interrupt only for catastrophic events, like the apocalypse or imminent power failure.
2. Keep ISRs short!
3. On processors with interrupt vector tables, fill every entry of the table.
4. Try not to use any RTOS resources in ISR (e.g. get or release semaphore, mails etc)
1. When an ISR grows too large or too slow, spawn another task and exit.
2. Large ISRs are a sure sign of a need to include a RTOS.
3. Never allocate or free memory in an ISR unless you have a clear understanding of the behavior of the memory allocation routines.
2.16 Coding Conventions, Spacing and Indentation, C Formatting, Compilers, Memory Management
1. No line may ever be more than 80 characters.
2. Never, ever use “magic numbers”.
3. Header files MUST be guarded from duplicate inclusion by testing for the definition of a value.
4. TABs should not be used as an indent.
1. A careful use of blank lines between code “paragraphs” can greatly enhance readability by making the logical structure of a sequence of lines more obvious.
2. Never nest IF statements more than two deep
3. Write all C code to the ANSI standard. Never use vendor-defined extensions, which create problems when changing compilers.
4. Some tool may be used to check for memory leaks. In case no tool is available, it should be checked manually.
5. Always assign a new value (for example NULL) to a pointer that points to de-allocated memory.
6. Avoid using explicit type conversion (casts)
7. Always use inclusive lower limits and exclusive upper limits.
HSS (London) Coding Guidelines, Rev 2
Firmware Development Standard by The Ganssle Group,
C Coding Standard by Micriµm, Inc.
Online Education for all