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