From c49fe825906adb48643d2052a6749d7566c15493 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 29 Jun 2024 16:10:51 +0200 Subject: [PATCH] PWM NMI fix --- cores/esp8266/core_esp8266_waveform_pwm.cpp | 36 ++++++++++++++++++++- cores/esp8266/core_version.h | 6 ++-- package.json | 2 +- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/core_esp8266_waveform_pwm.cpp b/cores/esp8266/core_esp8266_waveform_pwm.cpp index aea88e15b9..ebb6c4ac2a 100644 --- a/cores/esp8266/core_esp8266_waveform_pwm.cpp +++ b/cores/esp8266/core_esp8266_waveform_pwm.cpp @@ -1,3 +1,7 @@ +/* Modified to work around a fault in the NMI handling, which + can result in the system locking up and hard WDT crashes. +*/ + /* esp8266_waveform - General purpose waveform generation and control, supporting outputs on all pins in parallel. @@ -44,8 +48,12 @@ #include "ets_sys.h" #include "core_esp8266_waveform_pwm.h" #include "user_interface.h" + extern "C" { +// Linker magic +void usePWMFixedNMI() {}; + // Maximum delay between IRQs #define MAXIRQUS (10000) @@ -436,6 +444,30 @@ static inline ICACHE_RAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) { return (da < db) ? a : b; } +// NMI crash workaround +// Sometimes the NMI fails to return, stalling the CPU. When this happens, +// the next NMI gets a return address /inside the NMI handler function/. +// We work around this by caching the last NMI return address, and restoring +// the epc3 and eps3 registers to the previous values if the observed epc3 +// happens to be pointing to the _NMILevelVector function. +extern void _NMILevelVector(); +extern void _UserExceptionVector_1(); // the next function after _NMILevelVector +static inline ICACHE_RAM_ATTR void nmiCrashWorkaround() { + static uintptr_t epc3_backup, eps3_backup; + + uintptr_t epc3, eps3; + __asm__ __volatile__("rsr %0,epc3; rsr %1,eps3":"=a"(epc3),"=a" (eps3)); + if ((epc3 < (uintptr_t) &_NMILevelVector) || (epc3 >= (uintptr_t) &_UserExceptionVector_1)) { + // Address is good; save backup + epc3_backup = epc3; + eps3_backup = eps3; + } else { + // Address is inside the NMI handler -- restore from backup + __asm__ __volatile__("wsr %0,epc3; wsr %1,eps3"::"a"(epc3_backup),"a"(eps3_backup)); + } +} + + // The SDK and hardware take some time to actually get to our NMI code, so // decrement the next IRQ's timer value by a bit so we can actually catch the // real CPU cycle counter we want for the waveforms. @@ -452,9 +484,11 @@ static inline ICACHE_RAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) { #endif // When the time to the next edge is greater than this, RTI and set another IRQ to minimize CPU usage -#define MINIRQTIME microsecondsToClockCycles(4) +#define MINIRQTIME microsecondsToClockCycles(6) static ICACHE_RAM_ATTR void timer1Interrupt() { + nmiCrashWorkaround(); + // Flag if the core is at 160 MHz, for use by adjust() bool turbo = (*(uint32_t*)0x3FF00014) & 1 ? true : false; diff --git a/cores/esp8266/core_version.h b/cores/esp8266/core_version.h index 7db6da5824..317328b1b7 100644 --- a/cores/esp8266/core_version.h +++ b/cores/esp8266/core_version.h @@ -1,4 +1,4 @@ #define ARDUINO_ESP8266_GIT_VER 0x3969889e -#define ARDUINO_ESP8266_GIT_DESC 2.7.7 -#define ARDUINO_ESP8266_RELEASE_2_7_7 -#define ARDUINO_ESP8266_RELEASE "2_7_7" +#define ARDUINO_ESP8266_GIT_DESC 2.7.8 +#define ARDUINO_ESP8266_RELEASE_2_7_8 +#define ARDUINO_ESP8266_RELEASE "2_7_8" diff --git a/package.json b/package.json index e1754322c9..20f02bf499 100644 --- a/package.json +++ b/package.json @@ -2,5 +2,5 @@ "name": "framework-arduinoespressif8266", "description": "Arduino Wiring-based Framework for Tasmota (ESP8266 Core)", "url": "https://github.com/tasmota/Arduino", - "version": "2.7.7" + "version": "2.7.8" }