fix(spi_master): prevent TX underrun at transfer start under concurrent PSRAM DMA

Wait for DMA TX data before the master starts a full-duplex transfer, and give
the SPI GDMA channels top priority, so a GDMA channel busy with PSRAM can no
longer shift the transmitted stream by a few bytes at the start of a transfer.
This commit is contained in:
Harshal Patil
2026-09-10 16:47:27 +05:30
committed by wanckl
parent 3dd1a4671a
commit 39fd9f96f7
10 changed files with 103 additions and 2 deletions

View File

@@ -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);
}

View File

@@ -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)
*

View File

@@ -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)
*

View File

@@ -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)
*

View File

@@ -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)
*

View File

@@ -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)
*

View File

@@ -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)
*

View File

@@ -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.
*

View File

@@ -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)
*

View File

@@ -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 {