mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 17:05:36 +00:00
libretiny: drop BK72xx from IRAM_ATTR coverage
After confirming via the Beken SDKs (both BK7231T and BK7231N flash.c use
GLOBAL_INT_DISABLE around every erase/program) and searching the libretiny
+ esphome issue trackers, the ISR-during-flash race IRAM_ATTR is designed
to prevent cannot actually occur on any BK72xx variant:
- Beken SDK wraps every flash op in GLOBAL_INT_DISABLE(), masking FIQ +
IRQ at the CPU for the ~0.5-20 ms of the write, so no ISR fires while
flash is stalled.
- Interrupts are delayed (not dropped outright for single-shot sources)
by that mask, but that is an SDK-level design choice and cannot be
reduced from this layer.
- No BK72xx user has reported the crash pattern IRAM_ATTR fixes; the
real reports of that pattern are on RTL8710B (see libretiny#167).
Make IRAM_ATTR a no-op on every BK72xx variant and remove the BK7231N-
specific ITCM shuffling from patch_linker.py.script. The fix now covers
only the families where the race is real: RTL8710B, RTL8720C, LN882H.
This commit is contained in:
@@ -10,11 +10,6 @@ import re
|
||||
# function is callable while flash is busy (XIP stall, OTA, logger flash
|
||||
# write):
|
||||
#
|
||||
# - 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
|
||||
@@ -23,6 +18,11 @@ import re
|
||||
# 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.
|
||||
#
|
||||
# BK72xx (all variants) have no .ld patcher: the Beken SDK wraps every flash
|
||||
# operation in GLOBAL_INT_DISABLE() which masks FIQ + IRQ at the CPU, so no
|
||||
# ISR can fire during a flash stall and the race IRAM_ATTR guards against
|
||||
# cannot occur. IRAM_ATTR is a no-op on BK72xx (see esphome/core/hal.h).
|
||||
|
||||
|
||||
_MARKER = "/* esphome .sram.text */"
|
||||
@@ -34,40 +34,9 @@ _KEEP_LINE = (
|
||||
"__esphome_sram_text_end = .; "
|
||||
+ _MARKER + "\n"
|
||||
)
|
||||
_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)")
|
||||
|
||||
# On BK72xx the stock LibreTiny linker carves a 4.5 kB ".itcm" executable RAM
|
||||
# region; the SDK already fills most of it with its own ISR / flash / FreeRTOS
|
||||
# critical code, leaving <500 bytes for ESPHome IRAM_ATTR functions. Steal 4 kB
|
||||
# from the adjacent (rw!x) "tcm" data region and give it to itcm so our
|
||||
# .sram.text payload has room to grow. Physically tcm and itcm are contiguous
|
||||
# SRAM, so shifting the boundary is just a linker rewrite.
|
||||
_BK_TCM_LEN = re.compile(r"(\btcm\s*\(\s*rw!x\s*\)\s*:\s*ORIGIN\s*=\s*0x003F0000\s*,\s*LENGTH\s*=\s*)60k(\s*-\s*512\b)")
|
||||
_BK_ITCM_REGION = re.compile(
|
||||
r"(\bitcm\s*\(\s*rwx\s*\)\s*:\s*ORIGIN\s*=\s*)0x003FEE00(\s*,\s*LENGTH\s*=\s*)4k(\s*\+\s*512\b)"
|
||||
)
|
||||
|
||||
|
||||
def _grow_bk72xx_itcm(content):
|
||||
# Shrink tcm by 4 kB: 60k - 512 -> 56k - 512.
|
||||
new_content, tcm_count = _BK_TCM_LEN.subn(r"\g<1>56k\g<2>", content)
|
||||
# Shift itcm origin back 4 kB and grow length: 4k + 512 -> 8k + 512.
|
||||
new_content, itcm_count = _BK_ITCM_REGION.subn(
|
||||
r"\g<1>0x003FDE00\g<2>8k\g<3>", new_content
|
||||
)
|
||||
if tcm_count != 1 or itcm_count != 1:
|
||||
raise RuntimeError(
|
||||
"ESPHome: BK72xx linker script did not match the expected "
|
||||
"tcm/itcm declarations (tcm matches: {}, itcm matches: {}); "
|
||||
"refusing to link because IRAM_ATTR placement cannot be verified. "
|
||||
"LibreTiny probably changed the bk7231*_bsp.template.ld layout, "
|
||||
"update _BK_TCM_LEN / _BK_ITCM_REGION in patch_linker.py.script "
|
||||
"before shipping firmware.".format(tcm_count, itcm_count)
|
||||
)
|
||||
return new_content
|
||||
|
||||
|
||||
def _detect(env):
|
||||
prefix = "USE_LIBRETINY_VARIANT_"
|
||||
@@ -109,11 +78,8 @@ def _inject_keep(host_section):
|
||||
|
||||
# Variants not listed here intentionally have no .ld patcher:
|
||||
# - RTL8720C: stock linker already consumes *(.sram.text*).
|
||||
# - BK7231T / BK7231Q / BK7251: SDK wraps flash ops in GLOBAL_INT_DISABLE()
|
||||
# (FIQ + IRQ masked), so no ISR fires during a flash stall and IRAM_ATTR
|
||||
# is a no-op on those variants (see esphome/core/hal.h).
|
||||
# - BK72xx (all): SDK masks FIQ+IRQ around flash writes, IRAM_ATTR is no-op.
|
||||
_PATCHERS_BY_VARIANT = {
|
||||
"BK7231N": (_grow_bk72xx_itcm, _inject_keep(_BK_ITCM)),
|
||||
"LN882H": (_inject_keep(_LN_COPY),),
|
||||
"RTL8710B": (_inject_keep(_RTL8710B_IMAGE2),),
|
||||
}
|
||||
@@ -221,5 +187,7 @@ if _patchers:
|
||||
# as a pre-link action so it executes once the linker scripts exist.
|
||||
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", _pre_link)
|
||||
|
||||
# Post-link summary runs for every LibreTiny family.
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", _post_link)
|
||||
# Post-link summary runs for every LibreTiny family (except BK72xx where
|
||||
# IRAM_ATTR is a no-op and no symbols are relocated to RAM).
|
||||
if _patchers or _variant == "RTL8720C":
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", _post_link)
|
||||
|
||||
+11
-9
@@ -26,16 +26,18 @@
|
||||
// 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).
|
||||
// patch_linker.py.script routes ".sram.text" into each family's RAM-
|
||||
// executable output section: .itcm.code on BK7231N, .image2.ram.text on
|
||||
// RTL8710B, .flash_copysection on LN882H, stock *(.sram.text*) glob on
|
||||
// RTL8720C.
|
||||
// executable output section: .image2.ram.text on RTL8710B,
|
||||
// .flash_copysection on LN882H; RTL8720C's stock linker already consumes
|
||||
// *(.sram.text*) via its .ram.code_text output.
|
||||
//
|
||||
// BK7231T/Q/7251 are left as a no-op: their SDK wraps flash operations in
|
||||
// GLOBAL_INT_DISABLE() which masks FIQ + IRQ for the duration of the
|
||||
// write, so no ISR fires while flash is stalled and the scenario
|
||||
// IRAM_ATTR guards against does not occur there.
|
||||
#if defined(USE_LIBRETINY_VARIANT_BK7231T) || defined(USE_LIBRETINY_VARIANT_BK7231Q) || \
|
||||
defined(USE_LIBRETINY_VARIANT_BK7251)
|
||||
// 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")))
|
||||
|
||||
Reference in New Issue
Block a user