[core] Move per-platform hal_platform.h into components/platform/hal.h (#16183)

This commit is contained in:
J. Nick Koston authored and GitHub committed 2026-04-30 21:27:31 -05:00
1 parent 0980630f68
commit 5cc447e0da
12 files changed
+29 -17

No files matched your search

+12 -12
View File
@@ -8,22 +8,22 @@
// Per-platform HAL bits (IRAM_ATTR / PROGMEM macros, in_isr_context(),
// inline yield/delay/micros/millis/millis_64 wrappers, ESP8266 progmem
// helpers) live under esphome/core/hal/ and are dispatched here based on
// the active USE_* platform define. Each header guards its body with the
// matching #ifdef USE_<platform> and re-enters namespace esphome {} so it
// is safe to be re-included.
// helpers) live next to each platform component as components/<platform>/hal.h
// and are dispatched here based on the active USE_* platform define. Each
// header guards its body with the matching #ifdef USE_<platform> and re-enters
// namespace esphome {} so it is safe to be re-included.
#if defined(USE_ESP32)
#include "esphome/core/hal/hal_esp32.h"
#include "esphome/components/esp32/hal.h"
#elif defined(USE_ESP8266)
#include "esphome/core/hal/hal_esp8266.h"
#include "esphome/components/esp8266/hal.h"
#elif defined(USE_LIBRETINY)
#include "esphome/core/hal/hal_libretiny.h"
#include "esphome/components/libretiny/hal.h"
#elif defined(USE_RP2040)
#include "esphome/core/hal/hal_rp2040.h"
#include "esphome/components/rp2040/hal.h"
#elif defined(USE_HOST)
#include "esphome/core/hal/hal_host.h"
#include "esphome/components/host/hal.h"
#elif defined(USE_ZEPHYR)
#include "esphome/core/hal/hal_zephyr.h"
#include "esphome/components/zephyr/hal.h"
#else
#error "hal.h: not implemented for this platform"
#endif
@@ -33,12 +33,12 @@ namespace esphome {
// Cross-platform declarations. delayMicroseconds(), arch_feed_wdt(),
// arch_get_cpu_cycle_count(), arch_init(), arch_get_cpu_freq_hz() vary
// per platform (some inline, some out-of-line) so they live in
// hal/hal_<platform>.h.
// components/<platform>/hal.h.
void __attribute__((noreturn)) arch_restart();
#ifndef USE_ESP8266
// All non-ESP8266 platforms: PROGMEM is a no-op, so these are direct dereferences.
// ESP8266's out-of-line declarations live in hal/hal_esp8266.h.
// ESP8266's out-of-line declarations live in components/esp8266/hal.h.
inline uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
inline const char *progmem_read_ptr(const char *const *addr) { return *addr; }
inline uint16_t progmem_read_uint16(const uint16_t *addr) { return *addr; }
-50
View File
@@ -1,50 +0,0 @@
#pragma once
#ifdef USE_ESP32
#include <cstdint>
#include <esp_attr.h>
#include <esp_cpu.h>
#include <esp_task_wdt.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "esphome/core/time_conversion.h"
#ifndef PROGMEM
#define PROGMEM
#endif
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; }
// Forward decl from <esp_timer.h>.
// NOLINTNEXTLINE(readability-redundant-declaration)
extern "C" int64_t esp_timer_get_time(void);
__attribute__((always_inline)) inline void yield() { vPortYield(); }
__attribute__((always_inline)) inline void delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(esp_timer_get_time()); }
uint32_t millis();
__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(); }
void arch_init();
uint32_t arch_get_cpu_freq_hz();
} // namespace esphome
#endif // USE_ESP32
-71
View File
@@ -1,71 +0,0 @@
#pragma once
#ifdef USE_ESP8266
#include <c_types.h>
#include <core_esp8266_features.h>
#include <cstdint>
#include <pgmspace.h>
#include "esphome/core/time_64.h"
#ifndef PROGMEM
#define PROGMEM ICACHE_RODATA_ATTR
#endif
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
extern "C" void yield(void);
extern "C" void delay(unsigned long ms);
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 <user_interface.h> for arch_feed_wdt() inline below.
// NOLINTNEXTLINE(readability-redundant-declaration)
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
/// ESP8266 wake path is context-agnostic (wake_loop_impl uses esp_schedule
/// which is ISR-safe) so this helper is unused on this platform.
__attribute__((always_inline)) inline bool in_isr_context() { return false; }
__attribute__((always_inline)) inline void yield() { ::yield(); }
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
void delay(uint32_t ms);
uint32_t millis();
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
// 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<const char *>(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(); }
__attribute__((always_inline)) inline void arch_init() {}
// esp_get_cycle_count() declared in <core_esp8266_features.h>; F_CPU is a
// compiler-driven macro from the ESP8266 Arduino board defs (-DF_CPU=...).
__attribute__((always_inline)) inline uint32_t arch_get_cpu_cycle_count() { return esp_get_cycle_count(); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_freq_hz() { return F_CPU; }
} // namespace esphome
#endif // USE_ESP8266
-32
View File
@@ -1,32 +0,0 @@
#pragma once
#ifdef USE_HOST
#include <cstdint>
#include <sched.h>
#define IRAM_ATTR
#define PROGMEM
namespace esphome {
/// Returns true when executing inside an interrupt handler.
/// Host has no ISR concept.
__attribute__((always_inline)) inline bool in_isr_context() { return false; }
__attribute__((always_inline)) inline void yield() { ::sched_yield(); }
void delay(uint32_t ms);
uint32_t micros();
uint32_t millis();
uint64_t millis_64();
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
uint32_t arch_get_cpu_cycle_count();
__attribute__((always_inline)) inline void arch_init() {}
__attribute__((always_inline)) inline void arch_feed_wdt() {}
__attribute__((always_inline)) inline uint32_t arch_get_cpu_freq_hz() { return 1000000000U; }
} // namespace esphome
#endif // USE_HOST
-109
View File
@@ -1,109 +0,0 @@
#pragma once
#ifdef USE_LIBRETINY
#include <cstdint>
// For the inline millis() fast paths (xTaskGetTickCount, portTICK_PERIOD_MS).
#include <FreeRTOS.h>
#include <task.h>
#include "esphome/core/time_64.h"
// IRAM_ATTR places a function in executable RAM so it is callable from an
// ISR even while flash is busy (XIP stall, OTA, logger flash write).
// Each family uses a section its stock linker already routes to RAM:
// RTL8710B → .image2.ram.text, RTL8720C → .sram.text. LN882H is the
// exception: its stock linker has no matching glob, so patch_linker.py
// injects KEEP(*(.sram.text*)) into .flash_copysection at pre-link.
//
// BK72xx (all variants) are left as a no-op: their SDK wraps flash
// operations in GLOBAL_INT_DISABLE() which masks FIQ + IRQ at the CPU for
// the duration of every write, so no ISR fires while flash is stalled and
// the race IRAM_ATTR guards against cannot occur. The trade-off is that
// interrupts are delayed (not dropped) by up to ~20 ms during a sector
// erase, but that is an SDK-level choice and cannot be changed from this
// layer.
#if defined(USE_BK72XX)
#define IRAM_ATTR
#elif defined(USE_LIBRETINY_VARIANT_RTL8710B)
// Stock linker consumes *(.image2.ram.text*) into .ram_image2.text (> BD_RAM).
#define IRAM_ATTR __attribute__((noinline, section(".image2.ram.text")))
#else
// RTL8720C: stock linker consumes *(.sram.text*) into .ram.code_text.
// LN882H: patch_linker.py.script injects *(.sram.text*) into
// .flash_copysection (> RAM0 AT> FLASH).
#define IRAM_ATTR __attribute__((noinline, section(".sram.text")))
#endif
#define PROGMEM
#ifdef USE_BK72XX
// Declared in the Beken FreeRTOS port (portmacro.h) and built in ARM mode so
// it is callable from Thumb code via interworking. The MRS CPSR instruction
// is ARM-only and user code here may be built in Thumb, so in_isr_context()
// defers to this port helper on BK72xx instead of reading CPSR inline.
extern "C" uint32_t platform_is_in_interrupt_context(void);
#endif
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
extern "C" void yield(void);
extern "C" void delay(unsigned long ms);
extern "C" unsigned long micros(void);
extern "C" unsigned long millis(void);
extern "C" void delayMicroseconds(unsigned int us);
// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
// Forward decls from libretiny's <lt_api.h> family for the inline arch_*
// wrappers below. Pulling the full header would drag in the rest of the
// LibreTiny C API.
extern "C" void lt_wdt_feed(void);
extern "C" uint32_t lt_cpu_get_cycle_count(void);
extern "C" uint32_t lt_cpu_get_freq(void);
namespace esphome {
/// Returns true when executing inside an interrupt handler.
__attribute__((always_inline)) inline bool in_isr_context() {
#if defined(USE_BK72XX)
// BK72xx is ARM968E-S (ARM9); see extern declaration above.
return platform_is_in_interrupt_context() != 0;
#else
// Cortex-M (AmebaZ, AmebaZ2, LN882H). IPSR is the active exception number;
// non-zero means we're in a handler.
uint32_t ipsr;
__asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
return ipsr != 0;
#endif
}
__attribute__((always_inline)) inline void yield() { ::yield(); }
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
// Per-variant millis() fast path — matches MillisInternal::get().
#if defined(USE_RTL87XX) || defined(USE_LN882X)
static_assert(configTICK_RATE_HZ == 1000, "millis() fast path requires 1 kHz FreeRTOS tick");
__attribute__((always_inline)) inline uint32_t millis() {
// xTaskGetTickCountFromISR is mandatory in interrupt context per the FreeRTOS API contract.
return in_isr_context() ? xTaskGetTickCountFromISR() : xTaskGetTickCount();
}
#elif defined(USE_BK72XX)
static_assert(configTICK_RATE_HZ == 500, "BK72xx millis() fast path assumes 500 Hz FreeRTOS tick");
__attribute__((always_inline)) inline uint32_t millis() { return xTaskGetTickCount() * portTICK_PERIOD_MS; }
#else
__attribute__((always_inline)) inline uint32_t millis() { return static_cast<uint32_t>(::millis()); }
#endif
__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
// NOLINTNEXTLINE(readability-identifier-naming)
__attribute__((always_inline)) inline void delayMicroseconds(uint32_t us) { ::delayMicroseconds(us); }
__attribute__((hot, always_inline)) inline void arch_feed_wdt() { lt_wdt_feed(); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_cycle_count() { return lt_cpu_get_cycle_count(); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_freq_hz() { return lt_cpu_get_freq(); }
void arch_init();
} // namespace esphome
#endif // USE_LIBRETINY
-59
View File
@@ -1,59 +0,0 @@
#pragma once
#ifdef USE_RP2040
#include <cstdint>
#include "esphome/core/time_conversion.h"
#define IRAM_ATTR __attribute__((noinline, long_call, section(".time_critical")))
#define PROGMEM
// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
extern "C" void yield(void);
extern "C" void delay(unsigned long ms);
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 <pico/time.h>.
extern "C" uint64_t time_us_64(void);
// Forward decls from pico-sdk / FreeRTOS port for the inline arch_*
// wrappers below.
extern "C" void watchdog_update(void);
extern "C" unsigned long ulMainGetRunTimeCounterValue(void);
namespace esphome {
// Forward decl from 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() {
uint32_t ipsr;
__asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
return ipsr != 0;
}
__attribute__((always_inline)) inline void yield() { ::yield(); }
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
__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()); }
// 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() { watchdog_update(); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_cycle_count() {
return static_cast<uint32_t>(ulMainGetRunTimeCounterValue());
}
void arch_init();
uint32_t arch_get_cpu_freq_hz();
} // namespace esphome
#endif // USE_RP2040
-34
View File
@@ -1,34 +0,0 @@
#pragma once
#ifdef USE_ZEPHYR
#include <cstdint>
#include <zephyr/kernel.h>
#define IRAM_ATTR
#define PROGMEM
namespace esphome {
/// Returns true when executing inside an interrupt handler.
/// Zephyr/nRF52: not currently consulted — wake path is platform-specific.
__attribute__((always_inline)) inline bool in_isr_context() { return false; }
__attribute__((always_inline)) inline void yield() { ::k_yield(); }
__attribute__((always_inline)) inline void delay(uint32_t ms) { ::k_msleep(ms); }
__attribute__((always_inline)) inline uint32_t micros() { return k_ticks_to_us_floor32(k_uptime_ticks()); }
__attribute__((always_inline)) inline uint64_t millis_64() { return static_cast<uint64_t>(k_uptime_get()); }
__attribute__((always_inline)) inline uint32_t millis() { return static_cast<uint32_t>(millis_64()); }
// NOLINTNEXTLINE(readability-identifier-naming)
__attribute__((always_inline)) inline void delayMicroseconds(uint32_t us) { ::k_usleep(us); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_cycle_count() { return k_cycle_get_32(); }
__attribute__((always_inline)) inline uint32_t arch_get_cpu_freq_hz() { return sys_clock_hw_cycles_per_sec(); }
void arch_feed_wdt();
void arch_init();
} // namespace esphome
#endif // USE_ZEPHYR