FreeRTOS学习——接口宏portmacro.h,仅用于记录自己阅读与学习源码
FreeRTOS Kernel V10.5.1
portmacro版本:GCC/ARM_CM7
portmacro.h是什么
portmacro.h头文件,用于定义与特定硬件平台相关的数据类型和常量。
在移植过程中,portmacro.h文件是必须修改的文件之一。它主要负责为内核提供平台相关的定义,以确保在不同的硬件平台上正确运行FreeRTOS。
在portmacro.h中,通常会定义几个重要的类型,例如任务句柄、栈指针、上下文保存和恢复函数等。这些定义对于编写能够正确调度和管理任务的代码至关重要。此外,该文件还会包含关于中断处理的配置,以确保任务可以在中断触发时正确运行和切换。
移植FreeRTOS时,如果C编译器允许在C代码中插入汇编,并且支持用C语言编写的中断处理函数,那么port.asm文件的内容可以合并到port.c文件中,这样可以减少文件数量,简化项目结构
参考文章:
FreeRTOS 移植要点(1)
portmacro.h内容
我移植的是
GCC
ARM_CM7内核的接口
Type definitions
/* Type definitions. */
#define portCHAR char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT short
#define portSTACK_TYPE uint32_t
#define portBASE_TYPE long
typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
#if ( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
/* 32位架构上的32-bit tick type
* 所以读取它不需要临界区进行保护. */
#define portTICK_TYPE_IS_ATOMIC 1
#endif
当configUSE_16_BIT_TICKS为1时(在FreeRTOSConfig.h中配置),TickType_t就是uint32_t否则TickType_t为uint16_t。
portTICK_TYPE_IS_ATOMIC
默认configUSE_16_BIT_TICKS为0,也就是使用32位的。这时会配置另一个宏portTICK_TYPE_IS_ATOMIC为1。翻译一下就是TICK_TYPE是原子性的,此宏用于标识时间片(tick)类型是否为原子类型的宏定义
首先什么是原子操作?
原子操作的定义:原子操作是指在多线程或多任务环境中,**一个操作要么完全执行成功,要么完全不执行,不会在中间被挂起或打断。**对于时间片操作,若标识为原子,在此期间所有对时间片的修改和读取都是一个不可分割的动作。
当portTICK_TYPE_IS_ATOMIC为1时,就是32位架构上的32-bit tick type,所以读取它不需要临界区进行保护
当宏定义为原子时,它可以保证当前的时间片计数在操作期间不会被修改,避免了由于并发访问导致的错误。
如果没有原子性保证,在进行时间片相关的操作(如增加或减少时间片计数)时可能会有其他任务进行切换或者中断服务例程被激活。这种情况下会对时间片的值产生竞争条件,导致数据不一致或错误的调度结果。
通过确保时间片相关的操作是原子的,可以提高系统的稳定性和可靠性,确保调度行为按照预期进行,避免由于任务切换产生的不可预测行为。
我们来看看如果portTICK_TYPE_IS_ATOMIC为0,会有如下定义,即增加了TICK_TYPE临界区宏,对时间片操作时,要进行保护
#if ( portTICK_TYPE_IS_ATOMIC == 0 )
/* tick type非原子读取
* portTICK_TYPE_IS_ATOMIC == 0.
* 当tick count返回时映射临界区代码到标准临界区宏*/
#define portTICK_TYPE_ENTER_CRITICAL() portENTER_CRITICAL()
#define portTICK_TYPE_EXIT_CRITICAL() portEXIT_CRITICAL()
#define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( ( x ) )
#else
Architecture specifics
/* Architecture specifics. */
#define portSTACK_GROWTH ( -1 )
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT 8
#define portDONT_DISCARD __attribute__( ( used ) )
portSTACK_GROWTH
portSTACK_GROWTH为 -1 ,这个定义表示堆栈是向下增长的,即从高地址向低地址增长。
portTICK_PERIOD_MS
其中configTICK_RATE_HZ 在FreeRTOSConfig.h中配置,默认为1000Hz,所以TICK周期也就是1ms。
portBYTE_ALIGNMENT
在 FreeRTOS 中使用 portBYTE_ALIGNMENT 进行内存对齐,可以确保任务栈、队列和其他数据结构的正确对齐。这通常与内存分配器的实现配合使用。例如,在使用动态内存分配时,如 pvPortMalloc,内存对齐可以保证分配到的内存块是按照指定的对齐方式返回。
如果 portBYTE_ALIGNMENT 被定义为 8,那么所有与 FreeRTOS 相关的数据结构和任务栈都会在内存中位于 8 字节的边界上。
portDONT_DISCARD
#define portDONT_DISCARD attribute((used)) 是一个用于防止编译器丢弃某些未使用变量或函数的宏定义。
在编译过程中,编译器会进行一些优化,包括去除那些被认为没有使用的代码(如未调用的函数或未使用的变量)。使用 attribute((used)) 可以告诉编译器,即使该变量或函数在代码中未被直接引用,也要保留它。
Scheduler utilities
/* Scheduler utilities. */
#define portYIELD() \
{ \
/* 挂起 PendSV来请求上下文切换. */ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
\
/* Barriers are normally not required but do ensure the code is completely \
* within the specified behaviour for the architecture. */ \
__asm volatile ( "dsb" ::: "memory" ); \
__asm volatile ( "isb" ); \
}
#define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) )
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
#define portEND_SWITCHING_ISR( xSwitchRequired ) do { if( xSwitchRequired != pdFALSE ) portYIELD(); } while( 0 )
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
taskYIELD就是portYIELD用来发起调度,切换任务
portYIELD做的就是挂起 PendSV来请求上下文切换
__asm volatile ( “dsb” ::: “memory” ); 和 __asm volatile ( “isb” );
这是使用内嵌汇编实现的一些特定指令,通常在嵌入式系统或对实时性能有严格要求的程序中使用。__asm volatile ( “dsb” ::: “memory” ); 和 __asm volatile ( “isb” ); 是 ARM 体系结构中用于内存屏障操作的指令。它们的作用如下:
dsb(Data Synchronization Barrier):是一个数据同步屏障指令。
它的作用是确保在它之前的所有内存访问(读取或写入)在执行后续的指令之前完成。
通常用于确保数据的一致性,尤其是在多处理器系统中,确保一个处理器对内存的写入在另一个处理器读取之前完成。
isb(Instruction Synchronization Barrier):是一个指令同步屏障指令。
它的作用是使得所有之前的指令都必须完成执行,之后再执行后续的指令。
一般用于整个流水线刷新,以确保新加载的指令在它之前的指令已经完成之后才能执行。
volatile 关键字
使用 volatile 关键字表明这些汇编语句是“易变”的,这意味着编译器不会对它们进行优化,即编译器每次运行时都会实际执行这些汇编代码,而不是在某些情况下跳过它。
::: “memory” 语法
这个语法中的 ::: “memory” 表示告诉编译器在执行这些指令时,内存的状态可能会发生变化。这提示编译器在这段代码之前和之后都不要做一些可能依赖于内存状态的优化。
portNVIC_INT_CTRL_REG
关于portNVIC_INT_CTRL_REG,首先找到cortex m7内核手册。内容如下:
将1写入此位是将PendSV异常挂起的唯一方法