From 7277b04039c245cb5d6ab7b0a618e16f2f813cb5 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Fri, 18 Dec 2020 05:21:18 -0800 Subject: [PATCH] force a pwm cycle start (skip cycles) if late (#2097) * skip cycles if late * fail at lower count * start at -1 --- .../system/timer/pwm_generator_logic.cpp | 14 ++++++++++++-- .../controllers/system/timer/pwm_generator_logic.h | 3 +++ .../system/timer/single_timer_executor.cpp | 13 +++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/firmware/controllers/system/timer/pwm_generator_logic.cpp b/firmware/controllers/system/timer/pwm_generator_logic.cpp index 19738e0f1e..b16e2f9e34 100644 --- a/firmware/controllers/system/timer/pwm_generator_logic.cpp +++ b/firmware/controllers/system/timer/pwm_generator_logic.cpp @@ -160,13 +160,15 @@ void PwmConfig::handleCycleStart() { efiAssertVoid(CUSTOM_ERR_6580, periodNt != 0, "period not initialized"); efiAssertVoid(CUSTOM_ERR_6580, iterationLimit > 0, "iterationLimit invalid"); - if (safe.periodNt != periodNt || safe.iteration == iterationLimit) { + if (forceCycleStart || safe.periodNt != periodNt || safe.iteration == iterationLimit) { /** * period length has changed - we need to reset internal state */ safe.startNt = getTimeNowNt(); safe.iteration = 0; safe.periodNt = periodNt; + + forceCycleStart = false; #if DEBUG_PWM scheduleMsg(&logger, "state reset start=%d iteration=%d", state->safe.start, state->safe.iteration); #endif @@ -225,10 +227,18 @@ efitick_t PwmConfig::togglePwmState() { scheduleMsg(&logger, "%s: nextSwitchTime %d", state->name, nextSwitchTime); #endif /* DEBUG_PWM */ + // If we're very far behind schedule, restart the cycle fresh to avoid scheduling a huge pile of events all at once + // This can happen during config write or debugging where CPU is halted for multiple seconds + bool isVeryBehindSchedule = nextSwitchTimeNt < getTimeNowNt() - MS2NT(10); + safe.phaseIndex++; - if (safe.phaseIndex == phaseCount || mode != PM_NORMAL) { + if (isVeryBehindSchedule || safe.phaseIndex == phaseCount || mode != PM_NORMAL) { safe.phaseIndex = 0; // restart safe.iteration++; + + if (isVeryBehindSchedule) { + forceCycleStart = true; + } } #if EFI_UNIT_TEST printf("PWM: nextSwitchTimeNt=%d phaseIndex=%d iteration=%d\r\n", nextSwitchTimeNt, diff --git a/firmware/controllers/system/timer/pwm_generator_logic.h b/firmware/controllers/system/timer/pwm_generator_logic.h index d60f8317de..b7ab616792 100644 --- a/firmware/controllers/system/timer/pwm_generator_logic.h +++ b/firmware/controllers/system/timer/pwm_generator_logic.h @@ -110,6 +110,9 @@ private: * PWM generation is not happening while this value is NAN */ float periodNt; + + // Set if we are very far behind schedule and need to reset back to the beginning of a cycle to find our way + bool forceCycleStart = true; }; struct hardware_pwm; diff --git a/firmware/controllers/system/timer/single_timer_executor.cpp b/firmware/controllers/system/timer/single_timer_executor.cpp index 268217b8b1..a36de7d2c0 100644 --- a/firmware/controllers/system/timer/single_timer_executor.cpp +++ b/firmware/controllers/system/timer/single_timer_executor.cpp @@ -123,16 +123,21 @@ void SingleTimerExecutor::executeAllPendingActions() { * TODO: add a counter & figure out a limit of iterations? */ - executeCounter = 0; + // starts at -1 because do..while will run a minimum of once + executeCounter = -1; + bool didExecute; do { efitick_t nowNt = getTimeNowNt(); didExecute = queue.executeOne(nowNt); - if (executeCounter++ == 10000) { - firmwareError(CUSTOM_ERR_LOCK_ISSUE, "Looks like firmware is really busy"); + + // if we're stuck in a loop executing lots of events, panic! + if (executeCounter++ == 500) { + firmwareError(CUSTOM_ERR_LOCK_ISSUE, "Maximum scheduling run length exceeded - CPU load too high"); } } while (didExecute); + maxExecuteCounter = maxI(maxExecuteCounter, executeCounter); if (!isLocked()) { @@ -169,7 +174,7 @@ void initSingleTimerExecutorHardware(void) { void executorStatistics() { if (engineConfiguration->debugMode == DBG_EXECUTOR) { -#if EFI_TUNER_STUDIO && EFI_SIGNAL_EXECUTOR_ONE_TIMER +#if EFI_TUNER_STUDIO tsOutputChannels.debugIntField1 = ___engine.executor.timerCallbackCounter; tsOutputChannels.debugIntField2 = ___engine.executor.executeAllPendingActionsInvocationCounter; tsOutputChannels.debugIntField3 = ___engine.executor.scheduleCounter;