1 /**
2 * @file lv_freertos.c
3 *
4 */
5
6 /**
7 * Copyright 2023 NXP
8 *
9 * SPDX-License-Identifier: MIT
10 */
11
12 /*********************
13 * INCLUDES
14 *********************/
15 #include "lv_os.h"
16 #if LV_USE_OS == LV_OS_FREERTOS
17
18 #include "atomic.h"
19
20 #include "../tick/lv_tick.h"
21 #include "../misc/lv_log.h"
22 #include "../core/lv_global.h"
23
24 /*********************
25 * DEFINES
26 *********************/
27
28 #define ulMAX_COUNT 10U
29
30 #define globals LV_GLOBAL_DEFAULT()
31
32 /**********************
33 * TYPEDEFS
34 **********************/
35
36 /**********************
37 * STATIC PROTOTYPES
38 **********************/
39
40 static void prvRunThread(void * pxArg);
41
42 static void prvMutexInit(lv_mutex_t * pxMutex);
43
44 static void prvCheckMutexInit(lv_mutex_t * pxMutex);
45
46 static void prvCondInit(lv_thread_sync_t * pxCond);
47
48 static void prvCheckCondInit(lv_thread_sync_t * pxCond);
49
50 static void prvCheckCondInitIsr(lv_thread_sync_t * pxCond);
51
52 #if !LV_USE_FREERTOS_TASK_NOTIFY
53 static void prvTestAndDecrement(lv_thread_sync_t * pxCond,
54 uint32_t ulLocalWaitingThreads);
55 #endif
56
57 /**********************
58 * STATIC VARIABLES
59 **********************/
60
61 #ifdef ESP_PLATFORM
62 static portMUX_TYPE critSectionMux = portMUX_INITIALIZER_UNLOCKED;
63 #endif
64
65 /**********************
66 * MACROS
67 **********************/
68
69 #ifdef ESP_PLATFORM
70 #define _enter_critical() taskENTER_CRITICAL(&critSectionMux);
71 #define _exit_critical() taskEXIT_CRITICAL(&critSectionMux);
72 #define _enter_critical_isr() taskENTER_CRITICAL_FROM_ISR();
73 #define _exit_critical_isr(x) taskEXIT_CRITICAL_FROM_ISR(x);
74 #else
75 #define _enter_critical() taskENTER_CRITICAL();
76 #define _exit_critical() taskEXIT_CRITICAL();
77 #define _enter_critical_isr() taskENTER_CRITICAL_FROM_ISR();
78 #define _exit_critical_isr(x) taskEXIT_CRITICAL_FROM_ISR(x);
79 #endif
80
81 /**********************
82 * GLOBAL FUNCTIONS
83 **********************/
84
lv_thread_init(lv_thread_t * pxThread,const char * const name,lv_thread_prio_t xSchedPriority,void (* pvStartRoutine)(void *),size_t usStackSize,void * xAttr)85 lv_result_t lv_thread_init(lv_thread_t * pxThread, const char * const name,
86 lv_thread_prio_t xSchedPriority,
87 void (*pvStartRoutine)(void *), size_t usStackSize,
88 void * xAttr)
89 {
90 pxThread->pTaskArg = xAttr;
91 pxThread->pvStartRoutine = pvStartRoutine;
92
93 BaseType_t xTaskCreateStatus = xTaskCreate(
94 prvRunThread,
95 name,
96 (configSTACK_DEPTH_TYPE)(usStackSize / sizeof(StackType_t)),
97 (void *)pxThread,
98 tskIDLE_PRIORITY + xSchedPriority,
99 &pxThread->xTaskHandle);
100
101 /* Ensure that the FreeRTOS task was successfully created. */
102 if(xTaskCreateStatus != pdPASS) {
103 LV_LOG_ERROR("xTaskCreate failed!");
104 return LV_RESULT_INVALID;
105 }
106
107 return LV_RESULT_OK;
108 }
109
lv_thread_delete(lv_thread_t * pxThread)110 lv_result_t lv_thread_delete(lv_thread_t * pxThread)
111 {
112 vTaskDelete(pxThread->xTaskHandle);
113
114 return LV_RESULT_OK;
115 }
116
lv_mutex_init(lv_mutex_t * pxMutex)117 lv_result_t lv_mutex_init(lv_mutex_t * pxMutex)
118 {
119 /* If mutex in uninitialized, perform initialization. */
120 prvCheckMutexInit(pxMutex);
121
122 return LV_RESULT_OK;
123 }
124
lv_mutex_lock(lv_mutex_t * pxMutex)125 lv_result_t lv_mutex_lock(lv_mutex_t * pxMutex)
126 {
127 /* If mutex in uninitialized, perform initialization. */
128 prvCheckMutexInit(pxMutex);
129
130 BaseType_t xMutexTakeStatus = xSemaphoreTakeRecursive(pxMutex->xMutex, portMAX_DELAY);
131 if(xMutexTakeStatus != pdTRUE) {
132 LV_LOG_ERROR("xSemaphoreTake failed!");
133 return LV_RESULT_INVALID;
134 }
135
136 return LV_RESULT_OK;
137 }
138
lv_mutex_lock_isr(lv_mutex_t * pxMutex)139 lv_result_t lv_mutex_lock_isr(lv_mutex_t * pxMutex)
140 {
141 /* If mutex in uninitialized, perform initialization. */
142 prvCheckMutexInit(pxMutex);
143
144 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
145
146 BaseType_t xMutexTakeStatus = xSemaphoreTakeFromISR(pxMutex->xMutex, &xHigherPriorityTaskWoken);
147 if(xMutexTakeStatus != pdTRUE) {
148 LV_LOG_ERROR("xSemaphoreTake failed!");
149 return LV_RESULT_INVALID;
150 }
151
152 /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
153 should be performed to ensure the interrupt returns directly to the highest
154 priority task. The macro used for this purpose is dependent on the port in
155 use and may be called portEND_SWITCHING_ISR(). */
156 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
157
158 return LV_RESULT_OK;
159 }
160
lv_mutex_unlock(lv_mutex_t * pxMutex)161 lv_result_t lv_mutex_unlock(lv_mutex_t * pxMutex)
162 {
163 /* If mutex in uninitialized, perform initialization. */
164 prvCheckMutexInit(pxMutex);
165
166 BaseType_t xMutexGiveStatus = xSemaphoreGiveRecursive(pxMutex->xMutex);
167 if(xMutexGiveStatus != pdTRUE) {
168 LV_LOG_ERROR("xSemaphoreGive failed!");
169 return LV_RESULT_INVALID;
170 }
171
172 return LV_RESULT_OK;
173 }
174
lv_mutex_delete(lv_mutex_t * pxMutex)175 lv_result_t lv_mutex_delete(lv_mutex_t * pxMutex)
176 {
177 if(pxMutex->xIsInitialized == pdFALSE)
178 return LV_RESULT_INVALID;
179 vSemaphoreDelete(pxMutex->xMutex);
180 pxMutex->xIsInitialized = pdFALSE;
181
182 return LV_RESULT_OK;
183 }
184
lv_thread_sync_init(lv_thread_sync_t * pxCond)185 lv_result_t lv_thread_sync_init(lv_thread_sync_t * pxCond)
186 {
187 /* If the cond is uninitialized, perform initialization. */
188 prvCheckCondInit(pxCond);
189
190 return LV_RESULT_OK;
191 }
192
lv_thread_sync_wait(lv_thread_sync_t * pxCond)193 lv_result_t lv_thread_sync_wait(lv_thread_sync_t * pxCond)
194 {
195 lv_result_t lvRes = LV_RESULT_OK;
196
197 /* If the cond is uninitialized, perform initialization. */
198 prvCheckCondInit(pxCond);
199
200 #if LV_USE_FREERTOS_TASK_NOTIFY
201 TaskHandle_t xCurrentTaskHandle = xTaskGetCurrentTaskHandle();
202
203 _enter_critical();
204 BaseType_t xSyncSygnal = pxCond->xSyncSignal;
205 pxCond->xSyncSignal = pdFALSE;
206 if(xSyncSygnal == pdFALSE) {
207 /* The signal hasn't been sent yet. Tell the sender to notify this task */
208 pxCond->xTaskToNotify = xCurrentTaskHandle;
209 }
210 /* If we have a signal from the other task, we should not ask to be notified */
211 _exit_critical();
212
213 if(xSyncSygnal == pdFALSE) {
214 /* Wait for other task to notify this task. */
215 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
216 }
217 /* If the signal was received, no wait needs to be done */
218 #else
219 uint32_t ulLocalWaitingThreads;
220
221 /* Acquire the mutex. */
222 xSemaphoreTake(pxCond->xSyncMutex, portMAX_DELAY);
223
224 while(!pxCond->xSyncSignal) {
225 /* Increase the counter of threads blocking on condition variable, then
226 * release the mutex. */
227
228 /* Atomically increments thread waiting by 1, and
229 * stores number of threads waiting before increment. */
230 ulLocalWaitingThreads = Atomic_Increment_u32(&pxCond->ulWaitingThreads);
231
232 BaseType_t xMutexStatus = xSemaphoreGive(pxCond->xSyncMutex);
233
234 /* Wait on the condition variable. */
235 if(xMutexStatus == pdTRUE) {
236 BaseType_t xCondWaitStatus = xSemaphoreTake(
237 pxCond->xCondWaitSemaphore,
238 portMAX_DELAY);
239
240 /* Relock the mutex. */
241 xSemaphoreTake(pxCond->xSyncMutex, portMAX_DELAY);
242
243 if(xCondWaitStatus != pdTRUE) {
244 LV_LOG_ERROR("xSemaphoreTake(xCondWaitSemaphore) failed!");
245 lvRes = LV_RESULT_INVALID;
246
247 /* Atomically decrements thread waiting by 1.
248 * If iLocalWaitingThreads is updated by other thread(s) in between,
249 * this implementation guarantees to decrement by 1 based on the
250 * value currently in pxCond->ulWaitingThreads. */
251 prvTestAndDecrement(pxCond, ulLocalWaitingThreads + 1);
252 }
253 }
254 else {
255 LV_LOG_ERROR("xSemaphoreGive(xSyncMutex) failed!");
256 lvRes = LV_RESULT_INVALID;
257
258 /* Atomically decrements thread waiting by 1.
259 * If iLocalWaitingThreads is updated by other thread(s) in between,
260 * this implementation guarantees to decrement by 1 based on the
261 * value currently in pxCond->ulWaitingThreads. */
262 prvTestAndDecrement(pxCond, ulLocalWaitingThreads + 1);
263 }
264 }
265
266 pxCond->xSyncSignal = pdFALSE;
267
268 /* Release the mutex. */
269 xSemaphoreGive(pxCond->xSyncMutex);
270 #endif
271
272 return lvRes;
273 }
274
lv_thread_sync_signal(lv_thread_sync_t * pxCond)275 lv_result_t lv_thread_sync_signal(lv_thread_sync_t * pxCond)
276 {
277 /* If the cond is uninitialized, perform initialization. */
278 prvCheckCondInit(pxCond);
279
280 #if LV_USE_FREERTOS_TASK_NOTIFY
281 _enter_critical();
282 TaskHandle_t xTaskToNotify = pxCond->xTaskToNotify;
283 pxCond->xTaskToNotify = NULL;
284 if(xTaskToNotify == NULL) {
285 /* No task waiting to be notified. Send this signal for later */
286 pxCond->xSyncSignal = pdTRUE;
287 }
288 /* If a task is already waiting, there is no need to set the sync signal */
289 _exit_critical();
290
291 if(xTaskToNotify != NULL) {
292 /* There is a task waiting. Send a notification to it */
293 xTaskNotifyGive(xTaskToNotify);
294 }
295 /* If there was no task waiting to be notified, we sent a signal for it to see later. */
296 #else
297 /* Acquire the mutex. */
298 xSemaphoreTake(pxCond->xSyncMutex, portMAX_DELAY);
299
300 pxCond->xSyncSignal = pdTRUE;
301
302 /* Local copy of number of threads waiting. */
303 uint32_t ulLocalWaitingThreads = pxCond->ulWaitingThreads;
304
305 /* Test local copy of threads waiting is larger than zero. */
306 while(ulLocalWaitingThreads > 0) {
307 /* Atomically check whether the copy in memory has changed.
308 * If not, set the copy of threads waiting in memory to zero. */
309 if(ATOMIC_COMPARE_AND_SWAP_SUCCESS == Atomic_CompareAndSwap_u32(
310 &pxCond->ulWaitingThreads,
311 0,
312 ulLocalWaitingThreads)) {
313 /* Unblock all. */
314 for(uint32_t i = 0; i < ulLocalWaitingThreads; i++) {
315 xSemaphoreGive(pxCond->xCondWaitSemaphore);
316 }
317
318 break;
319 }
320
321 /* Local copy is out dated. Reload from memory and retry. */
322 ulLocalWaitingThreads = pxCond->ulWaitingThreads;
323 }
324
325 /* Release the mutex. */
326 xSemaphoreGive(pxCond->xSyncMutex);
327 #endif
328
329 return LV_RESULT_OK;
330 }
331
lv_thread_sync_delete(lv_thread_sync_t * pxCond)332 lv_result_t lv_thread_sync_delete(lv_thread_sync_t * pxCond)
333 {
334 #if !LV_USE_FREERTOS_TASK_NOTIFY
335 /* Cleanup all resources used by the cond. */
336 vSemaphoreDelete(pxCond->xCondWaitSemaphore);
337 vSemaphoreDelete(pxCond->xSyncMutex);
338 pxCond->ulWaitingThreads = 0;
339 #endif
340 pxCond->xSyncSignal = pdFALSE;
341 pxCond->xIsInitialized = pdFALSE;
342
343 return LV_RESULT_OK;
344 }
345
lv_thread_sync_signal_isr(lv_thread_sync_t * pxCond)346 lv_result_t lv_thread_sync_signal_isr(lv_thread_sync_t * pxCond)
347 {
348 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
349
350 /* If the cond is uninitialized, perform initialization. */
351 prvCheckCondInitIsr(pxCond);
352
353 #if LV_USE_FREERTOS_TASK_NOTIFY
354 uint32_t mask = _enter_critical_isr();
355 TaskHandle_t xTaskToNotify = pxCond->xTaskToNotify;
356 pxCond->xTaskToNotify = NULL;
357 if(xTaskToNotify == NULL) {
358 /* No task waiting to be notified. Send this signal for later */
359 pxCond->xSyncSignal = pdTRUE;
360 }
361 /* If a task is already waiting, there is no need to set the sync signal */
362 _exit_critical_isr(mask);
363
364 if(xTaskToNotify != NULL) {
365 /* There is a task waiting. Send a notification to it */
366 vTaskNotifyGiveFromISR(xTaskToNotify, &xHigherPriorityTaskWoken);
367 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
368 }
369 /* If there was no task waiting to be notified, we sent a signal for it to see later. */
370 #else
371 /* Enter critical section to prevent preemption. */
372 uint32_t mask = _enter_critical_isr();
373
374 pxCond->xSyncSignal = pdTRUE;
375 BaseType_t xAnyHigherPriorityTaskWoken = pdFALSE;
376
377 /* Unblock all. */
378 for(uint32_t i = 0; i < pxCond->ulWaitingThreads; i++) {
379 xSemaphoreGiveFromISR(pxCond->xCondWaitSemaphore, &xAnyHigherPriorityTaskWoken);
380 xHigherPriorityTaskWoken |= xAnyHigherPriorityTaskWoken;
381 }
382
383 _exit_critical_isr(mask);
384 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
385 #endif
386
387 return LV_RESULT_OK;
388 }
389
390
lv_freertos_task_switch_in(const char * name)391 void lv_freertos_task_switch_in(const char * name)
392 {
393 if(lv_strcmp(name, "IDLE")) globals->freertos_idle_task_running = false;
394 else globals->freertos_idle_task_running = true;
395
396 globals->freertos_task_switch_timestamp = lv_tick_get();
397 }
398
lv_freertos_task_switch_out(void)399 void lv_freertos_task_switch_out(void)
400 {
401 uint32_t elaps = lv_tick_elaps(globals->freertos_task_switch_timestamp);
402 if(globals->freertos_idle_task_running) globals->freertos_idle_time_sum += elaps;
403 else globals->freertos_non_idle_time_sum += elaps;
404 }
405
lv_os_get_idle_percent(void)406 uint32_t lv_os_get_idle_percent(void)
407 {
408 if(globals->freertos_non_idle_time_sum + globals->freertos_idle_time_sum == 0) {
409 LV_LOG_WARN("Not enough time elapsed to provide idle percentage");
410 return 0;
411 }
412
413 uint32_t pct = (globals->freertos_idle_time_sum * 100) / (globals->freertos_idle_time_sum +
414 globals->freertos_non_idle_time_sum);
415
416 globals->freertos_non_idle_time_sum = 0;
417 globals->freertos_idle_time_sum = 0;
418
419 return pct;
420 }
421
422 /**********************
423 * STATIC FUNCTIONS
424 **********************/
425
prvRunThread(void * pxArg)426 static void prvRunThread(void * pxArg)
427 {
428 lv_thread_t * pxThread = (lv_thread_t *)pxArg;
429
430 /* Run the thread routine. */
431 pxThread->pvStartRoutine((void *)pxThread->pTaskArg);
432
433 vTaskDelete(NULL);
434 }
435
prvMutexInit(lv_mutex_t * pxMutex)436 static void prvMutexInit(lv_mutex_t * pxMutex)
437 {
438 pxMutex->xMutex = xSemaphoreCreateRecursiveMutex();
439
440 /* Ensure that the FreeRTOS mutex was successfully created. */
441 if(pxMutex->xMutex == NULL) {
442 LV_LOG_ERROR("xSemaphoreCreateMutex failed!");
443 return;
444 }
445
446 /* Mutex successfully created. */
447 pxMutex->xIsInitialized = pdTRUE;
448 }
449
prvCheckMutexInit(lv_mutex_t * pxMutex)450 static void prvCheckMutexInit(lv_mutex_t * pxMutex)
451 {
452 /* Check if the mutex needs to be initialized. */
453 if(pxMutex->xIsInitialized == pdFALSE) {
454 /* Mutex initialization must be in a critical section to prevent two threads
455 * from initializing it at the same time. */
456 _enter_critical();
457
458 /* Check again that the mutex is still uninitialized, i.e. it wasn't
459 * initialized while this function was waiting to enter the critical
460 * section. */
461 if(pxMutex->xIsInitialized == pdFALSE) {
462 prvMutexInit(pxMutex);
463 }
464
465 /* Exit the critical section. */
466 _exit_critical();
467 }
468 }
469
prvCondInit(lv_thread_sync_t * pxCond)470 static void prvCondInit(lv_thread_sync_t * pxCond)
471 {
472 pxCond->xIsInitialized = pdTRUE;
473 pxCond->xSyncSignal = pdFALSE;
474
475 #if LV_USE_FREERTOS_TASK_NOTIFY
476 pxCond->xTaskToNotify = NULL;
477 #else
478 pxCond->xCondWaitSemaphore = xSemaphoreCreateCounting(ulMAX_COUNT, 0U);
479
480 /* Ensure that the FreeRTOS semaphore was successfully created. */
481 if(pxCond->xCondWaitSemaphore == NULL) {
482 LV_LOG_ERROR("xSemaphoreCreateCounting failed!");
483 return;
484 }
485
486 pxCond->xSyncMutex = xSemaphoreCreateMutex();
487
488 /* Ensure that the FreeRTOS mutex was successfully created. */
489 if(pxCond->xSyncMutex == NULL) {
490 LV_LOG_ERROR("xSemaphoreCreateMutex failed!");
491 /* Cleanup. */
492 vSemaphoreDelete(pxCond->xCondWaitSemaphore);
493 return;
494 }
495
496 /* Condition variable successfully created. */
497 pxCond->ulWaitingThreads = 0;
498 #endif
499 }
500
prvCheckCondInit(lv_thread_sync_t * pxCond)501 static void prvCheckCondInit(lv_thread_sync_t * pxCond)
502 {
503 /* Check if the condition variable needs to be initialized. */
504 if(pxCond->xIsInitialized == pdFALSE) {
505 /* Cond initialization must be in a critical section to prevent two
506 * threads from initializing it at the same time. */
507 _enter_critical();
508
509 /* Check again that the condition is still uninitialized, i.e. it wasn't
510 * initialized while this function was waiting to enter the critical
511 * section. */
512 if(pxCond->xIsInitialized == pdFALSE) {
513 prvCondInit(pxCond);
514 }
515
516 /* Exit the critical section. */
517 _exit_critical();
518 }
519 }
520
prvCheckCondInitIsr(lv_thread_sync_t * pxCond)521 static void prvCheckCondInitIsr(lv_thread_sync_t * pxCond)
522 {
523 /* Check if the condition variable needs to be initialized. */
524 if(pxCond->xIsInitialized == pdFALSE) {
525 /* Cond initialization must be in a critical section to prevent two
526 * threads from initializing it at the same time. */
527 uint32_t mask = _enter_critical_isr();
528
529 /* Check again that the condition is still uninitialized, i.e. it wasn't
530 * initialized while this function was waiting to enter the critical
531 * section. */
532 if(pxCond->xIsInitialized == pdFALSE) {
533 prvCondInit(pxCond);
534 }
535
536 /* Exit the critical section. */
537 _exit_critical_isr(mask);
538 }
539 }
540
541 #if !LV_USE_FREERTOS_TASK_NOTIFY
prvTestAndDecrement(lv_thread_sync_t * pxCond,uint32_t ulLocalWaitingThreads)542 static void prvTestAndDecrement(lv_thread_sync_t * pxCond,
543 uint32_t ulLocalWaitingThreads)
544 {
545 /* Test local copy of threads waiting is larger than zero. */
546 while(ulLocalWaitingThreads > 0) {
547 /* Atomically check whether the copy in memory has changed.
548 * If not, decrease the copy of threads waiting in memory. */
549 if(ATOMIC_COMPARE_AND_SWAP_SUCCESS == Atomic_CompareAndSwap_u32(
550 &pxCond->ulWaitingThreads,
551 ulLocalWaitingThreads - 1,
552 ulLocalWaitingThreads)) {
553 /* Signal one succeeded. Break. */
554 break;
555 }
556
557 /* Local copy may be out dated. Reload from memory and retry. */
558 ulLocalWaitingThreads = pxCond->ulWaitingThreads;
559 }
560 }
561 #endif
562
563 #endif /*LV_USE_OS == LV_OS_FREERTOS*/
564