Перевод может содержать ошибки. Читайте первоисточник: Coding Standard, Testing and Style Guide
Назад: [Как работает FreeRTOS] | Вверх: [Как работает FreeRTOS] | Вперёд: [Как работает FreeRTOS] |
На этой странице:
Основные исходные файлы FreeRTOS (которые являются общими для всех портов, но не уровня порта) соответствуют рекомендациям стандарта кодирования MISRA. Соответствие проверяется с использованием PC-lint с соответствующими файлами конфигурации (сохранённая копия от 20190319). Поскольку стандарт содержит много страниц, и доступен для покупки у MISRA за небольшую плату, мы не приводим здесь все правила.
Отклонения от стандарта MISRA перечислены ниже:
FreeRTOS собирается со многими различными компиляторами, некоторые из которых более совершенны, чем другие. По этой причине FreeRTOS не использует какие-либо особенности или синтаксис, введённые в язык C стандартом C99 или после него. Единственным исключением является использование заголовочного файла stdint.h. Каталог FreeRTOS/Source/include содержит файл, называемый stdint.readme, который может быть переименован в stdint.h для предоставления определений типов stdint, минимально необходимых для сборки FreeRTOS, если ваш компилятор не предоставит свой собственный.
В этом разделе описываются тесты, выполненные для общего кода (код, расположенный в каталоге FreeRTOS/Source, который создаётся всеми портами ядра FreeRTOS), и тесты, выполненные для кода уровня портирования (код, расположенный в подкаталогах FreeRTOS/Source/portable).
Стандартные демонстрационные/тестовые файлы пытаются обеспечить покрытие кода тестовыми "ветвлениями" (в большинстве случаев это фактически обеспечивает "условное" покрытие, поскольку стиль кодирования ядра намеренно сохраняет условия простыми специально для этого), в результате чего тесты гарантируют, что пути и 'true', и 'false' через каждое решение осуществлены. Покрытие "ветвлений" измеряется с использованием GCOV путём определения макроса mtCOVERAGE_TEST_MARKER() в виде инструкции NOP (нет операции) в пути ветки 'else' каждого оператора 'if', если путь 'else' в противном случае был бы пустым. mtCOVERAGE_TEST_MARKER() определяется только при измерении тестового покрытия - а в обычно это пустой макрос, который не вырабатывает никакого кода.
Код уровня порта тестируется с использованием задачи 'reg test' и, для портов, поддерживающих вложенные прерывания, задачей 'interrupt queue'.
Задачи 'reg test' создают несколько (обычно две) задач, которые сначала заполняют все регистры ЦП известным значением, а затем постоянно проверяют, поддерживает ли каждый регистр ожидаемое известное значение, в то время, как другие тесты непрерывно выполняются (тест выдержки). Каждая задача 'reg test' использует уникальные значения.
Задачи 'interrupt queue' выполняют тесты прерываний с различными приоритетами как минимум с тремя уровнями вложения. Макросы используются для вставки искусственных задержек в соответствующие места в коде, чтобы обеспечить желаемое покрытие кода тестами.
Интересно отметить, что тщательность этих тестов была причиной обнаружения ошибок в кремнии во многих случаях.
Исходный код ядра ОСРВ и демонстрационных приложений использует следующие соглашения:
Используются только типы из заголовочного файла stdint.h и собственные определения типов в ОСРВ, со следующими исключениями:
В соответствие с руководствами MISRA разрешены неквалифицированные типы char, но только если они используются для хранения символов ASCII.
В соответствие с руководствами MISRA, разрешены указатели на неквалифицированные типы char, но только когда они используются для указания на строки ASCII. Это избавляет от необходимости подавления мягких предупреждений компилятора, когда используются стандартные библиотечные функции, которые ожидают использования параметров char *, особенно учитывая, что некоторые компиляторы по-умолчанию используют неквалифицированные типы char со знаком, в то время, как другие компиляторы по-умолчанию определяют неквалифицированные типы char как беззнаковые.
Есть четыре типа данных, которые определены для каждого порта. Вот они:
Если configUSE_16_BIT_TICKS установлен в ненулевое значение (т.е. true), то TickType_t определяется как беззнаковый 16-битный целый тип. Если configUSE_16_BIT_TICKS установлен в ноль (т.е. false), то TickType_t is определяется как беззнаковый 32-битный целый тип. Полная информация находится здесь.
Для 32-битных архитектур значение configUSE_16_BIT_TICKS всегда должно быть нулевым (т.е. false).
Этот тип определяется как наиболее эффективный, естественный тип для архитектуры. Например, для 32-битной архитектуры BaseType_t будет определён как 32-битный тип. Для 16-битной архитектуры BaseType_t будет определён как 16-битный тип. Если BaseType_t определён как char, то следует позаботиться о том, чтобы в качестве возвращаемого функцией значения использовались char со знаком, т.к. отрицательные возвращаемые значения могут использоваться для указания на ошибку при выполнении.
Это беззнаковый тип, аналогичный BaseType_t.
Определяется на тип, используемый архитектурой для элементов, сохранямых в стеке. Обычно это 16-битный тип для 16-битных архитектур и 32-битный тип для 32-битных архитектур, хотя есть некоторые исключения. Используется внутри FreeRTOS.
Для отступа используются символы табуляции. Один символ табуляции эквивалентен четырём пробелам.
Комментарии никогда не выходят за столбец 80, если они не следуют за параметром для его описания.
Стиль комментирования C++ двойной косой чертой (//) не используется.
Структура исходного кода FreeRTOS разработана таким образом, чтобы быть насколько возможно лёгкой для просмотра и чтения. Приведённые ниже фрагменты кода показывают сначала структуру файла, затем форматирование кода на языке С.
/* Сначала подключаются библиотеки... */
#include <stdlib.h>
/* ...Далее подключаются заголовочные файлы FreeRTOS... */
#include "FreeRTOS.h"
/* ...и затем все прочие заголовочные файлы. */
#include "HardwareSpecifics.h"
/* Следом идут #defines. Где возможно, определения заключаются в скобки. */
#define A_DEFINITION ( 1 )
/*
* Далее идут прототипы статических функций с областью видимости внутри
* этого файла, с комментариями в этом стиле, с символом '*' в
* начале каждой строки.
*/
static void prvAFunction( uint32_t ulParameter );
/* Переменные области видимости файла это последнее, что находится перед определениями функций. Комментарии для переменных выполнены в этом стиле (без символа '*' в начале каждой строки). */
static BaseType_t xMyVariable.
/* Следующий разделитель используется после закрывающей скобки каждой функции,
с пустой строкой следующей перед стартом следующего определения функции. */
/*-----------------------------------------------------------*/
void vAFunction( void )
{
/* Определение функции здесь - обратите внимание на разделитель
после закрывающей фигурной скобки. */
}
/*-----------------------------------------------------------*/
static UBaseType_t prvNextFunction( void )
{
/* Здесь располагается определение функции. */
}
/*-----------------------------------------------------------*/
Структура файла
/* Имена функций всегда записываются в одной строке, включая тип
возвращаемого значения. Как всегда, пробела перед открывающей скобкой нет.
После открывающей скобки пробел есть. Перед закрывающей скобкой пробел есть.
После каждой запятой тоже есть пробел. Параметры имеют подробные описательные
имена (в отличие от этого примера!). Открывающие и закрывающие фигурные скобки
располагаются в отдельных строках и выстраиваются друг под другом. */
void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
{
/* Объявления переменных не имеют отступов. */
uint8_t ucByte;
/* Код располагается с отступом. Фигурные скобки находятся на отдельных
строках и выстраиваются друг под другом. */
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
/* Снова отступ. */
}
}
/* Конструкции 'for', 'do', 'while' и 'if' форматируются аналогичным
способом. Перед открывающей скобкой пробела нет. После открывающей скобки
пробел есть. Перед закрывающей скобкой пробел есть. Есть пробел после
каждой запятой (если они есть). Есть пробелы перед и после каждого оператора.
Нельзя полагаться на приоритет оператора - круглые скобки всегда используются,
чтобы сделать приоритет оператора явным. "Магические" числа, отличные от нуля,
всегда заменяются константой или макроопределением константы (#define).
Открывающие и закрывающие фигурные скобки располагаются в отдельных строках. */
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
}
while( ucByte < fileBUFFER_LENGTH )
{
}
/* Не должно быть никакой зависимости от приоритета оператора - каждое
условие в решении с несколькими условиями должно быть однозначно заключено
в скобки, как и все подвыражения. */
if( ( ucByte < fileBUFFER_LENGTH ) && ( ucByte != 0U ) )
{
/* Пример отсутствия влияния приоритета операторов! */
ulResult = ( ( ulValue1 + ulValue2 ) - ulValue3 ) * ulValue4;
}
/* Условная компиляция выкладывается и отступается как и любой другой код. */
#if( configUSE_TRACE_FACILITY == 1 )
{
/* Счётчик добавляется в TCB только для трассировки. */
pxNewTCB->uxTCBNumber = uxTaskNumber;
}
#endif
/* Пробел ставится после открывающей квадратной скобки и перед закрывающей
квадратной скобкой. */
ucBuffer[ 0 ] = 0U;
ucBuffer[ fileBUFFER_LENGTH - 1U ] = 0U;