rusEFI
The most advanced open source ECU
microsecond_timer.cpp
Go to the documentation of this file.
1 /**
2  * @file microsecond_timer.cpp
3  *
4  * Here we have a 1MHz timer dedicated to event scheduling. We are using one of the 32-bit timers here,
5  * so this timer can schedule events up to 4B/100M ~ 4000 seconds ~ 1 hour from current time.
6  *
7  * GPT5 timer clock: 84000000Hz
8  * If only it was a better multiplier of 2 (84000000 = 328125 * 256)
9  *
10  * @date Apr 14, 2014
11  * @author Andrey Belomutskiy, (c) 2012-2020
12  */
13 
14 #include "pch.h"
15 #include "microsecond_timer.h"
16 #include "port_microsecond_timer.h"
17 
18 #if EFI_PROD_CODE
19 
20 #include "periodic_task.h"
21 
22 // Just in case we have a mechanism to validate that hardware timer is clocked right and all the
23 // conversions between wall clock and hardware frequencies are done right
24 // delay in milliseconds
25 #define TEST_CALLBACK_DELAY_MS 10
26 // if hardware timer is 20% off we throw a critical error and call it a day
27 // maybe this threshold should be 5%? 10%?
28 #define TIMER_PRECISION_THRESHOLD 0.2
29 
30 /**
31  * Maximum duration of complete timer callback, all pending events together
32  * See also 'maxEventCallbackDuration' for maximum duration of one event
33  */
35 
36 static efitick_t lastSetTimerTimeNt;
37 static bool isTimerPending = false;
38 
39 static int timerCallbackCounter = 0;
40 static int timerRestartCounter = 0;
41 
42 static int timerFreezeCounter = 0;
43 static int setHwTimerCounter = 0;
44 static bool hwStarted = false;
45 
46 /**
47  * sets the alarm to the specified number of microseconds from now.
48  * This function should be invoked under kernel lock which would disable interrupts.
49  */
50 void setHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt) {
51  criticalAssertVoid(hwStarted, "HW.started");
52 
54 
55  // How many ticks in the future is this event?
56  const auto timeDeltaNt = setTimeNt - nowNt;
57 
58  /**
59  * #259 BUG error: not positive deltaTimeNt
60  * Once in a while we night get an interrupt where we do not expect it
61  */
62  if (timeDeltaNt <= 0) {
65  }
66 
67  // We need the timer to fire after we return - too close to now may actually schedule in the past
68  if (timeDeltaNt < US2NT(2)) {
69  setTimeNt = nowNt + US2NT(2);
70  } else if (timeDeltaNt >= TOO_FAR_INTO_FUTURE_NT) {
71  uint32_t delta32;
72  if (timeDeltaNt > UINT32_MAX) {
73  delta32 = UINT32_MAX;
74  } else {
75  delta32 = timeDeltaNt;
76  }
77 
78  // we are trying to set callback for too far into the future. This does not look right at all
79  firmwareError(ObdCode::CUSTOM_ERR_TIMER_OVERFLOW, "setHardwareSchedulerTimer() too far: %lu", delta32);
80  return;
81  }
82 
83  // Skip scheduling if there's a firmware error active
84  if (hasFirmwareError()) {
85  return;
86  }
87 
88  // Do the actual hardware-specific timer set operation
89  portSetHardwareSchedulerTimer(nowNt, setTimeNt);
90 
92  isTimerPending = true;
94 }
95 
96 void globalTimerCallback();
97 
100  isTimerPending = false;
101 
102  uint32_t before = getTimeNowLowerNt();
104  uint32_t precisionCallbackDuration = getTimeNowLowerNt() - before;
105  if (precisionCallbackDuration > maxPrecisionCallbackDuration) {
106  maxPrecisionCallbackDuration = precisionCallbackDuration;
107  }
108 }
109 
110 class MicrosecondTimerWatchdogController : public PeriodicTimerController {
111  void PeriodicTask() override {
112  efitick_t nowNt = getTimeNowNt();
113 
114  // 2 seconds of inactivity would not look right
115  efiAssertVoid(ObdCode::CUSTOM_TIMER_WATCHDOG, nowNt < lastSetTimerTimeNt + 2 * CORE_CLOCK, "Watchdog: no events for 2 seconds!");
116  }
117 
118  int getPeriodMs() override {
119  return 500;
120  }
121 };
122 
123 static MicrosecondTimerWatchdogController watchdogControllerInstance;
124 
126 
127 static void watchDogBuddyCallback(void*) {
128  /**
129  * the purpose of this periodic activity is to make watchdogControllerInstance
130  * watchdog happy by ensuring that we have scheduler activity even in case of very broken configuration
131  * without any PWM or input pins
132  */
134 }
135 
136 static volatile bool testSchedulingHappened = false;
137 static Timer testScheduling;
138 
139 static void timerValidationCallback(void*) {
140  testSchedulingHappened = true;
141  efitimems_t actualTimeSinceSchedulingMs = 1e3 * testScheduling.getElapsedSeconds();
142 
143  if (absI(actualTimeSinceSchedulingMs - TEST_CALLBACK_DELAY_MS) > TEST_CALLBACK_DELAY_MS * TIMER_PRECISION_THRESHOLD) {
144  firmwareError(ObdCode::CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME, "hwTimer broken precision: %ld ms", actualTimeSinceSchedulingMs);
145  }
146 }
147 
148 /**
149  * This method would validate that hardware timer callbacks happen with some reasonable precision
150  * helps to make sure our GPT hardware settings are somewhat right
151  */
152 static void validateHardwareTimer() {
153  if (hasFirmwareError()) {
154  return;
155  }
156  testScheduling.reset();
157 
158  // to save RAM let's use 'watchDogBuddy' here once before we enable watchdog
160  "hw-validate",
161  &watchDogBuddy,
162  getTimeNowNt() + MS2NT(TEST_CALLBACK_DELAY_MS),
164 
165  chThdSleepMilliseconds(TEST_CALLBACK_DELAY_MS + 2);
166  if (!testSchedulingHappened) {
168  }
169 }
170 
173 
174  hwStarted = true;
175 
177 
179 
180  watchDogBuddyCallback(NULL);
181 #if EFI_EMULATE_POSITION_SENSORS
183 #endif /* EFI_EMULATE_POSITION_SENSORS */
184 }
185 
186 #endif /* EFI_PROD_CODE */
SingleTimerExecutor executor
Definition: engine.h:241
virtual int getPeriodMs()=0
virtual void PeriodicTask()=0
void scheduleByTimestampNt(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s action) override
Schedule an action to be executed in the future.
efitick_t getTimeNowNt()
Definition: efitime.cpp:19
Engine * engine
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
void globalTimerCallback()
static bool hwStarted
static bool isTimerPending
static scheduling_s watchDogBuddy
static void timerValidationCallback(void *)
static int timerFreezeCounter
void setHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt)
static void watchDogBuddyCallback(void *)
static int timerRestartCounter
static MicrosecondTimerWatchdogController watchdogControllerInstance
static void validateHardwareTimer()
uint32_t maxPrecisionCallbackDuration
static Timer testScheduling
void portMicrosecondTimerCallback()
static int setHwTimerCounter
static efitick_t lastSetTimerTimeNt
static volatile bool testSchedulingHappened
static int timerCallbackCounter
void initMicrosecondTimer()
uint32_t getTimeNowLowerNt()
void portSetHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt)
void portInitMicrosecondTimer()
@ CUSTOM_ERR_TIMER_OVERFLOW
@ CUSTOM_ERR_TIMER_TEST_CALLBACK_NOT_HAPPENED
@ CUSTOM_TIMER_WATCHDOG
@ CUSTOM_OBD_LOCAL_FREEZE
@ CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME
uint32_t efitimems_t
Definition: rusefi_types.h:44