mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 23:40:25 +00:00
117 lines
5.2 KiB
C++
117 lines
5.2 KiB
C++
#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). All
|
|
// LibreTiny families that need it share the same .sram.text input section
|
|
// name; how that section is routed into RAM differs per family:
|
|
// RTL8720C: stock linker consumes *(.sram.text*) into .ram.code_text.
|
|
// RTL8710B: patch_linker.py.script injects KEEP(*(.sram.text*)) at the
|
|
// top of .ram_image2.data (which IS in ltchiptool's
|
|
// sections_ram). The stock linker has KEEP(*(.image2.ram.text*))
|
|
// in .ram_image2.text but that output section is NOT in
|
|
// ltchiptool's AmebaZ elf2bin sections_ram list, so code routed
|
|
// there is dropped from the flashed binary.
|
|
// LN882H: patch_linker.py.script injects KEEP(*(.sram.text*)) into
|
|
// .flash_copysection (> RAM0 AT> FLASH), after KEEP(*(.vectors))
|
|
// so the Cortex-M4 vector table stays 512-byte-aligned for VTOR.
|
|
//
|
|
// 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
|
|
#else
|
|
#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.
|
|
// NOLINTNEXTLINE(readability-redundant-declaration)
|
|
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.
|
|
// NOLINTBEGIN(readability-redundant-declaration)
|
|
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);
|
|
// NOLINTEND(readability-redundant-declaration)
|
|
|
|
namespace esphome::libretiny {}
|
|
|
|
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
|