rusefi/firmware/hw_layer/microsecond_timer/microsecond_timer.cpp

186 lines
5.6 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file microsecond_timer.cpp
*
* Here we have a 1MHz timer dedicated to event scheduling. We are using one of the 32-bit timers here,
* so this timer can schedule events up to 4B/100M ~ 4000 seconds ~ 1 hour from current time.
*
* GPT5 timer clock: 84000000Hz
* If only it was a better multiplier of 2 (84000000 = 328125 * 256)
*
* @date Apr 14, 2014
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*/
#include "pch.h"
2015-07-10 06:01:56 -07:00
#include "microsecond_timer.h"
#include "port_microsecond_timer.h"
2015-07-10 06:01:56 -07:00
#if EFI_PROD_CODE
#include "periodic_thread_controller.h"
// Just in case we have a mechanism to validate that hardware timer is clocked right and all the
// conversions between wall clock and hardware frequencies are done right
// delay in milliseconds
2024-04-25 13:52:34 -07:00
#define TEST_CALLBACK_DELAY_MS 10
2020-03-28 17:56:58 -07:00
// if hardware timer is 20% off we throw a critical error and call it a day
// maybe this threshold should be 5%? 10%?
#define TIMER_PRECISION_THRESHOLD 0.2
2017-05-21 07:25:35 -07:00
/**
* Maximum duration of complete timer callback, all pending events together
* See also 'maxEventCallbackDuration' for maximum duration of one event
*/
uint32_t maxPrecisionCallbackDuration = 0;
2015-07-10 06:01:56 -07:00
static efitick_t lastSetTimerTimeNt;
static bool isTimerPending = false;
2015-07-10 06:01:56 -07:00
static int timerCallbackCounter = 0;
static int timerRestartCounter = 0;
2015-07-10 06:01:56 -07:00
2016-01-30 19:03:36 -08:00
static int timerFreezeCounter = 0;
static int setHwTimerCounter = 0;
static bool hwStarted = false;
2016-01-30 19:03:36 -08:00
2015-07-10 06:01:56 -07:00
/**
* sets the alarm to the specified number of microseconds from now.
* This function should be invoked under kernel lock which would disable interrupts.
*/
void setHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt) {
criticalAssertVoid(hwStarted, "HW.started");
setHwTimerCounter++;
// How many ticks in the future is this event?
const auto timeDeltaNt = setTimeNt - nowNt;
2016-01-30 19:03:36 -08:00
/**
* #259 BUG error: not positive deltaTimeNt
2016-01-30 19:03:36 -08:00
* Once in a while we night get an interrupt where we do not expect it
*/
if (timeDeltaNt <= 0) {
2016-01-30 19:03:36 -08:00
timerFreezeCounter++;
warning(ObdCode::CUSTOM_OBD_LOCAL_FREEZE, "local freeze cnt=%d", timerFreezeCounter);
2016-01-30 19:03:36 -08:00
}
// We need the timer to fire after we return - too close to now may actually schedule in the past
if (timeDeltaNt < US2NT(2)) {
setTimeNt = nowNt + US2NT(2);
} else if (timeDeltaNt >= TOO_FAR_INTO_FUTURE_NT) {
uint32_t delta32;
if (timeDeltaNt > UINT32_MAX) {
delta32 = UINT32_MAX;
} else {
delta32 = timeDeltaNt;
}
2020-01-18 21:16:19 -08:00
// we are trying to set callback for too far into the future. This does not look right at all
firmwareError(ObdCode::CUSTOM_ERR_TIMER_OVERFLOW, "setHardwareSchedulerTimer() too far: %lu", delta32);
2018-05-20 10:36:15 -07:00
return;
}
// Skip scheduling if there's a firmware error active
if (hasFirmwareError()) {
2016-01-18 16:01:48 -08:00
return;
}
// Do the actual hardware-specific timer set operation
portSetHardwareSchedulerTimer(nowNt, setTimeNt);
2015-07-10 06:01:56 -07:00
lastSetTimerTimeNt = getTimeNowNt();
2020-04-24 18:21:04 -07:00
isTimerPending = true;
2015-07-10 06:01:56 -07:00
timerRestartCounter++;
}
2020-04-24 18:21:04 -07:00
void globalTimerCallback();
void portMicrosecondTimerCallback() {
2015-07-10 06:01:56 -07:00
timerCallbackCounter++;
isTimerPending = false;
2019-05-07 16:32:08 -07:00
uint32_t before = getTimeNowLowerNt();
2020-04-24 18:21:04 -07:00
globalTimerCallback();
2019-05-07 16:32:08 -07:00
uint32_t precisionCallbackDuration = getTimeNowLowerNt() - before;
2017-05-21 07:25:35 -07:00
if (precisionCallbackDuration > maxPrecisionCallbackDuration) {
maxPrecisionCallbackDuration = precisionCallbackDuration;
}
2015-07-10 06:01:56 -07:00
}
struct MicrosecondTimerWatchdogController : public PeriodicController<256> {
MicrosecondTimerWatchdogController()
: PeriodicController("MstWatchdog", NORMALPRIO, 2)
{
}
void PeriodicTask(efitick_t nowNt) override {
// 2 seconds of inactivity would not look right
efiAssertVoid(ObdCode::CUSTOM_TIMER_WATCHDOG, nowNt < lastSetTimerTimeNt + 2 * CORE_CLOCK, "Watchdog: no events for 2 seconds!");
2015-07-10 06:01:56 -07:00
}
};
2015-07-10 06:01:56 -07:00
2019-02-10 16:52:06 -08:00
static MicrosecondTimerWatchdogController watchdogControllerInstance;
2015-07-10 06:01:56 -07:00
static scheduling_s watchDogBuddy;
static void watchDogBuddyCallback(void*) {
/**
* the purpose of this periodic activity is to make watchdogControllerInstance
* watchdog happy by ensuring that we have scheduler activity even in case of very broken configuration
* without any PWM or input pins
*/
2024-07-11 17:03:13 -07:00
engine->scheduler.schedule("watch", &watchDogBuddy, getTimeNowNt() + MS2NT(1000), watchDogBuddyCallback);
}
static volatile bool testSchedulingHappened = false;
2024-04-25 13:52:34 -07:00
static Timer testScheduling;
static void timerValidationCallback(void*) {
testSchedulingHappened = true;
2024-04-25 13:52:34 -07:00
efitimems_t actualTimeSinceSchedulingMs = 1e3 * testScheduling.getElapsedSeconds();
2024-04-25 14:45:14 -07:00
2024-04-25 13:52:34 -07:00
if (absI(actualTimeSinceSchedulingMs - TEST_CALLBACK_DELAY_MS) > TEST_CALLBACK_DELAY_MS * TIMER_PRECISION_THRESHOLD) {
firmwareError(ObdCode::CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME, "hwTimer broken precision: %ld ms", actualTimeSinceSchedulingMs);
}
}
/**
* This method would validate that hardware timer callbacks happen with some reasonable precision
* helps to make sure our GPT hardware settings are somewhat right
*/
static void validateHardwareTimer() {
2019-11-12 19:56:56 -08:00
if (hasFirmwareError()) {
return;
}
2024-04-25 13:52:34 -07:00
testScheduling.reset();
// to save RAM let's use 'watchDogBuddy' here once before we enable watchdog
2024-07-11 17:03:13 -07:00
engine->scheduler.schedule(
2024-07-11 16:22:08 -07:00
"hw-validate",
&watchDogBuddy,
getTimeNowNt() + MS2NT(TEST_CALLBACK_DELAY_MS),
timerValidationCallback);
2024-04-25 13:52:34 -07:00
chThdSleepMilliseconds(TEST_CALLBACK_DELAY_MS + 2);
if (!testSchedulingHappened) {
firmwareError(ObdCode::CUSTOM_ERR_TIMER_TEST_CALLBACK_NOT_HAPPENED, "hwTimer not alive");
}
}
void initMicrosecondTimer() {
portInitMicrosecondTimer();
2019-02-27 14:12:52 -08:00
hwStarted = true;
2015-07-10 06:01:56 -07:00
lastSetTimerTimeNt = getTimeNowNt();
2019-11-12 18:42:44 -08:00
validateHardwareTimer();
watchDogBuddyCallback(NULL);
2015-07-10 06:01:56 -07:00
#if EFI_EMULATE_POSITION_SENSORS
2022-07-21 12:17:32 -07:00
watchdogControllerInstance.start();
#endif /* EFI_EMULATE_POSITION_SENSORS */
2015-07-10 06:01:56 -07:00
}
#endif /* EFI_PROD_CODE */