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