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
|