2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #define SRC_MAIN_SCHEDULER_C_
29 #include "drivers/accgyro/accgyro.h"
31 #include "build/build_config.h"
32 #include "build/debug.h"
34 #include "common/maths.h"
35 #include "common/time.h"
36 #include "common/utils.h"
38 #include "drivers/time.h"
39 #include "drivers/accgyro/accgyro.h"
40 #include "drivers/system.h"
45 #include "scheduler.h"
47 #include "sensors/gyro_init.h"
49 // DEBUG_SCHEDULER, timings for:
51 // 1 - pidController()
52 // 2 - time spent in scheduler
53 // 3 - time spent executing check function
55 // DEBUG_SCHEDULER_DETERMINISM, requires USE_LATE_TASK_STATISTICS to be defined
56 // 0 - Gyro task start cycle time in 10th of a us
57 // 1 - ID of late task
58 // 2 - Amount task is late in 10th of a us
59 // 3 - Gyro lock skew in clock cycles
61 // DEBUG_TIMING_ACCURACY, requires USE_LATE_TASK_STATISTICS to be defined
63 // 1 - Tasks late in last second
64 // 2 - Total lateness in last second in 10ths us
65 // 3 - Total tasks run in last second
67 extern task_t tasks
[];
69 static FAST_DATA_ZERO_INIT task_t
*currentTask
= NULL
;
70 static FAST_DATA_ZERO_INIT
bool ignoreCurrentTaskExecRate
;
71 static FAST_DATA_ZERO_INIT
bool ignoreCurrentTaskExecTime
;
73 int32_t schedLoopStartCycles
;
74 static int32_t schedLoopStartMinCycles
;
75 static int32_t schedLoopStartMaxCycles
;
76 static uint32_t schedLoopStartDeltaDownCycles
;
77 static uint32_t schedLoopStartDeltaUpCycles
;
79 int32_t taskGuardCycles
;
80 static int32_t taskGuardMinCycles
;
81 static int32_t taskGuardMaxCycles
;
82 static uint32_t taskGuardDeltaDownCycles
;
83 static uint32_t taskGuardDeltaUpCycles
;
85 FAST_DATA_ZERO_INIT
uint16_t averageSystemLoadPercent
= 0;
87 static FAST_DATA_ZERO_INIT
int taskQueuePos
= 0;
88 STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT
int taskQueueSize
= 0;
90 static FAST_DATA_ZERO_INIT
bool gyroEnabled
;
92 static int32_t desiredPeriodCycles
;
93 static uint32_t lastTargetCycles
;
95 #if defined(USE_LATE_TASK_STATISTICS)
96 static int16_t lateTaskCount
= 0;
97 static uint32_t lateTaskTotal
= 0;
98 static int16_t taskCount
= 0;
99 static uint32_t nextTimingCycles
;
102 // No need for a linked list for the queue, since items are only inserted at startup
104 STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT task_t
* taskQueueArray
[TASK_COUNT
+ 1]; // extra item for NULL pointer at end of queue
106 void queueClear(void)
108 memset(taskQueueArray
, 0, sizeof(taskQueueArray
));
113 bool queueContains(task_t
*task
)
115 for (int ii
= 0; ii
< taskQueueSize
; ++ii
) {
116 if (taskQueueArray
[ii
] == task
) {
123 bool queueAdd(task_t
*task
)
125 if ((taskQueueSize
>= TASK_COUNT
) || queueContains(task
)) {
128 for (int ii
= 0; ii
<= taskQueueSize
; ++ii
) {
129 if (taskQueueArray
[ii
] == NULL
|| taskQueueArray
[ii
]->id
->staticPriority
< task
->id
->staticPriority
) {
130 memmove(&taskQueueArray
[ii
+1], &taskQueueArray
[ii
], sizeof(task
) * (taskQueueSize
- ii
));
131 taskQueueArray
[ii
] = task
;
139 bool queueRemove(task_t
*task
)
141 for (int ii
= 0; ii
< taskQueueSize
; ++ii
) {
142 if (taskQueueArray
[ii
] == task
) {
143 memmove(&taskQueueArray
[ii
], &taskQueueArray
[ii
+1], sizeof(task
) * (taskQueueSize
- ii
));
152 * Returns first item queue or NULL if queue empty
154 FAST_CODE task_t
*queueFirst(void)
157 return taskQueueArray
[0]; // guaranteed to be NULL if queue is empty
161 * Returns next item in queue or NULL if at end of queue
163 FAST_CODE task_t
*queueNext(void)
165 return taskQueueArray
[++taskQueuePos
]; // guaranteed to be NULL at end of queue
168 static timeUs_t taskTotalExecutionTime
= 0;
170 void taskSystemLoad(timeUs_t currentTimeUs
)
172 static timeUs_t lastExecutedAtUs
;
173 timeDelta_t deltaTime
= cmpTimeUs(currentTimeUs
, lastExecutedAtUs
);
175 // Calculate system load
177 averageSystemLoadPercent
= 100 * taskTotalExecutionTime
/ deltaTime
;
178 taskTotalExecutionTime
= 0;
179 lastExecutedAtUs
= currentTimeUs
;
181 schedulerIgnoreTaskExecTime();
184 #if defined(SIMULATOR_BUILD)
185 averageSystemLoadPercent
= 0;
189 timeUs_t checkFuncMaxExecutionTimeUs
;
190 timeUs_t checkFuncTotalExecutionTimeUs
;
191 timeUs_t checkFuncMovingSumExecutionTimeUs
;
192 timeUs_t checkFuncMovingSumDeltaTimeUs
;
194 void getCheckFuncInfo(cfCheckFuncInfo_t
*checkFuncInfo
)
196 checkFuncInfo
->maxExecutionTimeUs
= checkFuncMaxExecutionTimeUs
;
197 checkFuncInfo
->totalExecutionTimeUs
= checkFuncTotalExecutionTimeUs
;
198 checkFuncInfo
->averageExecutionTimeUs
= checkFuncMovingSumExecutionTimeUs
/ TASK_STATS_MOVING_SUM_COUNT
;
199 checkFuncInfo
->averageDeltaTimeUs
= checkFuncMovingSumDeltaTimeUs
/ TASK_STATS_MOVING_SUM_COUNT
;
202 void getTaskInfo(taskId_e taskId
, taskInfo_t
* taskInfo
)
204 taskInfo
->isEnabled
= queueContains(getTask(taskId
));
205 taskInfo
->desiredPeriodUs
= getTask(taskId
)->id
->desiredPeriodUs
;
206 taskInfo
->staticPriority
= getTask(taskId
)->id
->staticPriority
;
207 taskInfo
->taskName
= getTask(taskId
)->id
->taskName
;
208 taskInfo
->subTaskName
= getTask(taskId
)->id
->subTaskName
;
209 taskInfo
->maxExecutionTimeUs
= getTask(taskId
)->maxExecutionTimeUs
;
210 taskInfo
->totalExecutionTimeUs
= getTask(taskId
)->totalExecutionTimeUs
;
211 taskInfo
->averageExecutionTimeUs
= getTask(taskId
)->anticipatedExecutionTime
>> TASK_EXEC_TIME_SHIFT
;
212 taskInfo
->averageDeltaTime10thUs
= getTask(taskId
)->movingSumDeltaTime10thUs
/ TASK_STATS_MOVING_SUM_COUNT
;
213 taskInfo
->latestDeltaTimeUs
= getTask(taskId
)->taskLatestDeltaTimeUs
;
214 taskInfo
->movingAverageCycleTimeUs
= getTask(taskId
)->movingAverageCycleTimeUs
;
215 #if defined(USE_LATE_TASK_STATISTICS)
216 taskInfo
->lateCount
= getTask(taskId
)->lateCount
;
217 taskInfo
->runCount
= getTask(taskId
)->runCount
;
218 taskInfo
->execTime
= getTask(taskId
)->execTime
;
222 void rescheduleTask(taskId_e taskId
, timeDelta_t newPeriodUs
)
226 if (taskId
== TASK_SELF
) {
228 } else if (taskId
< TASK_COUNT
) {
229 task
= getTask(taskId
);
233 task
->id
->desiredPeriodUs
= MAX(SCHEDULER_DELAY_LIMIT
, newPeriodUs
); // Limit delay to 100us (10 kHz) to prevent scheduler clogging
235 // Catch the case where the gyro loop is adjusted
236 if (taskId
== TASK_GYRO
) {
237 desiredPeriodCycles
= (int32_t)clockMicrosToCycles((uint32_t)getTask(TASK_GYRO
)->id
->desiredPeriodUs
);
241 void setTaskEnabled(taskId_e taskId
, bool enabled
)
243 if (taskId
== TASK_SELF
|| taskId
< TASK_COUNT
) {
244 task_t
*task
= taskId
== TASK_SELF
? currentTask
: getTask(taskId
);
245 if (enabled
&& task
->id
->taskFunc
) {
253 timeDelta_t
getTaskDeltaTimeUs(taskId_e taskId
)
255 if (taskId
== TASK_SELF
) {
256 return currentTask
->taskLatestDeltaTimeUs
;
257 } else if (taskId
< TASK_COUNT
) {
258 return getTask(taskId
)->taskLatestDeltaTimeUs
;
264 // Called by tasks executing what are known to be short states
265 void schedulerIgnoreTaskStateTime()
267 ignoreCurrentTaskExecRate
= true;
268 ignoreCurrentTaskExecTime
= true;
271 // Called by tasks with state machines to only count one state as determining rate
272 void schedulerIgnoreTaskExecRate()
274 ignoreCurrentTaskExecRate
= true;
277 // Called by tasks without state machines executing in what is known to be a shorter time than peak
278 void schedulerIgnoreTaskExecTime()
280 ignoreCurrentTaskExecTime
= true;
283 bool schedulerGetIgnoreTaskExecTime()
285 return ignoreCurrentTaskExecTime
;
288 void schedulerResetTaskStatistics(taskId_e taskId
)
290 if (taskId
== TASK_SELF
) {
291 currentTask
->anticipatedExecutionTime
= 0;
292 currentTask
->movingSumDeltaTime10thUs
= 0;
293 currentTask
->totalExecutionTimeUs
= 0;
294 currentTask
->maxExecutionTimeUs
= 0;
295 } else if (taskId
< TASK_COUNT
) {
296 getTask(taskId
)->anticipatedExecutionTime
= 0;
297 getTask(taskId
)->movingSumDeltaTime10thUs
= 0;
298 getTask(taskId
)->totalExecutionTimeUs
= 0;
299 getTask(taskId
)->maxExecutionTimeUs
= 0;
303 void schedulerResetTaskMaxExecutionTime(taskId_e taskId
)
305 if (taskId
== TASK_SELF
) {
306 currentTask
->maxExecutionTimeUs
= 0;
307 } else if (taskId
< TASK_COUNT
) {
308 task_t
*task
= getTask(taskId
);
309 task
->maxExecutionTimeUs
= 0;
310 #if defined(USE_LATE_TASK_STATISTICS)
317 void schedulerResetCheckFunctionMaxExecutionTime(void)
319 checkFuncMaxExecutionTimeUs
= 0;
322 void schedulerInit(void)
325 queueAdd(getTask(TASK_SYSTEM
));
327 schedLoopStartMinCycles
= clockMicrosToCycles(SCHED_START_LOOP_MIN_US
);
328 schedLoopStartMaxCycles
= clockMicrosToCycles(SCHED_START_LOOP_MAX_US
);
329 schedLoopStartCycles
= schedLoopStartMinCycles
;
330 schedLoopStartDeltaDownCycles
= clockMicrosToCycles(1) / SCHED_START_LOOP_DOWN_STEP
;
331 schedLoopStartDeltaUpCycles
= clockMicrosToCycles(1) / SCHED_START_LOOP_UP_STEP
;
333 taskGuardMinCycles
= clockMicrosToCycles(TASK_GUARD_MARGIN_MIN_US
);
334 taskGuardMaxCycles
= clockMicrosToCycles(TASK_GUARD_MARGIN_MAX_US
);
335 taskGuardCycles
= taskGuardMinCycles
;
336 taskGuardDeltaDownCycles
= clockMicrosToCycles(1) / TASK_GUARD_MARGIN_DOWN_STEP
;
337 taskGuardDeltaUpCycles
= clockMicrosToCycles(1) / TASK_GUARD_MARGIN_UP_STEP
;
339 desiredPeriodCycles
= (int32_t)clockMicrosToCycles((uint32_t)getTask(TASK_GYRO
)->id
->desiredPeriodUs
);
341 lastTargetCycles
= getCycleCounter();
343 #if defined(USE_LATE_TASK_STATISTICS)
344 nextTimingCycles
= lastTargetCycles
;
347 for (taskId_e taskId
= 0; taskId
< TASK_COUNT
; taskId
++) {
348 schedulerResetTaskStatistics(taskId
);
352 static timeDelta_t taskNextStateTime
;
354 FAST_CODE
void schedulerSetNextStateTime(timeDelta_t nextStateTime
)
356 taskNextStateTime
= nextStateTime
;
359 FAST_CODE timeUs_t
schedulerExecuteTask(task_t
*selectedTask
, timeUs_t currentTimeUs
)
361 timeUs_t taskExecutionTimeUs
= 0;
364 currentTask
= selectedTask
;
365 ignoreCurrentTaskExecRate
= false;
366 ignoreCurrentTaskExecTime
= false;
367 taskNextStateTime
= -1;
368 float period
= currentTimeUs
- selectedTask
->lastExecutedAtUs
;
369 selectedTask
->lastExecutedAtUs
= currentTimeUs
;
370 selectedTask
->lastDesiredAt
+= selectedTask
->id
->desiredPeriodUs
;
371 selectedTask
->dynamicPriority
= 0;
374 const timeUs_t currentTimeBeforeTaskCallUs
= micros();
375 selectedTask
->id
->taskFunc(currentTimeBeforeTaskCallUs
);
376 taskExecutionTimeUs
= micros() - currentTimeBeforeTaskCallUs
;
377 taskTotalExecutionTime
+= taskExecutionTimeUs
;
378 if (!ignoreCurrentTaskExecRate
) {
379 // Record task execution rate and max execution time
380 selectedTask
->taskLatestDeltaTimeUs
= cmpTimeUs(currentTimeUs
, selectedTask
->lastStatsAtUs
);
381 selectedTask
->movingSumDeltaTime10thUs
+= (selectedTask
->taskLatestDeltaTimeUs
* 10) - selectedTask
->movingSumDeltaTime10thUs
/ TASK_STATS_MOVING_SUM_COUNT
;
382 selectedTask
->lastStatsAtUs
= currentTimeUs
;
385 // Update estimate of expected task duration
386 if (taskNextStateTime
!= -1) {
387 selectedTask
->anticipatedExecutionTime
= taskNextStateTime
<< TASK_EXEC_TIME_SHIFT
;
388 } else if (!ignoreCurrentTaskExecTime
) {
389 if (taskExecutionTimeUs
> (selectedTask
->anticipatedExecutionTime
>> TASK_EXEC_TIME_SHIFT
)) {
390 selectedTask
->anticipatedExecutionTime
= taskExecutionTimeUs
<< TASK_EXEC_TIME_SHIFT
;
391 } else if (selectedTask
->anticipatedExecutionTime
> 1) {
392 // Slowly decay the max time
393 selectedTask
->anticipatedExecutionTime
--;
397 if (!ignoreCurrentTaskExecTime
) {
398 selectedTask
->maxExecutionTimeUs
= MAX(selectedTask
->maxExecutionTimeUs
, taskExecutionTimeUs
);
401 selectedTask
->totalExecutionTimeUs
+= taskExecutionTimeUs
; // time consumed by scheduler + task
402 selectedTask
->movingAverageCycleTimeUs
+= 0.05f
* (period
- selectedTask
->movingAverageCycleTimeUs
);
403 #if defined(USE_LATE_TASK_STATISTICS)
404 selectedTask
->runCount
++;
408 return taskExecutionTimeUs
;
411 #if defined(UNIT_TEST)
412 task_t
*unittest_scheduler_selectedTask
;
413 uint8_t unittest_scheduler_selectedTaskDynamicPriority
;
415 static void readSchedulerLocals(task_t
*selectedTask
, uint8_t selectedTaskDynamicPriority
)
417 unittest_scheduler_selectedTask
= selectedTask
;
418 unittest_scheduler_selectedTaskDynamicPriority
= selectedTaskDynamicPriority
;
422 FAST_CODE
void scheduler(void)
424 #if !defined(UNIT_TEST)
425 const timeUs_t schedulerStartTimeUs
= micros();
427 timeUs_t currentTimeUs
;
429 timeUs_t taskExecutionTimeUs
= 0;
430 task_t
*selectedTask
= NULL
;
431 uint16_t selectedTaskDynamicPriority
= 0;
432 uint32_t nextTargetCycles
= 0;
433 int32_t schedLoopRemainingCycles
;
436 // Realtime gyro/filtering/PID tasks get complete priority
437 task_t
*gyroTask
= getTask(TASK_GYRO
);
438 nowCycles
= getCycleCounter();
439 #if defined(UNIT_TEST)
440 lastTargetCycles
= clockMicrosToCycles(gyroTask
->lastExecutedAtUs
);
442 nextTargetCycles
= lastTargetCycles
+ desiredPeriodCycles
;
443 schedLoopRemainingCycles
= cmpTimeCycles(nextTargetCycles
, nowCycles
);
445 if (schedLoopRemainingCycles
< -desiredPeriodCycles
) {
446 /* A task has so grossly overrun that at entire gyro cycle has been skipped
447 * This is most likely to occur when connected to the configurator via USB as the serial
448 * task is non-deterministic
449 * Recover as best we can, advancing scheduling by a whole number of cycles
451 nextTargetCycles
+= desiredPeriodCycles
* (1 + (schedLoopRemainingCycles
/ -desiredPeriodCycles
));
452 schedLoopRemainingCycles
= cmpTimeCycles(nextTargetCycles
, nowCycles
);
455 // Tune out the time lost between completing the last task execution and re-entering the scheduler
456 if ((schedLoopRemainingCycles
< schedLoopStartMinCycles
) &&
457 (schedLoopStartCycles
< schedLoopStartMaxCycles
)) {
458 schedLoopStartCycles
+= schedLoopStartDeltaUpCycles
;
461 // Once close to the timing boundary, poll for it's arrival
462 if (schedLoopRemainingCycles
< schedLoopStartCycles
) {
463 if (schedLoopStartCycles
> schedLoopStartMinCycles
) {
464 schedLoopStartCycles
-= schedLoopStartDeltaDownCycles
;
466 #if !defined(UNIT_TEST)
467 while (schedLoopRemainingCycles
> 0) {
468 nowCycles
= getCycleCounter();
469 schedLoopRemainingCycles
= cmpTimeCycles(nextTargetCycles
, nowCycles
);
471 DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM
, 0, clockCyclesTo10thMicros(cmpTimeCycles(nowCycles
, lastTargetCycles
)));
473 currentTimeUs
= clockCyclesToMicros(nowCycles
);
474 taskExecutionTimeUs
+= schedulerExecuteTask(gyroTask
, currentTimeUs
);
476 if (gyroFilterReady()) {
477 taskExecutionTimeUs
+= schedulerExecuteTask(getTask(TASK_FILTER
), currentTimeUs
);
479 if (pidLoopReady()) {
480 taskExecutionTimeUs
+= schedulerExecuteTask(getTask(TASK_PID
), currentTimeUs
);
483 #if defined(USE_LATE_TASK_STATISTICS)
485 DEBUG_SET(DEBUG_TIMING_ACCURACY
, 0, getAverageSystemLoadPercent());
487 if (cmpTimeCycles(nextTimingCycles
, nowCycles
) < 0) {
488 nextTimingCycles
+= clockMicrosToCycles(1000000);
490 // Tasks late in last second
491 DEBUG_SET(DEBUG_TIMING_ACCURACY
, 1, lateTaskCount
);
492 // Total lateness in last second in us
493 DEBUG_SET(DEBUG_TIMING_ACCURACY
, 2, clockCyclesTo10thMicros(lateTaskTotal
));
494 // Total tasks run in last second
495 DEBUG_SET(DEBUG_TIMING_ACCURACY
, 3, taskCount
);
502 lastTargetCycles
= nextTargetCycles
;
505 gyroDev_t
*gyro
= gyroActiveDev();
507 // Bring the scheduler into lock with the gyro
508 if (gyro
->gyroModeSPI
!= GYRO_EXTI_NO_INT
) {
509 // Track the actual gyro rate over given number of cycle times and set the expected timebase
510 static uint32_t terminalGyroRateCount
= 0;
511 static int32_t sampleRateStartCycles
;
513 if ((terminalGyroRateCount
== 0)) {
514 terminalGyroRateCount
= gyro
->detectedEXTI
+ GYRO_RATE_COUNT
;
515 sampleRateStartCycles
= nowCycles
;
518 if (gyro
->detectedEXTI
>= terminalGyroRateCount
) {
519 // Calculate the number of clock cycles on average between gyro interrupts
520 uint32_t sampleCycles
= nowCycles
- sampleRateStartCycles
;
521 desiredPeriodCycles
= sampleCycles
/ GYRO_RATE_COUNT
;
522 sampleRateStartCycles
= nowCycles
;
523 terminalGyroRateCount
+= GYRO_RATE_COUNT
;
526 // Track the actual gyro rate over given number of cycle times and remove skew
527 static uint32_t terminalGyroLockCount
= 0;
528 static int32_t accGyroSkew
= 0;
530 int32_t gyroSkew
= cmpTimeCycles(nextTargetCycles
, gyro
->gyroSyncEXTI
) % desiredPeriodCycles
;
531 if (gyroSkew
> (desiredPeriodCycles
/ 2)) {
532 gyroSkew
-= desiredPeriodCycles
;
535 accGyroSkew
+= gyroSkew
;
537 if ((terminalGyroLockCount
== 0)) {
538 terminalGyroLockCount
= gyro
->detectedEXTI
+ GYRO_LOCK_COUNT
;
541 if (gyro
->detectedEXTI
>= terminalGyroLockCount
) {
542 terminalGyroLockCount
+= GYRO_LOCK_COUNT
;
544 // Move the desired start time of the gyroTask
545 lastTargetCycles
-= (accGyroSkew
/GYRO_LOCK_COUNT
);
546 DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM
, 3, clockCyclesTo10thMicros(accGyroSkew
/GYRO_LOCK_COUNT
));
554 nowCycles
= getCycleCounter();
555 schedLoopRemainingCycles
= cmpTimeCycles(nextTargetCycles
, nowCycles
);
557 if (!gyroEnabled
|| (schedLoopRemainingCycles
> (int32_t)clockMicrosToCycles(CHECK_GUARD_MARGIN_US
))) {
558 currentTimeUs
= micros();
560 // Update task dynamic priorities
561 for (task_t
*task
= queueFirst(); task
!= NULL
; task
= queueNext()) {
562 if (task
->id
->staticPriority
!= TASK_PRIORITY_REALTIME
) {
563 // Task has checkFunc - event driven
564 if (task
->id
->checkFunc
) {
565 // Increase priority for event driven tasks
566 if (task
->dynamicPriority
> 0) {
567 task
->taskAgeCycles
= 1 + (cmpTimeUs(currentTimeUs
, task
->lastSignaledAtUs
) / task
->id
->desiredPeriodUs
);
568 task
->dynamicPriority
= 1 + task
->id
->staticPriority
* task
->taskAgeCycles
;
569 } else if (task
->id
->checkFunc(currentTimeUs
, cmpTimeUs(currentTimeUs
, task
->lastExecutedAtUs
))) {
570 const uint32_t checkFuncExecutionTimeUs
= cmpTimeUs(micros(), currentTimeUs
);
571 #if !defined(UNIT_TEST)
572 DEBUG_SET(DEBUG_SCHEDULER
, 3, checkFuncExecutionTimeUs
);
574 checkFuncMovingSumExecutionTimeUs
+= checkFuncExecutionTimeUs
- checkFuncMovingSumExecutionTimeUs
/ TASK_STATS_MOVING_SUM_COUNT
;
575 checkFuncMovingSumDeltaTimeUs
+= task
->taskLatestDeltaTimeUs
- checkFuncMovingSumDeltaTimeUs
/ TASK_STATS_MOVING_SUM_COUNT
;
576 checkFuncTotalExecutionTimeUs
+= checkFuncExecutionTimeUs
; // time consumed by scheduler + task
577 checkFuncMaxExecutionTimeUs
= MAX(checkFuncMaxExecutionTimeUs
, checkFuncExecutionTimeUs
);
578 task
->lastSignaledAtUs
= currentTimeUs
;
579 task
->taskAgeCycles
= 1;
580 task
->dynamicPriority
= 1 + task
->id
->staticPriority
;
582 task
->taskAgeCycles
= 0;
585 // Task is time-driven, dynamicPriority is last execution age (measured in desiredPeriods)
586 // Task age is calculated from last execution
587 task
->taskAgeCycles
= (cmpTimeUs(currentTimeUs
, task
->lastExecutedAtUs
) / task
->id
->desiredPeriodUs
);
588 if (task
->taskAgeCycles
> 0) {
589 task
->dynamicPriority
= 1 + task
->id
->staticPriority
* task
->taskAgeCycles
;
593 if (task
->dynamicPriority
> selectedTaskDynamicPriority
) {
594 selectedTaskDynamicPriority
= task
->dynamicPriority
;
601 timeDelta_t taskRequiredTimeUs
= selectedTask
->anticipatedExecutionTime
>> TASK_EXEC_TIME_SHIFT
;
602 #if defined(USE_LATE_TASK_STATISTICS)
603 selectedTask
->execTime
= taskRequiredTimeUs
;
605 int32_t taskRequiredTimeCycles
= (int32_t)clockMicrosToCycles((uint32_t)taskRequiredTimeUs
);
607 nowCycles
= getCycleCounter();
608 schedLoopRemainingCycles
= cmpTimeCycles(nextTargetCycles
, nowCycles
);
610 // Allow a little extra time
611 taskRequiredTimeCycles
+= taskGuardCycles
;
613 if (!gyroEnabled
|| (taskRequiredTimeCycles
< schedLoopRemainingCycles
)) {
614 uint32_t antipatedEndCycles
= nowCycles
+ taskRequiredTimeCycles
;
615 taskExecutionTimeUs
+= schedulerExecuteTask(selectedTask
, currentTimeUs
);
616 nowCycles
= getCycleCounter();
617 int32_t cyclesOverdue
= cmpTimeCycles(nowCycles
, antipatedEndCycles
);
619 #if defined(USE_LATE_TASK_STATISTICS)
620 if (cyclesOverdue
> 0) {
621 if ((currentTask
- tasks
) != TASK_SERIAL
) {
622 DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM
, 1, currentTask
- tasks
);
623 DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM
, 2, clockCyclesTo10thMicros(cyclesOverdue
));
624 currentTask
->lateCount
++;
626 lateTaskTotal
+= cyclesOverdue
;
629 #endif // USE_LATE_TASK_STATISTICS
631 if ((cyclesOverdue
> 0) || (-cyclesOverdue
< taskGuardMinCycles
)) {
632 if (taskGuardCycles
< taskGuardMaxCycles
) {
633 taskGuardCycles
+= taskGuardDeltaUpCycles
;
635 } else if (taskGuardCycles
> taskGuardMinCycles
) {
636 taskGuardCycles
-= taskGuardDeltaDownCycles
;
638 #if defined(USE_LATE_TASK_STATISTICS)
640 #endif // USE_LATE_TASK_STATISTICS
641 } else if (selectedTask
->taskAgeCycles
> TASK_AGE_EXPEDITE_COUNT
) {
642 // If a task has been unable to run, then reduce it's recorded estimated run time to ensure
643 // it's ultimate scheduling
644 selectedTask
->anticipatedExecutionTime
*= TASK_AGE_EXPEDITE_SCALE
;
649 #if !defined(UNIT_TEST)
650 DEBUG_SET(DEBUG_SCHEDULER
, 2, micros() - schedulerStartTimeUs
- taskExecutionTimeUs
); // time spent in scheduler
653 #if defined(UNIT_TEST)
654 readSchedulerLocals(selectedTask
, selectedTaskDynamicPriority
);
658 void schedulerEnableGyro(void)
663 uint16_t getAverageSystemLoadPercent(void)
665 return averageSystemLoadPercent
;
668 float schedulerGetCycleTimeMultiplier(void)
670 return (float)clockMicrosToCycles(getTask(TASK_GYRO
)->id
->desiredPeriodUs
) / desiredPeriodCycles
;