fix(freertos): sync signal from isr fixed (#6793)

Signed-off-by: Nicușor Cîțu <nicusor.citu@nxp.com>
Signed-off-by: Cosmin-Daniel Radu <cosmin.radu_1@nxp.com>
Co-authored-by: Nicușor Cîțu <nicusor.citu@nxp.com>
This commit is contained in:
Cosmin-Daniel Radu
2024-09-24 14:35:39 +03:00
committed by GitHub
parent 55faa53bcc
commit 9db0ee3d02
7 changed files with 101 additions and 40 deletions

View File

@@ -162,6 +162,15 @@ menu "LVGL configuration"
string "Custom OS include header" string "Custom OS include header"
default "stdint.h" default "stdint.h"
depends on LV_OS_CUSTOM 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 endmenu
menu "Rendering Configuration" menu "Rendering Configuration"

View File

@@ -70,3 +70,9 @@ if(CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM)
target_compile_definitions(${COMPONENT_LIB} target_compile_definitions(${COMPONENT_LIB}
PUBLIC "-DLV_ATTRIBUTE_FAST_MEM=IRAM_ATTR") PUBLIC "-DLV_ATTRIBUTE_FAST_MEM=IRAM_ATTR")
endif() 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()

View File

@@ -111,6 +111,15 @@
#if LV_USE_OS == LV_OS_CUSTOM #if LV_USE_OS == LV_OS_CUSTOM
#define LV_OS_CUSTOM_INCLUDE <stdint.h> #define LV_OS_CUSTOM_INCLUDE <stdint.h>
#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.
*/
#define LV_USE_FREERTOS_TASK_NOTIFY 1
#endif
/*======================== /*========================
* RENDERING CONFIGURATION * RENDERING CONFIGURATION

View File

@@ -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; lv_draw_vglite_unit_t * draw_vglite_unit = (lv_draw_vglite_unit_t *) draw_unit;
draw_vglite_unit->wait_for_finish = true; 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) if(draw_vglite_unit->inited)
lv_thread_sync_signal(&draw_vglite_unit->sync); lv_thread_sync_signal(&draw_vglite_unit->sync);
/* Wait for finish now. */
lv_draw_dispatch_wait_for_request();
return 1; return 1;
} }
#endif #endif

View File

@@ -282,6 +282,25 @@
#endif #endif
#endif #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 * RENDERING CONFIGURATION

View File

@@ -15,11 +15,7 @@
#include "lv_os.h" #include "lv_os.h"
#if LV_USE_OS == LV_OS_FREERTOS #if LV_USE_OS == LV_OS_FREERTOS
#if (ESP_PLATFORM) #include "atomic.h"
#include "freertos/atomic.h"
#else
#include "atomic.h"
#endif
#include "../tick/lv_tick.h" #include "../tick/lv_tick.h"
#include "../misc/lv_log.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); 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, static void prvTestAndDecrement(lv_thread_sync_t * pxCond,
uint32_t ulLocalWaitingThreads); uint32_t ulLocalWaitingThreads);
#endif #endif
@@ -74,9 +72,13 @@ static void prvTestAndDecrement(lv_thread_sync_t * pxCond,
#if (ESP_PLATFORM) #if (ESP_PLATFORM)
#define _enter_critical() taskENTER_CRITICAL(&critSectionMux); #define _enter_critical() taskENTER_CRITICAL(&critSectionMux);
#define _exit_critical() taskEXIT_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 #else
#define _enter_critical() taskENTER_CRITICAL(); #define _enter_critical() taskENTER_CRITICAL();
#define _exit_critical() taskEXIT_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 #endif
/********************** /**********************
@@ -195,20 +197,20 @@ lv_result_t lv_thread_sync_wait(lv_thread_sync_t * pxCond)
/* If the cond is uninitialized, perform initialization. */ /* If the cond is uninitialized, perform initialization. */
prvCheckCondInit(pxCond); prvCheckCondInit(pxCond);
#if USE_FREERTOS_TASK_NOTIFY #if LV_USE_FREERTOS_TASK_NOTIFY
TaskHandle_t current_task_handle = xTaskGetCurrentTaskHandle(); TaskHandle_t xCurrentTaskHandle = xTaskGetCurrentTaskHandle();
_enter_critical(); _enter_critical();
BaseType_t signal_sent = pxCond->xSyncSignal; BaseType_t xSyncSygnal = pxCond->xSyncSignal;
pxCond->xSyncSignal = pdFALSE; pxCond->xSyncSignal = pdFALSE;
if(signal_sent == pdFALSE) { if(xSyncSygnal == pdFALSE) {
/* The signal hasn't been sent yet. Tell the sender to notify this task */ /* 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 */ /* If we have a signal from the other task, we should not ask to be notified */
_exit_critical(); _exit_critical();
if(signal_sent == pdFALSE) { if(xSyncSygnal == pdFALSE) {
/* Wait for other task to notify this task. */ /* Wait for other task to notify this task. */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); 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. */ /* If the cond is uninitialized, perform initialization. */
prvCheckCondInit(pxCond); prvCheckCondInit(pxCond);
#if USE_FREERTOS_TASK_NOTIFY #if LV_USE_FREERTOS_TASK_NOTIFY
_enter_critical(); _enter_critical();
TaskHandle_t task_to_notify = pxCond->xTaskToNotify; TaskHandle_t xTaskToNotify = pxCond->xTaskToNotify;
pxCond->xTaskToNotify = NULL; pxCond->xTaskToNotify = NULL;
if(task_to_notify == NULL) { if(xTaskToNotify == NULL) {
/* No task waiting to be notified. Send this signal for later */ /* No task waiting to be notified. Send this signal for later */
pxCond->xSyncSignal = pdTRUE; pxCond->xSyncSignal = pdTRUE;
} }
/* If a task is already waiting, there is no need to set the sync signal */ /* If a task is already waiting, there is no need to set the sync signal */
_exit_critical(); _exit_critical();
if(task_to_notify != NULL) { if(xTaskToNotify != NULL) {
/* There is a task waiting. Send a notification to it */ /* 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. */ /* If there was no task waiting to be notified, we sent a signal for it to see later. */
#else #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) 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. */ /* Cleanup all resources used by the cond. */
vSemaphoreDelete(pxCond->xCondWaitSemaphore); vSemaphoreDelete(pxCond->xCondWaitSemaphore);
vSemaphoreDelete(pxCond->xSyncMutex); vSemaphoreDelete(pxCond->xSyncMutex);
pxCond->ulWaitingThreads = 0; pxCond->ulWaitingThreads = 0;
pxCond->xSyncSignal = pdFALSE;
#endif #endif
pxCond->xSyncSignal = pdFALSE;
pxCond->xIsInitialized = pdFALSE; pxCond->xIsInitialized = pdFALSE;
return LV_RESULT_OK; 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; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* If the cond is uninitialized, perform initialization. */ /* If the cond is uninitialized, perform initialization. */
prvCheckCondInit(pxCond); prvCheckCondInitIsr(pxCond);
#if USE_FREERTOS_TASK_NOTIFY #if LV_USE_FREERTOS_TASK_NOTIFY
_enter_critical(); uint32_t mask = _enter_critical_isr();
TaskHandle_t task_to_notify = pxCond->xTaskToNotify; TaskHandle_t xTaskToNotify = pxCond->xTaskToNotify;
pxCond->xTaskToNotify = NULL; pxCond->xTaskToNotify = NULL;
if(task_to_notify == NULL) { if(xTaskToNotify == NULL) {
/* No task waiting to be notified. Send this signal for later */ /* No task waiting to be notified. Send this signal for later */
pxCond->xSyncSignal = pdTRUE; pxCond->xSyncSignal = pdTRUE;
} }
/* If a task is already waiting, there is no need to set the sync signal */ /* 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 */ /* There is a task waiting. Send a notification to it */
vTaskNotifyGiveFromISR(task_to_notify, &xHigherPriorityTaskWoken); vTaskNotifyGiveFromISR(xTaskToNotify, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} }
/* If there was no task waiting to be notified, we sent a signal for it to see later. */ /* If there was no task waiting to be notified, we sent a signal for it to see later. */
#else #else
/* Enter critical section to prevent preemption. */ /* Enter critical section to prevent preemption. */
_enter_critical(); uint32_t mask = _enter_critical_isr();
pxCond->xSyncSignal = pdTRUE; pxCond->xSyncSignal = pdTRUE;
BaseType_t xAnyHigherPriorityTaskWoken = pdFALSE; BaseType_t xAnyHigherPriorityTaskWoken = pdFALSE;
@@ -378,7 +380,7 @@ lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * pxCond)
xHigherPriorityTaskWoken |= xAnyHigherPriorityTaskWoken; xHigherPriorityTaskWoken |= xAnyHigherPriorityTaskWoken;
} }
_exit_critical(); _exit_critical_isr(mask);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif #endif
@@ -470,7 +472,7 @@ static void prvCondInit(lv_thread_sync_t * pxCond)
pxCond->xIsInitialized = pdTRUE; pxCond->xIsInitialized = pdTRUE;
pxCond->xSyncSignal = pdFALSE; pxCond->xSyncSignal = pdFALSE;
#if USE_FREERTOS_TASK_NOTIFY #if LV_USE_FREERTOS_TASK_NOTIFY
pxCond->xTaskToNotify = NULL; pxCond->xTaskToNotify = NULL;
#else #else
pxCond->xCondWaitSemaphore = xSemaphoreCreateCounting(ulMAX_COUNT, 0U); 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, static void prvTestAndDecrement(lv_thread_sync_t * pxCond,
uint32_t ulLocalWaitingThreads) uint32_t ulLocalWaitingThreads)
{ {

View File

@@ -37,14 +37,6 @@ extern "C" {
* DEFINES * 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 * TYPEDEFS
**********************/ **********************/
@@ -64,7 +56,7 @@ typedef struct {
BaseType_t BaseType_t
xIsInitialized; /**< Set to pdTRUE if this condition variable is initialized, pdFALSE otherwise. */ 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. */ 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. */ TaskHandle_t xTaskToNotify; /**< The task waiting to be signalled. NULL if nothing is waiting. */
#else #else
SemaphoreHandle_t xCondWaitSemaphore; /**< Threads block on this semaphore in lv_thread_sync_wait. */ SemaphoreHandle_t xCondWaitSemaphore; /**< Threads block on this semaphore in lv_thread_sync_wait. */