Skip to content

Fix: I2S TDM RX slot_bit_width set to 32BIT to prevent DMA out-of-bounds write - #17

Closed
lvips wants to merge 1 commit into
m5stack:mainfrom
lvips:fix/i2s-tdm-slot-width
Closed

lvips wants to merge 1 commit into
m5stack:mainfrom
lvips:fix/i2s-tdm-slot-width

Conversation

@lvips

@lvips lvips commented Aug 24, 2026 •

Copy link
Copy Markdown

Fix: I2S TDM RX slot_bit_width set to 32BIT to prevent DMA out-of-bounds write

Problem / 问题

On M5Stack Tab5, using the ES7210 codec in TDM mode for 4-microphone input causes a DMA out-of-bounds write that corrupts adjacent heap blocks, ultimately triggering CORRUPT HEAP panic and speaker popping/clicking noise.

在 M5Stack Tab5 上,ES7210 codec 以 TDM 模式采集 4 路麦克风时,会发生 DMA 越界写,腐蚀相邻 heap block,最终触发 CORRUPT HEAP panic 并伴随扬声器哒哒哒杂音。

Root Cause / 根因

The ES7210 outputs 32 bits per TDM slot, but the I2S RX channel was configured with slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO. ESP-IDF's auto-detection misinterprets this as 16-bit × 2-slot, causing the DMA intermediate buffer to be written with 86–588 extra bytes beyond its allocated size, overwriting the neighboring heap block.

ES7210 TDM 4mic 实际按 32bit/slot 输出,但 I2S RX 配置 slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO 被 ESP-IDF 误判为 16bit×2slot,DMA 中间 buffer 被多写入 86~588 字节,踩坏相邻 heap block。

Fix / 修复

In platforms/tab5/components/m5stack_tab5/m5stack_tab5.c, function bsp_audio_init():

Parameter Before After Reason
slot_bit_width I2S_SLOT_BIT_WIDTH_AUTO I2S_SLOT_BIT_WIDTH_32BIT Match ES7210 TDM actual 32bit/slot output
ws_width I2S_TDM_AUTO_WS_WIDTH 16 Explicit value, eliminate version ambiguity
dma_desc_num (default) 6 Increase DMA buffer headroom
dma_frame_num 480 256 Align to 64-byte boundary, eliminate auto-adjust warning
mic_selected ES7120_SEL_MIC1..4 ES7210_SEL_MIC1..4 Fix typo in macro name (ES7120→ES7210)

Code Diff / 代码差异

// slot_cfg: match ES7210 TDM 4mic actual 32bit/slot output
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,  // was: I2S_SLOT_BIT_WIDTH_AUTO
.ws_width       = 16,                         // was: I2S_TDM_AUTO_WS_WIDTH

// DMA buffer sizing
chan_cfg.dma_desc_num  = 6;
chan_cfg.dma_frame_num = 256;  // aligned to 64-byte boundary

// typo fix
es7210_cfg.mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC2 | ES7210_SEL_MIC3 | ES7210_SEL_MIC4;
//                        ^^^^^ was: ES7120_SEL_MIC

Impact & Verification / 影响与验证

  • Tested on: M5Stack Tab5 with ES7210 4-mic TDM input

  • Before fix: CORRUPT HEAP panic after minutes of audio capture; speaker clicking

  • After fix: Stable operation for extended periods, no heap corruption, clean audio

  • Risk: Low — only affects Tab5 BSP audio init path; no API changes

  • 实测平台: M5Stack Tab5 + ES7210 4mic TDM

  • 修复前: 采集几分钟后 CORRUPT HEAP panic,扬声器哒哒哒

  • 修复后: 长时间稳定运行,无堆腐蚀,音频干净

  • 风险: 低 — 仅影响 Tab5 BSP 音频初始化路径,无 API 变更

Checklist

  • Tested on real Tab5 hardware
  • No API changes
  • Single commit, minimal diff (1 file, +8/-3 lines)

根因:ES7210 TDM 4mic 实际按 32bit/slot 输出,但 I2S RX 配
slot_bit_width=AUTO 被 ESP-IDF 误判为 16bit×2slot,DMA 中间
buffer 被多写入 86~588 字节踩坏相邻 heap block,触发
CORRUPT HEAP → panic → 扬声器哒哒哒。

修复 (m5stack_tab5.c bsp_audio_init):
- slot_bit_width: AUTO → I2S_SLOT_BIT_WIDTH_32BIT
- ws_width: AUTO → 16 (消除版本歧义)
- dma_frame_num: 480 → 256 (对齐 64B 边界,消除 auto-adjust 警告)
- ES7120_SEL_MIC → ES7210_SEL_MIC (宏名拼写纠正)
@imliubo
imliubo requested a review from Forairaaaaa September 7, 2026 02:01
@imliubo
imliubo requested a review from LuoIsHere October 16, 2026 02:59
@LuoIsHere

Copy link
Copy Markdown

Hi @lvips, thank you for the contribution and for the detailed write-up. We looked into the changes against the current main and the ESP-IDF I2S driver, and unfortunately we don't think this PR fixes the issue in this repository. Here is a summary of what we found.

1. slot_bit_width / ws_width changes are overwritten before any capture starts

The TDM slot configuration written in bsp_audio_init() only lives until bsp_codec_init() calls bsp_codec_es7210_set(48000, 16, 4). That call goes through esp_codec_dev_open() → _i2s_data_set_fmt() → set_drv_fs() in esp_codec_dev/platform/audio_codec_data_i2s.c, which rebuilds the slot config from I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG() and applies it via i2s_channel_reconfig_tdm_slot(). That macro hard-codes slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO and ws_width = I2S_TDM_AUTO_WS_WIDTH, so the hardware ends up in exactly the same state (4 slots × 16-bit) with or without this patch.

Also note that the IDF driver derives the slot count from slot_mask (__builtin_popcount), not from the bit width, so it cannot "misinterpret" the config as 16-bit × 2-slot. And since es7210_set_fs() configures the codec for 16-bit words when 4 channels are opened, a 32-bit slot width would actually be a mismatch if it did take effect.

2. The DMA changes don't address heap corruption

  • dma_desc_num = 6 is already the default in I2S_CHANNEL_DEFAULT_CONFIG.
  • dma_frame_num = 256 only removes the cache-line alignment warning on the TX channel (240 × 2 = 480 bytes is not 64-byte aligned). The RX TDM buffer (240 × 4 × 2 = 1920 bytes) was already aligned, and it is recalculated and reallocated on every slot reconfig anyway.
  • The I2S DMA only writes into its own descriptor buffers, and i2s_channel_read() never copies more than the caller-supplied size, so the driver cannot overrun an application buffer.

3. The macro rename breaks the build

This repo pins espressif/esp_codec_dev to 1.3.4 (dependencies.lock), whose es7210_adc.h only defines ES7120_SEL_MIC1..4. ES7210_SEL_MIC* exists only in newer upstream versions, so the PR as-is fails to compile here. It looks like your local setup is using a different esp_codec_dev version than the demo.

4. The described workload is not from this demo

The comments mention audio_input reading 960 frames / 1920 bytes of mono 16-bit per call. The demo reads 4-channel interleaved data in hal_audio.cpp, so the heap corruption you observed most likely comes from the application-side buffer handling in your own code (e.g., allocating a mono buffer while receiving 4 interleaved channels) rather than from the BSP init path.


Given the above, we'll close this PR for now. If you can reproduce the CORRUPT HEAP panic with the unmodified demo firmware, or if you have a minimal repro, please open an issue or comment here and we'll be happy to dig into it together. Thanks again for taking the time to investigate and share your findings.

@LuoIsHere LuoIsHere closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants