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 % 109 108
Test Date: 2026-02-24 11:21:15 Functions: 100.0 % 36 36

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

Generated by: LCOV version 2.0-1