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