From 0f128d2f15ed9a1f0f1f2970a421fa0c9b5e47f1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 29 Apr 2026 05:37:48 -0500 Subject: [PATCH] [esp8266] Inline delayMicroseconds/arch_feed_wdt/progmem_read_* in hal_esp8266.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These wrappers were one-line forwarders to platform primitives: delayMicroseconds(us) -> delay_microseconds_safe(us) arch_feed_wdt() -> system_soft_wdt_feed() progmem_read_byte(p) -> pgm_read_byte(p) progmem_read_ptr(p) -> pgm_read_ptr(p) cast progmem_read_uint16(p) -> pgm_read_word(p) Mark them __attribute__((always_inline)) inline in hal_esp8266.h so the wrapper call/return is eliminated at every call site, and remove the out-of-line definitions from components/esp8266/core.cpp. The IRAM_ATTR previously on delayMicroseconds() was decorative — its body calls delay_microseconds_safe() which lives in flash, so an IRAM ISR caller already jumped from SRAM into flash. Inlining is no worse than the prior code (same reasoning as the libretiny IRAM_ATTR note in the parent commit's PR description). The dispatcher hal.h now gates its delayMicroseconds/arch_feed_wdt declarations behind #ifndef USE_ESP8266 so clang-tidy does not flag them as redundant on top of the inline definitions. --- esphome/components/esp8266/core.cpp | 13 +------------ esphome/core/hal.h | 6 +++++- esphome/core/hal/hal_esp8266.h | 28 ++++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/esphome/components/esp8266/core.cpp b/esphome/components/esp8266/core.cpp index ed280274457..9161ca6aaf5 100644 --- a/esphome/components/esp8266/core.cpp +++ b/esphome/components/esp8266/core.cpp @@ -84,7 +84,7 @@ void HOT delay(uint32_t ms) { optimistic_yield(1000); } } -void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); } +// delayMicroseconds(), arch_feed_wdt(), and progmem_read_*() are inlined in hal/hal_esp8266.h. void arch_restart() { system_restart(); // restart() doesn't always end execution @@ -93,17 +93,6 @@ void arch_restart() { } } void arch_init() {} -void HOT arch_feed_wdt() { system_soft_wdt_feed(); } - -uint8_t progmem_read_byte(const uint8_t *addr) { - return pgm_read_byte(addr); // NOLINT -} -const char *progmem_read_ptr(const char *const *addr) { - return reinterpret_cast(pgm_read_ptr(addr)); // NOLINT -} -uint16_t progmem_read_uint16(const uint16_t *addr) { - return pgm_read_word(addr); // NOLINT -} uint32_t IRAM_ATTR HOT arch_get_cpu_cycle_count() { return esp_get_cycle_count(); } uint32_t arch_get_cpu_freq_hz() { return F_CPU; } diff --git a/esphome/core/hal.h b/esphome/core/hal.h index b8bdc783ab7..e20797cf95d 100644 --- a/esphome/core/hal.h +++ b/esphome/core/hal.h @@ -30,10 +30,14 @@ namespace esphome { +// ESP8266 inlines delayMicroseconds() and arch_feed_wdt() in hal/hal_esp8266.h; +// every other platform keeps them out-of-line in components//core.cpp. +#ifndef USE_ESP8266 void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming) +void arch_feed_wdt(); +#endif void __attribute__((noreturn)) arch_restart(); void arch_init(); -void arch_feed_wdt(); uint32_t arch_get_cpu_cycle_count(); uint32_t arch_get_cpu_freq_hz(); diff --git a/esphome/core/hal/hal_esp8266.h b/esphome/core/hal/hal_esp8266.h index b4476f1ab3b..d947612bcc7 100644 --- a/esphome/core/hal/hal_esp8266.h +++ b/esphome/core/hal/hal_esp8266.h @@ -4,6 +4,7 @@ #include #include +#include #include "esphome/core/time_64.h" @@ -19,8 +20,15 @@ extern "C" unsigned long micros(void); extern "C" unsigned long millis(void); // NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration) +// Forward decl from for arch_feed_wdt() inline below. +extern "C" void system_soft_wdt_feed(void); + namespace esphome { +// Forward decl from helpers.h so this header stays cheap. +// NOLINTNEXTLINE(readability-redundant-declaration) +void delay_microseconds_safe(uint32_t us); + /// Returns true when executing inside an interrupt handler. /// ESP8266 has no reliable single-register ISR detection: PS.INTLEVEL is /// non-zero both in a real ISR and when user code masks interrupts. The @@ -34,10 +42,22 @@ void delay(uint32_t ms); uint32_t millis(); __attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); } -// ESP8266: pgm_read_* does real flash reads on Harvard architecture -uint8_t progmem_read_byte(const uint8_t *addr); -const char *progmem_read_ptr(const char *const *addr); -uint16_t progmem_read_uint16(const uint16_t *addr); +// ESP8266: pgm_read_* does aligned 32-bit flash reads on Harvard architecture. +// Inline-forward to the platform macros so the wrappers themselves don't +// occupy IRAM/flash on every call site. +__attribute__((always_inline)) inline uint8_t progmem_read_byte(const uint8_t *addr) { + return pgm_read_byte(addr); // NOLINT +} +__attribute__((always_inline)) inline const char *progmem_read_ptr(const char *const *addr) { + return reinterpret_cast(pgm_read_ptr(addr)); // NOLINT +} +__attribute__((always_inline)) inline uint16_t progmem_read_uint16(const uint16_t *addr) { + return pgm_read_word(addr); // NOLINT +} + +// NOLINTNEXTLINE(readability-identifier-naming) +__attribute__((always_inline)) inline void delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); } +__attribute__((always_inline)) inline void arch_feed_wdt() { system_soft_wdt_feed(); } } // namespace esphome