[esp32] Move HAL bodies from components/esp32/core.cpp into core/hal/

Per-platform follow-up to #15977.

Move out-of-line HAL bodies (millis(), arch_restart(), arch_get_cpu_freq_hz())
from components/esp32/core.cpp into a new esphome/core/hal/hal_esp32.cpp so
HAL implementation finally lives next to the HAL header.

Inline the trivial one-liners directly in hal_esp32.h:
  delayMicroseconds(us)        -> delay_microseconds_safe(us)
  arch_feed_wdt()              -> esp_task_wdt_reset()
  arch_get_cpu_cycle_count()   -> esp_cpu_get_cycle_count()

arch_init() stays in components/esp32/core.cpp because it uses
esp32::crash_handler_read_and_clear() from the component-private
crash_handler.h header — moving it would require an awkward layering
inversion of core/ -> components/esp32/.

config.py FILTER_SOURCE_FILES gains a hal/hal_esp32.cpp entry so only
ESP32 builds compile it (same pattern wake/wake_*.cpp uses).

Cross-platform decls of delayMicroseconds(), arch_feed_wdt(), and
arch_get_cpu_cycle_count() are dropped from the dispatcher hal.h and
moved into each per-platform header — that way subsequent per-platform
follow-up PRs (libretiny, rp2040, host, zephyr) only need to touch
their own platform header instead of accumulating gates in hal.h.
This commit is contained in:
J. Nick Koston
2026-04-29 05:46:56 -05:00
parent 0f128d2f15
commit 5be4f746ba
9 changed files with 87 additions and 42 deletions
+2 -35
View File
@@ -6,12 +6,8 @@
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
#include <esp_clk_tree.h>
#include <esp_cpu.h>
#include <esp_idf_version.h>
#include <esp_ota_ops.h>
#include <esp_task_wdt.h>
#include <esp_timer.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@@ -23,29 +19,8 @@ extern "C" __attribute__((weak)) void initArduino() {}
namespace esphome {
// yield(), delay(), micros(), millis_64() inlined in hal.h.
// Use xTaskGetTickCount() when tick rate is 1 kHz (ESPHome's default via sdkconfig),
// falling back to esp_timer for non-standard rates. IRAM_ATTR is required because
// Wiegand and ZyAura call millis() from IRAM_ATTR ISR handlers on ESP32.
// xTaskGetTickCountFromISR() is used in ISR context to satisfy the FreeRTOS API contract.
uint32_t IRAM_ATTR HOT millis() {
#if CONFIG_FREERTOS_HZ == 1000
if (xPortInIsrContext()) [[unlikely]] {
return xTaskGetTickCountFromISR();
}
return xTaskGetTickCount();
#else
return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time()));
#endif
}
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
void arch_restart() {
esp_restart();
// restart() doesn't always end execution
while (true) { // NOLINT(clang-diagnostic-unreachable-code)
yield();
}
}
// millis(), arch_restart(), arch_get_cpu_freq_hz() out-of-line in hal/hal_esp32.cpp.
// delayMicroseconds(), arch_feed_wdt(), arch_get_cpu_cycle_count() inlined in hal/hal_esp32.h.
void arch_init() {
#ifdef USE_ESP32_CRASH_HANDLER
// Read crash data from previous boot before anything else
@@ -61,14 +36,6 @@ void arch_init() {
esp_ota_mark_app_valid_cancel_rollback();
#endif
}
void HOT arch_feed_wdt() { esp_task_wdt_reset(); }
uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
uint32_t arch_get_cpu_freq_hz() {
uint32_t freq = 0;
esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_CPU, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq);
return freq;
}
TaskHandle_t loop_task_handle = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static StaticTask_t loop_task_tcb; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
+10
View File
@@ -815,6 +815,16 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
"wake/wake_zephyr.cpp": {
PlatformFramework.NRF52_ZEPHYR,
},
# Per-platform HAL out-of-line implementations — hal.h dispatches to
# exactly one platform header based on USE_*, and the matching .cpp
# provides the few HAL functions that stay out-of-line. Other
# platforms still keep their out-of-line bodies in
# components/<platform>/core.cpp until their per-platform follow-up
# PR moves them here.
"hal/hal_esp32.cpp": {
PlatformFramework.ESP32_ARDUINO,
PlatformFramework.ESP32_IDF,
},
# Note: lock_free_queue.h and event_pool.h are header files and don't need to be filtered
# as they are only included when needed by the preprocessor
}
+3 -7
View File
@@ -30,15 +30,11 @@
namespace esphome {
// ESP8266 inlines delayMicroseconds() and arch_feed_wdt() in hal/hal_esp8266.h;
// every other platform keeps them out-of-line in components/<platform>/core.cpp.
#ifndef USE_ESP8266
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
void arch_feed_wdt();
#endif
// Cross-platform declarations. delayMicroseconds(), arch_feed_wdt(),
// arch_get_cpu_cycle_count() vary per platform (some inline, some
// out-of-line) so they live in hal/hal_<platform>.h.
void __attribute__((noreturn)) arch_restart();
void arch_init();
uint32_t arch_get_cpu_cycle_count();
uint32_t arch_get_cpu_freq_hz();
#ifndef USE_ESP8266
+44
View File
@@ -0,0 +1,44 @@
#ifdef USE_ESP32
#include "esphome/core/hal.h"
#include <esp_clk_tree.h>
#include <esp_system.h>
#include <esp_timer.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
namespace esphome {
// Use xTaskGetTickCount() when tick rate is 1 kHz (ESPHome's default via sdkconfig),
// falling back to esp_timer for non-standard rates. IRAM_ATTR is required because
// Wiegand and ZyAura call millis() from IRAM_ATTR ISR handlers on ESP32.
// xTaskGetTickCountFromISR() is used in ISR context to satisfy the FreeRTOS API contract.
uint32_t IRAM_ATTR HOT millis() {
#if CONFIG_FREERTOS_HZ == 1000
if (xPortInIsrContext()) [[unlikely]] {
return xTaskGetTickCountFromISR();
}
return xTaskGetTickCount();
#else
return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time()));
#endif
}
void arch_restart() {
esp_restart();
// restart() doesn't always end execution
while (true) { // NOLINT(clang-diagnostic-unreachable-code)
yield();
}
}
uint32_t arch_get_cpu_freq_hz() {
uint32_t freq = 0;
esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_CPU, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq);
return freq;
}
} // namespace esphome
#endif // USE_ESP32
+12
View File
@@ -4,6 +4,8 @@
#include <cstdint>
#include <esp_attr.h>
#include <esp_cpu.h>
#include <esp_task_wdt.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@@ -15,6 +17,11 @@
namespace esphome {
// Forward decl from helpers.h (esphome/core/helpers.h) — kept here so this
// header does not need to pull the rest of helpers.h.
// NOLINTNEXTLINE(readability-redundant-declaration)
void delay_microseconds_safe(uint32_t us);
/// Returns true when executing inside an interrupt handler.
__attribute__((always_inline)) inline bool in_isr_context() { return xPortInIsrContext() != 0; }
@@ -30,6 +37,11 @@ __attribute__((always_inline)) inline uint64_t millis_64() {
return micros_to_millis<uint64_t>(static_cast<uint64_t>(esp_timer_get_time()));
}
// 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() { esp_task_wdt_reset(); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
} // namespace esphome
#endif // USE_ESP32
+4
View File
@@ -19,6 +19,10 @@ uint32_t micros();
uint32_t millis();
uint64_t millis_64();
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
void arch_feed_wdt();
uint32_t arch_get_cpu_cycle_count();
} // namespace esphome
#endif // USE_HOST
+4
View File
@@ -88,6 +88,10 @@ __attribute__((always_inline)) inline uint32_t millis() { return static_cast<uin
#endif
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
void arch_feed_wdt();
uint32_t arch_get_cpu_cycle_count();
} // namespace esphome
#endif // USE_LIBRETINY
+4
View File
@@ -35,6 +35,10 @@ __attribute__((always_inline)) inline uint32_t micros() { return static_cast<uin
__attribute__((always_inline)) inline uint32_t millis() { return micros_to_millis(::time_us_64()); }
__attribute__((always_inline)) inline uint64_t millis_64() { return micros_to_millis<uint64_t>(::time_us_64()); }
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
void arch_feed_wdt();
uint32_t arch_get_cpu_cycle_count();
} // namespace esphome
#endif // USE_RP2040
+4
View File
@@ -19,6 +19,10 @@ uint32_t micros();
uint32_t millis();
uint64_t millis_64();
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
void arch_feed_wdt();
uint32_t arch_get_cpu_cycle_count();
} // namespace esphome
#endif // USE_ZEPHYR