To maintain consistency within the SCP/MCP software source code a series of rules and guidelines have been created; these form the project's coding style.
The source code must use the UTF-8 encoding. Comments, documentation and strings may use non-ASCII characters when required (e.g. Greek letters used for units).
All files must begin with a license header of the following form, where first year describes the year the file was first upstreamed, and the second year the year when the file was last updated:
Arm SCP/MCP Software Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
To aid in establishing a uniform style across the code-base, this project uses Clang Format. When contributing patches, care should be taken to ensure that code has been formatted according to the rules laid down in the .clang-format file, and that deviations occur only where the tool is unable to format code reasonably.
You can automatically format any staged changes with:
git clang-format
One common deviation is found when defining a list of harware registers, for example:
struct some_reg {
uint8_t RESERVED0[0x820 - 0x818];
FWK_RW uint32_t REGISTER0;
FWK_RW uint32_t REGIRTER1;
};
The above indentation, although easy to read, will fail the formatting style. To preserve the legacy format and allow silent operation of the tool, only such definitions may be preceded and followed by the following special comments:
// clang-format off
struct some_reg {
uint8_t RESERVED0[0x820 - 0x818];
FWK_RW uint32_t REGISTER0;
FWK_RW uint32_t REGIRTER1;
};
// clang-format on
Conditional statements and iteration statements with a single line or multiple lines of code must be surrounded by braces:
if (condition) {
function_call(
long_variable_name_x,
long_variable_name_y,
long_variable_name_z);
}
This rule applies also to an if-else chain:
if (condition) {
function_call(
long_variable_name_x,
long_variable_name_y,
long_variable_name_z);
} else {
function_call_b();
}
Empty loop statements must use continue
:
while (condition) {
continue;
}
When using operators like sizeof
and alignof
, where possible use the
value-based version over the type-based version:
int counter;
sizeof(counter); /* Preferred over sizeof(int) */
Do not rely on the implicit precedence and associativity of operators. Use parentheses to make precedence and associativity explicit:
if ((a == 'a') || (x == 'x')) {
do_something();
}
Parentheses around a unary operator and its operand may be omitted:
if (!a && *b) {
do_something();
}
All headers must be wrapped with include guards to prevent accidental multiple
definitions of header contents. The definition name should be the upper-case
file name followed by _H
. An example for a file named fwk_mm.h
follows:
#ifndef FWK_MM_H
#define FWK_MM_H
(...)
#endif /* FWK_MM_H */
The closing endif
statement should be followed directly by a single-line
comment which replicates the full guard name. In long files this helps to
clarify what is being closed.
Public headers must be included directly except in the following situations:
- The header is already included by the counterpart to the current C file (e.g.
x/src/mod_x.c
does not need to include<stdint.h>
ifx/include/mod_x.h
includes it) - The header is an internal header and is included as part of another public
header (e.g.
x/src/mod_x.c
does not need to includex/include/internal/x.h
if another header includes it)
These rules do not apply to private headers (headers under a src/
directory).
All macro and constant names must be written in upper-case to differentiate them from functions and non-constants.
Logical groupings of constants should be defined as enumerations with a common
prefix, so that they can be used as parameter types and in constant expressions.
Enumerations that contain a "number of elements" value must use the _COUNT
suffix, and the final enumeration value should include a comma.
enum command_id {
COMMAND_ID_VERSION,
COMMAND_ID_PING,
COMMAND_ID_EXIT,
COMMAND_ID_COUNT,
};
void process_cmd(enum command_id id)
{
(...)
}
Function, variable, file name and type names must:
- Be written in lower-case
- Have compound words separated by underline characters (
_
) - Have descriptive names, avoiding contractions where possible (e.g.
cluster_count
instead ofclus_cnt
)
Avoid using:
- Camel case syntax (e.g.
cssClusterCount
) - Hungarian notation, encoding types within names (e.g.
int iSize
)
The following prefixes are reserved for these components:
- Architecture:
arch_
- Framework:
fwk_
- Module:
mod_
- Module description:
module_
- Module configuration:
config_
- Module description:
- Firmware:
fmw_
Use fwk_assert()
to mark invariants. Invariants are logical assertions about
the behaviour of the system which must hold true for the program to be valid.
Invariants are usually easy to communicate to human readers, but not very easy
to communicate to the compiler.
Use fwk_expect()
to mark expectations. Expectations may fail, and they may
or may not be handled, but they are not invariants in that they may reasonably
occur at some point in time.
It is acceptable to use the following common placeholder names for loop indices:
i
j
k
Avoid defining custom types with the typedef
keyword where possible. Names
defined with typedef
must use the _t
suffix.
To ensure a consistent look, the preferred style for single-line comments is to use the C89 style of paired forward-slashes and asterisks, ending with no punctuation:
/* A short, single-line comment */
Multi-line comments should follow a similar style, and always end with punctuation:
/*
* This is a very, very, very, very long multi-line comment where each line
* starts with an asterisk and paragraphs ends with punctation.
*/
Doxygen comments follow only the second style, for consistency in the generated documentation:
/*!
* \brief Hello, World.
*/
Preprocessor if 0
is preferred for commenting out blocks of code where it is
necessary to do so, as these can be nested (unlike multi-line comment blocks).
void function_a(int x, int y)
{
(...)
}
#if 0
void function_b(int x, int y)
{
(...)
}
#endif
Prefer functions to function-like macros where possible. Avoid using the
inline
keyword, as inlining sites are better-determined by the compiler.
When literals require explicit literal initialization, avoid implicit casts by using the proper integer literal suffix or initializer:
void *p = NULL;
unsigned int u = 0u;
float f = 0.0f;
double d = 0.0;
char c = '\0';
Array and structure initialization should use designated initializers. These allow elements to be initialized using array indices or structure field names without a fixed ordering:
uint32_t clock_frequencies[3] = {
[2] = 800,
[0] = 675,
};
struct clock clock_cpu = {
.name = "CPU",
.frequency = clock_frequencies[2],
};
The format for structures representing memory-mapped device registers is standardized:
- Register structure names are suffixed with
_reg
- All register names must be capitalized
- Non-reserved registers must be one of
FWK_R
,FWK_W
, orFWK_RW
- Bit-field masks and shifts use the
<DEVICE>_<REGISTER>_<FIELD>
name, suffixed with either_MASK
or_POS
#include <stdint.h>
#include <fwk_macros.h>
struct my_timer_reg {
FWK_RW uint32_t CONFIG;
uint32_t RESERVED1;
FWK_W uint32_t IRQ_CLEAR;
FWK_R uint32_t IRQ_STATUS;
uint8_t RESERVED2[0x40];
};
#define MY_TIMER_CONFIG_ENABLE UINT32_C(0x00000001)
#define MY_TIMER_CONFIG_SLEEP UINT32_C(0x00000002)
#define MY_TIMER_IRQ_STATUS_ALERT UINT32_C(0x00000001)
Note: A template file can be found in doc/template/device.h
The project APIs are documented using Doxygen comments.
It is mandatory to document every API exposed to other elements of the project. By default, the provided Doxygen configuration omits undocumented elements from the compiled documentation.
At a minimum:
- All functions and structures must have at least a
brief
tag - All functions must document their parameters (if any) with the
param
tag- Input parameters must use
param[in]
- Output parameters must use
param[out]
- Input/output parameters must use
param[in, out]
- Input parameters must use
- All functions returning a value should use the
return
and, optionally, theretval
tag to document their return valuereturn
should document what the type of the return value representsretval
should document what individual return values represent
Alignment and indentation:
- Documentation must also obey the 80 column limit
- Multiple lines of documentation on an entry (e.g. details) must be indented using the equivalent of two 4-space based tabs from the first column (see example below)
Function documentation example:
/*!
* \brief Enable the watchdog.
*
* \details This function enables the watchdog. If mod_wdog_set_interval() has
* not been called beforehand then the watchdog defaults to a 500
* millisecond timeout period.
*/
void mod_wdog_enable(void);
Structure documentation example:
/*!
* \brief A structure example.
*
* \details This is an example of a structure for the code style documentation.
*/
struct mod_structure {
/*!
* \brief Example field.
*/
unsigned int example;
};
Python based tools must follow the PEP8 specification. This is enforced
with the pycodestyle
tool.
Python code must be linted with pylint
.