[esp32] Inline millis() in hal.h, drop IRAM_ATTR

Move millis() to an inline function in hal.h that just returns
xTaskGetTickCount() -- eliminates the function call entirely at every
call site. No IRAM consumed, no flash function call.

Drop IRAM_ATTR from the fallback path. The original IRAM placement was
inherited from ESP8266 where Arduino millis() is called from ISR
handlers (Wiegand, ZyAura). ESPHome does not call millis() from ISR
on ESP32.
This commit is contained in:
J. Nick Koston
2026-04-12 08:49:04 -10:00
parent 6ac705cd66
commit c155f41110
2 changed files with 19 additions and 12 deletions
+6 -12
View File
@@ -23,19 +23,13 @@ extern "C" __attribute__((weak)) void initArduino() {}
namespace esphome {
void HOT yield() { vPortYield(); }
uint32_t IRAM_ATTR HOT millis() {
// ESPHome sets CONFIG_FREERTOS_HZ=1000 (see esp32/__init__.py), so one tick = one millisecond
// and xTaskGetTickCount() returns ms directly. This is a single volatile memory read (~20ns)
// vs esp_timer_get_time() + micros_to_millis() which does a hardware timer read + 64-bit
// multiply-shift conversion (~686ns measured). millis() is called 1+N times per main loop
// iteration (once at top + once per component for warn_blocking), so this saves ~3.4μs/loop
// on a 5-component device. micros() still uses esp_timer_get_time() for μs precision.
#if CONFIG_FREERTOS_HZ == 1000
return xTaskGetTickCount();
#else
return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time()));
// millis() is inlined in hal.h when CONFIG_FREERTOS_HZ == 1000 (just xTaskGetTickCount()).
// Fallback for non-standard tick rates. No IRAM_ATTR — the original IRAM placement was
// inherited from ESP8266 where Arduino's millis() is called from ISR handlers (Wiegand,
// ZyAura). ESPHome doesn't call millis() from ISR on ESP32.
#if CONFIG_FREERTOS_HZ != 1000
uint32_t HOT millis() { return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time())); }
#endif
}
uint64_t HOT millis_64() { return micros_to_millis<uint64_t>(static_cast<uint64_t>(esp_timer_get_time())); }
void HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); }
+13
View File
@@ -28,10 +28,23 @@
#endif
// On ESP32 with 1 kHz FreeRTOS tick rate, millis() is just xTaskGetTickCount() —
// a single volatile read of a DRAM global. Inlining it here eliminates the
// function call entirely at every call site. No IRAM needed (no flash code
// executed), no 64-bit math, no HAL call.
#if defined(USE_ESP32) && CONFIG_FREERTOS_HZ == 1000
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#endif
namespace esphome {
void yield();
#if defined(USE_ESP32) && CONFIG_FREERTOS_HZ == 1000
inline uint32_t millis() { return xTaskGetTickCount(); }
#else
uint32_t millis();
#endif
uint64_t millis_64();
uint32_t micros();
void delay(uint32_t ms);