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