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
|
|
|
*/
|
|
|
|
|
2018-09-16 19:26:57 -07:00
|
|
|
#include "global.h"
|
2019-07-03 18:48:04 -07:00
|
|
|
#include "os_access.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "microsecond_timer.h"
|
|
|
|
#include "scheduler.h"
|
2019-07-06 17:15:49 -07:00
|
|
|
#include "os_util.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
// https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fInterrupt%20on%20CEN%20bit%20setting%20in%20TIM7&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=474
|
|
|
|
|
2019-04-12 17:52:51 -07:00
|
|
|
#if EFI_PROD_CODE && HAL_USE_GPT
|
2019-03-31 14:44:34 -07:00
|
|
|
|
2019-07-13 08:08:17 -07:00
|
|
|
#include "periodic_task.h"
|
2019-07-29 19:47:39 -07:00
|
|
|
#include "engine.h"
|
|
|
|
EXTERN_ENGINE;
|
2017-05-21 07:03:17 -07:00
|
|
|
|
2019-11-10 20:54:08 -08:00
|
|
|
// 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
|
|
|
|
#define TEST_CALLBACK_DELAY 30
|
|
|
|
// if hardware timer is 20% off we throw a fatal 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
|
|
|
|
2017-05-21 07:25:35 -07:00
|
|
|
// must be one of 32 bit times
|
2019-03-28 19:46:10 -07:00
|
|
|
#ifndef GPTDEVICE
|
2015-07-10 06:01:56 -07:00
|
|
|
#define GPTDEVICE GPTD5
|
2019-03-28 19:46:10 -07:00
|
|
|
#endif /* GPTDEVICE */
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
static volatile efitick_t lastSetTimerTimeNt;
|
|
|
|
static int lastSetTimerValue;
|
|
|
|
static volatile bool isTimerPending = FALSE;
|
|
|
|
|
|
|
|
static volatile int timerCallbackCounter = 0;
|
|
|
|
static volatile int timerRestartCounter = 0;
|
|
|
|
|
|
|
|
schfunc_t globalTimerCallback;
|
|
|
|
|
|
|
|
static const char * msg;
|
|
|
|
|
|
|
|
static char buff[32];
|
|
|
|
|
2016-01-30 19:03:36 -08:00
|
|
|
static int timerFreezeCounter = 0;
|
2019-01-11 14:40:44 -08:00
|
|
|
static volatile int setHwTimerCounter = 0;
|
2019-02-27 14:12:52 -08:00
|
|
|
static volatile bool hwStarted = false;
|
2016-01-30 19:03:36 -08:00
|
|
|
|
2016-01-18 16:01:48 -08:00
|
|
|
extern bool hasFirmwareErrorFlag;
|
|
|
|
|
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.
|
|
|
|
*/
|
2020-01-18 21:16:19 -08:00
|
|
|
void setHardwareUsTimer(int32_t deltaTimeUs) {
|
2019-11-10 09:39:47 -08:00
|
|
|
enginePins.debugSetTimer.setValue(1);
|
2019-02-27 14:12:52 -08:00
|
|
|
efiAssertVoid(OBD_PCM_Processor_Fault, hwStarted, "HW.started");
|
2019-01-11 14:40:44 -08:00
|
|
|
setHwTimerCounter++;
|
2016-01-30 19:03:36 -08:00
|
|
|
/**
|
2020-01-18 21:16:19 -08:00
|
|
|
* #259 BUG error: not positive deltaTimeUs
|
2016-01-30 19:03:36 -08:00
|
|
|
* Once in a while we night get an interrupt where we do not expect it
|
|
|
|
*/
|
2020-01-18 21:16:19 -08:00
|
|
|
if (deltaTimeUs <= 0) {
|
2016-01-30 19:03:36 -08:00
|
|
|
timerFreezeCounter++;
|
2017-03-08 22:10:33 -08:00
|
|
|
warning(CUSTOM_OBD_LOCAL_FREEZE, "local freeze cnt=%d", timerFreezeCounter);
|
2016-01-30 19:03:36 -08:00
|
|
|
}
|
2020-01-18 21:16:19 -08:00
|
|
|
if (deltaTimeUs < 2)
|
|
|
|
deltaTimeUs = 2; // for some reason '1' does not really work
|
|
|
|
efiAssertVoid(CUSTOM_ERR_6681, deltaTimeUs > 0, "not positive deltaTimeUs");
|
2020-01-18 21:28:58 -08:00
|
|
|
if (deltaTimeUs >= TOO_FAR_INTO_FUTURE) {
|
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
|
2020-01-18 21:28:58 -08:00
|
|
|
firmwareError(CUSTOM_ERR_TIMER_OVERFLOW, "setHardwareUsTimer() too far: %d", deltaTimeUs);
|
2019-11-10 09:39:47 -08:00
|
|
|
// let's make this look special and NOT toggle enginePins.debugSetTimer
|
2017-01-21 15:02:13 -08:00
|
|
|
return;
|
|
|
|
}
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2018-05-30 21:32:55 -07:00
|
|
|
if (GPTDEVICE.state == GPT_ONESHOT) {
|
2015-07-10 06:01:56 -07:00
|
|
|
gptStopTimerI(&GPTDEVICE);
|
2018-05-30 21:32:55 -07:00
|
|
|
}
|
2018-05-20 10:36:15 -07:00
|
|
|
if (GPTDEVICE.state != GPT_READY) {
|
2019-01-11 14:40:44 -08:00
|
|
|
firmwareError(CUSTOM_HW_TIMER, "HW timer state %d/%d", GPTDEVICE.state, setHwTimerCounter);
|
2019-11-10 09:39:47 -08:00
|
|
|
// let's make this look special and NOT toggle enginePins.debugSetTimer
|
2018-05-20 10:36:15 -07:00
|
|
|
return;
|
|
|
|
}
|
2019-11-10 09:39:47 -08:00
|
|
|
if (hasFirmwareError()) {
|
|
|
|
// let's make this look special and NOT toggle enginePins.debugSetTimer
|
2016-01-18 16:01:48 -08:00
|
|
|
return;
|
2019-11-10 09:39:47 -08:00
|
|
|
}
|
2020-01-18 21:16:19 -08:00
|
|
|
gptStartOneShotI(&GPTDEVICE, deltaTimeUs);
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
lastSetTimerTimeNt = getTimeNowNt();
|
2020-01-18 21:16:19 -08:00
|
|
|
lastSetTimerValue = deltaTimeUs;
|
2015-07-10 06:01:56 -07:00
|
|
|
isTimerPending = TRUE;
|
|
|
|
timerRestartCounter++;
|
2019-11-10 09:39:47 -08:00
|
|
|
enginePins.debugSetTimer.setValue(0);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2019-01-08 21:25:45 -08:00
|
|
|
static void hwTimerCallback(GPTDriver *gptp) {
|
2015-07-10 06:01:56 -07:00
|
|
|
(void)gptp;
|
2019-11-10 09:39:47 -08:00
|
|
|
enginePins.debugTimerCallback.setValue(1);
|
2015-07-10 06:01:56 -07:00
|
|
|
timerCallbackCounter++;
|
|
|
|
if (globalTimerCallback == NULL) {
|
2017-05-21 07:03:17 -07:00
|
|
|
firmwareError(CUSTOM_ERR_NULL_TIMER_CALLBACK, "NULL globalTimerCallback");
|
2015-07-10 06:01:56 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
isTimerPending = false;
|
|
|
|
|
2019-05-07 16:32:08 -07:00
|
|
|
uint32_t before = getTimeNowLowerNt();
|
2015-07-10 06:01:56 -07:00
|
|
|
globalTimerCallback(NULL);
|
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;
|
2017-05-21 07:03:17 -07:00
|
|
|
}
|
2019-11-10 09:39:47 -08:00
|
|
|
enginePins.debugTimerCallback.setValue(0);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2019-07-13 08:08:17 -07:00
|
|
|
class MicrosecondTimerWatchdogController : public PeriodicTimerController {
|
|
|
|
void PeriodicTask() override {
|
2019-12-21 18:11:09 -08:00
|
|
|
efitick_t nowNt = getTimeNowNt();
|
2019-01-06 18:15:49 -08:00
|
|
|
if (nowNt >= lastSetTimerTimeNt + 2 * CORE_CLOCK) {
|
|
|
|
strcpy(buff, "no_event");
|
|
|
|
itoa10(&buff[8], lastSetTimerValue);
|
|
|
|
firmwareError(CUSTOM_ERR_SCHEDULING_ERROR, buff);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = isTimerPending ? "No_cb too long" : "Timer not awhile";
|
|
|
|
// 2 seconds of inactivity would not look right
|
|
|
|
efiAssertVoid(CUSTOM_ERR_6682, nowNt < lastSetTimerTimeNt + 2 * CORE_CLOCK, msg);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
2019-07-13 08:08:17 -07:00
|
|
|
|
|
|
|
int getPeriodMs() override {
|
|
|
|
return 500;
|
|
|
|
}
|
2019-01-06 18:15:49 -08: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
|
|
|
|
2019-11-10 20:54:08 -08:00
|
|
|
/*
|
|
|
|
* The specific 1MHz frequency is important here since 'setHardwareUsTimer' method takes microsecond parameter
|
|
|
|
* For any arbitrary frequency to work we would need an additional layer of conversion.
|
|
|
|
*/
|
2019-01-06 18:15:49 -08:00
|
|
|
static constexpr GPTConfig gpt5cfg = { 1000000, /* 1 MHz timer clock.*/
|
2019-01-08 21:25:45 -08:00
|
|
|
hwTimerCallback, /* Timer callback.*/
|
2017-03-22 16:46:05 -07:00
|
|
|
0, 0 };
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-07-29 19:47:39 -07:00
|
|
|
static scheduling_s watchDogBuddy;
|
|
|
|
|
|
|
|
static void watchDogBuddyCallback(void *arg) {
|
|
|
|
(void)arg;
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2020-01-06 21:41:18 -08:00
|
|
|
engine->executor.scheduleForLater(&watchDogBuddy, MS2US(1000), watchDogBuddyCallback);
|
2019-07-29 19:47:39 -07:00
|
|
|
}
|
|
|
|
|
2019-11-10 20:54:08 -08:00
|
|
|
static volatile bool testSchedulingHappened = false;
|
|
|
|
static efitimems_t testSchedulingStart;
|
|
|
|
|
|
|
|
static void timerValidationCallback(void *arg) {
|
|
|
|
(void)arg;
|
|
|
|
|
|
|
|
testSchedulingHappened = true;
|
|
|
|
efitimems_t actualTimeSinceScheduling = (currentTimeMillis() - testSchedulingStart);
|
|
|
|
|
|
|
|
if (absI(actualTimeSinceScheduling - TEST_CALLBACK_DELAY) > TEST_CALLBACK_DELAY * TIMER_PRECISION_THRESHOLD) {
|
|
|
|
firmwareError(CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME, "hwTimer broken precision");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
2019-11-10 20:54:08 -08:00
|
|
|
testSchedulingStart = currentTimeMillis();
|
|
|
|
|
|
|
|
// to save RAM let's use 'watchDogBuddy' here once before we enable watchdog
|
2020-01-06 21:41:18 -08:00
|
|
|
engine->executor.scheduleForLater(&watchDogBuddy, MS2US(TEST_CALLBACK_DELAY), timerValidationCallback);
|
2019-11-10 20:54:08 -08:00
|
|
|
|
|
|
|
chThdSleepMilliseconds(2 * TEST_CALLBACK_DELAY);
|
|
|
|
if (!testSchedulingHappened) {
|
|
|
|
firmwareError(CUSTOM_ERR_TIMER_TEST_CALLBACK_NOT_HAPPENED, "hwTimer not alive");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
void initMicrosecondTimer(void) {
|
|
|
|
|
|
|
|
gptStart(&GPTDEVICE, &gpt5cfg);
|
2019-01-11 14:40:44 -08:00
|
|
|
efiAssertVoid(CUSTOM_ERR_TIMER_STATE, GPTDEVICE.state == GPT_READY, "hw state");
|
2019-02-27 14:12:52 -08:00
|
|
|
hwStarted = true;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
lastSetTimerTimeNt = getTimeNowNt();
|
2019-07-29 19:47:39 -07:00
|
|
|
|
2019-11-12 18:42:44 -08:00
|
|
|
validateHardwareTimer();
|
2019-11-10 20:54:08 -08:00
|
|
|
|
2019-07-29 19:47:39 -07:00
|
|
|
watchDogBuddyCallback(NULL);
|
2015-07-10 06:01:56 -07:00
|
|
|
#if EFI_EMULATE_POSITION_SENSORS
|
2019-01-06 18:15:49 -08:00
|
|
|
watchdogControllerInstance.Start();
|
2019-07-13 08:08:17 -07:00
|
|
|
#endif /* EFI_EMULATE_POSITION_SENSORS */
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* EFI_PROD_CODE */
|