diff --git a/components/esp_driver_spi/src/gpspi/spi_master.c b/components/esp_driver_spi/src/gpspi/spi_master.c index 5bbb4cd96f9..50d18a17044 100644 --- a/components/esp_driver_spi/src/gpspi/spi_master.c +++ b/components/esp_driver_spi/src/gpspi/spi_master.c @@ -865,11 +865,13 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_ } #if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE spi_hal_clear_intr_mask(hal, SPI_LL_INTR_IN_FULL | SPI_LL_INTR_OUT_EMPTY); +#if !SPI_LL_SUPPORT_FD_TX_WAIT_DMA if (esp_ptr_dma_ext_capable(hal_trans.send_buffer)) { // ! Delay here is required for EDMA to pass data from PSRAM to GPSPI esp_rom_delay_us(SPI_EDMA_SETUP_TIME_US(hal_dev->timing_conf.real_freq)); } -#endif +#endif // !SPI_LL_SUPPORT_FD_TX_WAIT_DMA +#endif // CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE //Kick off transfer spi_hal_user_start(hal); } diff --git a/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt b/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt index c6f2d7d7b6f..f38a8418847 100644 --- a/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt +++ b/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt @@ -21,6 +21,6 @@ endif() idf_component_register( SRCS ${srcs} # esp_psram is required for CONFIG_SPIRAM, used by the release configs - PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm esp_driver_uart esp_psram + PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm esp_driver_uart esp_psram esp_driver_dma WHOLE_ARCHIVE ) 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 ee0491dde32..57f9dc11624 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 @@ -32,6 +32,7 @@ #include "test_utils.h" #include "test_spi_utils.h" #include "spi_performance.h" +#include "esp_async_memcpy.h" const static char TAG[] = "test_spi"; @@ -2240,7 +2241,81 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]") spi_bus_remove_device(dev_handle); spi_bus_free(TEST_SPI_HOST); } -#endif + +#if SOC_GDMA_SUPPORTED // only gmda support psram +#define TEST_PSRAM_DMA_XFER_LEN 4000 +#define TEST_PSRAM_DMA_XFER_CNT 2000 +static void psram_dma_disturber_task(void *arg) +{ + async_memcpy_handle_t mcp; + uint8_t *src = heap_caps_malloc(TEST_PSRAM_DMA_XFER_LEN, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_CACHE_ALIGNED); + uint8_t *dst = heap_caps_malloc(TEST_PSRAM_DMA_XFER_LEN, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT | MALLOC_CAP_CACHE_ALIGNED); + TEST_ASSERT_NOT_NULL(src); + TEST_ASSERT_NOT_NULL(dst); + TEST_ASSERT(esp_ptr_external_ram(src)); + async_memcpy_config_t mcp_cfg = ASYNC_MEMCPY_DEFAULT_CONFIG(); + TEST_ESP_OK(esp_async_memcpy_install_gdma_ahb(&mcp_cfg, &mcp)); + + while (!*((volatile bool *)arg)) { + TEST_ESP_OK(esp_memcpy_blocking(mcp, dst, src, TEST_PSRAM_DMA_XFER_LEN, -1)); + } + TEST_ESP_OK(esp_async_memcpy_uninstall(mcp)); + free(src); + free(dst); + vTaskDelete(NULL); +} + +TEST_CASE("SPI Master DMA trans under concurrent PSRAM GDMA traffic", "[spi]") +{ + spi_device_handle_t spi; + spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); + buscfg.miso_io_num = buscfg.mosi_io_num; + spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG(); + devcfg.clock_speed_hz = IDF_TARGET_MAX_SPI_CLK_FREQ; + TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); + TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi)); + + uint8_t *tx = heap_caps_malloc(TEST_PSRAM_DMA_XFER_LEN, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + uint8_t *rx = heap_caps_malloc(TEST_PSRAM_DMA_XFER_LEN, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(tx); + TEST_ASSERT_NOT_NULL(rx); + test_fill_random_to_buffers_dualboard(1001, tx, rx, TEST_PSRAM_DMA_XFER_LEN); + + // check spi transaction first + spi_transaction_t trans = { + .length = TEST_PSRAM_DMA_XFER_LEN * 8, + .tx_buffer = tx, + .rx_buffer = rx, + }; + TEST_ESP_OK(spi_device_transmit(spi, &trans)); + TEST_ASSERT_EQUAL_HEX8_ARRAY(tx, rx, TEST_PSRAM_DMA_XFER_LEN); + + // start concurrent PSRAM GDMA traffic task + bool stop_mcp = false; + TEST_ASSERT(xTaskCreate(psram_dma_disturber_task, "psram_dma", 4096, &stop_mcp, uxTaskPriorityGet(NULL), NULL) == pdPASS); + + // start spi transaction with concurrent PSRAM GDMA task + uint32_t mismatched = 0; + for (int n = 0; n < TEST_PSRAM_DMA_XFER_CNT; n++) { + memset(rx, 0, TEST_PSRAM_DMA_XFER_LEN); + TEST_ESP_OK(spi_device_transmit(spi, &trans)); + if (memcmp(tx, rx, TEST_PSRAM_DMA_XFER_LEN) != 0) { + mismatched++; + } + } + + stop_mcp = true; // stop GDMA traffic task + free(tx); + free(rx); + TEST_ESP_OK(spi_bus_remove_device(spi)); + TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); + + printf("%" PRIu32 " of %d transfers mismatched\n", mismatched, TEST_PSRAM_DMA_XFER_CNT); + TEST_ASSERT_EQUAL_UINT32(0, mismatched); + vTaskDelay(10); +} +#endif // SOC_GDMA_SUPPORTED +#endif // CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE #if SOC_SPI_SUPPORT_DDR_CLOCK TEST_CASE("Test master cmd/data DDR/SDR", "[spi]") diff --git a/components/esp_hal_gpspi/esp32c5/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32c5/include/hal/spi_ll.h index 3cda31dda0b..788d521aeae 100644 --- a/components/esp_hal_gpspi/esp32c5/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32c5/include/hal/spi_ll.h @@ -51,6 +51,7 @@ extern "C" { #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_MAX_SCT_CONF_LEN SPI_CONF_BITLEN +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -301,6 +302,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/esp32c6/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32c6/include/hal/spi_ll.h index f93021105d7..6ccbcc07edb 100644 --- a/components/esp_hal_gpspi/esp32c6/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32c6/include/hal/spi_ll.h @@ -48,6 +48,7 @@ extern "C" { #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -287,6 +288,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/esp32c61/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32c61/include/hal/spi_ll.h index 0c865de3d4c..e6d0f567ca8 100644 --- a/components/esp_hal_gpspi/esp32c61/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32c61/include/hal/spi_ll.h @@ -51,6 +51,7 @@ extern "C" { #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_MAX_SCT_CONF_LEN SPI_CONF_BITLEN +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -315,6 +316,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/esp32h2/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32h2/include/hal/spi_ll.h index b205387422f..4799c0464f8 100644 --- a/components/esp_hal_gpspi/esp32h2/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32h2/include/hal/spi_ll.h @@ -50,6 +50,7 @@ extern "C" { #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -288,6 +289,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/esp32h21/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32h21/include/hal/spi_ll.h index 5458c2af9f0..cccb9d0ab87 100644 --- a/components/esp_hal_gpspi/esp32h21/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32h21/include/hal/spi_ll.h @@ -50,6 +50,7 @@ extern "C" { #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the * calculation needs long time, it should be calculated during initialization and @@ -276,6 +277,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/esp32h4/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32h4/include/hal/spi_ll.h index 80219cb798f..c6c78b2bcb9 100644 --- a/components/esp_hal_gpspi/esp32h4/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32h4/include/hal/spi_ll.h @@ -51,6 +51,7 @@ extern "C" { #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_MAX_SCT_CONF_LEN SPI_CONF_BITLEN +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -336,6 +337,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h index a0d36a538d4..f636f43cac4 100644 --- a/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h @@ -52,6 +52,7 @@ extern "C" { #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_MAX_SCT_CONF_LEN SPI_CONF_BITLEN +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -374,6 +375,17 @@ static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw) return hw->cmd.usr; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Reset the slave peripheral before next transaction. * diff --git a/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h index 535bb3b0b4d..1cfb71bc734 100644 --- a/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h @@ -52,6 +52,7 @@ extern "C" { #define SPI_LL_SCT_MAGIC_NUMBER (0x2) #define SPI_LL_SCT_CONF_BUF_NUM (1 + 14) //1-word-bitmap + 14-word-regs according to TRM #define SPI_LL_MAX_SCT_CONF_LEN SPI_CONF_BITLEN +#define SPI_LL_SUPPORT_FD_TX_WAIT_DMA 1 //support tx wait data on fd mode /** * The data structure holding calculated clock configuration. Since the @@ -362,6 +363,17 @@ static inline void spi_ll_user_start(spi_dev_t *hw) hw->cmd.usr = 1; } +/** + * Configure whether a master full-duplex transfer waits for DMA TX data before it starts + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to start only once the TX AFIFO holds DMA data, false to start immediately + */ +static inline void spi_ll_master_enable_fd_wait_dma_tx_data(spi_dev_t *hw, bool enable) +{ + hw->slave.mst_fd_wait_dma_tx_data = enable; +} + /** * Get current running command bit-mask. (Preview) * diff --git a/components/esp_hal_gpspi/spi_hal_iram.c b/components/esp_hal_gpspi/spi_hal_iram.c index 65b7e145dd8..e00853ece48 100644 --- a/components/esp_hal_gpspi/spi_hal_iram.c +++ b/components/esp_hal_gpspi/spi_hal_iram.c @@ -179,8 +179,11 @@ void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev } spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num); +#if SPI_LL_SUPPORT_FD_TX_WAIT_DMA + // enable tx data wait only when tx is used, otherwise rx only trans will hang on start + spi_ll_master_enable_fd_wait_dma_tx_data(hw, trans->send_buffer && trans->tx_bitlen); +#endif spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen); - if (dev->half_duplex) { spi_ll_set_miso_bitlen(hw, trans->rx_bitlen); } else {