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