mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'fix/spi_master_fd_wait_dma_tx_data' into 'master'
fix(spi_master): prevent TX underrun at transfer start under concurrent PSRAM DMA See merge request espressif/esp-idf!52706
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user