From 0a752844eb32542ced2152c4de0f06d025940219 Mon Sep 17 00:00:00 2001 From: wanckl Date: Sat, 28 Feb 2026 19:09:39 +0800 Subject: [PATCH] fix(driver_spi): fixed master dma unaligned trans error --- .../esp_driver_spi/src/gpspi/spi_common.c | 4 ++- .../test_apps/master/main/test_spi_master.c | 31 +++++++++++++++++++ .../api-reference/peripherals/spi_master.rst | 4 +++ .../api-reference/peripherals/spi_master.rst | 4 +++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index d1e744f2ba..d8ca293bd2 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -413,9 +413,11 @@ esp_err_t SPI_COMMON_ISR_ATTR spicommon_dma_setup_priv_buffer(spi_host_device_t alignment = MAX(dma_ctx->dma_align_rx_int, bus_attr->cache_align_int); } } - need_malloc |= (((uint32_t)buffer | len) & (alignment - 1)); + // length also must be aligned if cache sync is required, otherwise don't need + need_malloc |= (use_psram || bus_attr->cache_align_int > 1) ? (((uint32_t)buffer | len) & (alignment - 1)) : (((uint32_t)buffer) & (alignment - 1)); uint32_t align_len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment ESP_EARLY_LOGV(SPI_TAG, "SPI%d %s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", host_id + 1, is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal"); + if (need_malloc) { ESP_RETURN_ON_FALSE_ISR(auto_malloc, ESP_ERR_INVALID_STATE, SPI_TAG, "%s addr&len not align to %d, or not dma_capable, suggest use 'heap_caps_malloc' or enable auto_align", is_tx ? "TX" : "RX", alignment); uint32_t *temp = heap_caps_aligned_alloc(alignment, align_len, mem_cap); diff --git a/components/esp_driver_spi/test_apps/master/main/test_spi_master.c b/components/esp_driver_spi/test_apps/master/main/test_spi_master.c index ad904a6b62..944fae75be 100644 --- a/components/esp_driver_spi/test_apps/master/main/test_spi_master.c +++ b/components/esp_driver_spi/test_apps/master/main/test_spi_master.c @@ -891,6 +891,37 @@ TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]") TEST_ASSERT(spi_bus_free(TEST_SPI_HOST) == ESP_OK); } +#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE // targets who need cache sync don't support unaligned trans +TEST_CASE("SPI Master DMA manually unaligned RX test", "[spi]") +{ + spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); + buscfg.miso_io_num = buscfg.mosi_io_num; + TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + spi_device_handle_t dev0; + spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG(); + TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); + + WORD_ALIGNED_ATTR uint8_t tx_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + WORD_ALIGNED_ATTR uint8_t rx_data[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; + spi_transaction_t trans_cfg = { + .tx_buffer = tx_data, + .rx_buffer = rx_data, + .length = 5 * 8, + .flags = SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL, + }; + printf("Sending %d bytes\n", trans_cfg.length / 8); + TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg)); + ESP_LOG_BUFFER_HEX("rx", rx_data, 8); +#if !SOC_IS(ESP32) + TEST_ASSERT_EQUAL(rx_data[6], 0xAA); +#endif + + TEST_ESP_OK(spi_bus_remove_device(dev0)); + TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); +} +#endif + #if (TEST_SPI_PERIPH_NUM >= 2) //These will only be enabled on chips with 2 or more SPI peripherals diff --git a/docs/en/api-reference/peripherals/spi_master.rst b/docs/en/api-reference/peripherals/spi_master.rst index 1b1d54dae0..a8d9c2ca88 100644 --- a/docs/en/api-reference/peripherals/spi_master.rst +++ b/docs/en/api-reference/peripherals/spi_master.rst @@ -852,6 +852,10 @@ Please note that the ISR is disabled during flash operation by default. To keep 4. ``cs_ena_pretrans`` is not compatible with the Command and Address phases of full-duplex transactions. + .. only:: esp32 + + 5. If DMA is enabled, the RX buffer should be word-aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes). Otherwise, DMA may overwrite the data in the unaligned part. + Application Examples -------------------- diff --git a/docs/zh_CN/api-reference/peripherals/spi_master.rst b/docs/zh_CN/api-reference/peripherals/spi_master.rst index 8016d58ff7..43f35e6330 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_master.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_master.rst @@ -852,6 +852,10 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_interface_con 4. 全双工传输事务模式中,命令阶段和地址阶段与 ``cs_ena_pretrans`` 不兼容。 + .. only:: esp32 + + 5. 若启用了 DMA,则 RX 缓冲区应该以字对齐(从 32 位边界开始,字节长度为 4 的倍数)。否则 DMA 可能覆盖未对齐部分的数据。 + 应用示例 -------------------