From 9db0ee3d02a113058c8296d68eee98baa7967d6f Mon Sep 17 00:00:00 2001 From: Cosmin-Daniel Radu Date: Tue, 24 Sep 2024 14:35:39 +0300 Subject: [PATCH] fix(freertos): sync signal from isr fixed (#6793) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicușor Cîțu Signed-off-by: Cosmin-Daniel Radu Co-authored-by: Nicușor Cîțu --- Kconfig | 9 +++ env_support/cmake/esp.cmake | 6 ++ lv_conf_template.h | 9 +++ src/draw/nxp/vglite/lv_draw_vglite.c | 4 ++ src/lv_conf_internal.h | 19 +++++++ src/osal/lv_freertos.c | 84 ++++++++++++++++++---------- src/osal/lv_freertos.h | 10 +--- 7 files changed, 101 insertions(+), 40 deletions(-) diff --git a/Kconfig b/Kconfig index e6f88aac5..1fd917da5 100644 --- a/Kconfig +++ b/Kconfig @@ -162,6 +162,15 @@ menu "LVGL configuration" string "Custom OS include header" default "stdint.h" depends on LV_OS_CUSTOM + + config LV_USE_FREERTOS_TASK_NOTIFY + bool "Use RTOS task with a direct notification for synchronization" + default y + depends on LV_OS_FREERTOS + help + Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + than unblocking a task using an intermediary object such as a binary semaphore. + RTOS task notifications can only be used when there is only one task that can be the recipient of the event. endmenu menu "Rendering Configuration" diff --git a/env_support/cmake/esp.cmake b/env_support/cmake/esp.cmake index 4983677ad..8aaed350e 100644 --- a/env_support/cmake/esp.cmake +++ b/env_support/cmake/esp.cmake @@ -70,3 +70,9 @@ if(CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM) target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLV_ATTRIBUTE_FAST_MEM=IRAM_ATTR") endif() + +if(CONFIG_FREERTOS_SMP) + target_include_directories(${COMPONENT_LIB} PRIVATE "${IDF_PATH}/components/freertos/FreeRTOS-Kernel-SMP/include/freertos/") +else() + target_include_directories(${COMPONENT_LIB} PRIVATE "${IDF_PATH}/components/freertos/FreeRTOS-Kernel/include/freertos/") +endif() \ No newline at end of file diff --git a/lv_conf_template.h b/lv_conf_template.h index bf1944a82..6ee24efe4 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -111,6 +111,15 @@ #if LV_USE_OS == LV_OS_CUSTOM #define LV_OS_CUSTOM_INCLUDE #endif +#if LV_USE_OS == LV_OS_FREERTOS + /* + * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + * than unblocking a task using an intermediary object such as a binary semaphore. + * + * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + */ + #define LV_USE_FREERTOS_TASK_NOTIFY 1 +#endif /*======================== * RENDERING CONFIGURATION diff --git a/src/draw/nxp/vglite/lv_draw_vglite.c b/src/draw/nxp/vglite/lv_draw_vglite.c index bf9dce96e..abb52c0da 100644 --- a/src/draw/nxp/vglite/lv_draw_vglite.c +++ b/src/draw/nxp/vglite/lv_draw_vglite.c @@ -340,9 +340,13 @@ static int32_t _vglite_wait_for_finish(lv_draw_unit_t * draw_unit) lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit; draw_vglite_unit->wait_for_finish = true; + /* Signal draw unit to finish its tasks and return READY state after completion. */ if(draw_vglite_unit->inited) lv_thread_sync_signal(&draw_vglite_unit->sync); + /* Wait for finish now. */ + lv_draw_dispatch_wait_for_request(); + return 1; } #endif diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 0c9006b6e..2c7d33661 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -282,6 +282,25 @@ #endif #endif #endif +#if LV_USE_OS == LV_OS_FREERTOS + /* + * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + * than unblocking a task using an intermediary object such as a binary semaphore. + * + * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + */ + #ifndef LV_USE_FREERTOS_TASK_NOTIFY + #ifdef LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_FREERTOS_TASK_NOTIFY + #define LV_USE_FREERTOS_TASK_NOTIFY CONFIG_LV_USE_FREERTOS_TASK_NOTIFY + #else + #define LV_USE_FREERTOS_TASK_NOTIFY 0 + #endif + #else + #define LV_USE_FREERTOS_TASK_NOTIFY 1 + #endif + #endif +#endif /*======================== * RENDERING CONFIGURATION diff --git a/src/osal/lv_freertos.c b/src/osal/lv_freertos.c index cb022e895..b5fd17d3d 100644 --- a/src/osal/lv_freertos.c +++ b/src/osal/lv_freertos.c @@ -15,11 +15,7 @@ #include "lv_os.h" #if LV_USE_OS == LV_OS_FREERTOS -#if (ESP_PLATFORM) - #include "freertos/atomic.h" -#else - #include "atomic.h" -#endif +#include "atomic.h" #include "../tick/lv_tick.h" #include "../misc/lv_log.h" @@ -54,7 +50,9 @@ static void prvCondInit(lv_thread_sync_t * pxCond); static void prvCheckCondInit(lv_thread_sync_t * pxCond); -#if !USE_FREERTOS_TASK_NOTIFY +static void prvCheckCondInitIsr(lv_thread_sync_t * pxCond); + +#if !LV_USE_FREERTOS_TASK_NOTIFY static void prvTestAndDecrement(lv_thread_sync_t * pxCond, uint32_t ulLocalWaitingThreads); #endif @@ -74,9 +72,13 @@ static void prvTestAndDecrement(lv_thread_sync_t * pxCond, #if (ESP_PLATFORM) #define _enter_critical() taskENTER_CRITICAL(&critSectionMux); #define _exit_critical() taskEXIT_CRITICAL(&critSectionMux); + #define _enter_critical_isr() taskENTER_CRITICAL_FROM_ISR(); + #define _exit_critical_isr(x) taskEXIT_CRITICAL_FROM_ISR(x); #else #define _enter_critical() taskENTER_CRITICAL(); #define _exit_critical() taskEXIT_CRITICAL(); + #define _enter_critical_isr() taskENTER_CRITICAL_FROM_ISR(); + #define _exit_critical_isr(x) taskEXIT_CRITICAL_FROM_ISR(x); #endif /********************** @@ -195,20 +197,20 @@ lv_result_t lv_thread_sync_wait(lv_thread_sync_t * pxCond) /* If the cond is uninitialized, perform initialization. */ prvCheckCondInit(pxCond); -#if USE_FREERTOS_TASK_NOTIFY - TaskHandle_t current_task_handle = xTaskGetCurrentTaskHandle(); +#if LV_USE_FREERTOS_TASK_NOTIFY + TaskHandle_t xCurrentTaskHandle = xTaskGetCurrentTaskHandle(); _enter_critical(); - BaseType_t signal_sent = pxCond->xSyncSignal; + BaseType_t xSyncSygnal = pxCond->xSyncSignal; pxCond->xSyncSignal = pdFALSE; - if(signal_sent == pdFALSE) { + if(xSyncSygnal == pdFALSE) { /* The signal hasn't been sent yet. Tell the sender to notify this task */ - pxCond->xTaskToNotify = current_task_handle; + pxCond->xTaskToNotify = xCurrentTaskHandle; } /* If we have a signal from the other task, we should not ask to be notified */ _exit_critical(); - if(signal_sent == pdFALSE) { + if(xSyncSygnal == pdFALSE) { /* Wait for other task to notify this task. */ ulTaskNotifyTake(pdTRUE, portMAX_DELAY); } @@ -275,20 +277,20 @@ lv_result_t lv_thread_sync_signal(lv_thread_sync_t * pxCond) /* If the cond is uninitialized, perform initialization. */ prvCheckCondInit(pxCond); -#if USE_FREERTOS_TASK_NOTIFY +#if LV_USE_FREERTOS_TASK_NOTIFY _enter_critical(); - TaskHandle_t task_to_notify = pxCond->xTaskToNotify; + TaskHandle_t xTaskToNotify = pxCond->xTaskToNotify; pxCond->xTaskToNotify = NULL; - if(task_to_notify == NULL) { + if(xTaskToNotify == NULL) { /* No task waiting to be notified. Send this signal for later */ pxCond->xSyncSignal = pdTRUE; } /* If a task is already waiting, there is no need to set the sync signal */ _exit_critical(); - if(task_to_notify != NULL) { + if(xTaskToNotify != NULL) { /* There is a task waiting. Send a notification to it */ - xTaskNotifyGive(task_to_notify); + xTaskNotifyGive(xTaskToNotify); } /* If there was no task waiting to be notified, we sent a signal for it to see later. */ #else @@ -329,13 +331,13 @@ lv_result_t lv_thread_sync_signal(lv_thread_sync_t * pxCond) lv_result_t lv_thread_sync_delete(lv_thread_sync_t * pxCond) { -#if !USE_FREERTOS_TASK_NOTIFY +#if !LV_USE_FREERTOS_TASK_NOTIFY /* Cleanup all resources used by the cond. */ vSemaphoreDelete(pxCond->xCondWaitSemaphore); vSemaphoreDelete(pxCond->xSyncMutex); pxCond->ulWaitingThreads = 0; - pxCond->xSyncSignal = pdFALSE; #endif + pxCond->xSyncSignal = pdFALSE; pxCond->xIsInitialized = pdFALSE; return LV_RESULT_OK; @@ -346,28 +348,28 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * pxCond) BaseType_t xHigherPriorityTaskWoken = pdFALSE; /* If the cond is uninitialized, perform initialization. */ - prvCheckCondInit(pxCond); + prvCheckCondInitIsr(pxCond); -#if USE_FREERTOS_TASK_NOTIFY - _enter_critical(); - TaskHandle_t task_to_notify = pxCond->xTaskToNotify; +#if LV_USE_FREERTOS_TASK_NOTIFY + uint32_t mask = _enter_critical_isr(); + TaskHandle_t xTaskToNotify = pxCond->xTaskToNotify; pxCond->xTaskToNotify = NULL; - if(task_to_notify == NULL) { + if(xTaskToNotify == NULL) { /* No task waiting to be notified. Send this signal for later */ pxCond->xSyncSignal = pdTRUE; } /* If a task is already waiting, there is no need to set the sync signal */ - _exit_critical(); + _exit_critical_isr(mask); - if(task_to_notify != NULL) { + if(xTaskToNotify != NULL) { /* There is a task waiting. Send a notification to it */ - vTaskNotifyGiveFromISR(task_to_notify, &xHigherPriorityTaskWoken); + vTaskNotifyGiveFromISR(xTaskToNotify, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } /* If there was no task waiting to be notified, we sent a signal for it to see later. */ #else /* Enter critical section to prevent preemption. */ - _enter_critical(); + uint32_t mask = _enter_critical_isr(); pxCond->xSyncSignal = pdTRUE; BaseType_t xAnyHigherPriorityTaskWoken = pdFALSE; @@ -378,7 +380,7 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * pxCond) xHigherPriorityTaskWoken |= xAnyHigherPriorityTaskWoken; } - _exit_critical(); + _exit_critical_isr(mask); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); #endif @@ -470,7 +472,7 @@ static void prvCondInit(lv_thread_sync_t * pxCond) pxCond->xIsInitialized = pdTRUE; pxCond->xSyncSignal = pdFALSE; -#if USE_FREERTOS_TASK_NOTIFY +#if LV_USE_FREERTOS_TASK_NOTIFY pxCond->xTaskToNotify = NULL; #else pxCond->xCondWaitSemaphore = xSemaphoreCreateCounting(ulMAX_COUNT, 0U); @@ -516,7 +518,27 @@ static void prvCheckCondInit(lv_thread_sync_t * pxCond) } } -#if !USE_FREERTOS_TASK_NOTIFY +static void prvCheckCondInitIsr(lv_thread_sync_t * pxCond) +{ + /* Check if the condition variable needs to be initialized. */ + if(pxCond->xIsInitialized == pdFALSE) { + /* Cond initialization must be in a critical section to prevent two + * threads from initializing it at the same time. */ + uint32_t mask = _enter_critical_isr(); + + /* Check again that the condition is still uninitialized, i.e. it wasn't + * initialized while this function was waiting to enter the critical + * section. */ + if(pxCond->xIsInitialized == pdFALSE) { + prvCondInit(pxCond); + } + + /* Exit the critical section. */ + _exit_critical_isr(mask); + } +} + +#if !LV_USE_FREERTOS_TASK_NOTIFY static void prvTestAndDecrement(lv_thread_sync_t * pxCond, uint32_t ulLocalWaitingThreads) { diff --git a/src/osal/lv_freertos.h b/src/osal/lv_freertos.h index 2e2384d37..67a5a1e7f 100644 --- a/src/osal/lv_freertos.h +++ b/src/osal/lv_freertos.h @@ -37,14 +37,6 @@ extern "C" { * DEFINES *********************/ -/* - * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM - * than unblocking a task using an intermediary object such as a binary semaphore. - * - * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. - */ -#define USE_FREERTOS_TASK_NOTIFY 1 - /********************** * TYPEDEFS **********************/ @@ -64,7 +56,7 @@ typedef struct { BaseType_t xIsInitialized; /**< Set to pdTRUE if this condition variable is initialized, pdFALSE otherwise. */ BaseType_t xSyncSignal; /**< Set to pdTRUE if the thread is signaled, pdFALSE otherwise. */ -#if USE_FREERTOS_TASK_NOTIFY +#if LV_USE_FREERTOS_TASK_NOTIFY TaskHandle_t xTaskToNotify; /**< The task waiting to be signalled. NULL if nothing is waiting. */ #else SemaphoreHandle_t xCondWaitSemaphore; /**< Threads block on this semaphore in lv_thread_sync_wait. */