rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
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"
17
18#if EFI_PROD_CODE
19
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
36static efitick_t lastSetTimerTimeNt;
37static bool isTimerPending = false;
38
39static int timerCallbackCounter = 0;
40static int timerRestartCounter = 0;
41
42static int timerFreezeCounter = 0;
43static int setHwTimerCounter = 0;
44static 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 */
50void 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
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
110struct MicrosecondTimerWatchdogController : public PeriodicController<256> {
111 MicrosecondTimerWatchdogController()
112 : PeriodicController("MstWatchdog", NORMALPRIO, 2)
113 {
114 }
115
116 void PeriodicTask(efitick_t nowNt) override {
117 // 2 seconds of inactivity would not look right
118 if (nowNt > lastSetTimerTimeNt + MS2NT(2000)) {
119 firmwareError(ObdCode::RUNTIME_CRITICAL_TIMER_WATCHDOG, "Watchdog: no events for 2 seconds!");
120 }
121 }
122};
123
124static MicrosecondTimerWatchdogController watchdogControllerInstance;
125
127
128static void watchDogBuddyCallback(void*) {
129 /**
130 * the purpose of this periodic activity is to make watchdogControllerInstance
131 * watchdog happy by ensuring that we have scheduler activity even in case of very broken configuration
132 * without any PWM or input pins
133 */
135}
136
137static volatile bool testSchedulingHappened = false;
138static Timer testScheduling;
139
140static void timerValidationCallback(void*) {
142 efitimems_t actualTimeSinceSchedulingMs = 1e3 * testScheduling.getElapsedSeconds();
143
144 if (absI(actualTimeSinceSchedulingMs - TEST_CALLBACK_DELAY_MS) > TEST_CALLBACK_DELAY_MS * TIMER_PRECISION_THRESHOLD) {
145 firmwareError(ObdCode::CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME, "hwTimer broken precision: %ld ms", actualTimeSinceSchedulingMs);
146 }
147}
148
149/**
150 * This method would validate that hardware timer callbacks happen with some reasonable precision
151 * helps to make sure our GPT hardware settings are somewhat right
152 */
154 if (hasFirmwareError()) {
155 return;
156 }
157 testScheduling.reset();
158
159 // to save RAM let's use 'watchDogBuddy' here once before we enable watchdog
161 "hw-validate",
163 getTimeNowNt() + MS2NT(TEST_CALLBACK_DELAY_MS),
165
166 chThdSleepMilliseconds(TEST_CALLBACK_DELAY_MS + 2);
169 }
170}
171
174
175 hwStarted = true;
176
178
180
182#if EFI_EMULATE_POSITION_SENSORS
184#endif /* EFI_EMULATE_POSITION_SENSORS */
185}
186
187#endif /* EFI_PROD_CODE */
SingleTimerExecutor scheduler
Definition engine.h:254
Base class for a controller that needs to run periodically to perform work.
virtual void PeriodicTask(efitick_t nowNt)=0
Called periodically. Override this method to do work for your controller.
void schedule(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
static Engine *const engine
Definition engine.h:386
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_OBD_LOCAL_FREEZE
@ CUSTOM_ERR_TIMER_TEST_CALLBACK_WRONG_TIME
@ RUNTIME_CRITICAL_TIMER_WATCHDOG
uint32_t efitimems_t