Summary
On 9.1, a freshly-defaulted SITL cannot arm. The Configurator's Status tab shows CPU in red, and arming is blocked by ARMING_DISABLED_SYSTEM_OVERLOADED. The machine is not loaded at all — averageSystemLoadPercent is structurally ≥100 in SITL builds since #11436.
Steps to reproduce
- Build/run the 9.1 SITL, defaults, UART1+UART2 = MSP, everything else off.
- Connect the Configurator → Status tab: CPU is red.
- Arming is refused with the system-overload flag.
Cause
averageSystemLoadPercent is a duty-cycle estimator that depends on busy-polling:
averageSystemLoadPercent = 100 * totalWaitingTasks / totalWaitingTasksSamples;
It samples once per scheduler() call and counts how many tasks were already overdue. On hardware, scheduler() spins thousands of times between task deadlines, so the vast majority of samples see zero waiting tasks and the
average stays well under 100.
#11436 (commit 8a47561) made scheduler() sleep until the next task is due:
#if defined(SITL_BUILD)
if (sitlEarliestNextTaskAt > nowUs + 50) {
usleep(sleepUs);
}
#endif
Every zero-waiting sample is now gone. The scheduler runs one task, sleeps until the next is due, and wakes with ≥1 task already overdue — so the average sits at 100%+ permanently, and 200%+ whenever two tasks come due in the
same wake-up (routine: the 1 kHz gyro task alongside the 100 Hz ones). isSystemOverloaded() is averageSystemLoadPercent >= 100, so fc_core.c raises ARMING_DISABLED_SYSTEM_OVERLOADED forever.
The CPU saving in #11436 is real and worth keeping — it's the load metric that no longer means anything once the idle samples are gone.
Suggested fix
Measure actual duty cycle in SITL rather than counting overdue tasks: accumulate task execution time and compare against wall-clock elapsed in the taskSystem() window (load = 100 * busyUs / elapsedUs). That reports something
truthful under both scheduling models. Excluding the CPU-load arming check under SITL_BUILD would unblock arming but leaves the Status tab lying.
Version
9.1.0 (regression from 9.0 — the SITL sleep does not exist there). Reproduced on Linux.
Summary
On 9.1, a freshly-defaulted SITL cannot arm. The Configurator's Status tab shows CPU in red, and arming is blocked by ARMING_DISABLED_SYSTEM_OVERLOADED. The machine is not loaded at all — averageSystemLoadPercent is structurally ≥100 in SITL builds since #11436.
Steps to reproduce
Cause
averageSystemLoadPercent is a duty-cycle estimator that depends on busy-polling:
averageSystemLoadPercent = 100 * totalWaitingTasks / totalWaitingTasksSamples;It samples once per scheduler() call and counts how many tasks were already overdue. On hardware, scheduler() spins thousands of times between task deadlines, so the vast majority of samples see zero waiting tasks and the
average stays well under 100.
#11436 (commit 8a47561) made scheduler() sleep until the next task is due:
Every zero-waiting sample is now gone. The scheduler runs one task, sleeps until the next is due, and wakes with ≥1 task already overdue — so the average sits at 100%+ permanently, and 200%+ whenever two tasks come due in the
same wake-up (routine: the 1 kHz gyro task alongside the 100 Hz ones). isSystemOverloaded() is averageSystemLoadPercent >= 100, so fc_core.c raises ARMING_DISABLED_SYSTEM_OVERLOADED forever.
The CPU saving in #11436 is real and worth keeping — it's the load metric that no longer means anything once the idle samples are gone.
Suggested fix
Measure actual duty cycle in SITL rather than counting overdue tasks: accumulate task execution time and compare against wall-clock elapsed in the taskSystem() window (load = 100 * busyUs / elapsedUs). That reports something
truthful under both scheduling models. Excluding the CPU-load arming check under SITL_BUILD would unblock arming but leaves the Status tab lying.
Version
9.1.0 (regression from 9.0 — the SITL sleep does not exist there). Reproduced on Linux.