LCOV - code coverage report
Current view: top level - libs/bsw/asyncFreeRtos/include/async - FreeRtosAdapter.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.1 % 115 114
Test Date: 2026-09-11 12:05:06 Functions: 100.0 % 38 38

            Line data    Source code
       1              : /********************************************************************************
       2              :  * Copyright (c) 2024 Accenture
       3              :  *
       4              :  * This program and the accompanying materials are made available under the
       5              :  * terms of the Apache License Version 2.0 which is available at
       6              :  * https://www.apache.org/licenses/LICENSE-2.0
       7              :  *
       8              :  * SPDX-License-Identifier: Apache-2.0
       9              :  ********************************************************************************/
      10              : 
      11              : /**
      12              :  * \ingroup async
      13              :  */
      14              : #pragma once
      15              : 
      16              : #include "async/TaskContext.h"
      17              : #include "async/TaskInitializer.h"
      18              : 
      19              : #include <etl/array.h>
      20              : #include <etl/error_handler.h>
      21              : 
      22              : namespace async
      23              : {
      24              : namespace internal
      25              : {
      26              : template<bool HasNestedInterrupts = (ASYNC_CONFIG_NESTED_INTERRUPTS != 0)>
      27              : class NestedInterruptLock : public LockType
      28              : {};
      29              : 
      30              : template<>
      31              : class NestedInterruptLock<false>
      32              : {};
      33              : 
      34              : template<size_t N, typename TaskConfig = ASYNC_TASK_CONFIG_TYPE>
      35              : class TaskConfigHolder
      36              : {
      37              : public:
      38              :     using TaskConfigType = TaskConfig;
      39              : 
      40              :     static void setTaskConfig(size_t taskIdx, TaskConfigType const& taskConfig);
      41              : 
      42              :     static TaskConfigType const* getTaskConfig(size_t taskIdx);
      43              : 
      44              : private:
      45              :     static ::etl::array<TaskConfigType, N> _taskConfigs;
      46              : };
      47              : 
      48              : template<size_t N>
      49              : class TaskConfigHolder<N, void>
      50              : {
      51              : public:
      52              :     struct TaskConfigType
      53              :     {};
      54              : 
      55              :     static void setTaskConfig(size_t taskIdx, TaskConfigType const& taskConfig);
      56              : 
      57              :     static TaskConfigType const* getTaskConfig(size_t taskIdx);
      58              : };
      59              : 
      60              : } // namespace internal
      61              : 
      62              : /**
      63              :  * Adapter class bridging FreeRTOS functionalities with the application's binding.
      64              :  *
      65              :  * The `FreeRtosAdapter` class serves as a centralized interface for managing tasks, timers,
      66              :  * and scheduling functionalities within a FreeRTOS environment. It utilizes the specified
      67              :  * `Binding` type to adapt and configure task-related components, such as `TaskContext`,
      68              :  * `TaskConfig`, and idle and timer tasks.
      69              :  *
      70              :  * \tparam Binding The binding type specifying application-specific configurations.
      71              :  */
      72              : template<class Binding>
      73              : class FreeRtosAdapter
      74              : {
      75              : public:
      76              :     static size_t const TASK_COUNT    = Binding::TASK_COUNT;
      77              :     static size_t const OS_TASK_COUNT = TASK_COUNT + 1U;
      78              :     static TickType_t const WAIT_EVENTS_TICK_COUNT
      79              :         = static_cast<TickType_t>(Binding::WAIT_EVENTS_TICK_COUNT);
      80              :     static ContextType const TASK_IDLE  = 0U;
      81              :     static ContextType const TASK_TIMER = static_cast<ContextType>(TASK_COUNT);
      82              : 
      83              :     using AdapterType = FreeRtosAdapter<Binding>;
      84              : 
      85              :     using TaskContextType  = TaskContext<AdapterType>;
      86              :     using TaskFunctionType = typename TaskContextType::TaskFunctionType;
      87              : 
      88              :     using TaskConfigsType = internal::TaskConfigHolder<OS_TASK_COUNT>;
      89              :     using TaskConfigType  = typename TaskConfigsType::TaskConfigType;
      90              : 
      91              :     using StartAppFunctionType = ::etl::delegate<void()>;
      92              : 
      93              :     template<size_t StackSize>
      94              :     using Stack           = internal::Stack<StackSize>;
      95              :     using TaskInitializer = internal::TaskInitializer<AdapterType>;
      96              : 
      97              :     template<size_t StackSize = 0U>
      98              :     using IdleTask = internal::IdleTask<AdapterType, StackSize>;
      99              :     template<size_t StackSize = 0U>
     100              :     using TimerTask = internal::TimerTask<AdapterType, StackSize>;
     101              :     template<ContextType Context, size_t StackSize = 0U>
     102              :     using Task = internal::Task<AdapterType, Context, StackSize>;
     103              :     template<ContextType Context>
     104              :     using TaskStack = internal::Task<AdapterType, Context>;
     105              : 
     106              :     /// Struct representing the stack usage for a specific task.
     107              :     struct StackUsage
     108              :     {
     109              :         StackUsage();
     110              : 
     111              :         uint32_t _stackSize;
     112              :         uint32_t _usedSize;
     113              :     };
     114              : 
     115              :     /**
     116              :      * Retrieves memory pointers for a FreeRTOS task.
     117              :      *
     118              :      * \tparam Context The task context.
     119              :      * \param ppxTaskTCBBuffer Pointer to the task control block buffer.
     120              :      * \param ppxTaskStackBuffer Pointer to the task stack buffer.
     121              :      * \param pulTaskStackSize Pointer to the task stack size.
     122              :      */
     123              :     template<ContextType Context>
     124              :     static void getTaskMemory(
     125              :         StaticTask_t** ppxTaskTCBBuffer,
     126              :         StackType_t** ppxTaskStackBuffer,
     127              :         uint32_t* pulTaskStackSize);
     128              : 
     129              :     static char const* getTaskName(size_t taskIdx);
     130              : 
     131              :     static TaskHandle_t getTaskHandle(size_t taskIdx);
     132              : 
     133              :     static TaskConfigType const* getTaskConfig(size_t taskIdx);
     134              : 
     135              :     static ContextType getCurrentTaskContext();
     136              : 
     137              :     /**
     138              :      * Runs the adapter. All tasks are initialized and the scheduler is started.
     139              :      * Once the async functions can safely be called the application defined startApp
     140              :      * function will be called.
     141              :      *
     142              :      * \param startApp The function to start the application.
     143              :      */
     144              :     static void run(StartAppFunctionType startApp);
     145              : 
     146              :     static void runningHook();
     147              : 
     148              :     /**
     149              :      * Retrieves the stack usage for a specified task.
     150              :      *
     151              :      * \param taskIdx The index of the task.
     152              :      * \param stackUsage A reference to the StackUsage struct to populate.
     153              :      * \return True if stack usage information is successfully retrieved.
     154              :      */
     155              :     static bool getStackUsage(size_t taskIdx, StackUsage& stackUsage);
     156              : 
     157              :     static void callIdleTaskFunction();
     158              : 
     159              :     /**
     160              :      * Executes a specified runnable within a given context.
     161              :      *
     162              :      * \param context The task context.
     163              :      * \param runnable The runnable to execute.
     164              :      */
     165              :     static void execute(ContextType context, RunnableType& runnable);
     166              : 
     167              :     /**
     168              :      * Schedules a runnable to execute after a delay.
     169              :      *
     170              :      * \param context The task context.
     171              :      * \param runnable The runnable to schedule.
     172              :      * \param timeout The timeout associated with the runnable.
     173              :      * \param delay The delay before execution.
     174              :      * \param unit The time unit for the delay.
     175              :      */
     176              :     static void schedule(
     177              :         ContextType context,
     178              :         RunnableType& runnable,
     179              :         TimeoutType& timeout,
     180              :         uint32_t delay,
     181              :         TimeUnitType unit);
     182              : 
     183              :     /**
     184              :      * Schedules a runnable to execute at a fixed rate.
     185              :      *
     186              :      * \param context The task context.
     187              :      * \param runnable The runnable to schedule.
     188              :      * \param timeout The timeout associated with the runnable.
     189              :      * \param delay The delay before the first execution.
     190              :      * \param unit The time unit for the delay.
     191              :      */
     192              :     static void scheduleAtFixedRate(
     193              :         ContextType context,
     194              :         RunnableType& runnable,
     195              :         TimeoutType& timeout,
     196              :         uint32_t delay,
     197              :         TimeUnitType unit);
     198              : 
     199              :     /**
     200              :      * Cancels a scheduled runnable.
     201              :      *
     202              :      * \param timeout The timeout associated with the runnable to cancel.
     203              :      */
     204              :     static void cancel(TimeoutType& timeout);
     205              : 
     206              :     /**
     207              :      * Suspends the task such that it is no longer scheduled by FreeRTOS.
     208              :      */
     209              :     static void suspend(ContextType context);
     210              : 
     211              :     /**
     212              :      * Resumes the task such that it is scheduled by FreeRTOS.
     213              :      */
     214              :     static void resume(ContextType context);
     215              : 
     216              :     /// Notifies the system of an interrupt entry.
     217              :     static void enterIsr();
     218              : 
     219              :     /// Notifies the system of an interrupt exit.
     220              :     static void leaveIsr();
     221              : 
     222              :     /**
     223              :      * Exits the ISR without yielding control.
     224              :      * \return True if yielding is not required after ISR.
     225              :      */
     226              :     static bool leaveIsrNoYield();
     227              : 
     228              :     /// \return A pointer to the task woken by the ISR.
     229              :     static BaseType_t* getHigherPriorityTaskWoken();
     230              : 
     231              : private:
     232              :     friend struct internal::TaskInitializer<AdapterType>;
     233              : 
     234              :     static void initTask(TaskInitializer& initializer);
     235              : 
     236              :     static void TaskFunction(void* param);
     237              : 
     238              :     static StartAppFunctionType _startApp;
     239              :     static TaskInitializer* _idleTaskInitializer;
     240              :     static TaskInitializer* _timerTaskInitializer;
     241              :     static ::etl::array<TaskContextType, TASK_COUNT> _taskContexts;
     242              :     static ::etl::array<uint32_t, OS_TASK_COUNT> _stackSizes;
     243              :     static char const* _timerTaskName;
     244              :     static BaseType_t _higherPriorityTaskWokenFlag;
     245              :     static BaseType_t* _higherPriorityTaskWoken;
     246              :     static uint8_t _nestedInterruptCount;
     247              : };
     248              : 
     249              : /**
     250              :  * Inline implementations.
     251              :  */
     252              : template<class Binding>
     253              : typename FreeRtosAdapter<Binding>::StartAppFunctionType FreeRtosAdapter<Binding>::_startApp;
     254              : template<class Binding>
     255              : typename FreeRtosAdapter<Binding>::TaskInitializer* FreeRtosAdapter<Binding>::_idleTaskInitializer
     256              :     = nullptr;
     257              : template<class Binding>
     258              : typename FreeRtosAdapter<Binding>::TaskInitializer* FreeRtosAdapter<Binding>::_timerTaskInitializer
     259              :     = nullptr;
     260              : template<class Binding>
     261              : ::etl::
     262              :     array<typename FreeRtosAdapter<Binding>::TaskContextType, FreeRtosAdapter<Binding>::TASK_COUNT>
     263              :         FreeRtosAdapter<Binding>::_taskContexts;
     264              : template<class Binding>
     265              : ::etl::array<uint32_t, FreeRtosAdapter<Binding>::OS_TASK_COUNT>
     266              :     FreeRtosAdapter<Binding>::_stackSizes;
     267              : template<class Binding>
     268              : char const* FreeRtosAdapter<Binding>::_timerTaskName;
     269              : template<class Binding>
     270              : BaseType_t FreeRtosAdapter<Binding>::_higherPriorityTaskWokenFlag = 0;
     271              : template<class Binding>
     272              : BaseType_t* FreeRtosAdapter<Binding>::_higherPriorityTaskWoken = nullptr;
     273              : template<class Binding>
     274              : uint8_t FreeRtosAdapter<Binding>::_nestedInterruptCount = 0U;
     275              : 
     276              : template<class Binding>
     277              : template<ContextType Context>
     278            2 : void FreeRtosAdapter<Binding>::getTaskMemory(
     279              :     StaticTask_t** const ppxTaskTCBBuffer,
     280              :     StackType_t** const ppxTaskStackBuffer,
     281              :     uint32_t* const pulTaskStackSize)
     282              : {
     283            2 :     TaskInitializer& initializer
     284              :         = *((Context == TASK_IDLE) ? _idleTaskInitializer : _timerTaskInitializer);
     285            2 :     ETL_ASSERT(ppxTaskTCBBuffer != nullptr, ETL_ERROR_GENERIC("tcb buffer must not be null"));
     286            2 :     ETL_ASSERT(ppxTaskStackBuffer != nullptr, ETL_ERROR_GENERIC("stack buffer must not be null"));
     287            2 :     ETL_ASSERT(pulTaskStackSize != nullptr, ETL_ERROR_GENERIC("stack size must not be null"));
     288            2 :     *ppxTaskTCBBuffer   = &initializer._task;
     289            2 :     *ppxTaskStackBuffer = initializer._stack.data();
     290              :     // Conversion to uint32_t is OK, stack will not exceed 4GB.
     291            2 :     *pulTaskStackSize   = static_cast<uint32_t>(initializer._stack.size());
     292            2 : }
     293              : 
     294              : template<class Binding>
     295            4 : inline char const* FreeRtosAdapter<Binding>::getTaskName(size_t const taskIdx)
     296              : {
     297            4 :     return (taskIdx < _taskContexts.size()) ? _taskContexts[taskIdx].getName() : _timerTaskName;
     298              : }
     299              : 
     300              : template<class Binding>
     301            1 : inline TaskHandle_t FreeRtosAdapter<Binding>::getTaskHandle(size_t const taskIdx)
     302              : {
     303            1 :     return _taskContexts[taskIdx].getTaskHandle();
     304              : }
     305              : 
     306              : template<class Binding>
     307              : inline typename FreeRtosAdapter<Binding>::TaskConfigType const*
     308            1 : FreeRtosAdapter<Binding>::getTaskConfig(size_t const taskIdx)
     309              : {
     310            0 :     return TaskConfigsType::getTaskConfig(taskIdx);
     311              : }
     312              : 
     313              : template<class Binding>
     314            7 : ContextType FreeRtosAdapter<Binding>::getCurrentTaskContext()
     315              : {
     316            7 :     if (_higherPriorityTaskWoken == nullptr)
     317              :     {
     318            6 :         return static_cast<ContextType>(uxTaskGetTaskNumber(xTaskGetCurrentTaskHandle()));
     319              :     }
     320            1 :     return CONTEXT_INVALID;
     321              : }
     322              : 
     323              : template<class Binding>
     324            9 : void FreeRtosAdapter<Binding>::initTask(TaskInitializer& initializer)
     325              : {
     326            9 :     ContextType const context                 = initializer._context;
     327            9 :     _stackSizes[static_cast<size_t>(context)] = static_cast<uint32_t>(
     328            9 :         static_cast<size_t>(initializer._stack.size()) * sizeof(BaseType_t));
     329            9 :     TaskConfigsType::setTaskConfig(context, initializer._config);
     330            9 :     if (context == TASK_TIMER)
     331              :     {
     332            2 :         _timerTaskInitializer = &initializer;
     333            2 :         _timerTaskName        = initializer._name;
     334              :     }
     335              :     else
     336              :     {
     337            7 :         TaskContextType& taskContext = _taskContexts[static_cast<size_t>(context)];
     338            7 :         if (context == TASK_IDLE)
     339              :         {
     340            1 :             _idleTaskInitializer = &initializer;
     341            1 :             taskContext.initTask(TASK_IDLE, initializer._name, initializer._taskFunction);
     342              :         }
     343              :         else
     344              :         {
     345            6 :             taskContext.createTask(
     346              :                 context,
     347            6 :                 initializer._task,
     348              :                 initializer._name,
     349              :                 static_cast<UBaseType_t>(context),
     350            6 :                 initializer._stack,
     351              :                 initializer._taskFunction);
     352              :         }
     353              :     }
     354            9 : }
     355              : 
     356              : template<class Binding>
     357            9 : void FreeRtosAdapter<Binding>::run(StartAppFunctionType startApp)
     358              : {
     359            9 :     _startApp = startApp;
     360            9 :     TaskInitializer::run();
     361            9 :     vTaskStartScheduler();
     362            9 : }
     363              : 
     364              : template<class Binding>
     365            1 : void FreeRtosAdapter<Binding>::runningHook()
     366              : {
     367            1 :     _taskContexts[TASK_IDLE].initTaskHandle(xTaskGetIdleTaskHandle());
     368            1 :     _startApp();
     369            1 : }
     370              : 
     371              : template<class Binding>
     372            3 : bool FreeRtosAdapter<Binding>::getStackUsage(size_t const taskIdx, StackUsage& stackUsage)
     373              : {
     374            3 :     if (taskIdx < OS_TASK_COUNT)
     375              :     {
     376            2 :         stackUsage._stackSize = _stackSizes[taskIdx];
     377            2 :         uint32_t const unusedSize
     378              :             = (taskIdx == TASK_TIMER)
     379            2 :                   ? TaskContextType::getUnusedStackSize(xTimerGetTimerDaemonTaskHandle())
     380            1 :                   : _taskContexts[taskIdx].getUnusedStackSize();
     381            2 :         stackUsage._usedSize = stackUsage._stackSize - unusedSize;
     382            2 :         return true;
     383              :     }
     384            1 :     return false;
     385              : }
     386              : 
     387              : template<class Binding>
     388            1 : inline void FreeRtosAdapter<Binding>::callIdleTaskFunction()
     389              : {
     390            1 :     _taskContexts[TASK_IDLE].callTaskFunction();
     391            1 : }
     392              : 
     393              : template<class Binding>
     394            2 : inline void FreeRtosAdapter<Binding>::execute(ContextType const context, RunnableType& runnable)
     395              : {
     396            2 :     _taskContexts[static_cast<size_t>(context)].execute(runnable);
     397            2 : }
     398              : 
     399              : template<class Binding>
     400            2 : inline void FreeRtosAdapter<Binding>::schedule(
     401              :     ContextType const context,
     402              :     RunnableType& runnable,
     403              :     TimeoutType& timeout,
     404              :     uint32_t const delay,
     405              :     TimeUnitType const unit)
     406              : {
     407            2 :     _taskContexts[static_cast<size_t>(context)].schedule(runnable, timeout, delay, unit);
     408            2 : }
     409              : 
     410              : template<class Binding>
     411            2 : inline void FreeRtosAdapter<Binding>::scheduleAtFixedRate(
     412              :     ContextType const context,
     413              :     RunnableType& runnable,
     414              :     TimeoutType& timeout,
     415              :     uint32_t const delay,
     416              :     TimeUnitType const unit)
     417              : {
     418            2 :     _taskContexts[static_cast<size_t>(context)].scheduleAtFixedRate(runnable, timeout, delay, unit);
     419            2 : }
     420              : 
     421              : template<class Binding>
     422            5 : inline void FreeRtosAdapter<Binding>::cancel(TimeoutType& timeout)
     423              : {
     424            5 :     LockType const lock;
     425            5 :     ContextType const context = timeout._context;
     426            5 :     if (context != CONTEXT_INVALID)
     427              :     {
     428            4 :         timeout._context = CONTEXT_INVALID;
     429            4 :         _taskContexts[static_cast<size_t>(context)].cancel(timeout);
     430              :     }
     431            5 : }
     432              : 
     433              : template<class Binding>
     434            1 : inline void FreeRtosAdapter<Binding>::suspend(ContextType context)
     435              : {
     436            1 :     _taskContexts[static_cast<size_t>(context)].suspend();
     437            1 : }
     438              : 
     439              : template<class Binding>
     440            1 : inline void FreeRtosAdapter<Binding>::resume(ContextType context)
     441              : {
     442            1 :     _taskContexts[static_cast<size_t>(context)].resume();
     443            1 : }
     444              : 
     445              : template<class Binding>
     446           34 : inline BaseType_t* FreeRtosAdapter<Binding>::getHigherPriorityTaskWoken()
     447              : {
     448           34 :     return _higherPriorityTaskWoken;
     449              : }
     450              : 
     451              : template<class Binding>
     452           10 : inline void FreeRtosAdapter<Binding>::enterIsr()
     453              : {
     454           10 :     internal::NestedInterruptLock<> const lock;
     455           10 :     ++_nestedInterruptCount;
     456           10 :     _higherPriorityTaskWoken = &FreeRtosAdapter::_higherPriorityTaskWokenFlag;
     457           10 : }
     458              : 
     459              : template<class Binding>
     460            5 : inline void FreeRtosAdapter<Binding>::leaveIsr()
     461              : {
     462            5 :     internal::NestedInterruptLock<> const lock;
     463            5 :     --_nestedInterruptCount;
     464            5 :     if (_nestedInterruptCount == 0U)
     465              :     {
     466            4 :         FreeRtosAdapter::_higherPriorityTaskWoken = nullptr;
     467            4 :         if (FreeRtosAdapter::_higherPriorityTaskWokenFlag != pdFALSE)
     468              :         {
     469            1 :             FreeRtosAdapter::_higherPriorityTaskWokenFlag = pdFALSE;
     470            1 :             portYIELD_FROM_ISR(pdTRUE);
     471              :         }
     472              :     }
     473            5 : }
     474              : 
     475              : template<class Binding>
     476            5 : inline bool FreeRtosAdapter<Binding>::leaveIsrNoYield()
     477              : {
     478            5 :     internal::NestedInterruptLock<> const lock;
     479            5 :     --_nestedInterruptCount;
     480            5 :     if (_nestedInterruptCount == 0U)
     481              :     {
     482            4 :         FreeRtosAdapter::_higherPriorityTaskWoken = nullptr;
     483            4 :         if (FreeRtosAdapter::_higherPriorityTaskWokenFlag != pdFALSE)
     484              :         {
     485            2 :             FreeRtosAdapter::_higherPriorityTaskWokenFlag = pdFALSE;
     486            2 :             return true;
     487              :         }
     488              :     }
     489            3 :     return false;
     490            5 : }
     491              : 
     492              : template<class Binding>
     493            1 : FreeRtosAdapter<Binding>::StackUsage::StackUsage() : _stackSize(0U), _usedSize(0U)
     494            1 : {}
     495              : 
     496              : namespace internal
     497              : {
     498              : template<size_t N, typename TaskConfig>
     499              : ::etl::array<TaskConfig, N> TaskConfigHolder<N, TaskConfig>::_taskConfigs;
     500              : 
     501              : template<size_t N, typename TaskConfig>
     502            1 : void TaskConfigHolder<N, TaskConfig>::setTaskConfig(
     503              :     size_t const taskIdx, TaskConfigType const& taskConfig)
     504              : {
     505            1 :     _taskConfigs[taskIdx] = taskConfig;
     506            1 : }
     507              : 
     508              : template<size_t N, typename TaskConfig>
     509            1 : TaskConfig const* TaskConfigHolder<N, TaskConfig>::getTaskConfig(size_t const taskIdx)
     510              : {
     511            1 :     return &_taskConfigs[taskIdx];
     512              : }
     513              : 
     514              : template<size_t N>
     515            9 : void TaskConfigHolder<N, void>::setTaskConfig(
     516              :     size_t const /*taskIdx*/,
     517              :     typename TaskConfigHolder<N, void>::TaskConfigType const& /*taskConfig*/)
     518            9 : {}
     519              : 
     520              : template<size_t N>
     521              : typename TaskConfigHolder<N, void>::TaskConfigType const*
     522            1 : TaskConfigHolder<N, void>::getTaskConfig(size_t const /*taskIdx*/)
     523              : {
     524            1 :     ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("not implemented"));
     525              :     return nullptr;
     526              : }
     527              : 
     528              : } // namespace internal
     529              : 
     530              : } // namespace async
        

Generated by: LCOV version 2.0-1