From c1d101dd41af4c883f6c3222a9295323e2cc51ae Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Fri, 20 Oct 2017 19:03:01 +0800 Subject: [PATCH] freertos/backport and test v9.0.0 functions This commit backports the following features from FreeRTOS v9.0.0 - uxSemaphoreGetCount() - vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime() - xTimerCreateStatic() - xEventGroupCreateStatic() - uxSemaphoreGetCount() Functions backported previously - xTaskCreateStatic() - xQueueCreateStatic() - xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic() - xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic() - pcQueueGetName() - vTaskSetThreadLocalStoragePointer() - pvTaskGetThreadLocalStoragePointer() Unit tests were also written for the functions above (except for pcQueueGetName which is tested in a separate Queue Registry MR). The original tlsp and del cb test case was deleted and integrated into the test cases of this MR. --- components/freertos/Kconfig | 2 +- components/freertos/event_groups.c | 118 ++++++-- .../freertos/include/freertos/FreeRTOS.h | 71 ++++- .../freertos/include/freertos/event_groups.h | 69 ++++- components/freertos/include/freertos/semphr.h | 12 + components/freertos/include/freertos/timers.h | 202 +++++++++++++- components/freertos/queue.c | 2 +- .../test/test_freertos_backported_functions.c | 253 ++++++++++++++++++ components/freertos/test/test_tls_deletecb.c | 58 ---- components/freertos/timers.c | 194 ++++++++++++-- docs/api-guides/freertos-smp.rst | 83 +++++- tools/unit-test-app/sdkconfig.defaults | 1 + 12 files changed, 932 insertions(+), 133 deletions(-) create mode 100644 components/freertos/test/test_freertos_backported_functions.c delete mode 100644 components/freertos/test/test_tls_deletecb.c diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index ceb184180d..6e56174db9 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -209,7 +209,7 @@ config SUPPORT_STATIC_ALLOCATION memory being allocated dynamically: - Tasks - - Software Timers + - Software Timers (Daemon task is still dynamic. See documentation) - Queues - Event Groups - Binary Semaphores diff --git a/components/freertos/event_groups.c b/components/freertos/event_groups.c index 69903817f4..3a06e23740 100644 --- a/components/freertos/event_groups.c +++ b/components/freertos/event_groups.c @@ -119,7 +119,11 @@ typedef struct xEventGroupDefinition UBaseType_t uxEventGroupNumber; #endif - portMUX_TYPE eventGroupMux; + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */ + #endif + + portMUX_TYPE eventGroupMux; //Mutex required due to SMP } EventGroup_t; @@ -137,26 +141,83 @@ static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, co /*-----------------------------------------------------------*/ -EventGroupHandle_t xEventGroupCreate( void ) -{ -EventGroup_t *pxEventBits; +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) - pxEventBits = pvPortMalloc( sizeof( EventGroup_t ) ); - if( pxEventBits != NULL ) + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) { - pxEventBits->uxEventBits = 0; - vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); - traceEVENT_GROUP_CREATE( pxEventBits ); - } - else - { - traceEVENT_GROUP_CREATE_FAILED(); + EventGroup_t *pxEventBits; + + /* A StaticEventGroup_t object must be provided. */ + configASSERT( pxEventGroupBuffer ); + + /* The user has provided a statically allocated event group - use it. */ + pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 EventGroup_t and StaticEventGroup_t are guaranteed to have the same size and alignment requirement - checked by configASSERT(). */ + + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Both static and dynamic allocation can be used, so note that + this event group was created statically in case the event group + is later deleted. */ + pxEventBits->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + vPortCPUInitializeMutex(&pxEventBits->eventGroupMux); + + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + traceEVENT_GROUP_CREATE_FAILED(); + } + + return ( EventGroupHandle_t ) pxEventBits; } - vPortCPUInitializeMutex(&pxEventBits->eventGroupMux); +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ - return ( EventGroupHandle_t ) pxEventBits; -} +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + EventGroupHandle_t xEventGroupCreate( void ) + { + EventGroup_t *pxEventBits; + + /* Allocate the event group. */ + pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); + + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Both static and dynamic allocation can be used, so note this + event group was allocated statically in case the event group is + later deleted. */ + pxEventBits->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + vPortCPUInitializeMutex(&pxEventBits->eventGroupMux); + + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + traceEVENT_GROUP_CREATE_FAILED(); + } + + return ( EventGroupHandle_t ) pxEventBits; + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ /*-----------------------------------------------------------*/ EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) @@ -600,8 +661,29 @@ void vEventGroupDelete( EventGroupHandle_t xEventGroup ) ( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); } - taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); - vPortFree( pxEventBits ); + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The event group can only have been allocated dynamically - free + it again. */ + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); + vPortFree( pxEventBits ); + } + #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The event group could have been allocated statically or + dynamically, so check before attempting to free the memory. */ + if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); //Exit mux of event group before deleting it + vPortFree( pxEventBits ); + } + else + { + taskEXIT_CRITICAL( &pxEventBits->eventGroupMux ); + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ } ( void ) xTaskResumeAll(); } diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 50ceae18d8..1bc931757c 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -974,18 +974,73 @@ typedef struct xSTATIC_QUEUE uint8_t ucDummy9; #endif - struct { - volatile uint32_t ucDummy10; - uint32_t ucDummy11; - #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG - void *pvDummy8; - UBaseType_t uxDummy12; - #endif - } sDummy1; + portMUX_TYPE muxDummy; //Mutex required due to SMP } StaticQueue_t; typedef StaticQueue_t StaticSemaphore_t; +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the event group structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create an event group then the size of the event group object needs to be + * know. The StaticEventGroup_t structure below is provided for this purpose. + * Its sizes and alignment requirements are guaranteed to match those of the + * genuine structure, no matter which architecture is being used, and no matter + * how the values in FreeRTOSConfig.h are set. Its contents are somewhat + * obfuscated in the hope users will recognise that it would be unwise to make + * direct use of the structure members. + */ +typedef struct xSTATIC_EVENT_GROUP +{ + TickType_t xDummy1; + StaticList_t xDummy2; + + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy3; + #endif + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy4; + #endif + + portMUX_TYPE muxDummy; //Mutex required due to SMP + +} StaticEventGroup_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the software timer structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create a software timer then the size of the queue object needs to be know. + * The StaticTimer_t structure below is provided for this purpose. Its sizes + * and alignment requirements are guaranteed to match those of the genuine + * structure, no matter which architecture is being used, and no matter how the + * values in FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in + * the hope users will recognise that it would be unwise to make direct use of + * the structure members. + */ +typedef struct xSTATIC_TIMER +{ + void *pvDummy1; + StaticListItem_t xDummy2; + TickType_t xDummy3; + UBaseType_t uxDummy4; + void *pvDummy5[ 2 ]; + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy6; + #endif + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy7; + #endif + +} StaticTimer_t; + #ifdef __cplusplus } #endif diff --git a/components/freertos/include/freertos/event_groups.h b/components/freertos/include/freertos/event_groups.h index c7d0156abe..a2d70a43f3 100644 --- a/components/freertos/include/freertos/event_groups.h +++ b/components/freertos/include/freertos/event_groups.h @@ -137,7 +137,17 @@ typedef TickType_t EventBits_t; EventGroupHandle_t xEventGroupCreate( void ); * - * Create a new event group. This function cannot be called from an interrupt. + * Create a new event group. + * + * Internally, within the FreeRTOS implementation, event groups use a [small] + * block of memory, in which the event group's structure is stored. If an event + * groups is created using xEventGropuCreate() then the required memory is + * automatically dynamically allocated inside the xEventGroupCreate() function. + * (see http://www.freertos.org/a00111.html). If an event group is created + * using xEventGropuCreateStatic() then the application writer must instead + * provide the memory that will get used by the event group. + * xEventGroupCreateStatic() therefore allows an event group to be created + * without using any dynamic memory allocation. * * Although event groups are not related to ticks, for internal implementation * reasons the number of bits available for use in an event group is dependent @@ -173,7 +183,62 @@ typedef TickType_t EventBits_t; * \defgroup xEventGroupCreate xEventGroupCreate * \ingroup EventGroup */ -EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; +#endif + +/** + * event_groups.h + *
+ EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
+ 
+ * + * Create a new event group. + * + * Internally, within the FreeRTOS implementation, event groups use a [small] + * block of memory, in which the event group's structure is stored. If an event + * groups is created using xEventGropuCreate() then the required memory is + * automatically dynamically allocated inside the xEventGroupCreate() function. + * (see http://www.freertos.org/a00111.html). If an event group is created + * using xEventGropuCreateStatic() then the application writer must instead + * provide the memory that will get used by the event group. + * xEventGroupCreateStatic() therefore allows an event group to be created + * without using any dynamic memory allocation. + * + * Although event groups are not related to ticks, for internal implementation + * reasons the number of bits available for use in an event group is dependent + * on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If + * configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit + * 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has + * 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store + * event bits within an event group. + * + * @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type + * StaticEventGroup_t, which will be then be used to hold the event group's data + * structures, removing the need for the memory to be allocated dynamically. + * + * @return If the event group was created then a handle to the event group is + * returned. If pxEventGroupBuffer was NULL then NULL is returned. + * + * Example usage: +
+	// StaticEventGroup_t is a publicly accessible structure that has the same
+	// size and alignment requirements as the real event group structure.  It is
+	// provided as a mechanism for applications to know the size of the event
+	// group (which is dependent on the architecture and configuration file
+	// settings) without breaking the strict data hiding policy by exposing the
+	// real event group internals.  This StaticEventGroup_t variable is passed
+	// into the xSemaphoreCreateEventGroupStatic() function and is used to store
+	// the event group's data structures
+	StaticEventGroup_t xEventGroupBuffer;
+
+	// Create the event group without dynamically allocating any memory.
+	xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
+   
+ */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION; +#endif /** * event_groups.h diff --git a/components/freertos/include/freertos/semphr.h b/components/freertos/include/freertos/semphr.h index 6343d0190a..049b994e53 100644 --- a/components/freertos/include/freertos/semphr.h +++ b/components/freertos/include/freertos/semphr.h @@ -1179,6 +1179,18 @@ typedef QueueHandle_t SemaphoreHandle_t; */ #define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) +/** + * semphr.h + *
UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );
+ * + * If the semaphore is a counting semaphore then uxSemaphoreGetCount() returns + * its current count value. If the semaphore is a binary semaphore then + * uxSemaphoreGetCount() returns 1 if the semaphore is available, and 0 if the + * semaphore is not available. + * + */ +#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) ) + #endif /* SEMAPHORE_H */ diff --git a/components/freertos/include/freertos/timers.h b/components/freertos/include/freertos/timers.h index f89cd1d70e..8656d069d7 100644 --- a/components/freertos/include/freertos/timers.h +++ b/components/freertos/include/freertos/timers.h @@ -135,9 +135,17 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * void * pvTimerID, * TimerCallbackFunction_t pxCallbackFunction ); * - * Creates a new software timer instance. This allocates the storage required - * by the new timer, initialises the new timers internal state, and returns a - * handle by which the new timer can be referenced. + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * http://www.freertos.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. * * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and @@ -250,14 +258,151 @@ typedef void (*PendedFunction_t)( void *, uint32_t ); * * // Starting the scheduler will start the timers running as they have already * // been set into the active state. - * xTaskStartScheduler(); + * vTaskStartScheduler(); * * // Should not reach here. * for( ;; ); * } * @endverbatim */ -TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreate( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#endif + + /** + * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * UBaseType_t uxAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction, + * StaticTimer_t *pxTimerBuffer ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * http://www.freertos.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @param pxTimerBuffer Must point to a variable of type StaticTimer_t, which + * will be then be used to hold the software timer's data structures, removing + * the need for the memory to be allocated dynamically. + * + * @return If the timer is created then a handle to the created timer is + * returned. If pxTimerBuffer was NULL then NULL is returned. + * + * Example usage: + * @verbatim + * + * // The buffer used to hold the software timer's data structure. + * static StaticTimer_t xTimerBuffer; + * + * // A variable that will be incremented by the software timer's callback + * // function. + * UBaseType_t uxVariableToIncrement = 0; + * + * // A software timer callback function that increments a variable passed to + * // it when the software timer was created. After the 5th increment the + * // callback function stops the software timer. + * static void prvTimerCallback( TimerHandle_t xExpiredTimer ) + * { + * UBaseType_t *puxVariableToIncrement; + * BaseType_t xReturned; + * + * // Obtain the address of the variable to increment from the timer ID. + * puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); + * + * // Increment the variable to show the timer callback has executed. + * ( *puxVariableToIncrement )++; + * + * // If this callback has executed the required number of times, stop the + * // timer. + * if( *puxVariableToIncrement == 5 ) + * { + * // This is called from a timer callback so must not block. + * xTimerStop( xExpiredTimer, staticDONT_BLOCK ); + * } + * } + * + * + * void main( void ) + * { + * // Create the software time. xTimerCreateStatic() has an extra parameter + * // than the normal xTimerCreate() API function. The parameter is a pointer + * // to the StaticTimer_t structure that will hold the software timer + * // structure. If the parameter is passed as NULL then the structure will be + * // allocated dynamically, just as if xTimerCreate() had been called. + * xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS. + * xTimerPeriod, // The period of the timer in ticks. + * pdTRUE, // This is an auto-reload timer. + * ( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function + * prvTimerCallback, // The function to execute when the timer expires. + * &xTimerBuffer ); // The buffer that will hold the software timer structure. + * + * // The scheduler has not started yet so a block time is not used. + * xReturned = xTimerStart( xTimer, 0 ); + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + #endif /* configSUPPORT_STATIC_ALLOCATION */ /** * void *pvTimerGetTimerID( TimerHandle_t xTimer ); @@ -281,6 +426,27 @@ TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTi */ void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +/** + * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); + * + * Sets the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being updated. + * + * @param pvNewID The ID to assign to the timer. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; + /** * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); * @@ -329,6 +495,32 @@ BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; */ TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); +/** + * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); + * + * Returns the period of a timer. + * + * @param xTimer The handle of the timer being queried. + * + * @return The period of the timer in ticks. + */ +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); + * + * Returns the time in ticks at which the timer will expire. If this is less + * than the current tick count then the expiry time has overflowed from the + * current time. + * + * @param xTimer The handle of the timer being queried. + * + * @return If the timer is running then the time in ticks at which the timer + * will next expire is returned. If the timer is not running then the return + * value is undefined. + */ +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + /** * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); * diff --git a/components/freertos/queue.c b/components/freertos/queue.c index d74f37b2a2..c5c02c1a98 100644 --- a/components/freertos/queue.c +++ b/components/freertos/queue.c @@ -165,7 +165,7 @@ typedef struct QueueDefinition uint8_t ucQueueType; #endif - portMUX_TYPE mux; + portMUX_TYPE mux; //Mutex required due to SMP } xQUEUE; diff --git a/components/freertos/test/test_freertos_backported_functions.c b/components/freertos/test/test_freertos_backported_functions.c new file mode 100644 index 0000000000..7aa8247be1 --- /dev/null +++ b/components/freertos/test/test_freertos_backported_functions.c @@ -0,0 +1,253 @@ +/* + * Test features that are backported from version FreeRTOS 9.0.0. + * + * 1) Test backported timer functions + * - xTimerCreateStatic(), vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime() + * 2) Test backported queue/semaphore functions + * - xQueueCreateStatic() + * - xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic(), uxSemaphoreGetCount() + * - xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic() + * 3) Test static allocation of tasks + * - xTaskCreateStaticPinnedToCore() + * 4) Test static allocation of event group + * - xEventGroupCreateStatic() + * 5) Test Thread Local Storage Pointers and Deletion Callbacks + * - vTaskSetThreadLocalStoragePointerAndDelCallback() + * - pvTaskGetThreadLocalStoragePointer() + * + * Note: The *pcQueueGetName() function is also backported, but is not tested in + * the following test cases (see Queue Registry test cases instead) + * For more details please refer the the ESP-IDF FreeRTOS changes documentation + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" +#include "unity.h" + +/* ---------------------Test 1: Backported Timer functions----------------------- + * Test xTimerCreateStatic(), vTimerSetTimerId(), xTimerGetPeriod(), xTimerGetExpiryTime() + * + * This test creates a one-shot static timer, sets/checks the timer's id and period. Then ensures + * the timer cb is executed in a timely fashion. + */ +#define TMR_PERIOD_TICKS 10 +#define TIMER_ID 0xFF +#define TICK_DELTA 5 + +static StaticTimer_t timer_buffer; +static TickType_t tmr_ideal_exp; + +static void tmr_cb(TimerHandle_t xtimer) +{ + //Check cb is called in timely fashion + TEST_ASSERT_UINT32_WITHIN(TICK_DELTA, tmr_ideal_exp, xTaskGetTickCount()); +} + +//No need for smp test as Timer Task always runs on core 0 +TEST_CASE("Test FreeRTOS backported timer functions", "[freertos]") +{ + //Create one shot static timer with period TMR_PERIOD_TICKS + TimerHandle_t tmr_handle = xTimerCreateStatic("static_tmr", TMR_PERIOD_TICKS, pdFALSE, NULL, tmr_cb, &timer_buffer); + TEST_ASSERT_EQUAL(TMR_PERIOD_TICKS, xTimerGetPeriod(tmr_handle)); //Test xTimerGetPeriod() + + vTimerSetTimerID(tmr_handle, (void *)TIMER_ID); + TEST_ASSERT_EQUAL(TIMER_ID, (uint32_t)pvTimerGetTimerID(tmr_handle)); //Test vTimerSetTimerID() + + TEST_ASSERT_EQUAL(pdTRUE, xTimerStart(tmr_handle, 1)); //Start Timer + tmr_ideal_exp = xTaskGetTickCount() + TMR_PERIOD_TICKS; //Calculate ideal expiration time + vTaskDelay(2); //Need to yield to allow daemon task to process start command, or else expiration time will be NULL + + TEST_ASSERT_UINT32_WITHIN(TICK_DELTA, tmr_ideal_exp, xTimerGetExpiryTime(tmr_handle)); //Test xTimerGetExpiryTime() + + vTaskDelay(2*TMR_PERIOD_TICKS); //Delay until one shot timer has triggered + TEST_ASSERT_EQUAL(pdPASS, xTimerDelete(tmr_handle, portMAX_DELAY)); //Clean up + +} + +/* ---------------Test backported queue/semaphore functions------------------- + * xQueueCreateStatic() + * xSemaphoreCreateBinaryStatic(), xSemaphoreCreateCountingStatic() + * xSemaphoreCreateMutexStatic(), xSemaphoreCreateRecursiveMutexStatic() + * uxSemaphoreGetCount() is also tested on the static counting semaphore + * + * This test creates various static queue/semphrs listed above and tests them by + * doing a simple send/give and rec/take. + */ + +#define ITEM_SIZE 3 +#define NO_OF_ITEMS 3 +#define DELAY_TICKS 2 + +static StaticQueue_t queue_buffer; //Queues, Semaphores, and Mutex use the same queue structure +static uint8_t queue_storage_area[(ITEM_SIZE*NO_OF_ITEMS)]; //Queue storage provided in separate buffer to queue struct + +TEST_CASE("Test FreeRTOS backported Queue and Semphr functions", "[freertos]") +{ + //Test static queue + uint8_t queue_item_to_send[ITEM_SIZE]; + uint8_t queue_item_received[ITEM_SIZE]; + for(int i = 0; i < ITEM_SIZE; i++){ + queue_item_to_send[i] = (0xF << i); + } + QueueHandle_t handle = xQueueCreateStatic(NO_OF_ITEMS, ITEM_SIZE,(uint8_t*) &queue_storage_area, &queue_buffer); + TEST_ASSERT_EQUAL(pdTRUE, xQueueSendToBack(handle, &queue_item_to_send, DELAY_TICKS)); + vTaskDelay(1); + TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(handle, queue_item_received, DELAY_TICKS)); + vTaskDelay(1); + for(int i = 0; i < ITEM_SIZE; i++){ + TEST_ASSERT_EQUAL(queue_item_to_send[i], queue_item_received[i]); //Check received contents are correct + } + vQueueDelete(handle); //Technically not needed as deleting static queue/semphr doesn't clear static memory + + //Test static binary semaphore + handle = xSemaphoreCreateBinaryStatic(&queue_buffer); //Queue and Semphr handles are the same + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle)); + vTaskDelay(1); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS)); + vTaskDelay(1); + vSemaphoreDelete(handle); + + //Test static counting semaphore and uxSemaphoreGetCount() + handle = xSemaphoreCreateCountingStatic(NO_OF_ITEMS, 0, &queue_buffer); + for(int i = 0; i < NO_OF_ITEMS; i++){ + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle)); + } + vTaskDelay(1); + TEST_ASSERT_EQUAL(NO_OF_ITEMS, uxSemaphoreGetCount(handle)); //Test uxSemaphoreGetCount() + for(int i = 0; i < NO_OF_ITEMS; i++){ + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS)); + } + vTaskDelay(1); + TEST_ASSERT_EQUAL(0, uxSemaphoreGetCount(handle)); + vSemaphoreDelete(handle); + + //Test static mutex + handle = xSemaphoreCreateMutexStatic(&queue_buffer); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(handle, DELAY_TICKS)); + vTaskDelay(1); + TEST_ASSERT_EQUAL_PTR((void *)xTaskGetCurrentTaskHandle(), xSemaphoreGetMutexHolder(handle)); //Current task should now hold mutex + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(handle)); + vTaskDelay(1); + TEST_ASSERT_EQUAL_PTR(NULL, xSemaphoreGetMutexHolder(handle)); //Mutex should have been released + vSemaphoreDelete(handle); + + //Test static mutex recursive + handle = xSemaphoreCreateRecursiveMutexStatic(&queue_buffer); + for(int i = 0; i < NO_OF_ITEMS; i++){ + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTakeRecursive(handle, DELAY_TICKS)); + } + vTaskDelay(1); + TEST_ASSERT_EQUAL_PTR((void *)xTaskGetCurrentTaskHandle(), xSemaphoreGetMutexHolder(handle)); //Current task should hold mutex + for(int i = 0; i < NO_OF_ITEMS; i++){ + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGiveRecursive(handle)); + } + vTaskDelay(1); + TEST_ASSERT_EQUAL_PTR(NULL, xSemaphoreGetMutexHolder(handle)); //Mutex should have been released + vSemaphoreDelete(handle); + +} + +/* -----------------Test backported static task allocation ------------------- + * Test xTaskCreateStaticPinnedToCore() but creating static task on each core + * and checking the task cb has run successfully. + */ + +#define STACK_SIZE 2048 //Task stack size + +static StackType_t task_stack[STACK_SIZE]; //Static buffer for task stack +static StaticTask_t task_buffer; //Static buffer for TCB +static bool has_run[portNUM_PROCESSORS]; + +static void task(void *arg) +{ + has_run[xPortGetCoreID()] = true; //Signify static task cb has run + vTaskDelete(NULL); +} + +TEST_CASE("Test FreeRTOS static task allocation", "[freertos]") +{ + for(int core = 0; core < portNUM_PROCESSORS; core++){ + has_run[core] = false; //Clear has_run flag + TaskHandle_t handle = xTaskCreateStaticPinnedToCore(task, "static task", STACK_SIZE, NULL, + UNITY_FREERTOS_PRIORITY + 1, (StackType_t *)&task_stack, + (StaticTask_t *)&task_buffer, core); + vTaskDelay(5); //Allow for static task to run, delete, and idle to clean up + TEST_ASSERT_NOT_EQUAL(NULL, handle); //Check static task was successfully allocated + TEST_ASSERT_TRUE(has_run[core]) //Check static task has run + } +} + +/* ------------- Test backported static event group allocation ------------------- + * Test xEventGroupCreateStatic() but creating static event group then waiting + * for an event. + */ + +#define WAIT_BITS 0x01 //Wait for first bit + +static StaticEventGroup_t event_group; +static EventGroupHandle_t eg_handle; + +TEST_CASE("Test FreeRTOS backported eventgroup functions", "[freertos]") +{ + eg_handle = xEventGroupCreateStatic((StaticEventGroup_t *)&event_group); + xEventGroupSetBits(eg_handle, WAIT_BITS); + TEST_ASSERT_EQUAL(WAIT_BITS, xEventGroupWaitBits(eg_handle, WAIT_BITS, pdTRUE, pdTRUE, portMAX_DELAY)); + //Cleanup static event + vEventGroupDelete(eg_handle); +} + +/* --------Test backported thread local storage pointer and deletion cb feature---------- + * vTaskSetThreadLocalStoragePointerAndDelCallback() + * pvTaskGetThreadLocalStoragePointer(), + * + * This test creates a task and set's the task's TLSPs. The task is then deleted + * which should trigger the deletion cb. + */ + +#define NO_OF_TLSP configNUM_THREAD_LOCAL_STORAGE_POINTERS +#define TLSP_SET_BASE 0x0F //0b1111 to be bit shifted by index +#define TLSP_DEL_BASE 0x05 //0b0101 to be bit shifted by index + +//The variables pointed to by Thread Local Storage Pointer +static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0}; + +static void del_cb(int index, void *ptr) +{ + *((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value +} + +static void task_cb() +{ + int core = xPortGetCoreID(); + for(int i = 0; i < NO_OF_TLSP; i++){ + task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number + vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element + } + + for(int i = 0; i < NO_OF_TLSP; i++){ + uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i); + TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value + } + + vTaskDelete(NULL); //Delete Task to Trigger TSLP deletion callback +} + +TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]") +{ + //Create Task + for(int core = 0; core < portNUM_PROCESSORS; core++){ + xTaskCreatePinnedToCore(task_cb, "task", 1024, NULL, UNITY_FREERTOS_PRIORITY+1, NULL, core); + } + vTaskDelay(10); //Delay long enough for tasks to run to completion + + for(int core = 0; core < portNUM_PROCESSORS; core++){ + for(int i = 0; i < NO_OF_TLSP; i++){ + TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value + } + } +} + diff --git a/components/freertos/test/test_tls_deletecb.c b/components/freertos/test/test_tls_deletecb.c deleted file mode 100644 index 8628f42ec9..0000000000 --- a/components/freertos/test/test_tls_deletecb.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include "rom/ets_sys.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "freertos/xtensa_api.h" -#include "unity.h" -#include "soc/uart_reg.h" -#include "soc/dport_reg.h" -#include "soc/io_mux_reg.h" - - - -static void tskdelcb(int no, void *arg) -{ - ets_printf("Delete callback: %d = %p!\n", no, arg); -} - - -static void tska(void *pvParameters) -{ - vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), 0, (void *)0xAAAAAAAA, tskdelcb); - while (1) { - vTaskDelay(10000000 / portTICK_PERIOD_MS); - } -} - -static void tskb(void *pvParameters) -{ - vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), 0, (void *)0xBBBBBBBB, tskdelcb); - vTaskDelay(2000 / portTICK_PERIOD_MS); - TaskHandle_t a = (TaskHandle_t)pvParameters; - printf("Killing task A\n"); - vTaskDelete(a); - while (1) { - vTaskDelay(10000000 / portTICK_PERIOD_MS); - } -} - - -// TODO: split this thing into separate orthogonal tests -TEST_CASE("Freertos TLS delete cb", "[freertos]") -{ - TaskHandle_t a, b; - - xTaskCreatePinnedToCore(tska , "tska" , 2048, NULL, 3, &a, 0); - xTaskCreatePinnedToCore(tskb , "tska" , 2048, a, 3, &b, 0); - - // Let stuff run for 20s - vTaskDelay(5000 / portTICK_PERIOD_MS); - printf("Killing task B\n"); - //Shut down b - vTaskDelete(b); -} - diff --git a/components/freertos/timers.c b/components/freertos/timers.c index 2c66f072bf..6427a215e2 100644 --- a/components/freertos/timers.c +++ b/components/freertos/timers.c @@ -113,6 +113,10 @@ typedef struct tmrTimerControl #if( configUSE_TRACE_FACILITY == 1 ) UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ #endif + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */ + #endif } xTIMER; /* The old xTIMER name is maintained above then typedefed to the new Timer_t @@ -239,6 +243,16 @@ static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIV */ static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; +/* + * Called after a Timer_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTimer( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + Timer_t *pxNewTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /*-----------------------------------------------------------*/ BaseType_t xTimerCreateTimerTask( void ) @@ -257,7 +271,10 @@ BaseType_t xReturn = pdFAIL; if( xTimerQueue != NULL ) { - #if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + /* Although static allocation has been backported from FreeRTOS v9.0.0, + the timer task is still allocated dynamically. The actual timers + however can be allocated statically.*/ + #if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) { /* Create the timer task, storing its handle in xTimerTaskHandle so it can be returned by the xTimerGetTimerDaemonTaskHandle() function. */ @@ -280,44 +297,108 @@ BaseType_t xReturn = pdFAIL; } /*-----------------------------------------------------------*/ -TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ -{ -Timer_t *pxNewTimer; +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - /* Allocate the timer structure. */ - if( xTimerPeriodInTicks == ( TickType_t ) 0U ) - { - pxNewTimer = NULL; - } - else + TimerHandle_t xTimerCreate( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { + Timer_t *pxNewTimer; + pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); + if( pxNewTimer != NULL ) { - /* Ensure the infrastructure used by the timer service task has been - created/initialised. */ - prvCheckForValidListAndQueue(); + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); - /* Initialise the timer structure members using the function parameters. */ - pxNewTimer->pcTimerName = pcTimerName; - pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; - pxNewTimer->uxAutoReload = uxAutoReload; - pxNewTimer->pvTimerID = pvTimerID; - pxNewTimer->pxCallbackFunction = pxCallbackFunction; - vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Timers can be created statically or dynamically, so note this + timer was created dynamically in case the timer is later + deleted. */ + pxNewTimer->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + } - traceTIMER_CREATE( pxNewTimer ); - } - else - { - traceTIMER_CREATE_FAILED(); - } + return pxNewTimer; } +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + Timer_t *pxNewTimer; + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticTimer_t equals the size of the real timer + structures. */ + volatile size_t xSize = sizeof( StaticTimer_t ); + configASSERT( xSize == sizeof( Timer_t ) ); + } + #endif /* configASSERT_DEFINED */ + + /* A pointer to a StaticTimer_t structure MUST be provided, use it. */ + configASSERT( pxTimerBuffer ); + pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + + if( pxNewTimer != NULL ) + { + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Timers can be created statically or dynamically so note this + timer was created statically in case it is later deleted. */ + pxNewTimer->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + } + + return pxNewTimer; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTimer( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + Timer_t *pxNewTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ /* 0 is not a valid value for xTimerPeriodInTicks. */ configASSERT( ( xTimerPeriodInTicks > 0 ) ); - return ( TimerHandle_t ) pxNewTimer; + if( pxNewTimer != NULL ) + { + /* Ensure the infrastructure used by the timer service task has been + created/initialised. */ + prvCheckForValidListAndQueue(); + + /* Initialise the timer structure members using the function + parameters. */ + pxNewTimer->pcTimerName = pcTimerName; + pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; + pxNewTimer->uxAutoReload = uxAutoReload; + pxNewTimer->pvTimerID = pvTimerID; + pxNewTimer->pxCallbackFunction = pxCallbackFunction; + vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + traceTIMER_CREATE( pxNewTimer ); + } } /*-----------------------------------------------------------*/ @@ -375,6 +456,26 @@ DaemonTaskMessage_t xMessage; #endif /*-----------------------------------------------------------*/ +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) +{ +Timer_t *pxTimer = ( Timer_t * ) xTimer; + + configASSERT( xTimer ); + return pxTimer->xTimerPeriodInTicks; +} +/*-----------------------------------------------------------*/ + +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) +{ +Timer_t * pxTimer = ( Timer_t * ) xTimer; +TickType_t xReturn; + + configASSERT( xTimer ); + xReturn = listGET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ) ); + return xReturn; +} + +/*-----------------------------------------------------------*/ const char * pcTimerGetTimerName( TimerHandle_t xTimer ) { Timer_t *pxTimer = ( Timer_t * ) xTimer; @@ -703,8 +804,29 @@ TickType_t xTimeNow; case tmrCOMMAND_DELETE : /* The timer has already been removed from the active list, - just free up the memory. */ - vPortFree( pxTimer ); + just free up the memory if the memory was dynamically + allocated. */ + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The timer can only have been allocated dynamically - + free it again. */ + vPortFree( pxTimer ); + } + #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The timer could have been allocated statically or + dynamically, so check before attempting to free the + memory. */ + if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxTimer ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ break; default : @@ -847,6 +969,20 @@ Timer_t * const pxTimer = ( Timer_t * ) xTimer; } /*-----------------------------------------------------------*/ +void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) +{ +Timer_t * const pxTimer = ( Timer_t * ) xTimer; + + configASSERT( xTimer ); + + //taskENTER_CRITICAL(); //Atomic instruction, critical not necessary + //{ + pxTimer->pvTimerID = pvNewID; + //} + //taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + #if( INCLUDE_xTimerPendFunctionCall == 1 ) BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) diff --git a/docs/api-guides/freertos-smp.rst b/docs/api-guides/freertos-smp.rst index 6d48b743c3..6d0c29bc4f 100644 --- a/docs/api-guides/freertos-smp.rst +++ b/docs/api-guides/freertos-smp.rst @@ -12,11 +12,13 @@ run tasks interchangeably between them. The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which supports symmetric multiprocessing (SMP). ESP-IDF FreeRTOS is based on the Xtensa port -of FreeRTOS v8.2.0, however features such as static task creation and Thread -Local Storage Pointers have been backported from later versions of FreeRTOS. -This guide outlines the major differences between vanilla FreeRTOS and -ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be found -via http://www.freertos.org/a00106.html +of FreeRTOS v8.2.0. This guide outlines the major differences between vanilla +FreeRTOS and ESP-IDF FreeRTOS. The API reference for vanilla FreeRTOS can be +found via http://www.freertos.org/a00106.html + +:ref:`backported-features`: Although ESP-IDF FreeRTOS is based on the Xtensa +port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported +to ESP-IDF. :ref:`tasks-and-task-creation`: Use ``xTaskCreatePinnedToCore()`` or ``xTaskCreateStaticPinnedToCore()`` to create tasks in ESP-IDF FreeRTOS. The @@ -24,30 +26,30 @@ last parameter of the two functions is ``xCoreID``. This parameter specifies which core the task is pinned to. Acceptable values are ``0`` for **PRO_CPU**, ``1`` for **APP_CPU**, or ``tskNO_AFFINITY`` which allows the task to run on both. - + :ref:`round-robin-scheduling`: The ESP-IDF FreeRTOS scheduler will skip tasks when implementing Round-Robin scheduling between multiple tasks in the Ready state that are of the same priority. To avoid this behavior, ensure that those tasks either enter a blocked state, or are distributed across a wider range of priorities. - + :ref:`scheduler-suspension`: Suspending the scheduler in ESP-IDF FreeRTOS will only affect the scheduler on the the calling core. In other words, calling ``vTaskSuspendAll()`` on **PRO_CPU** will not prevent **APP_CPU** from scheduling, and vice versa. Use critical sections or semaphores instead for simultaneous access protection. - + :ref:`tick-interrupt-synchronicity`: Tick interrupts of **PRO_CPU** and **APP_CPU** are not synchronized. Do not expect to use ``vTaskDelay`` or ``vTaskDelayUntil`` as an accurate method of synchronizing task execution between the two cores. Use a counting semaphore instead as their context switches are not tied to tick interrupts due to preemption. - + :ref:`critical-sections`: In ESP-IDF FreeRTOS, critical sections are implemented using mutexes. Entering critical sections involve taking a mutex, then disabling the scheduler and interrupts of the calling core. However the other core is left unaffected. If the other core attemps to take same mutex, it will spin until the calling core has released the mutex by exiting the critical section. - + :ref:`deletion-callbacks`: ESP-IDF FreeRTOS has backported the Thread Local Storage Pointers feature. However they have the extra feature of deletion callbacks. Deletion callbacks are used to @@ -64,6 +66,61 @@ configured using ``make meunconfig`` such as running ESP-IDF in Unicore Mode, or configuring the number of Thread Local Storage Pointers each task will have. +.. _backported-features: + +Backported Features +------------------- + +The following features have been backported from FreeRTOS v9.0.0 to ESP-IDF. + +Static Alocation +^^^^^^^^^^^^^^^^^ + +This feature has been backported from FreeRTOS v9.0.0 to ESP-IDF. The +:ref:`CONFIG_SUPPORT_STATIC_ALLOCATION` option must be enabled in `menuconfig` +in order for static allocation functions to be available. Once enabled, the +following functions can be called... + + - ``xTaskCreateStatic()`` See :ref:`backporting-notes` below + - ``xQueueCreateStatic()`` + - ``xSemaphoreCreateBinaryStatic()`` + - ``xSemaphoreCreateCountingStatic()`` + - ``xSemaphoreCreateMutexStatic()`` + - ``xSemaphoreCreateRecursiveMutexStatic()`` + - ``xTimerCreateStatic()`` See :ref:`backporting-notes` below + - ``xEventGroupCreateStatic()`` + +Other Features +^^^^^^^^^^^^^^ + + - ``vTaskSetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below + - ``pvTaskGetThreadLocalStoragePointer()`` See :ref:`backporting-notes` below + - ``vTimerSetTimerID()`` + - ``xTimerGetPeriod()`` + - ``xTimerGetExpiryTime()`` + - ``pcQueueGetName()`` + - ``uxSemaphoreGetCount()`` + +.. _backporting-notes: + +Backporting Notes +^^^^^^^^^^^^^^^^^ + +**1)** ``xTaskCreateStatic`` has been made SMP compatible in a similar +fashion to ``xTaskCreate`` (see :ref:`tasks-and-task-creation`). Therefore +``xTaskCreateStaticPinnedToCore()`` can also be called. + +**2)** Although vanilla FreeRTOS allows the Timer feature's daemon task to +be statically allocated, the daemon task is always dynamically allocated in +ESP-IDF. Therefore ``vApplicationGetTimerTaskMemory`` **does not** need to be +defined when using statically allocated timers in ESP-IDF FreeRTOS. + +**3)** The Thread Local Storage Pointer feature has been modified in ESP-IDF +FreeRTOS to include Deletion Callbacks (see :ref:`deletion-callbacks`). Therefore +the function ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` can also be +called. + + .. _tasks-and-task-creation: Tasks and Task Creation @@ -75,7 +132,7 @@ appending ``PinnedToCore`` to the names of the task creation functions in vanilla FreeRTOS. The vanilla FreeRTOS functions of ``xTaskCreate()`` and ``xTaskCreateStatic()`` have led to the addition of ``xTaskCreatePinnedToCore()`` and ``xTaskCreateStaticPinnedToCore()`` in -ESP-IDF FreeRTOS. +ESP-IDF FreeRTOS (see :ref:`backported-features`). For more details see :component_file:`freertos/task.c` @@ -234,6 +291,7 @@ protecting shared resources in ESP-IDF FreeRTOS. In general, it's better to use other RTOS primitives like mutex semaphores to protect against data shared between tasks, rather than ``vTaskSuspendAll()``. + .. _tick-interrupt-synchronicity: Tick Interrupt Synchronicity @@ -266,6 +324,7 @@ Therefore, task delays should **NOT** be used as a method of synchronization between tasks in ESP-IDF FreeRTOS. Instead, consider using a counting semaphore to unblock multiple tasks at the same time. + .. _critical-sections: Critical Sections & Disabling Interrupts @@ -315,6 +374,7 @@ called as they are all defined to call the same function. As long as the same mutex is provided upon entering and exiting, the type of call should not matter. + .. _deletion-callbacks: Thread Local Storage Pointers & Deletion Callbacks @@ -351,6 +411,7 @@ Other indexes can be used for any purpose, provided For more details see :component_file:`freertos/include/freertos/task.h` + .. _esp-idf-freertos-configuration: Configuring ESP-IDF FreeRTOS diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index aae249f8a1..534eca74ed 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -23,3 +23,4 @@ CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7 CONFIG_STACK_CHECK_STRONG=y CONFIG_STACK_CHECK=y +CONFIG_SUPPORT_STATIC_ALLOCATION=y