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