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 : /// Notifies the system of an interrupt entry.
207 : static void enterIsr();
208 :
209 : /// Notifies the system of an interrupt exit.
210 : static void leaveIsr();
211 :
212 : /**
213 : * Exits the ISR without yielding control.
214 : * \return True if yielding is not required after ISR.
215 : */
216 : static bool leaveIsrNoYield();
217 :
218 : /// \return A pointer to the task woken by the ISR.
219 : static BaseType_t* getHigherPriorityTaskWoken();
220 :
221 : private:
222 : friend struct internal::TaskInitializer<AdapterType>;
223 :
224 : static void initTask(TaskInitializer& initializer);
225 :
226 : static void TaskFunction(void* param);
227 :
228 : static StartAppFunctionType _startApp;
229 : static TaskInitializer* _idleTaskInitializer;
230 : static TaskInitializer* _timerTaskInitializer;
231 : static ::etl::array<TaskContextType, TASK_COUNT> _taskContexts;
232 : static ::etl::array<uint32_t, OS_TASK_COUNT> _stackSizes;
233 : static char const* _timerTaskName;
234 : static BaseType_t _higherPriorityTaskWokenFlag;
235 : static BaseType_t* _higherPriorityTaskWoken;
236 : static uint8_t _nestedInterruptCount;
237 : };
238 :
239 : /**
240 : * Inline implementations.
241 : */
242 : template<class Binding>
243 : typename FreeRtosAdapter<Binding>::StartAppFunctionType FreeRtosAdapter<Binding>::_startApp;
244 : template<class Binding>
245 : typename FreeRtosAdapter<Binding>::TaskInitializer* FreeRtosAdapter<Binding>::_idleTaskInitializer
246 : = nullptr;
247 : template<class Binding>
248 : typename FreeRtosAdapter<Binding>::TaskInitializer* FreeRtosAdapter<Binding>::_timerTaskInitializer
249 : = nullptr;
250 : template<class Binding>
251 : ::etl::
252 : array<typename FreeRtosAdapter<Binding>::TaskContextType, FreeRtosAdapter<Binding>::TASK_COUNT>
253 : FreeRtosAdapter<Binding>::_taskContexts;
254 : template<class Binding>
255 : ::etl::array<uint32_t, FreeRtosAdapter<Binding>::OS_TASK_COUNT>
256 : FreeRtosAdapter<Binding>::_stackSizes;
257 : template<class Binding>
258 : char const* FreeRtosAdapter<Binding>::_timerTaskName;
259 : template<class Binding>
260 : BaseType_t FreeRtosAdapter<Binding>::_higherPriorityTaskWokenFlag = 0;
261 : template<class Binding>
262 : BaseType_t* FreeRtosAdapter<Binding>::_higherPriorityTaskWoken = nullptr;
263 : template<class Binding>
264 : uint8_t FreeRtosAdapter<Binding>::_nestedInterruptCount = 0U;
265 :
266 : template<class Binding>
267 : template<ContextType Context>
268 2 : void FreeRtosAdapter<Binding>::getTaskMemory(
269 : StaticTask_t** const ppxTaskTCBBuffer,
270 : StackType_t** const ppxTaskStackBuffer,
271 : uint32_t* const pulTaskStackSize)
272 : {
273 2 : TaskInitializer& initializer
274 : = *((Context == TASK_IDLE) ? _idleTaskInitializer : _timerTaskInitializer);
275 2 : ETL_ASSERT(ppxTaskTCBBuffer != nullptr, ETL_ERROR_GENERIC("tcb buffer must not be null"));
276 2 : ETL_ASSERT(ppxTaskStackBuffer != nullptr, ETL_ERROR_GENERIC("stack buffer must not be null"));
277 2 : ETL_ASSERT(pulTaskStackSize != nullptr, ETL_ERROR_GENERIC("stack size must not be null"));
278 2 : *ppxTaskTCBBuffer = &initializer._task;
279 2 : *ppxTaskStackBuffer = initializer._stack.data();
280 : // Conversion to uint32_t is OK, stack will not exceed 4GB.
281 2 : *pulTaskStackSize = static_cast<uint32_t>(initializer._stack.size());
282 2 : }
283 :
284 : template<class Binding>
285 4 : inline char const* FreeRtosAdapter<Binding>::getTaskName(size_t const taskIdx)
286 : {
287 4 : return (taskIdx < _taskContexts.size()) ? _taskContexts[taskIdx].getName() : _timerTaskName;
288 : }
289 :
290 : template<class Binding>
291 1 : inline TaskHandle_t FreeRtosAdapter<Binding>::getTaskHandle(size_t const taskIdx)
292 : {
293 1 : return _taskContexts[taskIdx].getTaskHandle();
294 : }
295 :
296 : template<class Binding>
297 : inline typename FreeRtosAdapter<Binding>::TaskConfigType const*
298 1 : FreeRtosAdapter<Binding>::getTaskConfig(size_t const taskIdx)
299 : {
300 0 : return TaskConfigsType::getTaskConfig(taskIdx);
301 : }
302 :
303 : template<class Binding>
304 7 : ContextType FreeRtosAdapter<Binding>::getCurrentTaskContext()
305 : {
306 7 : if (_higherPriorityTaskWoken == nullptr)
307 : {
308 6 : return static_cast<ContextType>(uxTaskGetTaskNumber(xTaskGetCurrentTaskHandle()));
309 : }
310 1 : return CONTEXT_INVALID;
311 : }
312 :
313 : template<class Binding>
314 8 : void FreeRtosAdapter<Binding>::initTask(TaskInitializer& initializer)
315 : {
316 8 : ContextType const context = initializer._context;
317 8 : _stackSizes[static_cast<size_t>(context)] = static_cast<uint32_t>(
318 8 : static_cast<size_t>(initializer._stack.size()) * sizeof(BaseType_t));
319 8 : TaskConfigsType::setTaskConfig(context, initializer._config);
320 8 : if (context == TASK_TIMER)
321 : {
322 2 : _timerTaskInitializer = &initializer;
323 2 : _timerTaskName = initializer._name;
324 : }
325 : else
326 : {
327 6 : TaskContextType& taskContext = _taskContexts[static_cast<size_t>(context)];
328 6 : if (context == TASK_IDLE)
329 : {
330 1 : _idleTaskInitializer = &initializer;
331 1 : taskContext.initTask(TASK_IDLE, initializer._name, initializer._taskFunction);
332 : }
333 : else
334 : {
335 5 : taskContext.createTask(
336 : context,
337 5 : initializer._task,
338 : initializer._name,
339 : static_cast<UBaseType_t>(context),
340 5 : initializer._stack,
341 : initializer._taskFunction);
342 : }
343 : }
344 8 : }
345 :
346 : template<class Binding>
347 8 : void FreeRtosAdapter<Binding>::run(StartAppFunctionType startApp)
348 : {
349 8 : _startApp = startApp;
350 8 : TaskInitializer::run();
351 8 : vTaskStartScheduler();
352 8 : }
353 :
354 : template<class Binding>
355 1 : void FreeRtosAdapter<Binding>::runningHook()
356 : {
357 1 : _taskContexts[TASK_IDLE].initTaskHandle(xTaskGetIdleTaskHandle());
358 1 : _startApp();
359 1 : }
360 :
361 : template<class Binding>
362 3 : bool FreeRtosAdapter<Binding>::getStackUsage(size_t const taskIdx, StackUsage& stackUsage)
363 : {
364 3 : if (taskIdx < OS_TASK_COUNT)
365 : {
366 2 : stackUsage._stackSize = _stackSizes[taskIdx];
367 2 : uint32_t const unusedSize
368 : = (taskIdx == TASK_TIMER)
369 2 : ? TaskContextType::getUnusedStackSize(xTimerGetTimerDaemonTaskHandle())
370 1 : : _taskContexts[taskIdx].getUnusedStackSize();
371 2 : stackUsage._usedSize = stackUsage._stackSize - unusedSize;
372 2 : return true;
373 : }
374 1 : return false;
375 : }
376 :
377 : template<class Binding>
378 1 : inline void FreeRtosAdapter<Binding>::callIdleTaskFunction()
379 : {
380 1 : _taskContexts[TASK_IDLE].callTaskFunction();
381 1 : }
382 :
383 : template<class Binding>
384 2 : inline void FreeRtosAdapter<Binding>::execute(ContextType const context, RunnableType& runnable)
385 : {
386 2 : _taskContexts[static_cast<size_t>(context)].execute(runnable);
387 2 : }
388 :
389 : template<class Binding>
390 2 : inline void FreeRtosAdapter<Binding>::schedule(
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)].schedule(runnable, timeout, delay, unit);
398 2 : }
399 :
400 : template<class Binding>
401 2 : inline void FreeRtosAdapter<Binding>::scheduleAtFixedRate(
402 : ContextType const context,
403 : RunnableType& runnable,
404 : TimeoutType& timeout,
405 : uint32_t const delay,
406 : TimeUnitType const unit)
407 : {
408 2 : _taskContexts[static_cast<size_t>(context)].scheduleAtFixedRate(runnable, timeout, delay, unit);
409 2 : }
410 :
411 : template<class Binding>
412 5 : inline void FreeRtosAdapter<Binding>::cancel(TimeoutType& timeout)
413 : {
414 5 : LockType const lock;
415 5 : ContextType const context = timeout._context;
416 5 : if (context != CONTEXT_INVALID)
417 : {
418 4 : timeout._context = CONTEXT_INVALID;
419 4 : _taskContexts[static_cast<size_t>(context)].cancel(timeout);
420 : }
421 5 : }
422 :
423 : template<class Binding>
424 34 : inline BaseType_t* FreeRtosAdapter<Binding>::getHigherPriorityTaskWoken()
425 : {
426 34 : return _higherPriorityTaskWoken;
427 : }
428 :
429 : template<class Binding>
430 10 : inline void FreeRtosAdapter<Binding>::enterIsr()
431 : {
432 10 : internal::NestedInterruptLock<> const lock;
433 10 : ++_nestedInterruptCount;
434 10 : _higherPriorityTaskWoken = &FreeRtosAdapter::_higherPriorityTaskWokenFlag;
435 10 : }
436 :
437 : template<class Binding>
438 5 : inline void FreeRtosAdapter<Binding>::leaveIsr()
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 1 : FreeRtosAdapter::_higherPriorityTaskWokenFlag = pdFALSE;
448 1 : portYIELD_FROM_ISR(pdTRUE);
449 : }
450 : }
451 5 : }
452 :
453 : template<class Binding>
454 5 : inline bool FreeRtosAdapter<Binding>::leaveIsrNoYield()
455 : {
456 5 : internal::NestedInterruptLock<> const lock;
457 5 : --_nestedInterruptCount;
458 5 : if (_nestedInterruptCount == 0U)
459 : {
460 4 : FreeRtosAdapter::_higherPriorityTaskWoken = nullptr;
461 4 : if (FreeRtosAdapter::_higherPriorityTaskWokenFlag != pdFALSE)
462 : {
463 2 : FreeRtosAdapter::_higherPriorityTaskWokenFlag = pdFALSE;
464 2 : return true;
465 : }
466 : }
467 3 : return false;
468 5 : }
469 :
470 : template<class Binding>
471 1 : FreeRtosAdapter<Binding>::StackUsage::StackUsage() : _stackSize(0U), _usedSize(0U)
472 1 : {}
473 :
474 : namespace internal
475 : {
476 : template<size_t N, typename TaskConfig>
477 : ::etl::array<TaskConfig, N> TaskConfigHolder<N, TaskConfig>::_taskConfigs;
478 :
479 : template<size_t N, typename TaskConfig>
480 1 : void TaskConfigHolder<N, TaskConfig>::setTaskConfig(
481 : size_t const taskIdx, TaskConfigType const& taskConfig)
482 : {
483 1 : _taskConfigs[taskIdx] = taskConfig;
484 1 : }
485 :
486 : template<size_t N, typename TaskConfig>
487 1 : TaskConfig const* TaskConfigHolder<N, TaskConfig>::getTaskConfig(size_t const taskIdx)
488 : {
489 1 : return &_taskConfigs[taskIdx];
490 : }
491 :
492 : template<size_t N>
493 8 : void TaskConfigHolder<N, void>::setTaskConfig(
494 : size_t const /*taskIdx*/,
495 : typename TaskConfigHolder<N, void>::TaskConfigType const& /*taskConfig*/)
496 8 : {}
497 :
498 : template<size_t N>
499 : typename TaskConfigHolder<N, void>::TaskConfigType const*
500 1 : TaskConfigHolder<N, void>::getTaskConfig(size_t const /*taskIdx*/)
501 : {
502 1 : ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("not implemented"));
503 : return nullptr;
504 : }
505 :
506 : } // namespace internal
507 :
508 : } // namespace async
|