Skip to content

Esp32: place tcpip_kick_thread() in IRAM - #3044

Merged
slaff merged 1 commit into
SmingHub:developfrom
pljakobs:fix/tcp_kick_thread
Nov 12, 2026
Merged

slaff merged 1 commit into
SmingHub:developfrom
pljakobs:fix/tcp_kick_thread

Conversation

@pljakobs

@pljakobs pljakobs commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

another corner case:

I had issues with OTA failing while Esp32HardwarePWM was running.
HardwarePWM uses end-of-fade interrupts to enqueue new fades using System.queueCallback().
During OTA, I understand that flash cache is disabled and thus only routines explicitly marked IRAM_ATTR is placed in IRAM. If a fade end happens within this no-cache window, this chain falls through to tcpip_kick_thread() which is not in IRAM.

Below is the analysis that copilot has provided, it looks consistent and the below fix adds IRAM_ATTR for tcp_kick_thread().

==== Copilot analysis below ====

system_os_post() is IRAM_ATTR and documented as ISR-safe, and is used from IRAM interrupt handlers via System.queueCallback() (e.g. the GPIO interrupt processor, or LEDC fade-end callbacks, whose ISR IDF always registers with ESP_INTR_FLAG_IRAM). However, it calls tcpip_kick_thread(), which was not marked IRAM_ATTR.

If such an interrupt fires while the flash cache is disabled (during a flash write/erase, e.g. OTA or filesystem writes), fetching tcpip_kick_thread() from flash faults. Observed on ESP32-C3 (debug build) as "Illegal instruction" with MEPC in tcpip_kick_thread() and RA in system_os_post(), called from ledc_fade_isr.

Why this is rarely seen:

  • The interrupt must coincide with a cache-disabled window, so it needs both frequent IRAM interrupts that post to the task queue and flash writes at the same time.
  • When the compiler inlines tcpip_kick_thread() into system_os_post(), the code ends up in IRAM anyway. In a debug (-Og) build it is emitted as a separate function in flash (.flash.text).

The function only uses ISR-safe FreeRTOS/lwIP calls, so marking it IRAM_ATTR is sufficient.

system_os_post() is IRAM_ATTR and documented as ISR-safe, and is used
from IRAM interrupt handlers via System.queueCallback() (e.g. the GPIO
interrupt processor, or LEDC fade-end callbacks, whose ISR IDF always
registers with ESP_INTR_FLAG_IRAM). However, it calls
tcpip_kick_thread(), which was not marked IRAM_ATTR.

If such an interrupt fires while the flash cache is disabled (during a
flash write/erase, e.g. OTA or filesystem writes), fetching
tcpip_kick_thread() from flash faults. Observed on ESP32-C3 (debug build)
as "Illegal instruction" with MEPC in tcpip_kick_thread() and RA in
system_os_post(), called from ledc_fade_isr.

Why this is rarely seen:
- The interrupt must coincide with a cache-disabled window, so it needs
  both frequent IRAM interrupts that post to the task queue and flash
  writes at the same time.
- When the compiler inlines tcpip_kick_thread() into system_os_post(),
  the code ends up in IRAM anyway. In a debug (-Og) build it is emitted
  as a separate function in flash (.flash.text).

The function only uses ISR-safe FreeRTOS/lwIP calls, so marking it
IRAM_ATTR is sufficient.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@what-the-diff

what-the-diff Bot commented Nov 9, 2026

Copy link
Copy Markdown

PR Summary

  • Makes TCP/IP thread wake-ups safe during interrupts. Places tcpip_kick_thread() in memory that remains accessible while the flash cache is disabled, helping prevent failures when the function is called from interrupt context.

@mikee47 mikee47 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted, thank you

@slaff slaff added this to the 6.3.0 milestone Nov 10, 2026
@slaff
slaff merged commit 8d4475b into SmingHub:develop Nov 12, 2026
35 of 41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants