Download FreeRTOS
 

Quality RTOS & Embedded Software

KERNEL
WHAT'S NEW
Simplifying Authenticated Cloud Connectivity for Any Device.
Designing an energy efficient and cloud-connected IoT solution with CoAP.
Introducing FreeRTOS Kernel version 11.0.0:
FreeRTOS Roadmap and Code Contribution process.
OPC-UA over TSN with FreeRTOS.

Coding Standard, Testing and Style Guide

On this page:

Coding Standard / MISRA C:2012 Compliance

The core FreeRTOS source files (those that are common to all ports, but not the port layer) conform to the MISRA coding standard guidelines. Compliance is checked using Coverity with the coverity MISRA configuration file. As the standard is many pages long, and is available for purchase from MISRA for a very small fee, we have not replicated all the rules here. Deviations from the MISRA standard are listed in this document.

FreeRTOS builds with many different compilers, some of which are more advanced than others. For that reason FreeRTOS does not use any of the features or syntax that have been introduced to the C language by, or since, the C99 standard. The one exception to this is the use of the stdint.h header file. The FreeRTOS/Source/include directory contains a file called stdint.readme that can be renamed stdint.h to provide the minimum stdint type definitions necessary to build FreeRTOS - should your compiler not provide its own.


Coding Standard / MISRA C:2004 Compliance ( FreeRTOS Kernel versions earlier than 11.0.0 )

MISRA C:2004 compliance for FreeRTOS Kernel versions earlier than 11.0.0 is checked using pc-lint with the linked lint configuration files.

Deviations from the MISRA standard are listed below:

  • Two API functions have more than one exit point. A deviation was permitted in these two cases for reasons of critical efficiency.
  • When creating tasks, the source code manipulates memory addresses to locate the start and end addresses of the stack allocated to the created task. The code has to work for all the architectures to which FreeRTOS has been ported - which includes architectures with 8, 16, 20, 24 and 32-bit buses. This inevitably requires some pointer arithmetic. When pointer arithmetic is used, the arithmetic result is programmatically checked for correctness.
  • The trace macros are, by default, empty, so they do not generate any code. Therefore, MISRA compliance checking is performed with dummy macro definitions.
  • MISRA rules are turned off on a line by line basis, as deemed appropriate (that is, when complying with the rule is deemed to create less appropriate code for a deeply embedded system than carefully not complying). Each such occurrence is accompanied by a justification using the special pc-lint MISRA comment mark-up syntax.

 


Testing

This section describes the tests performed on common code (the code located in the FreeRTOS/Source directory that is built by all FreeRTOS kernel ports), and the tests performed on the portable layer code (the code located in subdirectories of the FreeRTOS/Source/portable directory).

  • Common code

    The standard demo/test files attempt to provide 'branch' test coverage whereby the tests ensure both 'true' and 'false' paths through each decision are exercised. (In most cases this actually achieves 'condition' coverage because the kernel's coding style deliberately keeps conditions simple, specifically for this purpose.) 'branch' coverage is measured using GCOV by defining the mtCOVERAGE_TEST_MARKER() macro to a NOP (no operation) instruction in the 'else' path of each 'if()' condition if the 'else' path would otherwise be empty. mtCOVERAGE_TEST_MARKER() is only defined while measuring test coverage - normally it is an empty macro that does not generate any code.

  • Port layer

    Port layer code is tested using 'reg test' tasks, and, for ports that support interrupt nesting, the 'interrupt queue' tasks.

    The 'reg test' tasks create multiple (normally two) tasks that first fill all the CPU registers with known values, then continuously check that every register maintains its expected known value as the other tests execute continuously (soak test). Each reg test task uses unique values.

    The 'interrupt queue' tasks perform tests on interrupts of different priorities that nest at least three deep. Macros are used to insert artificial delays into pertinent points within the code to ensure the desired test coverage is achieved.

It is worth noting that the thoroughness of these tests has been responsible for finding bugs in silicon on multiple occasions.

 


Memory Safety Proofs

FreeRTOS Core (and FreeRTOS for AWS IoT) libraries include memory safety proofs. The same proofs are being applied to the FreeRTOS kernel and FreeRTOS-Plus-TCP stack, but these two older libraries do not have full coverage yet.

 


Security Review

Non trivial updates must pass Applied Security (APSEC) review and penetration testing (pentesting) prior to release (services provided by AWS).

 


Naming Conventions

The RTOS kernel and demo application source code use the following conventions:

  • Variables
    • Variable names use camel case, are unambiguously descriptive, and use full words (no abbreviations, except universally accepted acronyms).
    • Variables of type uint32_t are prefixed ul, where the 'u' denotes 'unsigned' and the 'l' denotes 'long'.
    • Variables of type uint16_t are prefixed us, where the 'u' denotes 'unsigned' and the 's' denotes 'short'.
    • Variables of type uint8_t are prefixed uc, where the 'u' denotes 'unsigned' and the 'c' denotes 'char'.
    • Variables of non stdint types are prefixed x. Examples include BaseType_t and TickType_t, which are portable layer defined typedefs for the type that is the natural or most efficient type for the architecture, and the type used to hold the RTOS tick count, respectively.
    • Unsigned variables of non stdint types have an additional prefix u. For example, variables of type UBaseType_t (unsigned BaseType_t) are prefixed ux.
    • Variables of type size_t are also prefixed x.
    • Enumerated variables are prefixed e
    • Pointers have an additional p prefixed, for example, a pointer to a uint16_t will have prefix pus.
    • In line with MISRA guides, unqualified standard char types are only permitted to hold ASCII characters and are prefixed c.
    • In line with MISRA guides, variables of type char * are only permitted to hold pointers to ASCII strings and are prefixed pc.
  • Functions
    • Function names use camel case, are unambiguously descriptive, and use full words (no abbreviations, except universally accepted acronyms).
    • File scope static (private) functions are prefixed with prv.
    • API functions are prefixed with their return type, as per the convention defined for variables with the addition of the prefix v for void.
    • API function names start with the name of the file in which they are defined. For example vTaskDelete is defined in tasks.c, and has a void return type.
  • Macros
    • Macro are unambiguously descriptive, and use full words (no abbreviations, except universally accepted acronyms).
    • Macros are prefixed with the file in which they are defined. The pre-fix is lower case. For example, configUSE_PREEMPTION is defined in FreeRTOSConfig.h.
    • Other than the prefix, macros are written in all upper case, and use an underscore to separate words.

 


Data Types

Only stdint.h types and the RTOS's own typedefs are used, with the following exceptions:

  • char

    In line with MISRA guides, unqualified char types are permitted, but only when they are used to hold ASCII characters.

  • char *

    In line with MISRA guides, unqualified character pointers are permitted, but only when they are used to point to ASCII strings. This removes the need to suppress benign compiler warnings when standard library functions that expect char * parameters are used, especially considering some compilers default unqualified char types to be signed while other compilers default unqualified char types to be unsigned.


There are four types that are defined for each port. These are:

  • TickType_t

    Use either configUSE_16_BIT_TICKS or configTICK_TYPE_WIDTH_IN_BITS to control the type (and therefore the bit-width) of TickType_t:

    • If configUSE_16_BIT_TICKS is set to non-zero (true), then TickType_t is defined to be an unsigned 16-bit type. If configUSE_16_BIT_TICKS is set to zero (false), then TickType_t is defined to be an unsigned 32-bit type.

    • If configTICK_TYPE_WIDTH_IN_BITS is set to TICK_TYPE_WIDTH_16_BITS, then TickType_t is defined as an unsigned 16-bit type. If configTICK_TYPE_WIDTH_IN_BITS is set to TICK_TYPE_WIDTH_32_BITS, then TickType_t is defined as an unsigned 32-bit type. If configTICK_TYPE_WIDTH_IN_BITS is set to TICK_TYPE_WIDTH_64_BITS, then TickType_t is defined as an unsigned 64-bit type.

    Only one of configUSE_16_BIT_TICKS and configTICK_TYPE_WIDTH_IN_BITS must be used. See the customisation section of the API documentation for full information.

  • BaseType_t

    This is defined to be the most efficient, natural type for the architecture. For example, on a 32-bit architecture, BaseType_t will be defined to be a 32-bit type. On a 16-bit architecture, BaseType_t will be defined to be a 16-bit type. If BaseType_t is defined to char, then particular care must be taken to ensure signed chars are used for function return values that can be negative to indicate an error.

  • UBaseType_t

    This is an unsigned BaseType_t.

  • StackType_t

    Defined to the type used by the architecture for items stored on the stack. Normally this would be a 16-bit type on 16-bit architectures and a 32-bit type on 32-bit architectures, although there are some exceptions. Used internally by FreeRTOS.

 


Style Guide

  • Indentation

    Four space characters are used to indent.

  • Comments

    Comments never pass column 80, unless they follow, and describe, a parameter.

    C++ style double slash (//) comments are not used.

  • Layout

    The FreeRTOS source code layout is designed to be as easy to view and read as possible. The code snippets below show first the file layout, then the C code formatting.


/* Library includes come first... */
#include <stdlib.h>
/* ...followed by FreeRTOS includes... */
#include "FreeRTOS.h"
/* ...followed by other includes. */
#include "HardwareSpecifics.h"
/* #defines come next, bracketed where possible. */
#define A_DEFINITION ( 1 )

/*
* Static (file private) function prototypes appear next, with comments
* in this style - each line starting with a '*'.
*/

static void prvAFunction( uint32_t ulParameter );

/* File scope variables are the last thing before the function definitions.
Comments for variables are in this style (without each line starting with
a '*'). */

static BaseType_t xMyVariable;

/* The following separator is used after the closing bracket of each function,
with a blank line following it before the start of the next function definition. */


/*-----------------------------------------------------------*/

void vAFunction( void )
{
/* Function definition goes here - note the separator after the closing
curly bracket. */

}
/*-----------------------------------------------------------*/

static UBaseType_t prvNextFunction( void )
{
/* Function definition goes here. */
}
/*-----------------------------------------------------------*/

File Layout



/* Variables are given verbose, unabbreviated, descriptive names. */
UBaseType_t uxCurrentNumberOfTasks = 0U;
TickType_t xTickCount = 0U;
uint32_t ulTopReadyPriority = 0UL;

/* Booleans use the microcontroller architecture's most efficient type, which
is defined in the kernel's portable layer as BaseType_t. */

BaseType_t xSchedulerRunning = pdFALSE;

/* Function names are always written on a single line, including the return
type. As always, there is no space before the opening parenthesis. There
is a space after an opening parenthesis. There is a space before a closing
parenthesis. There is a space after each comma. Parameters are given
verbose, descriptive names (unlike this example!). The opening and closing
curly brackets appear on their own lines, lined up underneath each other. */

void vAnExampleFunction( uint32_t ulParameter1, uint16_t usParameter2 )
{
/* Variable declarations are not indented, and use stdint.h defined types. */
uint8_t ucByte;

/* Code is indented. Curly brackets are always on their own lines
and lined up underneath each other. */

for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
/* Indent again. */
}
}

/* For, while, do and if constructs follow a similar pattern. There is no
space before the opening parenthesis. There is a space after an opening
parenthesis. There is a space before a closing parenthesis. There is a
space after each semicolon (if there are any). There are spaces before and
after each operator. No reliance is placed on operator precedence -
parenthesis are always used to make precedence explicit. Magic numbers,
other than zero, are always replaced with a constant or #defined constant.
The opening and closing curly brackets appear on their own lines. */

for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
}

while( ucByte < fileBUFFER_LENGTH )
{
}

/* There must be no reliance on operator precedence - every condition in a
multi-condition decision must uniquely be bracketed, as must all
sub-expressions. Ternary and comma operators are not allowed (except for
variable declarations). See the following comment block for a preferred layout
of the following code. */

if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )
{
/* Example of no reliance on operator precedence! */
ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
}

/* To improve code coverage metrics, each condition in a decision should be
tested individually. For example, the if() statement in the code snipped
immediately above is better written as the code snippet immediately below. */

if( ucByte < fileBUFFER_LENGTH )
{
if( ucByte != 0U )
{
/* Example of no reliance on operator precedence! */
ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
}
}

/* Conditional compilations are laid out and indented as per any
other code. */

#if( configUSE_TRACE_FACILITY == 1 )
{
/* Add a counter into the TCB for tracing only. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif

/* A space is placed after an opening square bracket, and before a closing
square bracket. */

ucBuffer[ 0 ] = 0U;
ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;

Formatting of C Constructs

 

 

 

 

Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.