LCOV - code coverage report
Current view: top level - asyncFreeRtos/include/async - FreeRtosAdapter.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 98 98 100.0 %
Date: 2025-08-28 06:39:11 Functions: 17 17 100.0 %

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

Generated by: LCOV version 1.14