ESPHome 2026.5.0b1
Loading...
Searching...
No Matches
hal.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef USE_LIBRETINY
4
5#include <cstdint>
6
7// For the inline millis() fast paths (xTaskGetTickCount, portTICK_PERIOD_MS).
8#include <FreeRTOS.h>
9#include <task.h>
10
12
13// IRAM_ATTR places a function in executable RAM so it is callable from an
14// ISR even while flash is busy (XIP stall, OTA, logger flash write).
15// Each family uses a section its stock linker already routes to RAM:
16// RTL8710B → .image2.ram.text, RTL8720C → .sram.text. LN882H is the
17// exception: its stock linker has no matching glob, so patch_linker.py
18// injects KEEP(*(.sram.text*)) into .flash_copysection at pre-link.
19//
20// BK72xx (all variants) are left as a no-op: their SDK wraps flash
21// operations in GLOBAL_INT_DISABLE() which masks FIQ + IRQ at the CPU for
22// the duration of every write, so no ISR fires while flash is stalled and
23// the race IRAM_ATTR guards against cannot occur. The trade-off is that
24// interrupts are delayed (not dropped) by up to ~20 ms during a sector
25// erase, but that is an SDK-level choice and cannot be changed from this
26// layer.
27#if defined(USE_BK72XX)
28#define IRAM_ATTR
29#elif defined(USE_LIBRETINY_VARIANT_RTL8710B)
30// Stock linker consumes *(.image2.ram.text*) into .ram_image2.text (> BD_RAM).
31#define IRAM_ATTR __attribute__((noinline, section(".image2.ram.text")))
32#else
33// RTL8720C: stock linker consumes *(.sram.text*) into .ram.code_text.
34// LN882H: patch_linker.py.script injects *(.sram.text*) into
35// .flash_copysection (> RAM0 AT> FLASH).
36#define IRAM_ATTR __attribute__((noinline, section(".sram.text")))
37#endif
38#define PROGMEM
39
40#ifdef USE_BK72XX
41// Declared in the Beken FreeRTOS port (portmacro.h) and built in ARM mode so
42// it is callable from Thumb code via interworking. The MRS CPSR instruction
43// is ARM-only and user code here may be built in Thumb, so in_isr_context()
44// defers to this port helper on BK72xx instead of reading CPSR inline.
46#endif
47
48// Forward decls from Arduino's <Arduino.h> for the inline wrappers below.
49// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
50extern "C" void yield(void);
51extern "C" void delay(unsigned long ms);
52extern "C" unsigned long micros(void);
53extern "C" unsigned long millis(void);
54extern "C" void delayMicroseconds(unsigned int us);
55// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration)
56
57// Forward decls from libretiny's <lt_api.h> family for the inline arch_*
58// wrappers below. Pulling the full header would drag in the rest of the
59// LibreTiny C API.
60extern "C" void lt_wdt_feed(void);
62extern "C" uint32_t lt_cpu_get_freq(void);
63
64namespace esphome::libretiny {}
65
66namespace esphome {
67
69__attribute__((always_inline)) inline bool in_isr_context() {
70#if defined(USE_BK72XX)
71 // BK72xx is ARM968E-S (ARM9); see extern declaration above.
73#else
74 // Cortex-M (AmebaZ, AmebaZ2, LN882H). IPSR is the active exception number;
75 // non-zero means we're in a handler.
76 uint32_t ipsr;
77 __asm__ volatile("mrs %0, ipsr" : "=r"(ipsr));
78 return ipsr != 0;
79#endif
80}
81
82__attribute__((always_inline)) inline void yield() { ::yield(); }
83__attribute__((always_inline)) inline void delay(uint32_t ms) { ::delay(ms); }
84__attribute__((always_inline)) inline uint32_t micros() { return static_cast<uint32_t>(::micros()); }
85
86// Per-variant millis() fast path — matches MillisInternal::get().
87#if defined(USE_RTL87XX) || defined(USE_LN882X)
88static_assert(configTICK_RATE_HZ == 1000, "millis() fast path requires 1 kHz FreeRTOS tick");
89__attribute__((always_inline)) inline uint32_t millis() {
90 // xTaskGetTickCountFromISR is mandatory in interrupt context per the FreeRTOS API contract.
91 return in_isr_context() ? xTaskGetTickCountFromISR() : xTaskGetTickCount();
92}
93#elif defined(USE_BK72XX)
94static_assert(configTICK_RATE_HZ == 500, "BK72xx millis() fast path assumes 500 Hz FreeRTOS tick");
95__attribute__((always_inline)) inline uint32_t millis() { return xTaskGetTickCount() * portTICK_PERIOD_MS; }
96#else
97__attribute__((always_inline)) inline uint32_t millis() { return static_cast<uint32_t>(::millis()); }
98#endif
99__attribute__((always_inline)) inline uint64_t millis_64() { return Millis64Impl::compute(millis()); }
100
101// NOLINTNEXTLINE(readability-identifier-naming)
102__attribute__((always_inline)) inline void delayMicroseconds(uint32_t us) { ::delayMicroseconds(us); }
103__attribute__((hot, always_inline)) inline void arch_feed_wdt() { lt_wdt_feed(); }
104__attribute__((always_inline)) inline uint32_t arch_get_cpu_cycle_count() { return lt_cpu_get_cycle_count(); }
105__attribute__((always_inline)) inline uint32_t arch_get_cpu_freq_hz() { return lt_cpu_get_freq(); }
106
107void arch_init();
108
109} // namespace esphome
110
111#endif // USE_LIBRETINY
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:32
unsigned long millis(void)
void delay(unsigned long ms)
unsigned long micros(void)
void yield(void)
uint32_t platform_is_in_interrupt_context(void)
uint32_t lt_cpu_get_freq(void)
void delayMicroseconds(unsigned int us)
uint32_t lt_cpu_get_cycle_count(void)
void lt_wdt_feed(void)
uint32_t arch_get_cpu_cycle_count()
Definition hal.cpp:71
void arch_init()
Definition hal.cpp:47
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
uint32_t arch_get_cpu_freq_hz()
Definition hal.cpp:63
uint64_t millis_64()
Definition hal.cpp:29
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
void arch_feed_wdt()
Definition hal.cpp:53
void HOT delay(uint32_t ms)
Definition hal.cpp:82
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void uint32_t