Skip to content

Commit bc7d279

Browse files
committed
Alternate CPU frequency scaling mitigation.
1 parent 59d3143 commit bc7d279

File tree

1 file changed

+29
-21
lines changed

1 file changed

+29
-21
lines changed

cores/esp8266/core_esp8266_waveform.cpp

+29-21
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(10000);
5151
// Maximum servicing time for any single IRQ
5252
constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(18);
5353
// The latency between in-ISR rearming of the timer and the earliest firing
54-
constexpr int32_t IRQLATENCYCCYS = microsecondsToClockCycles(2);
54+
constexpr int32_t IRQLATENCYCCYS = ISCPUFREQ160MHZ ?
55+
microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2);
56+
// The SDK and hardware take some time to actually get to our NMI code
57+
constexpr int32_t DELTAIRQCCYS = ISCPUFREQ160MHZ ?
58+
microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2);
5559

5660
// for INFINITE, the NMI proceeds on the waveform without expiry deadline.
5761
// for EXPIRES, the NMI expires the waveform automatically on the expiry ccy.
@@ -251,8 +255,9 @@ static inline ICACHE_RAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool i
251255
}
252256

253257
static ICACHE_RAM_ATTR void timer1Interrupt() {
254-
const bool isCPU2X = CPU2X & 1;
255258
const uint32_t isrStartCcy = ESP.getCycleCount();
259+
int32_t clockDrift = isrStartCcy - waveform.nextEventCcy - DELTAIRQCCYS;
260+
const bool isCPU2X = CPU2X & 1;
256261
if ((waveform.toSetBits && !(waveform.enabled & waveform.toSetBits)) || waveform.toDisableBits) {
257262
// Handle enable/disable requests from main app.
258263
waveform.enabled = (waveform.enabled & ~waveform.toDisableBits) | waveform.toSetBits; // Set the requested waveforms on/off
@@ -268,13 +273,9 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
268273
waveform.states &= ~waveform.toSetBits; // Clear the state of any just started
269274
if (wave.alignPhase >= 0 && waveform.enabled & (1UL << wave.alignPhase)) {
270275
wave.nextPeriodCcy = waveform.pins[wave.alignPhase].nextPeriodCcy + wave.nextPeriodCcy;
271-
if (static_cast<int32_t>(waveform.nextEventCcy - wave.nextPeriodCcy) > 0) {
272-
waveform.nextEventCcy = wave.nextPeriodCcy;
273-
}
274276
}
275277
else {
276-
wave.nextPeriodCcy = isrStartCcy;
277-
waveform.nextEventCcy = wave.nextPeriodCcy;
278+
wave.nextPeriodCcy = waveform.nextEventCcy;
278279
}
279280
if (!wave.expiryCcy) {
280281
wave.mode = WaveformMode::INFINITE;
@@ -294,10 +295,8 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
294295

295296
// Exit the loop if the next event, if any, is sufficiently distant.
296297
const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS;
297-
uint32_t busyPins = (static_cast<int32_t>(waveform.nextEventCcy - isrTimeoutCcy) < 0) ? waveform.enabled : 0;
298-
if (!waveform.enabled || busyPins) {
299-
waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS;
300-
}
298+
uint32_t busyPins = waveform.enabled;
299+
waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS;
301300

302301
uint32_t now = ESP.getCycleCount();
303302
uint32_t isrNextEventCcy = now;
@@ -315,6 +314,12 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
315314

316315
Waveform& wave = waveform.pins[pin];
317316

317+
if (clockDrift) {
318+
wave.endDutyCcy += clockDrift;
319+
wave.nextPeriodCcy += clockDrift;
320+
wave.expiryCcy += clockDrift;
321+
}
322+
318323
uint32_t waveNextEventCcy = (waveform.states & pinBit) ? wave.endDutyCcy : wave.nextPeriodCcy;
319324
if (WaveformMode::EXPIRES == wave.mode &&
320325
static_cast<int32_t>(waveNextEventCcy - wave.expiryCcy) >= 0 &&
@@ -394,29 +399,32 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
394399
}
395400
now = ESP.getCycleCount();
396401
}
402+
clockDrift = 0;
397403
}
398404

399405
int32_t callbackCcys = 0;
400406
if (waveform.timer1CB) {
401407
callbackCcys = scaleCcys(microsecondsToClockCycles(waveform.timer1CB()), isCPU2X);
402408
}
403409
now = ESP.getCycleCount();
404-
int32_t nextTimerCcys = waveform.nextEventCcy - now;
410+
int32_t nextEventCcys = waveform.nextEventCcy - now;
405411
// Account for unknown duration of timer1CB().
406-
if (waveform.timer1CB && nextTimerCcys > callbackCcys) {
407-
nextTimerCcys = callbackCcys;
412+
if (waveform.timer1CB && nextEventCcys > callbackCcys) {
413+
waveform.nextEventCcy = now + callbackCcys;
414+
nextEventCcys = callbackCcys;
408415
}
409416

410-
// Firing timer too soon, the NMI occurs before ISR has returned.
411-
if (nextTimerCcys < IRQLATENCYCCYS) {
412-
nextTimerCcys = IRQLATENCYCCYS;
417+
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
418+
if (isCPU2X) {
419+
nextEventCcys >>= 1;
413420
}
414421

415-
// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
416-
if (ISCPUFREQ160MHZ || isCPU2X) {
417-
nextTimerCcys >>= 1;
422+
// Firing timer too soon, the NMI occurs before ISR has returned.
423+
if (nextEventCcys < IRQLATENCYCCYS) {
424+
waveform.nextEventCcy = now + IRQLATENCYCCYS;
425+
nextEventCcys = IRQLATENCYCCYS;
418426
}
419427

420428
// Register access is fast and edge IRQ was configured before.
421-
T1L = nextTimerCcys;
429+
T1L = nextEventCcys;
422430
}

0 commit comments

Comments
 (0)