FreeRTOS编码标准、测试和风格指南
本帖最后由 sunsili 于 2021-11-28 20:34 编辑FreeRTOS编码标准、测试和风格指南
编码标准 / MISRA 合规性核心 FreeRTOS 源文件(那些对所有端口通用,但不是端口层的)符合MISRA编码标准指南。使用pc-lint和链接的 lint 配置文件检查合规性。由于该标准有很多页,并且可以从 MISRA 以很少的费用购买,因此我们没有在此处复制所有规则。下面列出了与 MISRA 标准的偏差:
[*]两个 API 函数有多个退出点。出于关键效率的原因,在这两种情况下允许偏差。
[*]创建任务时,源代码通过操作内存地址来定位分配给创建任务的堆栈的起始地址和结束地址。该代码必须适用于移植 FreeRTOS 的所有架构 - 其中包括具有 8、16、20、24 和 32 位总线的架构。这不可避免地需要一些指针运算。使用指针算术时,会以编程方式检查算术结果的正确性。
[*]默认情况下,跟踪宏为空,因此不生成任何代码。因此,MISRA 合规性检查是使用虚拟宏定义执行的。
[*]MISRA 规则在被认为合适的情况下逐行关闭(即,与小心不遵守规则相比,遵守规则被视为为深度嵌入式系统创建不太合适的代码)。每个这样的事件都伴随着使用特殊的 pc-lint MISRA 注释标记语法的理由。
FreeRTOS 使用许多不同的编译器构建,其中一些编译器比其他编译器更先进。因此,FreeRTOS 不使用由 C99 标准或自 C99 标准引入 C 语言的任何功能或语法。对此的一个例外是使用stdint.h头文件。该FreeRTOS的/来源/包括目录包含一个名为stdint.readme可以改名stdint.h提供必要建立FreeRTOS的最低stdint类型定义-要你的编译器不提供其自身。
测试本节介绍对通用代码(位于FreeRTOS/Source目录中的代码,由所有 FreeRTOS 内核端口构建)执行的测试,以及对可移植层代码(位于FreeRTOS/Source子目录中的代码)执行的测试/便携式目录)。
[*]通用代码标准演示/测试文件试图提供“分支”测试覆盖率(在大多数情况下,这实际上实现了“条件”覆盖率,因为内核的编码风格专门为此目的而特意保持条件简单),从而测试确保“真”和“假” '通过每个决定的路径被执行。如果 'else' 路径为空,则使用 GCOV 通过将 mtCOVERAGE_TEST_MARKER() 宏定义为每个 'if()' 条件的 'else' 路径中的 NOP(无操作)指令来测量“分支”覆盖率。mtCOVERAGE_TEST_MARKER() 仅在测量测试覆盖率时定义 - 通常它是一个不生成任何代码的空宏。
[*]端口层端口层代码使用“reg test”任务进行测试,对于支持中断嵌套的端口,使用“中断队列”任务进行测试。
“reg test”任务创建多个(通常为两个)任务,首先用已知值填充所有 CPU 寄存器,然后在其他测试连续执行时不断检查每个寄存器是否保持其预期的已知值(浸泡测试)。每个 reg 测试任务都使用唯一的值。
“中断队列”任务对嵌套至少三个深度的不同优先级的中断执行测试。宏用于将人为延迟插入代码中的相关点,以确保实现所需的测试覆盖率。
值得注意的是,这些测试的彻底性导致了多次在芯片中发现错误。命名约定RTOS 内核和演示应用程序源代码使用以下约定:
[*]变量
[*]uint32_t类型的变量以ul为前缀,其中“u”表示“无符号”,“l”表示“long”。
[*]uint16_t类型的变量以us为前缀,其中“u”表示“unsigned”,“s”表示“short”。
[*]uint8_t类型的变量以uc为前缀,其中“u”表示“unsigned”,“c”表示“char”。
[*]非 stdint 类型的变量以x为前缀。示例包括 BaseType_t 和 TickType_t,它们是可移植层定义的 typedef,分别用于架构的自然或最有效类型和用于保存 RTOS 滴答计数的类型。
[*]非 stdint 类型的无符号变量有一个额外的前缀u。例如 UBaseType_t(无符号 BaseType_t)类型的变量以ux为前缀。
[*]size_t类型的变量也以x为前缀。>
[*]枚举变量以e为前缀
[*]指针有一个额外的前缀p,例如指向 uint16_t 的指针将有前缀pus。
[*]根据 MISRA 指南,不合格的标准字符类型只允许包含 ASCII 字符并以c为前缀。
[*]根据 MISRA 指南,char *类型的变量只允许保存指向 ASCII 字符串的指针,并以pc为前缀。
[*]职能
[*]文件范围静态(私有)函数以prv为前缀。
[*]根据为变量定义的约定,API 函数以其返回类型为前缀,并为void添加前缀v。
[*]API 函数名称以定义它们的文件的名称开头。例如 v Task Delete 在 tasks.c 中定义,并且具有 void 返回类型。
[*]宏
[*]宏以定义它们的文件为前缀。前缀是小写。例如,配置USE_PREEMPTION 在 FreeRTOSConfig.h 中定义。
[*]除了前缀之外,宏全部大写,并使用下划线分隔单词。
数据类型仅使用 stdint.h 类型和 RTOS 自己的 typedef,以下情况除外:
[*]字符根据 MISRA 指南,允许使用不合格的 char 类型,但仅当它们用于保存 ASCII 字符时。
[*]字符 *根据 MISRA 指南,允许使用非限定字符指针,但仅当它们用于指向 ASCII 字符串时。当使用期望 char * 参数的标准库函数时,这消除了抑制良性编译器警告的需要,特别是考虑到一些编译器默认未限定的 char 类型是有符号的,而其他编译器默认非限定的 char 类型是无符号的。
每个端口定义了四种类型。这些是:
[*]TickType_t如果 configUSE_16_BIT_TICKS 设置为非零 (true),则 TickType_t 被定义为无符号 16 位类型。如果 configUSE_16_BIT_TICKS 设置为零 (false),则 TickType_t 被定义为无符号 32 位类型。有关完整信息,请参阅API 文档的自定义部分。
32 位架构应始终将 configUSE_16_BIT_TICKS 设置为 0。
[*]基本类型_t这被定义为最有效、最自然的架构类型。例如,在 32 位架构上 BaseType_t 将被定义为 32 位类型。在 16 位架构上 BaseType_t 将被定义为 16 位类型。如果 BaseType_t 被定义为 char,则必须特别注意确保使用有符号字符作为函数返回值,这些值可以为负数以指示错误。
[*]UBaseType_t这是一个未签名的 BaseType_t。
[*]堆栈类型_t定义为体系结构用于存储在堆栈上的项目的类型。通常,这在 16 位架构上是 16 位类型,在 32 位架构上是 32 位类型,尽管有一些例外。由 FreeRTOS 内部使用。
时尚指南
[*]缩进四个空格字符用于缩进。
[*]注释注释永远不会通过第 80 列,除非它们跟随并描述一个参数。
不使用 C++ 风格的双斜线 (//) 注释。
[*]布局
本帖最后由 sunsili 于 2021-11-28 20:37 编辑
FreeRTOS 源代码布局旨在尽可能易于查看和阅读。下面的代码片段首先显示文件布局,然后显示 C 代码格式。
/* 库包含首先... */
#include <stdlib.h>
/* ...紧跟 FreeRTOS 包含... */
#include "FreeRTOS.h"
/* ...紧跟其他包含。*/
#include "HardwareSpecifics.h"
/* #defines 接下来,尽可能用括号括起来。*/
#define A_DEFINITION ( 1 )
/*
* 接下来出现静态(文件私有)函数原型,带有注释
* 这种风格 - 每行以“*”开头。
*/
静态无效 prvAFunction( uint32_t ulParameter );
/* 文件作用域变量是函数定义之前的最后一件事。
变量注释采用这种风格(每行不以“*”开头)。
*/
/* 下面的分隔符用在每个函数的右括号之后,在下一个函数定义开始之前跟一个空行。*/
/*---------------------------------------------- -------------*/
void vAFunction( void )
{
/* 函数定义在这里 - 注意 大括号后的分隔符。*/
}
/*-------------------------------------------- --------------*/
static UBaseType_t prvNextFunction( void )
{
/* 函数定义在这里。*/
}
/*-------------------------------------------- --------------*/</p><p>/* 函数名总是写在一行上,包括返回
类型。与往常一样,左括号前没有空格。
左括号后有一个空格。右括号前有一个空格。每个逗号后面都有一个空格。参数被赋予
详细的描述性名称(不像这个例子!)。
左花括号和右花括号出现在各自的行上,并排在彼此的下方。*/
void vAnExampleFunction( long lParameter1, unsigned short usParameter2 )
{
/* 变量声明不缩进。*/
uint8_t ucByte;
/* 代码缩进。花括号总是在自己的线上 ,并排在彼此的下方。*/
for( ucByte = 0U; ucByte < fileBUFFER_LENGTH; ucByte++ )
{
/* 再次缩进。*/
}
}
/* For、while、do 和 if 结构遵循类似的模式。
左括号前没有空格。
左括号后有一个空格。右括号前有一个空格。
每个分号后面都有一个空格(如果有的话)。
每个运算符前后都有空格。不依赖运算符优先级 -
始终使用括号来明确优先级。
除了零之外的幻数总是被替换为常量或#defined 常量。
左花括号和右花括号出现在各自的行上。*/
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 = 0U;
ucBuffer = 0U;
C 结构的格式化
页:
[1]