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 
53  // How many ticks in the future is this event?
54  auto timeDeltaNt = setTimeNt - nowNt;
55 
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 - 1 doesn't work as it may actually schedule in the past
68  if (timeDeltaNt < US2NT(2)) {
69  timeDeltaNt = US2NT(2);
70  }
71 
72  if (timeDeltaNt >= TOO_FAR_INTO_FUTURE_NT) {
73  // we are trying to set callback for too far into the future. This does not look right at all
74  firmwareError(ObdCode::CUSTOM_ERR_TIMER_OVERFLOW, "setHardwareSchedulerTimer() too far: %d", timeDeltaNt);
75  return;
76  }
77 
78  // Skip scheduling if there's a firmware error active
79  if (hasFirmwareError()) {
80  return;
81  }
82 
83  // Do the actual hardware-specific timer set operation
84  portSetHardwareSchedulerTimer(nowNt, setTimeNt);
85 
87  isTimerPending = true;
89 }
90 
91 void globalTimerCallback();
92 
95  isTimerPending = false;
96 
97  uint32_t before = getTimeNowLowerNt();
99  uint32_t precisionCallbackDuration = getTimeNowLowerNt() - before;
100  if (precisionCallbackDuration > maxPrecisionCallbackDuration) {
101  maxPrecisionCallbackDuration = precisionCallbackDuration;
102  }
103 }
104 
105 class MicrosecondTimerWatchdogController : public PeriodicTimerController {
106  void PeriodicTask() override {
107  efitick_t nowNt = getTimeNowNt();
108  if (nowNt >= lastSetTimerTimeNt + 2 * CORE_CLOCK) {
110  return;
111  }
112 
113  const char* msg = isTimerPending ? "No_cb too long" : "Timer not awhile";
114  // 2 seconds of inactivity would not look right
115  efiAssertVoid(ObdCode::CUSTOM_TIMER_WATCHDOG, nowNt < lastSetTimerTimeNt + 2 * CORE_CLOCK, msg);
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
159  engine->executor.scheduleForLater("hw-validate", &watchDogBuddy, MS2US(TEST_CALLBACK_DELAY_MS), timerValidationCallback);
160 
161  chThdSleepMilliseconds(TEST_CALLBACK_DELAY_MS + 2);
162  if (!testSchedulingHappened) {
164  }
165 }
166 
169 
170  hwStarted = true;
171 
173 
175 
176  watchDogBuddyCallback(NULL);
177 #if EFI_EMULATE_POSITION_SENSORS
179 #endif /* EFI_EMULATE_POSITION_SENSORS */
180 }
181 
182 #endif /* EFI_PROD_CODE */
SingleTimerExecutor executor
Definition: engine.h:241
virtual int getPeriodMs()=0
virtual void PeriodicTask()=0
void scheduleForLater(const char *msg, scheduling_s *scheduling, int delayUs, action_s action) override
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_ERR_SCHEDULING_ERROR
@ CUSTOM_TIMER_WATCHDOG
@ CUSTOM_OBD_LOCAL_FREEZE
@ CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME
uint32_t efitimems_t
Definition: rusefi_types.h:43