BSPACM  20150113
Board Support Package for ARM Cortex-M Microcontrollers
BSPACM Coding Style

Table of Contents

The coding style for BSPACM is primarily the one the author converged to over the last twenty-five years, but has a few lingering influences from BSP430.

Indentation and Syntactic Style

BSPACM's coding style may be obtained using the following Emacs ccmode style:

(c-add-style "pab"
'("gnu"
(indent-tabs-mode . nil) ; No tabs in the source
(c-offsets-alist
(case-label . 2) ; Indent the case labels
)))

An acceptably close approximation can be had using the following Artistic Style (astyle) command line:

astyle \
--options=none \
--style=1tbs \
--indent=spaces=2 \
--indent-switches \
--pad-header \
--max-instatement-indent=60 \
-r \
'*.c' \
'*.h' \
'*.inc'

Where astyle and emacs reformat the other's code, astyle's decision is preferred.

Note in particular that the one-true-brace-style expects single statements in loop, if and else bodies to be enclosed in braces, e.g.:

if (condition) {
f1();
} else {
f2();
}

Identifiers and Naming Conventions

Care is taken in BSPACM to conform to the rules for C regarding global identifiers. In particular, underscores may not appear at the start of an identifier (including macro parameters), and certain suffixes like _t are reserved.

Type Selection

Anybody using a Cortex-M device should know that they are operating on a 32-bit device, and BSPACM is by design restricted to Cortex-M devices.

Miscellaneous

Comparison Operands

BSPACM places comparison operands which are lvalues on the right side of the operator, as with:

if (4 <= len) {
/* Too long */
}

Yes, even if the conditional operator "couldn't possibly be mistyped" as an assignment operator. Up with it you must put.

Function Return Values

Where a function implements an operation that may succeed or fail, that state reflected in the return value following standard POSIX conventions: a zero indicates a successful execution and a negative value indicates an error. Where useful information about a successful result may be expressed as a non-negative value (e.g., the number of bytes successfully transmitted), that may be included in the return value.

This is often counter-intuitive to programmers unfamiliar with POSIX, but the correct test to see whether the I2C addresses were successfully set is indeed:

if (0 == iBSPACMserialI2CsetAddresses_rh(i2c, oa, sa)) {
/* Success here... */
}

Include Files

Protection Against Nested Inclusion

Be aware that in BSPACM a generic header such as <bspacm/utility/led.h> will in turn include a device-specific <bspacm/utility/led_.h> to complete the necessary data structures and inline function definitions. This file is located by compiler flags that provide a prioritized list of locations to look for that file.

Include file protection symbols encode the path by which the include file is normally accessed, preceded by components from its containing path if appropriate, with non-alphanumeric characters replaced by underscores, and the resulting token expressed in upper case. For example, the generic header <bspacm/utility/led.h> is located at $(BSPACM_ROOT)/include/bspacm/utility/led.h and is protected with:

#ifndef BSPACM_UTILITY_LED_H #define BSPACM_UTILITY_LED_H /*
... */ /* Provide device-specific information */ #include
<bspacm/utility/led_.h> #endif /* BSPACM_UTILITY_LED_H */

When compiling for an EFM32 device the reference to <bspacm/utility/led_.h> will find the header located at $(BSPACM_ROOT)/device/efm32/include/bspacm/utility/led_.h, and its include protection symbol is:

#ifndef BSPACM_DEVICE_EFM32_INTERNAL_UTILITY_LED_H
#define BSPACM_DEVICE_EFM32_INTERNAL_UTILITY_LED_H
/* ... */
#endif /* BSPACM_DEVICE_EFM32_INTERNAL_UTILITY_LED_H */
Note
Because this header is for internal use, its base name ends with an underscore. To avoid violation of the C rule that consecutive underscores in identifiers are reserved for the implementation, the protection symbol instead inserts the sequence _INTERNAL between the device-specific path and the relative name.

Standard Order for Include Files

See also bspacm_config for naming conventions and standards related overriding default configurations.

Header Files (*.h)

Every header file that does not include another BSPACM header file must include <bspacm/core.h> first to ensure the necessary preprocessor directives that are affected by <bspacm/config.h> are applied in the standard order.

<bspacm/core.h> may be expected to include the following external material:

#include <stdint.h> /* Size-annotated integral types (uint8_t) */
#include <stddef.h> /* NULL and size_t */
#include <bspacm/device.h> /* device-specific header */
#include <bspacm/config.h> /* application-specific header */

The files <bspacm/device.h> and <bspacm/config.h> do not exist in the primary include directory, but in alternative hierarchies placed within the device and board subdirectories. <bspacm/config.h> in particular may also be superseded through use of an application-specific include hierarchy.

Implementation Files (*.c)

Every implementation file should include <bspacm/core.h> first to ensure that platform-specific overrides have been evaluated and are available to override defaults in other headers.

Preprocessor Feature Tests

See also bspacm_config for naming conventions and standards related to feature tests.

Where a macro value is used for conditional compilation, the state of being true is determined with the following pattern:

#if (FEATURE_FLAG - 0)
/* Feature is enabled */
#else /* MACRO_VALUE */
/* Feature is not enabled */
#endif /* MACRO_VALUE */

This technique allows use of:

#define FEATURE_FLAG 0

to denote that the feature is being explicitly disabled. Developers of application or infrastructure code should ensure that any feature test flag has a definition to either 1 or 0 (or to an expression that will evaluate to either a true or false value when used within a preprocessor condition). The documentation explicitly marks macro definitions that are expected to have such values with the @cppflag annotation in the documentation block, which will produce:

C Preprocessor Only:
This macro may have a value that restricts its use to C preprocessor conditional directives.
Note
While other macros may be documented to have true or false values that are valid in both code and preprocessor expressions, macros with the @cppflag may have values that include preprocessor operators like defined which restrict their use to preprocessor directives.

The ability to explicitly disable features that default to being enabled is critical and requires a value-based comparison. The subtraction of 0 in the conditional expression allows syntactic correctness when somebody inappropriately defines the feature flag with no value: such a case is equivalent to disabling the feature. For consistency these tests should be enclosed in parentheses. The syntax also makes clear that the code was not intended to be #ifdef FEATURE_FLAG.

Warning
When features identified with null macro definitions are processed according to the BSPACM standard conditional directives, they are interpreted as being turned off, not on.

The correct way of detecting features when interoperating with external libraries using the null-definition style is:

#if defined(FEATURE_FLAG)
/* Function is available and feature is enabled */
#else /* FEATURE */
/* Function is missing or feature is disabled */
#endif /* FEATURE */

The short-hand #ifdef may be used instead of the defined() test if no further conditions are required, but is discouraged for consistency.

Documentation Expectations

BSPACM uses Doxygen for architectural and API documentation. The following macros are used in API documentation to highlight expectations of particular macros, variables, and functions.

Copyright 2014-2015, Peter A. Bigot