#include <xtensa/config/core.h>
#include <xtensa/config/system.h> /* required for XSHAL_CLIB */
#include <xtensa/xtruntime.h>
+#include "esp_crosscore_int.h"
+
//#include "xtensa_context.h"
void _frxt_setup_switch( void );
#define portYIELD() vPortYield()
#define portYIELD_FROM_ISR() _frxt_setup_switch()
+
+static inline uint32_t xPortGetCoreID();
+
+/* Yielding within an API call (when interrupts are off), means the yield should be delayed
+ until interrupts are re-enabled.
+
+ To do this, we use the "cross-core" interrupt as a trigger to yield on this core when interrupts are re-enabled.This
+ is the same interrupt & code path which is used to trigger a yield between CPUs, although in this case the yield is
+ happening on the same CPU.
+*/
+#define portYIELD_WITHIN_API() esp_crosscore_int_send_yield(xPortGetCoreID())
+
/*-----------------------------------------------------------*/
/* Task function macros as described on the FreeRTOS.org WEB site. */
#if( configUSE_PREEMPTION == 0 )
/* If the cooperative scheduler is being used then a yield should not be
performed just because a higher priority task has been woken. */
- #define queueYIELD_IF_USING_PREEMPTION_MUX()
#define queueYIELD_IF_USING_PREEMPTION()
#else
#define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
- #define queueYIELD_IF_USING_PREEMPTION_MUX(mux) { \
- taskEXIT_CRITICAL(mux); \
- portYIELD_WITHIN_API(); \
- taskENTER_CRITICAL(mux); \
- } while(0)
#endif
/*
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE )
{
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
/* The queue is a member of a queue set, and posting
to the queue set caused a higher priority task to
unblock. A context switch is required. */
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
our own so yield immediately. Yes it is ok to
do this from within the critical section - the
kernel takes care of that. */
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
executed if the task was holding multiple mutexes
and the mutexes were given back in an order that is
different to that in which they were taken. */
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
our own so yield immediately. Yes it is ok to do
this from within the critical section - the kernel
takes care of that. */
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
executed if the task was holding multiple mutexes and
the mutexes were given back in an order that is
different to that in which they were taken. */
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE )
{
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The task waiting has a higher priority than this task. */
- queueYIELD_IF_USING_PREEMPTION_MUX(&pxQueue->mux);
+ queueYIELD_IF_USING_PREEMPTION();
}
else
{
/* If the cooperative scheduler is being used then a yield should not be
performed just because a higher priority task has been woken. */
#define taskYIELD_IF_USING_PREEMPTION()
- #define queueYIELD_IF_USING_PREEMPTION_MUX(mux)
#else
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
- #define taskYIELD_IF_USING_PREEMPTION_MUX(mux) { \
- taskEXIT_CRITICAL(mux); \
- portYIELD_WITHIN_API(); \
- taskENTER_CRITICAL(mux); \
- } while(0)
#endif
{
if( xCoreID == xPortGetCoreID() )
{
- taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
+ taskYIELD_IF_USING_PREEMPTION();
}
else {
taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority);
if( xYieldRequired == pdTRUE )
{
- taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
+ taskYIELD_IF_USING_PREEMPTION();
}
else
{
/* This yield may not cause the task just resumed to run,
but will leave the lists in the correct state for the
next yield. */
- taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
+ taskYIELD_IF_USING_PREEMPTION();
}
else if( pxTCB->xCoreID != xPortGetCoreID() )
{
xAlreadyYielded = pdTRUE;
}
#endif
- taskYIELD_IF_USING_PREEMPTION_MUX(&xTaskQueueMutex);
+ taskYIELD_IF_USING_PREEMPTION();
}
else
{
--- /dev/null
+/*
+ Unit tests for FreeRTOS preemption
+*/
+
+#include <esp_types.h>
+#include <stdio.h>
+#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/cpu.h"
+
+
+static volatile bool flag;
+
+static void task_yield(void *param)
+{
+ QueueHandle_t queue = (QueueHandle_t) param;
+ uint32_t ccount;
+ RSR(CCOUNT, ccount);
+ flag = true;
+ xQueueSendToBack(queue, &ccount, 0);
+ /* This is to ensure that higher priority task
+ won't wake anyhow, due to this task terminating.
+ */
+ for (int i = 0; i < 1000; i++) {
+ ets_delay_us(1000);
+ }
+ vTaskDelete(NULL);
+}
+
+TEST_CASE("Yield from lower priority task, same CPU", "[freertos]")
+{
+ /* Do this 3 times, mostly for the benchmark value - the first
+ run includes a cache miss so uses more cycles than it should. */
+ for (int i = 0; i < 3; i++) {
+ QueueHandle_t queue = xQueueCreate(1, sizeof(uint32_t));
+ flag = false;
+
+ /* "yield" task sits on our CPU, lower priority to us */
+ xTaskCreatePinnedToCore(task_yield, "YIELD", 2048, (void *)queue, UNITY_FREERTOS_PRIORITY - 1, NULL, UNITY_FREERTOS_CPU);
+
+ uint32_t yield_ccount, now_ccount, delta;
+ TEST_ASSERT( xQueueReceive(queue, &yield_ccount, 100 / portTICK_PERIOD_MS) );
+ RSR(CCOUNT, now_ccount);
+ TEST_ASSERT( flag );
+
+ delta = now_ccount - yield_ccount;
+ printf("Yielding from lower priority task took %u cycles\n", delta);
+ TEST_ASSERT(delta < 10000);
+
+ vQueueDelete(queue);
+ }
+}
#include <esp_err.h>
+/* Some definitions applicable to Unity running in FreeRTOS */
+#define UNITY_FREERTOS_PRIORITY 5
+#define UNITY_FREERTOS_CPU 0
+
#define UNITY_EXCLUDE_FLOAT
#define UNITY_EXCLUDE_DOUBLE
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
+#include "unity_config.h"
-
-void unityTask(void *pvParameters)
+void unityTask(void *pvParameters)
{
vTaskDelay(1000 / portTICK_PERIOD_MS);
unity_run_menu();
{
// Note: if unpinning this task, change the way run times are calculated in
// unity_platform
- xTaskCreatePinnedToCore(unityTask, "unityTask", 4096, NULL, 5, NULL, 0);
+ xTaskCreatePinnedToCore(unityTask, "unityTask", 4096, NULL,
+ UNITY_FREERTOS_PRIORITY, NULL, UNITY_FREERTOS_CPU);
}
-