For most uses the default value of 10 is OK.
+config FREERTOS_QUEUE_REGISTRY_SIZE
+ int "FreeRTOS queue registry size"
+ range 0 20
+ default 0
+ help
+ FreeRTOS uses the queue registry as a means for kernel aware debuggers to locate queues, semaphores,
+ and mutexes. The registry allows for a textual name to be associated with a queue for easy identification
+ within a debugging GUI. A value of 0 will disable queue registry functionality, and a value larger than 0
+ will specify the number of queues/semaphores/mutexes that the registry can hold.
+
config FREERTOS_USE_TRACE_FACILITY
bool "Enable FreeRTOS trace facility"
default n
config FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
bool "Enable FreeRTOS stats formatting functions"
- depends on FREERTOS_USE_TRACE_FACILITY
+ depends on FREERTOS_USE_TRACE_FACILITY || FREERTOS_GENERATE_RUN_TIME_STATS
default n
help
If enabled, configUSE_STATS_FORMATTING_FUNCTIONS will be defined as 1 in
FreeRTOS. This will allow the usage of stats formatting functions such
as vTaskList().
+config FREERTOS_GENERATE_RUN_TIME_STATS
+ bool "Enable FreeRTOS to collect run time stats"
+ default n
+ help
+ If enabled, configGENERATE_RUN_TIME_STATS will be defined as 1 in
+ FreeRTOS. This will allow FreeRTOS to collect information regarding the
+ usage of processor time amongst FreeRTOS tasks. Run time stats are
+ generated using either the ESP Timer or the CPU Clock as the clock
+ source (Note that run time stats are only valid until the clock source
+ overflows). The function vTaskGetRunTimeStats() will also be available
+ if FREERTOS_USE_STATS_FORMATTING_FUNCTIONS and
+ FREERTOS_USE_TRACE_FACILITY are enabled. vTaskGetRunTimeStats() will
+ display the run time of each task as a % of the total run time of all
+ CPUs (task run time / no of CPUs) / (total run time / 100 )
+
+
+choice FREERTOS_RUN_TIME_STATS_CLK
+ prompt "Choose the clock source for run time stats"
+ depends on FREERTOS_GENERATE_RUN_TIME_STATS
+ default FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
+ help
+ Choose the clock source for FreeRTOS run time stats. Options are CPU0's
+ CPU Clock or the ESP Timer. Both clock sources are 32 bits. The CPU
+ Clock can run at a higher frequency hence provide a finer resolution
+ but will overflow much quicker. Note that run time stats are only valid
+ until the clock source overflows.
+
+config FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
+ bool "Use ESP TIMER for run time stats"
+ help
+ ESP Timer will be used as the clock source for FreeRTOS run time stats.
+ The ESP Timer runs at a frequency of 1MHz regardless of Dynamic
+ Frequency Scaling. Therefore the ESP Timer will overflow in
+ approximately 4290 seconds.
+
+config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK
+ bool "Use CPU Clock for run time stats"
+ help
+ CPU Clock will be used as the clock source for the generation of run
+ time stats. The CPU Clock has a frequency dependent on
+ ESP32_DEFAULT_CPU_FREQ_MHZ and Dynamic Frequency Scaling (DFS).
+ Therefore the CPU Clock frequency can fluctuate between 80 to 240MHz.
+ Run time stats generated using the CPU Clock represents the number of
+ CPU cycles each task is allocated and DOES NOT reflect the amount of
+ time each task runs for (as CPU clock frequency can change). If the CPU
+ clock consistently runs at the maximum frequency of 240MHz, it will
+ overflow in approximately 17 seconds.
+
+endchoice
+
menuconfig FREERTOS_DEBUG_INTERNALS
bool "Debug FreeRTOS internals"
default n
#define configBENCHMARK 0 /* Provided by Xtensa port patch */
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 0
-#define configQUEUE_REGISTRY_SIZE 0
+#define configQUEUE_REGISTRY_SIZE CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 1
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
#include <xtensa/xtruntime.h>
#include "esp_crosscore_int.h"
+#include "esp_timer.h" /* required for FreeRTOS run time stats */
#include <esp_heap_caps.h>
/* Fine resolution time */
#define portGET_RUN_TIME_COUNTER_VALUE() xthal_get_ccount()
+//ccount or esp_timer are initialized elsewhere
+#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()
+
+#ifdef CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER
+/* Coarse resolution time (us) */
+#define portALT_GET_RUN_TIME_COUNTER_VALUE(x) x = (uint32_t)esp_timer_get_time()
+#endif
+
+
/* Kernel utilities. */
void vPortYield( void );
void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
#endif
+/*
+ * @note This function has been back ported from FreeRTOS v9.0.0
+ *
+ * The queue registry is provided as a means for kernel aware debuggers to
+ * locate queues, semaphores and mutexes. Call pcQueueGetName() to look
+ * up and return the name of a queue in the queue registry from the queue's
+ * handle.
+ *
+ * @param xQueue The handle of the queue the name of which will be returned.
+ * @return If the queue is in the registry then a pointer to the name of the
+ * queue is returned. If the queue is not in the registry then NULL is
+ * returned.
+ */
+#if( configQUEUE_REGISTRY_SIZE > 0 )
+ const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
+#endif
+
/*
* Generic version of the function used to creaet a queue using dynamic memory
* allocation. This is called by other functions and macros that create other
array position being vacant. */
QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ];
+ //Need to add queue registry mutex to protect against simultaneous access
+ static portMUX_TYPE queue_registry_spinlock = portMUX_INITIALIZER_UNLOCKED;
+
#endif /* configQUEUE_REGISTRY_SIZE */
{
UBaseType_t ux;
- UNTESTED_FUNCTION();
+ portENTER_CRITICAL(&queue_registry_spinlock);
/* See if there is an empty space in the registry. A NULL name denotes
a free slot. */
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
mtCOVERAGE_TEST_MARKER();
}
}
+ portEXIT_CRITICAL(&queue_registry_spinlock);
+ }
+
+#endif /* configQUEUE_REGISTRY_SIZE */
+/*-----------------------------------------------------------*/
+
+#if ( configQUEUE_REGISTRY_SIZE > 0 )
+
+ //This function is backported from FreeRTOS v9.0.0
+ const char *pcQueueGetName( QueueHandle_t xQueue ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
+ {
+ UBaseType_t ux;
+ const char *pcReturn = NULL; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
+
+ portENTER_CRITICAL(&queue_registry_spinlock);
+ /* Note there is nothing here to protect against another task adding or
+ removing entries from the registry while it is being searched. */
+ for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
+ {
+ if( xQueueRegistry[ ux ].xHandle == xQueue )
+ {
+ pcReturn = xQueueRegistry[ ux ].pcQueueName;
+ break;
+ }
+ else
+ {
+ mtCOVERAGE_TEST_MARKER();
+ }
+ }
+ portEXIT_CRITICAL(&queue_registry_spinlock);
+
+ return pcReturn;
}
#endif /* configQUEUE_REGISTRY_SIZE */
{
UBaseType_t ux;
+ portENTER_CRITICAL(&queue_registry_spinlock);
/* See if the handle of the queue being unregistered in actually in the
registry. */
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
mtCOVERAGE_TEST_MARKER();
}
}
+ portEXIT_CRITICAL(&queue_registry_spinlock);
} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
- PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */
+ PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime[portNUM_PROCESSORS] = {0U}; /*< Holds the value of a timer/counter the last time a task was switched in on a particular core. */
PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */
#endif
against suspect run time stat counter implementations - which
are provided by the application, not the kernel. */
taskENTER_CRITICAL_ISR(&xTaskQueueMutex);
- if( ulTotalRunTime > ulTaskSwitchedInTime )
+ if( ulTotalRunTime > ulTaskSwitchedInTime[ xPortGetCoreID() ] )
{
- pxCurrentTCB[ xPortGetCoreID() ]->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
+ pxCurrentTCB[ xPortGetCoreID() ]->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime[ xPortGetCoreID() ] );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskEXIT_CRITICAL_ISR(&xTaskQueueMutex);
- ulTaskSwitchedInTime = ulTotalRunTime;
+ ulTaskSwitchedInTime[ xPortGetCoreID() ] = ulTotalRunTime;
}
#endif /* configGENERATE_RUN_TIME_STATS */
volatile UBaseType_t uxArraySize, x;
uint32_t ulTotalTime, ulStatsAsPercentage;
- UNTESTED_FUNCTION();
#if( configUSE_TRACE_FACILITY != 1 )
{
#error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats().
/* What percentage of the total run time has the task used?
This will always be rounded down to the nearest integer.
ulTotalRunTimeDiv100 has already been divided by 100. */
- ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime;
+ /* Also need to consider total run time of all */
+ ulStatsAsPercentage = (pxTaskStatusArray[ x ].ulRunTimeCounter/portNUM_PROCESSORS)/ ulTotalTime;
/* Write the task name to the string, padding with
spaces so it can be printed in tabular form more
--- /dev/null
+/*
+ * Test FreeRTOS debug functions and utilities.
+ * - Queue registry functions vQueueAddToRegistry(), vQueueUnregisterQueue(),
+ * and pcQueueGetName(backported)
+ *
+ *
+ */
+
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include "freertos/queue.h"
+#include "unity.h"
+
+#if (CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE > 0)
+#define NO_OF_QUEUES_PER_CORE ((int)((CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE - 3)/portNUM_PROCESSORS)) //Save space for some preallocated tasks
+#define NO_OF_QUEUES_TOTAL (NO_OF_QUEUES_PER_CORE * portNUM_PROCESSORS)
+#define QUEUE_NAME_MAX_LENGTH 10
+
+static SemaphoreHandle_t start_sem = NULL;
+static SemaphoreHandle_t done_sem = NULL;
+static char *names[NO_OF_QUEUES_TOTAL];
+static QueueHandle_t handles[NO_OF_QUEUES_TOTAL];
+
+void test_queue_registry_task(void *arg)
+{
+ int core = xPortGetCoreID();
+ int offset = core * NO_OF_QUEUES_PER_CORE;
+ //Create queues and accompanying queue names
+ for(int i = 0; i < NO_OF_QUEUES_PER_CORE; i++){
+ handles[i + offset] = xQueueCreate(1,1); //Create queues
+ names[i + offset] = calloc(QUEUE_NAME_MAX_LENGTH, sizeof(char));
+ sprintf(names[i + offset], "Queue%d%d", core, i);
+ }
+
+ xSemaphoreTake(start_sem, portMAX_DELAY); //Wait for start vQueueAddToRegistry()
+ for(int i = 0; i < NO_OF_QUEUES_PER_CORE; i++){
+ vQueueAddToRegistry(handles[i + offset] , names[i + offset]); //Register queues to queue registry
+ }
+ xSemaphoreGive(done_sem); //Signal that vQueueAddToRegistry() has completed
+
+ vTaskDelay(1);
+
+ xSemaphoreTake(start_sem, portMAX_DELAY); //Wait to start vQueueUnregisterQueue()
+ for(int i = 0; i < NO_OF_QUEUES_PER_CORE; i++){
+ vQueueDelete(handles[i + offset]); //Internally calls vQueueUnregisterQueue
+ }
+ xSemaphoreGive(done_sem); //Signal done
+
+ vTaskDelete(NULL); //Delete self
+}
+
+TEST_CASE("Test FreeRTOS Queue Registry", "[freertos]")
+{
+ //Create synchronization semaphores
+ start_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
+ done_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
+ for(int i = 0; i < portNUM_PROCESSORS; i++){ //Create tasks to test queue registry
+ xTaskCreatePinnedToCore(test_queue_registry_task, "testing task", 4096, NULL, UNITY_FREERTOS_PRIORITY+1, NULL, i);
+ }
+
+ portDISABLE_INTERRUPTS();
+ for(int i = 0; i < portNUM_PROCESSORS; i++){
+ xSemaphoreGive(start_sem); //Trigger start
+ }
+ portENABLE_INTERRUPTS();
+ for(int i = 0; i < portNUM_PROCESSORS; i++){
+ xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete vQueueAddToRegistry
+ }
+ for(int i = 0; i < NO_OF_QUEUES_TOTAL; i++){
+ const char *addr = pcQueueGetName(handles[i]);
+ TEST_ASSERT(addr == names[i]) //Check vQueueAddToRegistry was successful
+ }
+
+ portDISABLE_INTERRUPTS();
+ for(int i = 0; i < portNUM_PROCESSORS; i++){
+ xSemaphoreGive(start_sem); //Trigger start
+ }
+ portENABLE_INTERRUPTS();
+ for(int i = 0; i < portNUM_PROCESSORS; i++){
+ xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete vQueueUnregisterQueue
+ }
+ for(int i = 0; i < NO_OF_QUEUES_TOTAL; i++){
+ const char *addr = pcQueueGetName(handles[i]);
+ TEST_ASSERT(addr == NULL) //Check vQueueUnregisterQueue was successful
+ handles[i] = NULL;
+ }
+
+ //Cleanup
+ for(int i = 0; i < NO_OF_QUEUES_TOTAL; i++){
+ free(names[i]);
+ names[i] = NULL;
+ }
+ vSemaphoreDelete(start_sem);
+ start_sem = NULL;
+ vSemaphoreDelete(done_sem);
+ done_sem = NULL;
+}
+#endif //(CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE > 0)
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_TASK_WDT=n
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y
+CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7