diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 47ab92418b..b8833e5a55 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -468,10 +468,11 @@ async def component_to_code(config): build_src_flags += " -Os" cg.add_platformio_option("build_src_flags", build_src_flags) # IRAM_ATTR on LibreTiny expands to section(".sram.text") (see - # esphome/core/hal.h). This pre-link hook rewrites the processed .ld files - # in the build dir so that section lands in RAM on each family: - # - BK72xx: flip SRAM region (rw!x) -> (rwx) and inject - # KEEP(*(.sram.text*)) into the .data output section. + # esphome/core/hal.h). This pre-link hook rewrites the processed .ld + # files in the build dir so that section lands in an executable RAM + # output section on each family: + # - BK72xx: inject KEEP(*(.sram.text*)) into .itcm.code (the only + # rwx RAM region; main SRAM is rw!x on ARM968E-S). # - LN882H: inject KEEP(*(.sram.text*)) into .flash_copysection. # - RTL8710B: inject KEEP(*(.sram.text*)) into .image2.ram.text. # - RTL8720C: no-op (stock linker already consumes *(.sram.text*)). diff --git a/esphome/components/libretiny/patch_linker.py.script b/esphome/components/libretiny/patch_linker.py.script index a1c2f0f3e6..8d447630f7 100644 --- a/esphome/components/libretiny/patch_linker.py.script +++ b/esphome/components/libretiny/patch_linker.py.script @@ -6,17 +6,23 @@ import re # ESPHome marks ISR code IRAM_ATTR, which on LibreTiny expands to # section(".sram.text") (see esphome/core/hal.h). Each family's linker script -# needs that section routed into RAM-resident code so the function is callable -# while flash is busy (XIP stall, OTA, logger flash write): +# needs that section routed into RAM-resident *executable* memory so the +# function is callable while flash is busy (XIP stall, OTA, logger flash +# write): # -# - RTL8720C (AmebaZ2): stock linker already consumes "*(.sram.text*)", no-op. -# - RTL8710B (AmebaZ): stock linker has ".image2.ram.text" — inject -# "*(.sram.text*)" into it. -# - BK72xx: stock linker has a (rw!x) SRAM region that blocks executable -# placement; flip to (rwx) and inject "KEEP(*(.sram.text*))" into the ".data" -# output (which is already flash-to-SRAM copied at startup by the SDK). +# - BK72xx: ARM968E-S has tightly-coupled instruction RAM ("itcm", 4.5 kB, +# rwx) that the SDK uses for its own ISR / flash / FreeRTOS critical +# routines; main SRAM ("ram", 192 kB) is rw!x (bus does not allow +# instruction fetches). Inject "KEEP(*(.sram.text*))" into ".itcm.code" +# so our IRAM_ATTR functions share the only executable RAM region. # - LN882H: stock linker has ".flash_copysection" which is flash-to-RAM0 # copied at startup; inject "KEEP(*(.sram.text*))" there. +# - RTL8710B (AmebaZ): stock linker has ".image2.ram.text" — inject +# "KEEP(*(.sram.text*))" into it. +# - RTL8720C (AmebaZ2): stock linker already consumes "*(.sram.text*)", +# no-op. Loaded directly from the framework package so we cannot inject +# our __esphome_sram_text_start/end markers either; the post-link summary +# falls back to reading known IRAM_ATTR symbol addresses instead. _MARKER = "/* esphome .sram.text */" @@ -28,13 +34,9 @@ _KEEP_LINE = ( "__esphome_sram_text_end = .; " + _MARKER + "\n" ) -_BK_DATA = re.compile(r"(\.data\s*:\s*\{\s*\n)") -_BK_RW_NO_X = re.compile(r"(\bram\s*\()\s*rw\s*!\s*x(\s*\))") +_BK_ITCM = re.compile(r"(\.itcm\.code\s*ALIGN\s*\(\s*\d+\s*\)\s*:\s*\{\s*\n)") _LN_COPY = re.compile(r"(\.flash_copysection\s*:\s*\{\s*\n)") _RTL8710B_IMAGE2 = re.compile(r"(\.image2\.ram\.text\s*:\s*\{\s*\n)") -# RTL8720C loads its linker script directly from the framework package so we -# cannot inject PROVIDE markers for it; the summary falls back to reading -# addresses of known IRAM_ATTR symbols instead of measuring a bracketed span. def _detect(env): @@ -75,17 +77,13 @@ def _inject_keep(host_section): return patch -def _flip_bk72xx_rwx(content): - return _BK_RW_NO_X.sub(r"\1rwx\2", content) - - # RTL8720C is absent: its stock linker already consumes *(.sram.text*), so # no .ld patch is needed; the summary falls back to reading symbol addresses. _PATCHERS_BY_VARIANT = { - "BK7231N": (_flip_bk72xx_rwx, _inject_keep(_BK_DATA)), - "BK7231T": (_flip_bk72xx_rwx, _inject_keep(_BK_DATA)), - "BK7231Q": (_flip_bk72xx_rwx, _inject_keep(_BK_DATA)), - "BK7251": (_flip_bk72xx_rwx, _inject_keep(_BK_DATA)), + "BK7231N": (_inject_keep(_BK_ITCM),), + "BK7231T": (_inject_keep(_BK_ITCM),), + "BK7231Q": (_inject_keep(_BK_ITCM),), + "BK7251": (_inject_keep(_BK_ITCM),), "LN882H": (_inject_keep(_LN_COPY),), "RTL8710B": (_inject_keep(_RTL8710B_IMAGE2),), } diff --git a/esphome/core/hal.h b/esphome/core/hal.h index 2803056730..35ee55076a 100644 --- a/esphome/core/hal.h +++ b/esphome/core/hal.h @@ -23,13 +23,13 @@ #elif defined(USE_LIBRETINY) -// IRAM_ATTR places a function in SRAM so it is callable from an ISR even -// while flash is busy (XIP stall, OTA, logger flash write). All LibreTiny -// families use ".sram.text". RTL8720C (AmebaZ2) already consumes that -// section; RTL8710B (AmebaZ), BK72xx, and LN882H get it routed into their -// RAM-resident output section via patch_linker.py.script. Using a custom -// name avoids the assembler "setting incorrect section attributes" warning -// that ".data.*" triggers when we place executable code there. +// 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 use ".sram.text"; patch_linker.py.script routes it +// into each family's RAM-executable output section (.itcm.code on BK72xx, +// .image2.ram.text on RTL8710B, .flash_copysection on LN882H). RTL8720C's +// stock linker already consumes *(.sram.text*) via its .ram.code_text +// output. #define IRAM_ATTR __attribute__((noinline, section(".sram.text"))) #define PROGMEM