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

Generated by: LCOV version 1.14