From 6ed0a35d88859790fef5b888bd6b5f5d964cc5aa Mon Sep 17 00:00:00 2001 From: wanckl Date: Tue, 16 Dec 2025 17:22:05 +0800 Subject: [PATCH 1/6] refactor(driver_spi): use more info in spi_common --- components/esp_adc/adc_dma_internal.h | 2 +- components/esp_adc/esp32s2/adc_dma.c | 13 +- components/esp_driver_dac/esp32s2/dac_dma.c | 8 +- .../include/esp_private/spi_common_internal.h | 17 +- .../esp_driver_spi/src/gpspi/spi_common.c | 163 +++++++++--------- .../esp_driver_spi/src/gpspi/spi_slave.c | 51 +++--- .../esp_driver_spi/src/gpspi/spi_slave_hd.c | 22 +-- .../spi_bench_mark/include/spi_performance.h | 10 +- .../test_apps/master/main/test_spi_master.c | 70 ++++---- 9 files changed, 169 insertions(+), 187 deletions(-) diff --git a/components/esp_adc/adc_dma_internal.h b/components/esp_adc/adc_dma_internal.h index 747ea7d5cc..f9f365b4b4 100644 --- a/components/esp_adc/adc_dma_internal.h +++ b/components/esp_adc/adc_dma_internal.h @@ -23,8 +23,8 @@ typedef struct { gdma_channel_handle_t gdma_chan; #elif CONFIG_IDF_TARGET_ESP32S2 //On ESP32S2, there is no gdma, so use SPI DMA to transmit data - spi_dma_ctx_t *spi_dma_ctx; spi_dev_t *adc_spi_dev; + uint32_t dma_chan_id; #elif CONFIG_IDF_TARGET_ESP32 //On ESP32, there is no gdma, so use I2S DMA to transmit data i2s_dev_t *adc_i2s_dev; diff --git a/components/esp_adc/esp32s2/adc_dma.c b/components/esp_adc/esp32s2/adc_dma.c index 07afab8946..e1fe2153fd 100644 --- a/components/esp_adc/esp32s2/adc_dma.c +++ b/components/esp_adc/esp32s2/adc_dma.c @@ -28,7 +28,7 @@ static IRAM_ATTR void adc_dma_intr_handler(void *arg) bool conversion_finish = spi_ll_get_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); if (conversion_finish) { spi_ll_clear_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); - intptr_t desc_addr = spi_dma_ll_get_in_suc_eof_desc_addr(ctx->adc_dma.adc_spi_dev, ctx->adc_dma.spi_dma_ctx->rx_dma_chan.chan_id); + intptr_t desc_addr = spi_dma_ll_get_in_suc_eof_desc_addr(ctx->adc_dma.adc_spi_dev, ctx->adc_dma.dma_chan_id); ctx->rx_eof_desc_addr = desc_addr; need_yield = ctx->adc_intr_func(ctx); } @@ -58,10 +58,11 @@ esp_err_t adc_dma_init(adc_dma_t *adc_dma) if (spi_success != true) { return ESP_FAIL; } - ret = spicommon_dma_chan_alloc(SPI3_HOST, SPI_DMA_CH_AUTO, &(adc_dma->spi_dma_ctx)); + ret = spicommon_dma_chan_alloc(ADC_DMA_SPI_HOST, SPI_DMA_CH_AUTO); if (ret != ESP_OK) { return ret; } + adc_dma->dma_chan_id = spi_bus_get_dma_ctx(ADC_DMA_SPI_HOST)->rx_dma_chan.chan_id; adc_dma->adc_spi_dev = SPI_LL_GET_HW(ADC_DMA_SPI_HOST); return ESP_OK; @@ -70,7 +71,7 @@ esp_err_t adc_dma_init(adc_dma_t *adc_dma) esp_err_t adc_dma_deinit(adc_dma_t adc_dma) { esp_intr_free(adc_dma.dma_intr_hdl); - spicommon_dma_chan_free(adc_dma.spi_dma_ctx); + spicommon_dma_chan_free(ADC_DMA_SPI_HOST); spicommon_periph_free(ADC_DMA_SPI_HOST); return ESP_OK; } @@ -79,7 +80,7 @@ esp_err_t adc_dma_start(adc_dma_t adc_dma, dma_descriptor_t *addr) { spi_ll_clear_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); spi_ll_enable_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); - spi_dma_ll_rx_start(adc_dma.adc_spi_dev, adc_dma.spi_dma_ctx->rx_dma_chan.chan_id, (lldesc_t *)addr); + spi_dma_ll_rx_start(adc_dma.adc_spi_dev, adc_dma.dma_chan_id, (lldesc_t *)addr); return ESP_OK; } @@ -87,12 +88,12 @@ esp_err_t adc_dma_stop(adc_dma_t adc_dma) { spi_ll_disable_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); spi_ll_clear_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); - spi_dma_ll_rx_stop(adc_dma.adc_spi_dev, adc_dma.spi_dma_ctx->rx_dma_chan.chan_id); + spi_dma_ll_rx_stop(adc_dma.adc_spi_dev, adc_dma.dma_chan_id); return ESP_OK; } esp_err_t adc_dma_reset(adc_dma_t adc_dma) { - spi_dma_ll_rx_reset(adc_dma.adc_spi_dev, adc_dma.spi_dma_ctx->rx_dma_chan.chan_id); + spi_dma_ll_rx_reset(adc_dma.adc_spi_dev, adc_dma.dma_chan_id); return ESP_OK; } diff --git a/components/esp_driver_dac/esp32s2/dac_dma.c b/components/esp_driver_dac/esp32s2/dac_dma.c index cbe3d3c9f9..fa6c98a20b 100644 --- a/components/esp_driver_dac/esp32s2/dac_dma.c +++ b/components/esp_driver_dac/esp32s2/dac_dma.c @@ -40,7 +40,6 @@ typedef struct { void *periph_dev; /* DMA peripheral device address */ uint32_t dma_chan; - spi_dma_ctx_t *spi_dma_ctx; /* spi_dma context */ intr_handle_t intr_handle; /* Interrupt handle */ bool use_apll; /* Whether use APLL as digital controller clock source */ } dac_dma_periph_spi_t; @@ -145,10 +144,9 @@ esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll) /* When transmit alternately, twice frequency is needed to guarantee the convert frequency in one channel */ uint32_t trans_freq_hz = freq_hz * (is_alternate ? 2 : 1); ESP_GOTO_ON_ERROR(s_dac_dma_periph_set_clock(trans_freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral"); - ESP_GOTO_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO, &s_ddp->spi_dma_ctx), + ESP_GOTO_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO), err, TAG, "Failed to allocate dma peripheral channel"); - - s_ddp->dma_chan = s_ddp->spi_dma_ctx->rx_dma_chan.chan_id; + s_ddp->dma_chan = spi_bus_get_dma_ctx(DAC_DMA_PERIPH_SPI_HOST)->rx_dma_chan.chan_id; spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF); dac_ll_digi_set_convert_mode(is_alternate); return ret; @@ -162,7 +160,7 @@ esp_err_t dac_dma_periph_deinit(void) ESP_RETURN_ON_FALSE(s_ddp != NULL, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral is not initialized"); ESP_RETURN_ON_FALSE(s_ddp->intr_handle == NULL, ESP_ERR_INVALID_STATE, TAG, "The interrupt is not deregistered yet"); if (s_ddp->dma_chan) { - ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(s_ddp->spi_dma_ctx), TAG, "Failed to free dma peripheral channel"); + ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST), TAG, "Failed to free dma peripheral channel"); } ESP_RETURN_ON_FALSE(spicommon_periph_free(DAC_DMA_PERIPH_SPI_HOST), ESP_FAIL, TAG, "Failed to release DAC DMA peripheral"); spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF); diff --git a/components/esp_driver_spi/include/esp_private/spi_common_internal.h b/components/esp_driver_spi/include/esp_private/spi_common_internal.h index e604254ed8..edcec0bc72 100644 --- a/components/esp_driver_spi/include/esp_private/spi_common_internal.h +++ b/components/esp_driver_spi/include/esp_private/spi_common_internal.h @@ -106,19 +106,18 @@ esp_err_t spicommon_bus_free(spi_host_device_t host_id); * * @param host_id SPI host ID * @param dma_chan DMA channel to be used - * @param out_dma_ctx Actual DMA channel context (if you choose to assign a specific DMA channel, this will be the channel you assigned before) * * @return * - ESP_OK: On success * - ESP_ERR_NO_MEM: No enough memory * - ESP_ERR_NOT_FOUND: There is no available DMA channel */ -esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t **out_dma_ctx); +esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan); /** * @brief Alloc DMA descriptors for SPI * - * @param dma_ctx DMA context returned by `spicommon_dma_chan_alloc` + * @param[in] host_id SPI host ID * @param[in] cfg_max_sz Expected maximum transfer size, in bytes. * @param[out] actual_max_sz Actual max transfer size one transaction can be, in bytes. * @@ -126,7 +125,7 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma * - ESP_OK: On success * - ESP_ERR_NO_MEM: No enough memory */ -esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *actual_max_sz); +esp_err_t spicommon_dma_desc_alloc(spi_host_device_t host_id, int cfg_max_sz, int *actual_max_sz); /** * Setupt/Configure dma descriptor link list @@ -141,12 +140,12 @@ void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, in /** * @brief Free DMA for SPI * - * @param dma_ctx spi_dma_ctx_t struct pointer + * @param host_id SPI host ID * * @return * - ESP_OK: On success */ -esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx); +esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id); /** * @brief Connect a SPI peripheral to GPIO pins @@ -182,7 +181,7 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx); * - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_OK on success */ -esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o, uint64_t *io_reserved); +esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o); /** * @brief Free the IO used by a SPI peripheral @@ -295,7 +294,7 @@ void spicommon_dmaworkaround_transfer_active(int dmachan); * @param host_id The specified host to get attribute * @return (Const) Pointer to the attributes */ -const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id); +spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id); /** * @brief Get the dma context of a specified SPI bus. @@ -303,7 +302,7 @@ const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id); * @param host_id The specified host to get attribute * @return (Const) Pointer to the dma context */ -const spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id); +spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id); /** * @brief Register a function to a initialized bus to make it called when deinitializing the bus. diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index bb9665967d..ca16ebb2a8 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -70,11 +70,11 @@ typedef struct { spi_destroy_func_t destroy_func; void* destroy_arg; spi_bus_attr_t bus_attr; - spi_dma_ctx_t *dma_ctx; } spicommon_bus_context_t; static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT(); static spicommon_bus_context_t* bus_ctx[SOC_SPI_PERIPH_NUM] = {&s_mainbus}; +static spi_dma_ctx_t *spi_dma_ctx[SOC_SPI_PERIPH_NUM]; #if CONFIG_SPI_FLASH_SHARE_SPI1_BUS /* The lock for the share SPI1 bus is registered here in a constructor due to need to access the context @@ -98,6 +98,10 @@ esp_err_t spicommon_bus_alloc(spi_host_device_t host_id, const char *name) SPI_COMMON_PERI_CLOCK_ATOMIC() { spi_ll_enable_clock(host_id, true); } + // Get cache alignment constraints + esp_cache_get_alignment(MALLOC_CAP_DMA, &ctx->bus_attr.cache_align_int); + esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &ctx->bus_attr.cache_align_ext); + ctx->host_id = host_id; bus_ctx[host_id] = ctx; return ESP_OK; @@ -115,20 +119,6 @@ esp_err_t spicommon_bus_free(spi_host_device_t host_id) return ESP_OK; } -#if SOC_GDMA_SUPPORTED -//NOTE!! If both A and B are not defined, '#if (A==B)' is true, because GCC use 0 stand for undefined symbol -#if defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI) -#define SPI_GDMA_NEW_CHANNEL gdma_new_axi_channel -#elif defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB) -#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel -#endif - -#else -//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1 -static uint8_t spi_dma_chan_enabled = 0; -static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED; -#endif //!SOC_GDMA_SUPPORTED - static inline bool is_valid_host(spi_host_device_t host) { #if (SOC_SPI_PERIPH_NUM == 2) @@ -150,44 +140,52 @@ int spicommon_irqdma_source_for_host(spi_host_device_t host) //----------------------------------------------------------alloc dma periph-------------------------------------------------------// #if !SOC_GDMA_SUPPORTED +//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1 +static uint8_t spi_dma_chan_enabled = 0; +static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED; -#if SPI_LL_DMA_SHARED -static inline shared_periph_module_t get_dma_periph(int dma_chan) +static inline void _spicommon_dma_rcc_clock_ctrl(spi_dma_chan_t dma_chan, bool enable) { - assert(dma_chan >= 1 && dma_chan <= SOC_SPI_DMA_CHAN_NUM); +#if SPI_LL_DMA_SHARED + shared_periph_module_t dma_periph; if (dma_chan == 1) { - return PERIPH_SPI2_DMA_MODULE; + dma_periph = PERIPH_SPI2_DMA_MODULE; } else if (dma_chan == 2) { - return PERIPH_SPI3_DMA_MODULE; + dma_periph = PERIPH_SPI3_DMA_MODULE; } else { abort(); } -} + + if (enable) { + PERIPH_RCC_ACQUIRE_ATOMIC(dma_periph, ref_count) { + if (ref_count == 0) { + spi_dma_ll_enable_bus_clock(dma_chan, true); + spi_dma_ll_reset_register(dma_chan); + } + } + } else { + PERIPH_RCC_RELEASE_ATOMIC(dma_periph, ref_count) { + if (ref_count == 0) { + spi_dma_ll_enable_bus_clock(dma_chan, false); + } + } + } +#else + SPI_COMMON_RCC_CLOCK_ATOMIC() { + spi_dma_ll_enable_bus_clock(dma_chan, enable); + spi_dma_ll_reset_register(dma_chan); + } #endif +} static bool claim_dma_chan(int dma_chan, uint32_t *out_actual_dma_chan) { bool ret = false; portENTER_CRITICAL(&spi_dma_spinlock); - bool is_used = (BIT(dma_chan) & spi_dma_chan_enabled); - if (!is_used) { + if (!(BIT(dma_chan) & spi_dma_chan_enabled)) { spi_dma_chan_enabled |= BIT(dma_chan); -#if SPI_LL_DMA_SHARED - PERIPH_RCC_ACQUIRE_ATOMIC(get_dma_periph(dma_chan), ref_count) { - //esp32s2: dma_chan index is same as spi host_id, no matter dma_chan_auto or not - if (ref_count == 0) { - spi_dma_ll_enable_bus_clock(dma_chan, true); - spi_dma_ll_reset_register(dma_chan); - } - } -#else - SPI_COMMON_RCC_CLOCK_ATOMIC() { - //esp32: have only one spi_dma - spi_dma_ll_enable_bus_clock(dma_chan, true); - spi_dma_ll_reset_register(dma_chan); - } -#endif + _spicommon_dma_rcc_clock_ctrl(dma_chan, true); *out_actual_dma_chan = dma_chan; ret = true; } @@ -257,6 +255,12 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch } #else //SOC_GDMA_SUPPORTED +//NOTE!! If both A and B are not defined, '#if (A==B)' is true, because GCC use 0 stand for undefined symbol +#if defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI) +#define SPI_GDMA_NEW_CHANNEL gdma_new_axi_channel +#elif defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB) +#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel +#endif static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t *dma_ctx) { @@ -299,7 +303,7 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch } #endif //#if !SOC_GDMA_SUPPORTED -esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t **out_dma_ctx) +esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan) { assert(is_valid_host(host_id)); #if CONFIG_IDF_TARGET_ESP32 @@ -319,7 +323,7 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma if (ret != ESP_OK) { goto cleanup; } - *out_dma_ctx = dma_ctx; + spi_dma_ctx[host_id] = dma_ctx; return ret; cleanup: @@ -327,13 +331,17 @@ cleanup: return ret; } -esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *actual_max_sz) +esp_err_t spicommon_dma_desc_alloc(spi_host_device_t host_id, int cfg_max_sz, int *actual_max_sz) { int dma_desc_ct = (cfg_max_sz + DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED - 1) / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; if (dma_desc_ct == 0) { dma_desc_ct = 1; //default to 4k when max is not given } + spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id); + if (!dma_ctx) { + return ESP_ERR_INVALID_STATE; + } dma_ctx->dmadesc_tx = heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); dma_ctx->dmadesc_rx = heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); if (dma_ctx->dmadesc_tx == NULL || dma_ctx->dmadesc_rx == NULL) { @@ -381,9 +389,9 @@ void SPI_COMMON_ISR_ATTR spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, } //----------------------------------------------------------free dma periph-------------------------------------------------------// -esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx) +esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id) { - assert(dma_ctx); + spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id); #if !SOC_GDMA_SUPPORTED //On ESP32S2, each SPI controller has its own DMA channel @@ -392,17 +400,7 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx) portENTER_CRITICAL(&spi_dma_spinlock); spi_dma_chan_enabled &= ~BIT(dma_chan); -#if SPI_LL_DMA_SHARED - PERIPH_RCC_RELEASE_ATOMIC(get_dma_periph(dma_chan), ref_count) { - if (ref_count == 0) { - spi_dma_ll_enable_bus_clock(dma_ctx->tx_dma_chan.host_id, false); - } - } -#else - SPI_COMMON_RCC_CLOCK_ATOMIC() { - spi_dma_ll_enable_bus_clock(dma_ctx->tx_dma_chan.host_id, false); - } -#endif + _spicommon_dma_rcc_clock_ctrl(dma_chan, false); portEXIT_CRITICAL(&spi_dma_spinlock); #else //SOC_GDMA_SUPPORTED @@ -416,7 +414,14 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx) } #endif + if (dma_ctx->dmadesc_tx) { + free(dma_ctx->dmadesc_tx); + } + if (dma_ctx->dmadesc_rx) { + free(dma_ctx->dmadesc_rx); + } free(dma_ctx); + spi_dma_ctx[host_id] = NULL; return ESP_OK; } @@ -555,7 +560,7 @@ Do the common stuff to hook up a SPI host to a bus defined by a bunch of GPIO pi bus config struct and it'll set up the GPIO matrix and enable the device. If a pin is set to non-negative value, it should be able to be initialized. */ -esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t* flags_o, uint64_t *io_reserved) +esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t* flags_o) { #if SOC_SPI_SUPPORT_OCT // In the driver of previous version, spi data4 ~ spi data7 are not in spi_bus_config_t struct. So the new-added pins come as 0 @@ -826,18 +831,14 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED); bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; if (bus_attr->dma_enabled) { - err = spicommon_dma_chan_alloc(host_id, dma_chan, &ctx->dma_ctx); + err = spicommon_dma_chan_alloc(host_id, dma_chan); if (err != ESP_OK) { goto cleanup; } - err = spicommon_dma_desc_alloc(ctx->dma_ctx, bus_config->max_transfer_sz, &bus_attr->max_transfer_sz); + err = spicommon_dma_desc_alloc(host_id, bus_config->max_transfer_sz, &bus_attr->max_transfer_sz); if (err != ESP_OK) { goto cleanup; } - - // Get cache alignment constraints - esp_cache_get_alignment(MALLOC_CAP_DMA, &bus_attr->cache_align_int); - esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &bus_attr->cache_align_ext); } spi_bus_lock_config_t lock_config = { @@ -891,7 +892,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t * } #endif //CONFIG_PM_ENABLE - err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, NULL, NULL); + err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, NULL); if (err != ESP_OK) { goto cleanup; } @@ -906,12 +907,10 @@ cleanup: if (bus_attr->lock) { spi_bus_deinit_lock(bus_attr->lock); } - if (ctx->dma_ctx) { - free(ctx->dma_ctx->dmadesc_tx); - free(ctx->dma_ctx->dmadesc_rx); - spicommon_dma_chan_free(ctx->dma_ctx); - ctx->dma_ctx = NULL; - } + } + if (bus_attr->dma_enabled) { + // free dma channel and descriptors + spicommon_dma_chan_free(host_id); } spicommon_bus_free(host_id); return err; @@ -920,19 +919,20 @@ cleanup: void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps) { SPI_CHECK(bus_ctx[host_id], "SPI %d not initialized", NULL, host_id + 1); + spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id); - size_t alignment = 16; - // detailed alignment requirement is not available for slave bus, so use 16 bytes as default - if (bus_ctx[host_id]->bus_attr.flags & SPICOMMON_BUSFLAG_MASTER) { + size_t alignment = 1; // return 1 anyway if dma not used but user use it. + if (dma_ctx) { // As don't know the buffer will used for TX or RX, so use the max alignment requirement alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \ - MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_ext, bus_ctx[host_id]->dma_ctx->dma_align_rx_ext) : \ - MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_int, bus_ctx[host_id]->dma_ctx->dma_align_rx_int); + MAX(dma_ctx->dma_align_tx_ext, dma_ctx->dma_align_rx_ext) : \ + MAX(dma_ctx->dma_align_tx_int, dma_ctx->dma_align_rx_int); } + return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA); } -const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id) +spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id) { if (bus_ctx[host_id] == NULL) { return NULL; @@ -941,13 +941,9 @@ const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id) return &bus_ctx[host_id]->bus_attr; } -const spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id) +spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id) { - if (bus_ctx[host_id] == NULL) { - return NULL; - } - - return bus_ctx[host_id]->dma_ctx; + return spi_dma_ctx[host_id]; } esp_err_t spi_bus_free(spi_host_device_t host_id) @@ -987,11 +983,8 @@ esp_err_t spi_bus_free(spi_host_device_t host_id) #endif spi_bus_deinit_lock(bus_attr->lock); - if (ctx->dma_ctx) { - free(ctx->dma_ctx->dmadesc_tx); - free(ctx->dma_ctx->dmadesc_rx); - spicommon_dma_chan_free(ctx->dma_ctx); - ctx->dma_ctx = NULL; + if (bus_attr->dma_enabled) { + spicommon_dma_chan_free(host_id); } spicommon_bus_free(host_id); return err; diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index 52a1652ad0..f2010658dc 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -59,7 +59,7 @@ typedef struct { } spi_slave_trans_priv_t; typedef struct { - int id; + spi_host_device_t id; _Atomic spi_bus_fsm_t fsm; spi_bus_attr_t* bus_attr; spi_dma_ctx_t *dma_ctx; @@ -69,10 +69,8 @@ typedef struct { spi_slave_trans_priv_t cur_trans; uint32_t flags; uint32_t intr_flags; - int max_transfer_sz; QueueHandle_t trans_queue; QueueHandle_t ret_queue; - bool dma_enabled; bool cs_iomux; uint8_t cs_in_signal; uint16_t internal_mem_align_size; @@ -185,13 +183,15 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b atomic_store(&spihost[host]->fsm, SPI_BUS_FSM_ENABLED); spi_slave_hal_context_t *hal = &spihost[host]->hal; - spihost[host]->dma_enabled = (dma_chan != SPI_DMA_DISABLED); - if (spihost[host]->dma_enabled) { - ret = spicommon_dma_chan_alloc(host, dma_chan, &spihost[host]->dma_ctx); + spihost[host]->bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED); + spihost[host]->bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; + if (spihost[host]->bus_attr->dma_enabled) { + ret = spicommon_dma_chan_alloc(host, dma_chan); if (ret != ESP_OK) { goto cleanup; } - ret = spicommon_dma_desc_alloc(spihost[host]->dma_ctx, bus_config->max_transfer_sz, &spihost[host]->max_transfer_sz); + spihost[host]->dma_ctx = spi_bus_get_dma_ctx(host); + ret = spicommon_dma_desc_alloc(host, bus_config->max_transfer_sz, &spihost[host]->bus_attr->max_transfer_sz); if (ret != ESP_OK) { goto cleanup; } @@ -207,12 +207,9 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b #else spihost[host]->internal_mem_align_size = 4; #endif - } else { - //We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most. - spihost[host]->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; } - err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL); + err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL); if (err != ESP_OK) { ret = err; goto cleanup; @@ -226,7 +223,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b spihost[host]->flags = spihost[host]->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called // The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line. - if (spihost[host]->dma_enabled) { + if (spihost[host]->bus_attr->dma_enabled) { freeze_cs(spihost[host]); } @@ -313,7 +310,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b hal->rx_lsbfirst = (slave_config->flags & SPI_SLAVE_RXBIT_LSBFIRST) ? 1 : 0; hal->tx_lsbfirst = (slave_config->flags & SPI_SLAVE_TXBIT_LSBFIRST) ? 1 : 0; hal->mode = slave_config->mode; - hal->use_dma = spihost[host]->dma_enabled; + hal->use_dma = spihost[host]->bus_attr->dma_enabled; spi_slave_hal_setup_device(hal); return ESP_OK; @@ -332,10 +329,8 @@ esp_err_t spi_slave_free(spi_host_device_t host) if (spihost[host]->ret_queue) { vQueueDelete(spihost[host]->ret_queue); } - if (spihost[host]->dma_enabled) { - free(spihost[host]->dma_ctx->dmadesc_tx); - free(spihost[host]->dma_ctx->dmadesc_rx); - spicommon_dma_chan_free(spihost[host]->dma_ctx); + if (spihost[host]->bus_attr->dma_enabled) { + spicommon_dma_chan_free(host); } spicommon_bus_free_io_cfg(&spihost[host]->bus_attr->bus_cfg, &spihost[host]->bus_attr->gpio_reserve); if (spihost[host]->cfg.spics_io_num >= 0) { @@ -409,12 +404,12 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t { __attribute__((unused)) spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans; #if CONFIG_IDF_TARGET_ESP32 - if (spihost[host]->dma_enabled && (trans->trans_len % 32)) { + if (spihost[host]->bus_attr->dma_enabled && (trans->trans_len % 32)) { ESP_EARLY_LOGW(SPI_TAG, "Use DMA but real trans_len is not 4 bytes aligned, slave may loss data"); } #endif #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - if (spihost[host]->dma_enabled) { + if (spihost[host]->bus_attr->dma_enabled) { if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) { free(priv_trans->tx_buffer); } @@ -437,7 +432,7 @@ static esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_setup_priv_trans(spi_host_device_t uint16_t alignment = spihost[host]->internal_mem_align_size; uint32_t buffer_byte_len = (trans->length + 7) / 8; - if (spihost[host]->dma_enabled && trans->tx_buffer) { + if (spihost[host]->bus_attr->dma_enabled && trans->tx_buffer) { if ((!esp_ptr_dma_capable(trans->tx_buffer) || ((((uint32_t)trans->tx_buffer) | buffer_byte_len) & (alignment - 1)))) { ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "TX buffer addr&len not align to %d byte, or not dma_capable", alignment); //if txbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one @@ -454,7 +449,7 @@ static esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_setup_priv_trans(spi_host_device_t esp_err_t ret = esp_cache_msync((void *)priv_trans->tx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync c2m(writeback) fail"); } - if (spihost[host]->dma_enabled && trans->rx_buffer) { + if (spihost[host]->bus_attr->dma_enabled && trans->rx_buffer) { if ((!esp_ptr_dma_capable(trans->rx_buffer) || ((((uint32_t)trans->rx_buffer) | (trans->length + 7) / 8) & (alignment - 1)))) { ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "RX buffer addr&len not align to %d byte, or not dma_capable", alignment); //if rxbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one @@ -478,7 +473,7 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi BaseType_t r; SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG); SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG); - SPI_CHECK(spihost[host]->dma_enabled == 0 || trans_desc->tx_buffer == NULL || esp_ptr_dma_capable(trans_desc->tx_buffer), + SPI_CHECK(spihost[host]->bus_attr->dma_enabled == 0 || trans_desc->tx_buffer == NULL || esp_ptr_dma_capable(trans_desc->tx_buffer), "txdata not in DMA-capable memory", ESP_ERR_INVALID_ARG); // We don't check length WORD alignment for rx when using DMA, seems break DMA requirement, @@ -486,12 +481,12 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi // ATTENTION!: On esp32, peripheral can NOT stop DMA, if length not WORD aligned, // remain bytes in last word domain will overwritten by DMA HW, which may cause unexpected issues! // But driver already used for long time, to avoid breaking changes, we still don't add alignment limit. - SPI_CHECK(spihost[host]->dma_enabled == 0 || trans_desc->rx_buffer == NULL || + SPI_CHECK(spihost[host]->bus_attr->dma_enabled == 0 || trans_desc->rx_buffer == NULL || (esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) && (trans_desc->length % 8 == 0)), "rxdata not in DMA-capable memory or not BYTE aligned", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans_desc->length <= spihost[host]->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG); + SPI_CHECK(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG); spi_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc}; SPI_CHECK(ESP_OK == spi_slave_setup_priv_trans(host, &priv_trans), "slave setup priv_trans failed", ESP_ERR_NO_MEM); @@ -543,8 +538,8 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c BaseType_t do_yield = pdFALSE; ESP_RETURN_ON_FALSE_ISR(is_valid_host(host), ESP_ERR_INVALID_ARG, SPI_TAG, "invalid host"); ESP_RETURN_ON_FALSE_ISR(spihost[host], ESP_ERR_INVALID_ARG, SPI_TAG, "host not slave"); - ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum"); - if (spihost[host]->dma_enabled) { + ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum"); + if (spihost[host]->bus_attr->dma_enabled) { uint16_t alignment = spihost[host]->internal_mem_align_size; (void) alignment; @@ -665,7 +660,7 @@ static void SPI_SLAVE_ISR_ATTR s_spi_slave_prepare_data(spi_slave_t *host) { spi_slave_hal_context_t *hal = &host->hal; - if (host->dma_enabled) { + if (host->bus_attr->dma_enabled) { s_spi_slave_dma_prepare_data(host->dma_ctx, &host->hal); } else { //No DMA. Copy data to transmit buffers. @@ -700,7 +695,7 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) assert(spi_slave_hal_usr_is_done(hal)); - bool use_dma = host->dma_enabled; + bool use_dma = host->bus_attr->dma_enabled; if (host->cur_trans.trans) { // When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready. if (use_dma) { diff --git a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c index 6c831f89b9..f76558d324 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2010-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2010-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -49,8 +49,6 @@ typedef struct { _Atomic spi_bus_fsm_t fsm; spi_dma_ctx_t *dma_ctx; uint16_t internal_mem_align_size; - int max_transfer_sz; - uint32_t flags; portMUX_TYPE int_spinlock; intr_handle_t intr; intr_handle_t intr_dma; @@ -131,10 +129,11 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b host->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id); host->cs_io_num = config->spics_io_num; - ret = spicommon_dma_chan_alloc(host_id, config->dma_chan, &host->dma_ctx); + ret = spicommon_dma_chan_alloc(host_id, config->dma_chan); if (ret != ESP_OK) { goto cleanup; } + host->dma_ctx = spi_bus_get_dma_ctx(host_id); #if SOC_GDMA_SUPPORTED gdma_strategy_config_t dma_strategy = { .auto_update_desc = true, @@ -145,7 +144,7 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b spi_dma_ll_enable_out_auto_wrback(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1); spi_dma_ll_set_out_eof_generation(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1); #endif - ret = spicommon_dma_desc_alloc(host->dma_ctx, bus_config->max_transfer_sz, &host->max_transfer_sz); + ret = spicommon_dma_desc_alloc(host_id, bus_config->max_transfer_sz, &host->bus_attr->max_transfer_sz); if (ret != ESP_OK) { goto cleanup; } @@ -171,12 +170,11 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b host->internal_mem_align_size = 4; #endif - ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL); + ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL); if (ret != ESP_OK) { goto cleanup; } spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), NULL); - host->flags = host->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called spi_slave_hd_hal_config_t hal_config = { .host_id = host_id, @@ -351,12 +349,10 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id) spicommon_bus_free_io_cfg(&host->bus_attr->bus_cfg, &host->bus_attr->gpio_reserve); spicommon_cs_free_io(host->cs_io_num, &host->bus_attr->gpio_reserve); - spicommon_bus_free(host_id); - free(host->dma_ctx->dmadesc_tx); - free(host->dma_ctx->dmadesc_rx); free(host->hal.dmadesc_tx); free(host->hal.dmadesc_rx); - spicommon_dma_chan_free(host->dma_ctx); + spicommon_dma_chan_free(host_id); + spicommon_bus_free(host_id); free(host); spihost[host_id] = NULL; @@ -842,7 +838,7 @@ esp_err_t spi_slave_hd_queue_trans(spi_host_device_t host_id, spi_slave_chan_t c SPIHD_CHECK(host->append_mode == 0, "This API should be used for SPI Slave HD Segment Mode", ESP_ERR_INVALID_STATE); SPIHD_CHECK(esp_ptr_dma_capable(trans->data), "The buffer should be DMA capable.", ESP_ERR_INVALID_ARG); - SPIHD_CHECK(trans->len <= host->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG); + SPIHD_CHECK(trans->len <= host->bus_attr->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG); SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG); spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans}; @@ -895,7 +891,7 @@ esp_err_t spi_slave_hd_append_trans(spi_host_device_t host_id, spi_slave_chan_t SPIHD_CHECK(trans->len <= SPI_MAX_DMA_LEN, "Currently we only support transaction with data length within 4092 bytes", ESP_ERR_INVALID_ARG); SPIHD_CHECK(host->append_mode == 1, "This API should be used for SPI Slave HD Append Mode", ESP_ERR_INVALID_STATE); SPIHD_CHECK(esp_ptr_dma_capable(trans->data), "The buffer should be DMA capable.", ESP_ERR_INVALID_ARG); - SPIHD_CHECK(trans->len <= host->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG); + SPIHD_CHECK(trans->len <= host->bus_attr->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG); SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG); spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans}; diff --git a/components/esp_driver_spi/test_apps/components/spi_bench_mark/include/spi_performance.h b/components/esp_driver_spi/test_apps/components/spi_bench_mark/include/spi_performance.h index 5b5a0ba4d5..6766eb674d 100644 --- a/components/esp_driver_spi/test_apps/components/spi_bench_mark/include/spi_performance.h +++ b/components/esp_driver_spi/test_apps/components/spi_bench_mark/include/spi_performance.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -27,7 +27,7 @@ #elif CONFIG_IDF_TARGET_ESP32S3 #define IDF_TARGET_MAX_SPI_CLK_FREQ 40*1000*1000 -#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 15 +#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 17 #define IDF_TARGET_MAX_TRANS_TIME_POLL_CPU 15 #define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 32 #define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 30 @@ -42,9 +42,9 @@ #elif CONFIG_IDF_TARGET_ESP32C3 #define IDF_TARGET_MAX_SPI_CLK_FREQ 40*1000*1000 #if !CONFIG_FREERTOS_SMP // IDF-5223 -#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 15 +#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 17 #define IDF_TARGET_MAX_TRANS_TIME_POLL_CPU 15 -#define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 33 +#define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 35 #define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 30 #else #define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 17 @@ -56,7 +56,7 @@ #elif CONFIG_IDF_TARGET_ESP32C6 #define IDF_TARGET_MAX_SPI_CLK_FREQ 26666*1000 #define IDF_TARGET_MAX_TRANS_TIME_INTR_DMA 35 //TODO: IDF-9551, check perform -#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 17 +#define IDF_TARGET_MAX_TRANS_TIME_POLL_DMA 19 #define IDF_TARGET_MAX_TRANS_TIME_INTR_CPU 32 #define IDF_TARGET_MAX_TRANS_TIME_POLL_CPU 15 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 d154bdd2d2..a7897e08ec 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 @@ -124,7 +124,7 @@ TEST_CASE("SPI Master clockdiv calculation routines", "[spi]") // Test All clock source #define TEST_CLK_BYTE_LEN 10000 -#define TEST_TRANS_TIME_BIAS_RATIO (float)8.0/100 // think 8% transfer time bias as acceptable +#define TEST_TRANS_TIME_BIAS_RATIO (float)10.0/100 // think 10% transfer time bias as acceptable TEST_CASE("SPI Master clk_source and divider accuracy", "[spi]") { int64_t start = 0, end = 0; @@ -491,9 +491,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); ESP_LOGI(TAG, "test 4 iomux output pins..."); @@ -502,9 +502,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); ESP_LOGI(TAG, "test 6 output pins..."); @@ -514,9 +514,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); ESP_LOGI(TAG, "test 4 output pins..."); @@ -526,9 +526,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); #if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored. @@ -538,7 +538,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); ESP_LOGI(TAG, "test slave 5 output pins and MISO on input-only pin..."); @@ -547,7 +547,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); ESP_LOGI(TAG, "test master 3 output pins and MOSI on input-only pin..."); @@ -557,7 +557,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); ESP_LOGI(TAG, "test slave 3 output pins and MISO on input-only pin..."); @@ -566,7 +566,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o); //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored. #endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS @@ -578,8 +578,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); ESP_LOGI(TAG, "check native flag for 4 output pins..."); flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS; @@ -588,8 +588,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); #if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored. ESP_LOGI(TAG, "check dual flag for master 5 output pins and MISO/MOSI on input-only pin..."); @@ -598,14 +598,14 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); cfg = (spi_bus_config_t) { .mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); ESP_LOGI(TAG, "check dual flag for master 3 output pins and MISO/MOSI on input-only pin..."); flags_expected = SPICOMMON_BUSFLAG_DUAL | SPICOMMON_BUSFLAG_GPIO_PINS; @@ -613,14 +613,14 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); cfg = (spi_bus_config_t) { .mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored. #endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS @@ -630,8 +630,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = -1, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); ESP_LOGI(TAG, "check mosi flag..."); flags_expected = SPICOMMON_BUSFLAG_MOSI; @@ -639,8 +639,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = -1, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); ESP_LOGI(TAG, "check miso flag..."); flags_expected = SPICOMMON_BUSFLAG_MISO; @@ -648,8 +648,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = -1, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); ESP_LOGI(TAG, "check quad flag..."); flags_expected = SPICOMMON_BUSFLAG_QUAD; @@ -657,14 +657,14 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]") .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); cfg = (spi_bus_config_t) { .mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = -1, .max_transfer_sz = 8, .flags = flags_expected }; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o)); } TEST_CASE("SPI Master no response when switch from host1 (SPI2) to host2 (SPI3)", "[spi]") From df4ea176f81942e859642b73450400ac52b145b4 Mon Sep 17 00:00:00 2001 From: wanckl Date: Tue, 16 Dec 2025 20:04:10 +0800 Subject: [PATCH 2/6] feature(driver_spi): slave driver support psram transfer --- .../include/test_spi_utils.h | 7 +- .../test_driver_utils/test_spi_utils.c | 20 +-- .../esp_driver_spi/include/driver/spi_slave.h | 2 + .../include/esp_private/spi_common_internal.h | 17 +++ .../esp_driver_spi/src/gpspi/spi_common.c | 56 ++++++- .../esp_driver_spi/src/gpspi/spi_master.c | 73 ++------- .../esp_driver_spi/src/gpspi/spi_slave.c | 140 ++++++------------ .../test_apps/master/main/test_spi_master.c | 14 +- .../test_apps/master/main/test_spi_sio.c | 2 +- .../test_apps/slave/main/test_spi_slave.c | 139 +++++++++++++---- .../slave/sdkconfig.ci.release.esp32c5 | 1 + .../slave/sdkconfig.ci.release.esp32c61 | 1 + .../slave/sdkconfig.ci.release.esp32h4 | 1 + .../slave/sdkconfig.ci.release.esp32p4 | 1 + .../slave/sdkconfig.ci.release.esp32s3 | 1 + .../slave_hd/main/test_spi_slave_hd.c | 8 +- .../esp_hal_gpspi/include/hal/spi_slave_hal.h | 17 +++ components/esp_hal_gpspi/spi_slave_hal_iram.c | 12 ++ .../api-reference/peripherals/spi_slave.rst | 8 + .../api-reference/peripherals/spi_slave.rst | 8 + 20 files changed, 313 insertions(+), 215 deletions(-) create mode 100644 components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c5 create mode 100644 components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c61 create mode 100644 components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32h4 create mode 100644 components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32p4 create mode 100644 components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32s3 diff --git a/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h b/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h index e5da02e8aa..55d843484d 100644 --- a/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h +++ b/components/driver/test_apps/components/test_driver_utils/include/test_spi_utils.h @@ -266,10 +266,9 @@ void spitest_gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx); //use this function to fix the input source when assign multiple functions to a same pin void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx); -//Note this cs_num is the ID of the connected devices' ID, e.g. if 2 devices are connected to the bus, -//then the cs_num of the 1st and 2nd devices are 0 and 1 respectively. -//Enable `soft_master` to connect to soft spi master instead of hardware master. -void same_pin_func_sel(spi_bus_config_t bus, uint8_t cs_pin, uint8_t cs_dev_id, bool soft_master); +// Connect master and slave to the same pin +// master_id and slave_id are the IDs of the master and slave devices, set 0 for each to use soft master/slave. +void same_pin_func_sel(spi_host_device_t master_id, spi_host_device_t slave_id, spi_bus_config_t bus, uint8_t cs_pin); // Soft simulated spi master host for slave testing // `speed_hz` max 500kHz diff --git a/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c b/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c index 6f4e657d01..fcdd17882c 100644 --- a/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c +++ b/components/driver/test_apps/components/test_driver_utils/test_spi_utils.c @@ -224,20 +224,20 @@ void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx) esp_rom_gpio_connect_in_signal(gpio_num, signal_idx, 0); } -void same_pin_func_sel(spi_bus_config_t bus, uint8_t cs_pin, uint8_t cs_dev_id, bool soft_master) +void same_pin_func_sel(spi_host_device_t master_id, spi_host_device_t slave_id, spi_bus_config_t bus, uint8_t cs_pin) { - spitest_gpio_output_sel(bus.mosi_io_num, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spid_out); - spitest_gpio_input_sel(bus.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spid_in); + spitest_gpio_output_sel(bus.mosi_io_num, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spid_out); + spitest_gpio_input_sel(bus.mosi_io_num, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spid_in); - spitest_gpio_output_sel(bus.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out); - spitest_gpio_input_sel(bus.miso_io_num, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spiq_in); + spitest_gpio_input_sel(bus.miso_io_num, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spiq_in); + spitest_gpio_output_sel(bus.miso_io_num, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spiq_out); - gpio_set_level(cs_pin, 1); //ensure CS is inactive when select to soft_master and before transaction start - spitest_gpio_output_sel(cs_pin, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spics_out[cs_dev_id]); - spitest_gpio_input_sel(cs_pin, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spics_in); + gpio_set_level(cs_pin, 1); //ensure CS is inactive when select 0 for soft_master and before transaction start + spitest_gpio_output_sel(cs_pin, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spics_out[0]); + spitest_gpio_input_sel(cs_pin, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spics_in); - spitest_gpio_output_sel(bus.sclk_io_num, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spiclk_out); - spitest_gpio_input_sel(bus.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiclk_in); + spitest_gpio_output_sel(bus.sclk_io_num, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spiclk_out); + spitest_gpio_input_sel(bus.sclk_io_num, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spiclk_in); } #define GPIO_MAX_FREQ 500*1000 //max of soft spi clock at delay(0) diff --git a/components/esp_driver_spi/include/driver/spi_slave.h b/components/esp_driver_spi/include/driver/spi_slave.h index b22b0078dd..4a9e8ba3d0 100644 --- a/components/esp_driver_spi/include/driver/spi_slave.h +++ b/components/esp_driver_spi/include/driver/spi_slave.h @@ -185,6 +185,7 @@ esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transact * @return * - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_ERR_NOT_SUPPORTED if flag `SPI_SLAVE_NO_RETURN_RESULT` is set + * - ESP_ERR_INVALID_STATE if dma over/underflow error occurs during psram transfer * - ESP_OK on success */ esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, uint32_t ticks_to_wait); @@ -204,6 +205,7 @@ esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transacti * out. * @return * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_STATE if dma over/underflow error occurs during psram transfer * - ESP_OK on success */ esp_err_t spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait); diff --git a/components/esp_driver_spi/include/esp_private/spi_common_internal.h b/components/esp_driver_spi/include/esp_private/spi_common_internal.h index edcec0bc72..1a0df7fbd7 100644 --- a/components/esp_driver_spi/include/esp_private/spi_common_internal.h +++ b/components/esp_driver_spi/include/esp_private/spi_common_internal.h @@ -137,6 +137,23 @@ esp_err_t spicommon_dma_desc_alloc(spi_host_device_t host_id, int cfg_max_sz, in */ void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx); +/** + * @brief Setup private buffer for DMA transfer + * + * @param host_id SPI host ID to access the DMA context + * @param buffer buffer to be setup + * @param len length of buffer, in byte + * @param is_tx if buffer is for tx/transmit direction + * @param psram_prefer if psram is preferred + * @param auto_malloc if auto malloc is enabled + * @param ret_buffer return buffer, which is the buffer that is actually used for DMA transfer + * + * @return + * - ESP_OK: On success + * - ESP_ERR_NO_MEM: No enough memory + */ +esp_err_t spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer); + /** * @brief Free DMA for SPI * diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index ca16ebb2a8..b4b83ead0c 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -14,7 +14,7 @@ #include "esp_cache.h" #include "esp_rom_gpio.h" #include "esp_heap_caps.h" -#include "soc/spi_periph.h" +#include "esp_memory_utils.h" #include "driver/spi_master.h" #include "driver/gpio.h" #include "esp_private/gpio.h" @@ -388,6 +388,56 @@ void SPI_COMMON_ISR_ATTR spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, dmadesc[n - 1].next = NULL; } +esp_err_t SPI_COMMON_ISR_ATTR spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer) +{ + spi_bus_attr_t *bus_attr = spi_bus_get_attr(host_id); + spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id); + assert(bus_attr && dma_ctx); + if ((buffer == NULL) || (len == 0)) { + *ret_buffer = buffer; + return ESP_OK; + } + + bool is_ptr_ext = esp_ptr_external_ram(buffer); + bool use_psram = is_ptr_ext && psram_prefer; +#if SOC_IS(ESP32S2) + ESP_RETURN_ON_FALSE_ISR((host_id != SPI3_HOST) || !use_psram, ESP_ERR_NOT_SUPPORTED, SPI_TAG, "SPI3 does not support external memory"); +#endif + bool need_malloc = is_ptr_ext ? (!use_psram || !esp_ptr_dma_ext_capable(buffer)) : !esp_ptr_dma_capable(buffer); + // If psram is wanted, re-malloc also from psram. + uint32_t mem_cap = MALLOC_CAP_DMA | (use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL); + uint16_t alignment = 0; + if (is_tx) { + alignment = use_psram ? dma_ctx->dma_align_tx_ext : dma_ctx->dma_align_tx_int; + } else { + // RX cache sync still need consider the cache alignment requirement + if (use_psram) { + alignment = MAX(dma_ctx->dma_align_rx_ext, bus_attr->cache_align_ext); + } else { + alignment = MAX(dma_ctx->dma_align_rx_int, bus_attr->cache_align_int); + } + } + need_malloc |= (((uint32_t)buffer | len) & (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); + ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX"); + + if (is_tx) { + memcpy(temp, buffer, len); + } + buffer = temp; + } + *ret_buffer = buffer; + uint32_t sync_flags = is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C; + esp_err_t ret = esp_cache_msync((void *)buffer, need_malloc ? align_len : len, sync_flags); + // ESP_ERR_NOT_SUPPORTED stands for not cache sync required, it's allowed here + ESP_RETURN_ON_FALSE_ISR((ret == ESP_OK) || (ret == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX"); + return ESP_OK; +} + //----------------------------------------------------------free dma periph-------------------------------------------------------// esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id) { @@ -932,7 +982,7 @@ void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA); } -spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id) +SPI_COMMON_ISR_ATTR spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id) { if (bus_ctx[host_id] == NULL) { return NULL; @@ -941,7 +991,7 @@ spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id) return &bus_ctx[host_id]->bus_attr; } -spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id) +SPI_COMMON_ISR_ATTR spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id) { return spi_dma_ctx[host_id]; } diff --git a/components/esp_driver_spi/src/gpspi/spi_master.c b/components/esp_driver_spi/src/gpspi/spi_master.c index 99adebb6d2..2e4794ad9a 100644 --- a/components/esp_driver_spi/src/gpspi/spi_master.c +++ b/components/esp_driver_spi/src/gpspi/spi_master.c @@ -124,10 +124,10 @@ We have two bits to control the interrupt: #include "esp_ipc.h" #include "esp_cache.h" #include "esp_heap_caps.h" +#include "esp_memory_utils.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" -#include "soc/soc_memory_layout.h" #include "driver/gpio.h" #include "hal/spi_hal.h" #include "hal/spi_ll.h" @@ -1175,47 +1175,6 @@ static SPI_MASTER_ISR_ATTR void uninstall_priv_desc(spi_trans_priv_t* trans_buf) } } -static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uint32_t *buffer, uint32_t len, bool is_tx, uint32_t flags, uint32_t **ret_buffer) -{ -#if CONFIG_IDF_TARGET_ESP32S2 - ESP_RETURN_ON_FALSE_ISR((host->id != SPI3_HOST) || !(flags & SPI_TRANS_DMA_USE_PSRAM), ESP_ERR_NOT_SUPPORTED, SPI_TAG, "SPI3 does not support external memory"); -#endif - bool is_ptr_ext = esp_ptr_external_ram(buffer); - bool use_psram = is_ptr_ext && (flags & SPI_TRANS_DMA_USE_PSRAM); - bool need_malloc = is_ptr_ext ? (!use_psram || !esp_ptr_dma_ext_capable(buffer)) : !esp_ptr_dma_capable(buffer); - uint16_t alignment = 0; - // If psram is wanted, re-malloc also from psram. - uint32_t mem_cap = MALLOC_CAP_DMA | (use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL); - if (is_tx) { - alignment = use_psram ? host->dma_ctx->dma_align_tx_ext : host->dma_ctx->dma_align_tx_int; - } else { - // RX cache sync still need consider the cache alignment requirement - if (use_psram) { - alignment = MAX(host->dma_ctx->dma_align_rx_ext, host->bus_attr->cache_align_ext); - } else { - alignment = MAX(host->dma_ctx->dma_align_rx_int, host->bus_attr->cache_align_int); - } - } - need_malloc |= (((uint32_t)buffer | len) & (alignment - 1)); - ESP_EARLY_LOGV(SPI_TAG, "%s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", 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(!(flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but %s addr&len not align to %d, or not dma_capable", is_tx ? "TX" : "RX", alignment); - len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment - uint32_t *temp = heap_caps_aligned_alloc(alignment, len, mem_cap); - ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX"); - - if (is_tx) { - memcpy(temp, buffer, len); - } - buffer = temp; - } - esp_err_t ret = esp_cache_msync((void *)buffer, len, is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C); - // ESP_ERR_NOT_SUPPORTED stands for not cache sync required, it's allowed here - ESP_RETURN_ON_FALSE_ISR((ret == ESP_OK) || (ret == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX"); - *ret_buffer = buffer; - return ESP_OK; -} - static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans_priv_t* priv_desc) { spi_transaction_t *trans_desc = priv_desc->trans; @@ -1225,25 +1184,23 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans uint32_t* rcv_ptr = (trans_desc->flags & SPI_TRANS_USE_RXDATA) ? (uint32_t *)trans_desc->rx_data : (uint32_t *)trans_desc->rx_buffer; // tx memory assign uint32_t *send_ptr = (trans_desc->flags & SPI_TRANS_USE_TXDATA) ? (uint32_t *)trans_desc->tx_data : (uint32_t *)trans_desc->tx_buffer; - - esp_err_t ret = ESP_OK; - if (send_ptr && bus_attr->dma_enabled) { - ret = setup_dma_priv_buffer(host, send_ptr, (trans_desc->length + 7) / 8, true, trans_desc->flags, &send_ptr); - if (ret != ESP_OK) { - goto clean_up; - } + if (!bus_attr->dma_enabled) { + priv_desc->buffer_to_send = send_ptr; + priv_desc->buffer_to_rcv = rcv_ptr; + return ESP_OK; } - if (rcv_ptr && bus_attr->dma_enabled) { - ret = setup_dma_priv_buffer(host, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, trans_desc->flags, &rcv_ptr); - if (ret != ESP_OK) { - goto clean_up; - } + bool use_psram = trans_desc->flags & SPI_TRANS_DMA_USE_PSRAM; + bool auto_malloc = !(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL); + esp_err_t ret = spicommon_dma_setup_priv_buffer(host->id, send_ptr, (trans_desc->length + 7) / 8, true, use_psram, auto_malloc, (void *)&priv_desc->buffer_to_send); + if (ret != ESP_OK) { + goto clean_up; } - - priv_desc->buffer_to_send = send_ptr; - priv_desc->buffer_to_rcv = rcv_ptr; - return ESP_OK; + ret = spicommon_dma_setup_priv_buffer(host->id, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, use_psram, auto_malloc, &priv_desc->buffer_to_rcv); + if (ret != ESP_OK) { + goto clean_up; + } + return ret; clean_up: uninstall_priv_desc(priv_desc); diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index f2010658dc..bcdab9516e 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -54,8 +54,9 @@ static const char *SPI_TAG = "spi_slave"; /// struct to hold private transaction data (like tx and rx buffer for DMA). typedef struct { spi_slave_transaction_t *trans; //original trans - void *tx_buffer; //actually tx buffer (re-malloced if needed) - void *rx_buffer; //actually rx buffer (re-malloced if needed) + uint32_t *tx_buffer; //actually tx buffer (re-malloced if needed) + uint32_t *rx_buffer; //actually rx buffer (re-malloced if needed) + bool dma_hw_error; //true if DMA hardware over/underflow occurred } spi_slave_trans_priv_t; typedef struct { @@ -73,7 +74,6 @@ typedef struct { QueueHandle_t ret_queue; bool cs_iomux; uint8_t cs_in_signal; - uint16_t internal_mem_align_size; #ifdef CONFIG_PM_ENABLE esp_pm_lock_handle_t pm_lock; #endif @@ -199,14 +199,6 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b hal->dmadesc_tx = spihost[host]->dma_ctx->dmadesc_tx; hal->dmadesc_rx = spihost[host]->dma_ctx->dmadesc_rx; hal->dmadesc_n = spihost[host]->dma_ctx->dma_desc_num; - -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - size_t alignment; - esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment); - spihost[host]->internal_mem_align_size = alignment; -#else - spihost[host]->internal_mem_align_size = 4; -#endif } err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL); @@ -408,7 +400,6 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t ESP_EARLY_LOGW(SPI_TAG, "Use DMA but real trans_len is not 4 bytes aligned, slave may loss data"); } #endif -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE if (spihost[host]->bus_attr->dma_enabled) { if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) { free(priv_trans->tx_buffer); @@ -418,54 +409,28 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t free(priv_trans->rx_buffer); } } -#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE } -static esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_setup_priv_trans(spi_host_device_t host, spi_slave_trans_priv_t *priv_trans) +static esp_err_t SPI_SLAVE_ATTR spi_slave_setup_priv_trans(spi_host_device_t host, spi_slave_trans_priv_t *priv_trans) { spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans; - - priv_trans->tx_buffer = (void *)trans->tx_buffer; - priv_trans->rx_buffer = trans->rx_buffer; - -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - uint16_t alignment = spihost[host]->internal_mem_align_size; - uint32_t buffer_byte_len = (trans->length + 7) / 8; - - if (spihost[host]->bus_attr->dma_enabled && trans->tx_buffer) { - if ((!esp_ptr_dma_capable(trans->tx_buffer) || ((((uint32_t)trans->tx_buffer) | buffer_byte_len) & (alignment - 1)))) { - ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "TX buffer addr&len not align to %d byte, or not dma_capable", alignment); - //if txbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one - ESP_EARLY_LOGD(SPI_TAG, "Allocate TX buffer for DMA"); - buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment" - uint32_t *temp = heap_caps_aligned_alloc(alignment, buffer_byte_len, MALLOC_CAP_DMA); - if (temp == NULL) { - return ESP_ERR_NO_MEM; - } - - memcpy(temp, trans->tx_buffer, (trans->length + 7) / 8); - priv_trans->tx_buffer = temp; - } - esp_err_t ret = esp_cache_msync((void *)priv_trans->tx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync c2m(writeback) fail"); + if (!spihost[host]->bus_attr->dma_enabled) { + priv_trans->tx_buffer = (uint32_t *)trans->tx_buffer; + priv_trans->rx_buffer = (uint32_t *)trans->rx_buffer; + return ESP_OK; } - if (spihost[host]->bus_attr->dma_enabled && trans->rx_buffer) { - if ((!esp_ptr_dma_capable(trans->rx_buffer) || ((((uint32_t)trans->rx_buffer) | (trans->length + 7) / 8) & (alignment - 1)))) { - ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "RX buffer addr&len not align to %d byte, or not dma_capable", alignment); - //if rxbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one - ESP_EARLY_LOGD(SPI_TAG, "Allocate RX buffer for DMA"); - buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment" - priv_trans->rx_buffer = heap_caps_aligned_alloc(alignment, buffer_byte_len, MALLOC_CAP_DMA); - if (priv_trans->rx_buffer == NULL) { - free(priv_trans->tx_buffer); - return ESP_ERR_NO_MEM; - } - } - esp_err_t ret = esp_cache_msync((void *)priv_trans->rx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync m2c(invalid) fail"); + + bool auto_malloc = (trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO); + esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, (trans->length + 7) / 8, true, true, auto_malloc, &priv_trans->tx_buffer); + if (ret != ESP_OK) { + spi_slave_uninstall_priv_trans(host, priv_trans); + return ret; } -#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - return ESP_OK; + ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, (trans->length + 7) / 8, false, true, auto_malloc, &priv_trans->rx_buffer); + if (ret != ESP_OK) { + spi_slave_uninstall_priv_trans(host, priv_trans); + } + return ret; } esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait) @@ -473,19 +438,6 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi BaseType_t r; SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG); SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG); - SPI_CHECK(spihost[host]->bus_attr->dma_enabled == 0 || trans_desc->tx_buffer == NULL || esp_ptr_dma_capable(trans_desc->tx_buffer), - "txdata not in DMA-capable memory", ESP_ERR_INVALID_ARG); - - // We don't check length WORD alignment for rx when using DMA, seems break DMA requirement, - // however peripheral can also stop DMA from over writing memory even if it not aligned (except esp32). - // ATTENTION!: On esp32, peripheral can NOT stop DMA, if length not WORD aligned, - // remain bytes in last word domain will overwritten by DMA HW, which may cause unexpected issues! - // But driver already used for long time, to avoid breaking changes, we still don't add alignment limit. - SPI_CHECK(spihost[host]->bus_attr->dma_enabled == 0 || trans_desc->rx_buffer == NULL || - (esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) && - (trans_desc->length % 8 == 0)), - "rxdata not in DMA-capable memory or not BYTE aligned", ESP_ERR_INVALID_ARG); - SPI_CHECK(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG); spi_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc}; @@ -539,29 +491,17 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c ESP_RETURN_ON_FALSE_ISR(is_valid_host(host), ESP_ERR_INVALID_ARG, SPI_TAG, "invalid host"); ESP_RETURN_ON_FALSE_ISR(spihost[host], ESP_ERR_INVALID_ARG, SPI_TAG, "host not slave"); ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum"); - if (spihost[host]->bus_attr->dma_enabled) { - uint16_t alignment = spihost[host]->internal_mem_align_size; - (void) alignment; - -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - // For those targets length and addr alignment is still required from Cache side - uint32_t buffer_byte_len = (trans_desc->length + 7) / 8; - bool tx_aligned = (trans_desc->tx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->tx_buffer) && ((((uint32_t)trans_desc->tx_buffer | buffer_byte_len) & (alignment - 1)) == 0)); - bool rx_aligned = (trans_desc->rx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->rx_buffer) && ((((uint32_t)trans_desc->rx_buffer | buffer_byte_len) & (alignment - 1)) == 0)); -#else - bool tx_aligned = (trans_desc->tx_buffer == NULL) || esp_ptr_dma_capable(trans_desc->tx_buffer); - bool rx_aligned = (trans_desc->rx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) && (trans_desc->length % 8 == 0)); -#endif - - ESP_RETURN_ON_FALSE_ISR(tx_aligned, ESP_ERR_INVALID_ARG, SPI_TAG, "txdata addr & len not align to %d bytes or not dma_capable", alignment); - ESP_RETURN_ON_FALSE_ISR(rx_aligned, ESP_ERR_INVALID_ARG, SPI_TAG, "rxdata addr & len not align to %d bytes or not dma_capable", alignment); - } spi_slave_trans_priv_t priv_trans = { .trans = (spi_slave_transaction_t *)trans_desc, .tx_buffer = (void *)trans_desc->tx_buffer, .rx_buffer = trans_desc->rx_buffer, }; + if (spihost[host]->bus_attr->dma_enabled) { + // isr api is not allowed to auto_malloc, so don't need to 'uninstall' anything here, return directly + ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->tx_buffer, (trans_desc->length + 7) / 8, true, true, false, &priv_trans.tx_buffer), SPI_TAG, ""); + ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->rx_buffer, (trans_desc->length + 7) / 8, false, true, false, &priv_trans.rx_buffer), SPI_TAG, ""); + } r = xQueueSendFromISR(spihost[host]->trans_queue, (void *)&priv_trans, &do_yield); if (!r) { return ESP_ERR_NO_MEM; @@ -612,7 +552,7 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_get_trans_result(spi_host_device_t host, spi_ spi_slave_uninstall_priv_trans(host, &priv_trans); *trans_desc = priv_trans.trans; - return ESP_OK; + return priv_trans.dma_hw_error ? ESP_ERR_INVALID_STATE : ESP_OK; } esp_err_t SPI_SLAVE_ATTR spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait) @@ -683,6 +623,20 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_restart_after_dmareset(void *arg) } #endif //#if CONFIG_IDF_TARGET_ESP32 +static void SPI_SLAVE_ISR_ATTR spi_slave_trans_dma_error_check(spi_slave_t *host) +{ +#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma + if (esp_ptr_external_ram(host->cur_trans.rx_buffer) && spi_slave_hal_get_intr_status(&host->hal, SPI_LL_INTR_IN_FULL)) { + host->cur_trans.dma_hw_error = true; + ESP_DRAM_LOGE(SPI_TAG, "DMA RX overflow detected"); + } + if (esp_ptr_external_ram(host->cur_trans.tx_buffer) && spi_slave_hal_get_intr_status(&host->hal, SPI_LL_INTR_OUT_EMPTY)) { + host->cur_trans.dma_hw_error = true; + ESP_DRAM_LOGE(SPI_TAG, "DMA TX underflow detected"); + } +#endif +} + //This is run in interrupt context and apart from initialization and destruction, this is the only code //touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are //no muxes in this code. @@ -700,6 +654,7 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) // When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready. if (use_dma) { freeze_cs(host); + spi_slave_trans_dma_error_check(host); } spi_slave_hal_store_result(hal); @@ -713,17 +668,6 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) } #endif //#if CONFIG_IDF_TARGET_ESP32 -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible - if (use_dma && host->cur_trans.rx_buffer) { - uint16_t alignment = host->internal_mem_align_size; - uint32_t buffer_byte_len = (host->cur_trans.trans->length + 7) / 8; - buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); - // invalidate priv_trans.buffer_to_rcv anyway, only user provide aligned buffer can rcv correct data in post_cb - esp_err_t ret = esp_cache_msync((void *)host->cur_trans.rx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - assert(ret == ESP_OK); - (void)ret; - } -#endif if (host->cfg.post_trans_cb) { host->cfg.post_trans_cb(host->cur_trans.trans); } @@ -784,7 +728,9 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) if (use_dma) { restore_cs(host); } - +#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE + spi_slave_hal_clear_intr_status(hal, SPI_LL_INTR_IN_FULL | SPI_LL_INTR_OUT_EMPTY); +#endif //Kick off transfer spi_slave_hal_user_start(hal); if (host->cfg.post_setup_cb) { 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 a7897e08ec..c65747ed7d 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 @@ -914,7 +914,7 @@ void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first) TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi)); //connecting pins to two peripherals breaks the output, fix it. - same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false); + same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num); for (int i = 0; i < 8; i++) { //prepare slave tx data @@ -1099,7 +1099,7 @@ TEST_CASE("SPI master variable dummy test", "[spi]") spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_DISABLED)); - same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false); + same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num); uint8_t data_to_send[] = {0x12, 0x34, 0x56, 0x78}; @@ -1142,7 +1142,7 @@ TEST_CASE("SPI master hd dma TX without RX test", "[spi]") spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_CH_AUTO)); - same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false); + same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num); uint32_t buf_size = 32; uint8_t *mst_send_buf = spi_bus_dma_memory_alloc(TEST_SPI_HOST, buf_size, 0); @@ -1993,11 +1993,11 @@ void test_spi_psram_trans(spi_device_handle_t dev_handle, void *tx, void *rx) // To use psram, hardware will pass data through MSPI and GDMA to GPSPI, which need some time // GPSPI bandwidth(speed * line_num) should always no more than PSRAM bandwidth - trans_cfg.override_freq_hz = (CONFIG_SPIRAM_SPEED / 4) * 1000 * 1000; + trans_cfg.override_freq_hz = MIN(80000000, (CONFIG_SPIRAM_SPEED / 2) * 1000 * 1000); printf("%d TX %p RX %p len %d @%ld kHz\n", cnt, trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len, trans_cfg.override_freq_hz / 1000); TEST_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg)); TEST_ASSERT(!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL))); - spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len); + TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len); trans_cfg.tx_buffer += trans_len; trans_cfg.rx_buffer += trans_len; trans_len ++; @@ -2042,9 +2042,11 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]") TEST_ASSERT(i ? (before - after) < 2 * TEST_EDMA_TRANS_LEN : (before - after) > 2 * TEST_EDMA_TRANS_LEN); spi_device_polling_end(dev_handle, portMAX_DELAY); printf("TX fail: %d, RX fail: %d\n", !!(trans_cfg.flags & SPI_TRANS_DMA_TX_FAIL), !!(trans_cfg.flags & SPI_TRANS_DMA_RX_FAIL)); +#if !SOC_IS(ESP32P4) // P4 can't reach error condition since it has powerful 16bits ddr psram TEST_ASSERT((!!i) == !!(trans_cfg.flags & (SPI_TRANS_DMA_TX_FAIL | SPI_TRANS_DMA_RX_FAIL))); +#endif if (!i) { // data should be correct if using auto malloc - spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN); + TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN); } } diff --git a/components/esp_driver_spi/test_apps/master/main/test_spi_sio.c b/components/esp_driver_spi/test_apps/master/main/test_spi_sio.c index 0ca09926ad..1b6064a76c 100644 --- a/components/esp_driver_spi/test_apps/master/main/test_spi_sio.c +++ b/components/esp_driver_spi/test_apps/master/main/test_spi_sio.c @@ -75,7 +75,7 @@ TEST_CASE("SPI Single Board Test SIO", "[spi]") spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_DISABLED)); - same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false); + same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num); inner_connect(bus_cfg); WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320]; diff --git a/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c b/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c index db34cd463d..27717ae0dd 100644 --- a/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c +++ b/components/esp_driver_spi/test_apps/slave/main/test_spi_slave.c @@ -8,6 +8,7 @@ */ #include +#include #include "sdkconfig.h" #include "unity.h" #include "test_utils.h" @@ -32,13 +33,9 @@ static WORD_ALIGNED_ATTR uint8_t slave_rxbuf[320]; static const uint8_t master_send[] = { 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43 }; static const uint8_t slave_send[] = { 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0 }; -#if (TEST_SPI_PERIPH_NUM >= 2) -//These will only be enabled on chips with 2 or more SPI peripherals -#ifndef CONFIG_SPIRAM -//This test should be removed once the timing test is merged. - +static spi_host_device_t master_slave_ids[2]; static spi_device_handle_t spi; -static void custom_setup(void) +void custom_setup(spi_host_device_t master_id, spi_host_device_t slave_id) { //Initialize buffers memset(master_txbuf, 0, sizeof(master_txbuf)); @@ -46,46 +43,60 @@ static void custom_setup(void) memset(slave_txbuf, 0, sizeof(slave_txbuf)); memset(slave_rxbuf, 0, sizeof(slave_rxbuf)); - //Initialize SPI Master spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; + buscfg.max_transfer_sz = 40960; spi_device_interface_config_t devcfg = { .clock_speed_hz = 4 * 1000 * 1000, //currently only up to 4MHz for internal connect - .mode = 0, //SPI mode 0 .spics_io_num = PIN_NUM_CS, //CS pin .queue_size = 7, //We want to be able to queue 7 transactions at a time - .pre_cb = NULL, .cs_ena_posttrans = 5, .cs_ena_pretrans = 1, }; - //Initialize the SPI bus - TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO)); - //Attach the device to the SPI bus - TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi)); - //Configuration for the SPI slave interface - spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); - //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected. - gpio_set_pull_mode(PIN_NUM_MOSI, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(PIN_NUM_CLK, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(PIN_NUM_CS, GPIO_PULLUP_ONLY); - //Initialize SPI slave interface - TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO)); + //Initialize SPI Master + if (master_id) { + //Initialize the SPI bus + TEST_ESP_OK(spi_bus_initialize(master_id, &buscfg, SPI_DMA_CH_AUTO)); + //Attach the device to the SPI bus + TEST_ESP_OK(spi_bus_add_device(master_id, &devcfg, &spi)); + } + + if (slave_id) { + //Configuration for the SPI slave interface + spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); + //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected. + slave_pull_up(&buscfg, devcfg.spics_io_num); + //Initialize SPI slave interface + TEST_ESP_OK(spi_slave_initialize(slave_id, &buscfg, &slvcfg, SPI_DMA_CH_AUTO)); + } //Do internal connections - same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false); + same_pin_func_sel(master_id, slave_id, buscfg, devcfg.spics_io_num); + master_slave_ids[0] = master_id; + master_slave_ids[1] = slave_id; } -static void custom_teardown(void) +void custom_teardown(void) { - TEST_ESP_OK(spi_slave_free(TEST_SLAVE_HOST)); - TEST_ESP_OK(spi_bus_remove_device(spi)); - TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); + if (master_slave_ids[1]) { + TEST_ESP_OK(spi_slave_free(master_slave_ids[1])); + master_slave_ids[1] = 0; + } + if (spi != NULL) { + TEST_ESP_OK(spi_bus_remove_device(spi)); + spi = NULL; + } + if (master_slave_ids[0]) { + TEST_ESP_OK(spi_bus_free(master_slave_ids[0])); + master_slave_ids[0] = 0; + } } +#if (TEST_SPI_PERIPH_NUM >= 2) TEST_CASE("test fullduplex slave with only RX direction", "[spi]") { - custom_setup(); + custom_setup(TEST_SPI_HOST, TEST_SLAVE_HOST); memcpy(master_txbuf, master_send, sizeof(master_send)); @@ -132,7 +143,7 @@ TEST_CASE("test fullduplex slave with only RX direction", "[spi]") TEST_CASE("test fullduplex slave with only TX direction", "[spi]") { - custom_setup(); + custom_setup(TEST_SPI_HOST, TEST_SLAVE_HOST); memcpy(slave_txbuf, slave_send, sizeof(slave_send)); @@ -191,7 +202,7 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi] TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED)); //initialize master and slave on the same pins break some of the output configs, fix them - same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false); + same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num); uint8_t master_tx[TEST_SLV_RX_BUF_LEN], slave_rx[TEST_SLV_RX_BUF_LEN]; for (uint8_t i = 0; i < TEST_SLV_RX_BUF_LEN; i++) { @@ -249,16 +260,80 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi] TEST_ESP_OK(spi_bus_remove_device(spidev0)); TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); } -#endif // !CONFIG_SPIRAM #endif // #if (TEST_SPI_PERIPH_NUM >= 2) +#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE +#define PSRAM_TRANS_LEN 16000 +TEST_CASE("test slave using external ram", "[spi]") +{ + custom_setup((TEST_SPI_PERIPH_NUM >= 2) ? TEST_SLAVE_HOST : 0, TEST_SPI_HOST); + + uint8_t *slave_ext_tx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM); + uint8_t *slave_ext_rx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM); + uint8_t *master_tx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA); + uint8_t *master_rx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA); + + spi_slave_transaction_t slave_tans = {}, *out_trans; + slave_tans.length = 8 * PSRAM_TRANS_LEN; + slave_tans.tx_buffer = slave_ext_tx; + slave_tans.rx_buffer = slave_ext_rx; + slave_tans.flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO; + + spi_transaction_t master_tans = {}; + master_tans.override_freq_hz = MIN(60000000, (CONFIG_SPIRAM_SPEED / 2) * 1000 * 1000); + master_tans.tx_buffer = master_tx; + master_tans.rx_buffer = master_rx; + + for (int i = 0; i < 6; i ++) { + test_fill_random_to_buffers_dualboard(7 + i, master_tx, slave_ext_tx, PSRAM_TRANS_LEN); + slave_tans.length -= i * 8; + master_tans.length = slave_tans.length; + master_tans.rxlength = slave_tans.length; + ESP_LOGI(SLAVE_TAG, "Test freq: %ld, tx: %p, rx: %p, len: %d", master_tans.override_freq_hz, slave_tans.tx_buffer, slave_tans.rx_buffer, slave_tans.length / 8); + + uint32_t before = esp_get_free_heap_size(); + TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &slave_tans, portMAX_DELAY)); + uint32_t after = esp_get_free_heap_size(); +#if (TEST_SPI_PERIPH_NUM >= 2) + spi_device_transmit(spi, &master_tans); +#else + spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); + spi_master_trans_impl_gpio(buscfg, PIN_NUM_CS, 0, (uint8_t *)master_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8, false); +#endif + ESP_LOGI(SLAVE_TAG, "slave malloc: %ld", after - before); + TEST_ASSERT(i ? (before - after) > PSRAM_TRANS_LEN : (before - after) < PSRAM_TRANS_LEN); + TEST_ESP_OK(spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY)); + + TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len); + TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8); + TEST_ASSERT_EQUAL_HEX8_ARRAY(master_tans.tx_buffer, slave_tans.rx_buffer, master_tans.length / 8); + ESP_LOGI(SLAVE_TAG, "ok\n"); + } + +#if (TEST_SPI_PERIPH_NUM >= 2) && !SOC_IS(ESP32P4) // P4 can't reach error condition since it has powerful 16bits ddr psram + master_tans.override_freq_hz = 61000000; // real freq will be 40 if just config 60M + ESP_LOGI(SLAVE_TAG, "Testing over freq: %ld", master_tans.override_freq_hz); + TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &slave_tans, portMAX_DELAY)); + spi_device_transmit(spi, (spi_transaction_t *)&master_tans); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY)); +#endif + + free(slave_ext_tx); + free(slave_ext_rx); + free(master_tx); + free(master_rx); + custom_teardown(); + ESP_LOGI(SLAVE_TAG, "test passed."); +} +#endif // CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE + TEST_CASE("test slave send unaligned", "[spi]") { spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO)); - same_pin_func_sel(buscfg, slvcfg.spics_io_num, 0, true); + same_pin_func_sel(0, TEST_SLAVE_HOST, buscfg, slvcfg.spics_io_num); memcpy(master_txbuf, master_send, sizeof(master_send)); memcpy(slave_txbuf, slave_send, sizeof(slave_send)); @@ -678,7 +753,7 @@ TEST_CASE("test_spi_slave_sleep_retention", "[spi]") buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED)); - same_pin_func_sel(buscfg, slvcfg.spics_io_num, 0, true); + same_pin_func_sel(0, TEST_SLAVE_HOST, buscfg, slvcfg.spics_io_num); for (uint8_t cnt = 0; cnt < 3; cnt ++) { printf("Going into sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold"); diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c5 b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c5 new file mode 100644 index 0000000000..cc641ea603 --- /dev/null +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c5 @@ -0,0 +1 @@ +CONFIG_SPIRAM=y diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c61 b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c61 new file mode 100644 index 0000000000..cc641ea603 --- /dev/null +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32c61 @@ -0,0 +1 @@ +CONFIG_SPIRAM=y diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32h4 b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32h4 new file mode 100644 index 0000000000..cc641ea603 --- /dev/null +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32h4 @@ -0,0 +1 @@ +CONFIG_SPIRAM=y diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32p4 b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32p4 new file mode 100644 index 0000000000..cc641ea603 --- /dev/null +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32p4 @@ -0,0 +1 @@ +CONFIG_SPIRAM=y diff --git a/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32s3 b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32s3 new file mode 100644 index 0000000000..cc641ea603 --- /dev/null +++ b/components/esp_driver_spi/test_apps/slave/sdkconfig.ci.release.esp32s3 @@ -0,0 +1 @@ +CONFIG_SPIRAM=y diff --git a/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c b/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c index cbeadf8b8c..4ff0817d02 100644 --- a/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c +++ b/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c @@ -171,7 +171,7 @@ static void test_hd_start(spi_device_handle_t *spi, int freq, const spitest_para //when test with single board via same set of mosi, miso, clk and cs pins. spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG(); spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); - same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, false); + same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num); wait_wrbuf_sig(ctx, 0); wait_rdbuf_sig(ctx, 0); @@ -529,7 +529,7 @@ TEST_CASE("test spi slave hd segment mode, master too long", "[spi][spi_slv_hd]" //Use GPIO matrix to connect signal of master and slave via same set of pins on one board. spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG(); spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); - same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true); + same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num); const int send_buf_size = 1024; WORD_ALIGNED_ATTR uint8_t* slave_send_buf = malloc(send_buf_size * 2); @@ -955,7 +955,7 @@ TEST_CASE("test_spi_slave_hd_sleep_retention", "[spi]") bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg)); - same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true); + same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num); vTaskDelay(1); for (uint8_t cnt = 0; cnt < 3; cnt ++) { @@ -1033,7 +1033,7 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]") spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE; TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg)); - same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true); + same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num); vTaskDelay(1); for (uint8_t i = 0; i < 2; i++) { diff --git a/components/esp_hal_gpspi/include/hal/spi_slave_hal.h b/components/esp_hal_gpspi/include/hal/spi_slave_hal.h index 12e3c0a28d..96effcb09d 100644 --- a/components/esp_hal_gpspi/include/hal/spi_slave_hal.h +++ b/components/esp_hal_gpspi/include/hal/spi_slave_hal.h @@ -172,6 +172,23 @@ void spi_slave_hal_user_start(const spi_slave_hal_context_t *hal); */ bool spi_slave_hal_usr_is_done(spi_slave_hal_context_t* hal); +/** + * Get SPI interrupt bits status by mask + * + * @param hal Context of the HAL layer. + * @param mask Mask of the interrupt bits to check. + * @return True if the masked interrupts are set, false otherwise. + */ +bool spi_slave_hal_get_intr_status(spi_slave_hal_context_t *hal, uint32_t mask); + +/** + * Clear SPI interrupt bits by mask + * + * @param hal Context of the HAL layer. + * @param mask Mask of the interrupt bits to clear. + */ +void spi_slave_hal_clear_intr_status(spi_slave_hal_context_t *hal, uint32_t mask); + /** * Post transaction operations, fetch data from the buffer and recorded the length. * diff --git a/components/esp_hal_gpspi/spi_slave_hal_iram.c b/components/esp_hal_gpspi/spi_slave_hal_iram.c index 1918e53a21..0fa06f60c6 100644 --- a/components/esp_hal_gpspi/spi_slave_hal_iram.c +++ b/components/esp_hal_gpspi/spi_slave_hal_iram.c @@ -99,3 +99,15 @@ bool spi_slave_hal_dma_need_reset(const spi_slave_hal_context_t *hal) #endif // SPI_LL_SLAVE_NEEDS_RESET_WORKAROUND return ret; } + +#if SOC_SPI_SUPPORT_SLAVE_HD_VER2 +bool spi_slave_hal_get_intr_status(spi_slave_hal_context_t *hal, uint32_t mask) +{ + return spi_ll_get_intr(hal->hw, mask); +} + +void spi_slave_hal_clear_intr_status(spi_slave_hal_context_t *hal, uint32_t mask) +{ + spi_ll_clear_intr(hal->hw, mask); +} +#endif diff --git a/docs/en/api-reference/peripherals/spi_slave.rst b/docs/en/api-reference/peripherals/spi_slave.rst index 39e693f5f7..b7a6f6c3a3 100644 --- a/docs/en/api-reference/peripherals/spi_slave.rst +++ b/docs/en/api-reference/peripherals/spi_slave.rst @@ -79,6 +79,14 @@ As not every transaction requires both writing and reading data, you can choose A Host should not start a transaction before its Device is ready for receiving data. It is recommended to use another GPIO pin for a handshake signal to sync the Devices. For more details, see :ref:`transaction_interval`. +.. only:: SOC_PSRAM_DMA_CAPABLE + + Using PSRAM for DMA transfer + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + SPI Slave driver supports using PSRAM for DMA transfer. Directly passing a PSRAM address as :cpp:member:`spi_slave_transaction_t::tx_buffer` or :cpp:member:`spi_slave_transaction_t::rx_buffer` is supported. For the rx_buffer, it has alignment requirements, using :cpp:func:`heap_caps_malloc` to allocate memory can automatically handle the alignment requirements. For the buffers that you can not control, you can also use the :c:macro:`SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO` flag to enable driver to automatically align the buffer from PSRAM. + + Note that this feature shares the MSPI bus bandwidth (bus frequency * bus width), so the transmission bandwidth of the host to this device should be less than the PSRAM bandwidth, otherwise **data may be lost**, and the ``spi_slave_transmit`` function will return the :c:macro:`ESP_ERR_INVALID_STATE` error. Driver Usage ------------ diff --git a/docs/zh_CN/api-reference/peripherals/spi_slave.rst b/docs/zh_CN/api-reference/peripherals/spi_slave.rst index 8975f3c119..00a18e6f31 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_slave.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_slave.rst @@ -79,6 +79,14 @@ SPI 传输事务 主机应在从机设备准备好接收数据之后再进行传输事务。建议使用另外一个 GPIO 管脚作为握手信号来同步设备。更多细节,请参阅 :ref:`transaction_interval`。 +.. only:: SOC_PSRAM_DMA_CAPABLE + + 使用 PSRAM 的传输 + ^^^^^^^^^^^^^^^^^^ + + SPI Slave 驱动程序支持使用 PSRAM 进行传输。直接传入 PSRAM 地址作为 :cpp:member:`spi_slave_transaction_t::tx_buffer` 或 :cpp:member:`spi_slave_transaction_t::rx_buffer` 即可。对于 rx_buffer ,其地址和传输长度有对齐要求,使用 :cpp:func:`heap_caps_malloc` 分配内存可以自动处理对齐要求。对于不能控制的内存,也可以使用 :c:macro:`SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO` 标志位,驱动会自动从 PSRAM 重新分配满足要求的内存。 + + 请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此主机对该设备的传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**,此时 ``spi_slave_transmit`` 函数将会返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。 使用驱动程序 ------------ From 88e4c52b19fe28cea8541600e23acccb4c4f8660 Mon Sep 17 00:00:00 2001 From: wanckl Date: Wed, 21 Jan 2026 15:22:04 +0800 Subject: [PATCH 3/6] feat(driver_spi): slave hd driver support to using psram buffer --- .../include/driver/spi_slave_hd.h | 4 +- .../esp_driver_spi/src/gpspi/spi_slave_hd.c | 92 +++++++------------ .../test_apps/param/main/test_spi_param.c | 22 +++-- .../slave_hd/main/test_spi_slave_hd.c | 65 ++++++++++++- .../include/hal/spi_slave_hd_hal.h | 11 ++- components/esp_hal_gpspi/spi_slave_hd_hal.c | 14 ++- .../peripherals/spi_slave_hd.rst | 6 ++ .../peripherals/spi_slave_hd.rst | 6 ++ 8 files changed, 146 insertions(+), 74 deletions(-) diff --git a/components/esp_driver_spi/include/driver/spi_slave_hd.h b/components/esp_driver_spi/include/driver/spi_slave_hd.h index df9a10517f..73271a2043 100644 --- a/components/esp_driver_spi/include/driver/spi_slave_hd.h +++ b/components/esp_driver_spi/include/driver/spi_slave_hd.h @@ -166,7 +166,7 @@ esp_err_t spi_slave_hd_queue_trans(spi_host_device_t host_id, spi_slave_chan_t c * - ESP_OK: on success * - ESP_ERR_INVALID_ARG: Function is not valid * - ESP_ERR_TIMEOUT: There's no transaction done before timeout - * - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under segment mode. + * - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under segment mode. Or DMA hardware over/underflow occurred. */ esp_err_t spi_slave_hd_get_trans_res(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout); @@ -223,7 +223,7 @@ esp_err_t spi_slave_hd_append_trans(spi_host_device_t host_id, spi_slave_chan_t * - ESP_OK: on success * - ESP_ERR_INVALID_ARG: Function is not valid * - ESP_ERR_TIMEOUT: There's no transaction done before timeout - * - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under append mode. + * - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under append mode. Or DMA hardware over/underflow occurred. */ esp_err_t spi_slave_hd_get_append_trans_res(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout); diff --git a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c index f76558d324..0fc06fa117 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c @@ -40,6 +40,7 @@ typedef struct { spi_slave_hd_data_t *trans; //original trans void *aligned_buffer; //actually trans buffer (re-malloced if needed) + bool dma_hw_error; //true if DMA hardware over/underflow occurred } spi_slave_hd_trans_priv_t; typedef struct { @@ -48,7 +49,6 @@ typedef struct { spi_bus_attr_t* bus_attr; _Atomic spi_bus_fsm_t fsm; spi_dma_ctx_t *dma_ctx; - uint16_t internal_mem_align_size; portMUX_TYPE int_spinlock; intr_handle_t intr; intr_handle_t intr_dma; @@ -162,14 +162,6 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b host->hal.dmadesc_rx[i].desc = &host->dma_ctx->dmadesc_rx[i]; } -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - size_t alignment; - esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment); - host->internal_mem_align_size = alignment; -#else - host->internal_mem_align_size = 4; -#endif - ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL); if (ret != ESP_OK) { goto cleanup; @@ -422,6 +414,25 @@ static inline SPI_SLAVE_ISR_ATTR BaseType_t intr_check_clear_callback(spi_slave_ } return cb_awoken; } +static void SPI_SLAVE_ISR_ATTR spi_slave_hd_tx_dma_error_check(spi_slave_hd_slot_t *host, spi_slave_hd_trans_priv_t priv_trans) +{ +#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma + if (esp_ptr_external_ram(priv_trans.aligned_buffer) && spi_slave_hd_hal_check_clear_intr(&host->hal, SPI_LL_INTR_OUT_EMPTY)) { + priv_trans.dma_hw_error = true; + ESP_DRAM_LOGE(TAG, "DMA TX underflow detected"); + } +#endif +} +static void SPI_SLAVE_ISR_ATTR spi_slave_hd_rx_dma_error_check(spi_slave_hd_slot_t *host, spi_slave_hd_trans_priv_t priv_trans) +{ +#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma + if (esp_ptr_external_ram(priv_trans.aligned_buffer) && spi_slave_hd_hal_check_clear_intr(&host->hal, SPI_LL_INTR_IN_FULL)) { + priv_trans.dma_hw_error = true; + ESP_DRAM_LOGE(TAG, "DMA RX overflow detected"); + } +#endif +} + static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg) { spi_slave_hd_slot_t *host = (spi_slave_hd_slot_t *)arg; @@ -446,6 +457,7 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg) portEXIT_CRITICAL_ISR(&host->int_spinlock); if (tx_done) { + spi_slave_hd_tx_dma_error_check(host, host->tx_curr_trans); bool ret_queue = true; if (callback->cb_sent) { spi_slave_hd_event_t ev = { @@ -464,15 +476,9 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg) host->tx_curr_trans.trans = NULL; } if (rx_done) { + spi_slave_hd_rx_dma_error_check(host, host->rx_curr_trans); bool ret_queue = true; host->rx_curr_trans.trans->trans_len = spi_slave_hd_hal_rxdma_seg_get_len(hal); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible - uint16_t alignment = host->internal_mem_align_size; - uint32_t buff_len = (host->rx_curr_trans.trans->len + alignment - 1) & (~(alignment - 1)); - esp_err_t ret = esp_cache_msync((void *)host->rx_curr_trans.aligned_buffer, buff_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - assert(ret == ESP_OK); - (void)ret; -#endif if (callback->cb_recv) { spi_slave_hd_event_t ev = { .event = SPI_EV_RECV, @@ -553,7 +559,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_tx_isr(void *arg) BaseType_t awoken = pdFALSE; BaseType_t ret __attribute__((unused)); - spi_slave_hd_trans_priv_t ret_priv_trans; + spi_slave_hd_trans_priv_t ret_priv_trans = {}; while (1) { bool trans_finish = false; trans_finish = spi_slave_hd_hal_get_tx_finished_trans(hal, (void **)&ret_priv_trans.trans, &ret_priv_trans.aligned_buffer); @@ -565,6 +571,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_tx_isr(void *arg) portEXIT_CRITICAL_ISR(&host->int_spinlock); bool ret_queue = true; + spi_slave_hd_tx_dma_error_check(host, ret_priv_trans); if (callback->cb_sent) { spi_slave_hd_event_t ev = { .event = SPI_EV_SEND, @@ -596,7 +603,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg) BaseType_t awoken = pdFALSE; BaseType_t ret __attribute__((unused)); - spi_slave_hd_trans_priv_t ret_priv_trans; + spi_slave_hd_trans_priv_t ret_priv_trans = {}; size_t trans_len; while (1) { bool trans_finish = false; @@ -609,14 +616,8 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg) portEXIT_CRITICAL_ISR(&host->int_spinlock); ret_priv_trans.trans->trans_len = trans_len; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible - uint16_t alignment = host->internal_mem_align_size; - uint32_t buff_len = (ret_priv_trans.trans->len + alignment - 1) & (~(alignment - 1)); - esp_err_t ret = esp_cache_msync((void *)ret_priv_trans.aligned_buffer, buff_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - assert(ret == ESP_OK); - (void)ret; -#endif bool ret_queue = true; + spi_slave_hd_rx_dma_error_check(host, ret_priv_trans); if (callback->cb_recv) { spi_slave_hd_event_t ev = { .event = SPI_EV_RECV, @@ -685,7 +686,6 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_append_legacy_isr(void *arg) static void s_spi_slave_hd_destroy_priv_trans(spi_host_device_t host, spi_slave_hd_trans_priv_t *priv_trans, spi_slave_chan_t chan) { -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE spi_slave_hd_data_t *orig_trans = priv_trans->trans; if (priv_trans->aligned_buffer != orig_trans->data) { if (chan == SPI_SLAVE_CHAN_RX) { @@ -693,39 +693,19 @@ static void s_spi_slave_hd_destroy_priv_trans(spi_host_device_t host, spi_slave_ } free(priv_trans->aligned_buffer); } -#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE } static esp_err_t s_spi_slave_hd_setup_priv_trans(spi_host_device_t host, spi_slave_hd_trans_priv_t *priv_trans, spi_slave_chan_t chan) { spi_slave_hd_data_t *orig_trans = priv_trans->trans; - priv_trans->aligned_buffer = orig_trans->data; - -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - uint16_t alignment = spihost[host]->internal_mem_align_size; - uint32_t byte_len = orig_trans->len; - - if (((uint32_t)orig_trans->data | byte_len) & (alignment - 1)) { - ESP_RETURN_ON_FALSE(orig_trans->flags & SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, TAG, "data buffer addr&len not align to %d byte, or not dma_capable", alignment); - byte_len = (byte_len + alignment - 1) & (~(alignment - 1)); // up align to alignment - ESP_LOGD(TAG, "Re-allocate %s buffer of len %" PRIu32 " for DMA", (chan == SPI_SLAVE_CHAN_TX) ? "TX" : "RX", byte_len); - priv_trans->aligned_buffer = heap_caps_aligned_alloc(64, byte_len, MALLOC_CAP_DMA); - if (priv_trans->aligned_buffer == NULL) { - return ESP_ERR_NO_MEM; - } + bool auto_malloc = (orig_trans->flags & SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO); + bool is_tx = (chan == SPI_SLAVE_CHAN_TX); + esp_err_t ret = spicommon_dma_setup_priv_buffer(host, (uint32_t *)orig_trans->data, orig_trans->len, is_tx, true, auto_malloc, (uint32_t **)&priv_trans->aligned_buffer); + if (ret != ESP_OK) { + s_spi_slave_hd_destroy_priv_trans(host, priv_trans, chan); + return ret; } - if (chan == SPI_SLAVE_CHAN_TX) { - ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-overlapping-buffers") // TODO IDF-11086 - memcpy(priv_trans->aligned_buffer, orig_trans->data, orig_trans->len); - ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-overlapping-buffers") - esp_err_t ret = esp_cache_msync((void *)priv_trans->aligned_buffer, byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - ESP_RETURN_ON_FALSE(ESP_OK == ret, ESP_ERR_INVALID_STATE, TAG, "mem sync c2m(writeback) fail"); - } else { - esp_err_t ret = esp_cache_msync((void *)priv_trans->aligned_buffer, byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ESP_RETURN_ON_FALSE(ESP_OK == ret, ESP_ERR_INVALID_STATE, TAG, "mem sync m2c(invalid) fail"); - } -#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE return ESP_OK; } @@ -746,7 +726,7 @@ static esp_err_t get_ret_queue_result(spi_host_device_t host_id, spi_slave_chan_ s_spi_slave_hd_destroy_priv_trans(host_id, &hd_priv_trans, chan); *out_trans = hd_priv_trans.trans; - return ESP_OK; + return hd_priv_trans.dma_hw_error ? ESP_ERR_INVALID_STATE : ESP_OK; } esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data, size_t len, void *arg) @@ -837,12 +817,11 @@ esp_err_t spi_slave_hd_queue_trans(spi_host_device_t host_id, spi_slave_chan_t c spi_slave_hd_slot_t *host = spihost[host_id]; SPIHD_CHECK(host->append_mode == 0, "This API should be used for SPI Slave HD Segment Mode", ESP_ERR_INVALID_STATE); - SPIHD_CHECK(esp_ptr_dma_capable(trans->data), "The buffer should be DMA capable.", ESP_ERR_INVALID_ARG); SPIHD_CHECK(trans->len <= host->bus_attr->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG); SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG); spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans}; - SPIHD_CHECK(ESP_OK == s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), "No mem to allocate new cache buffer", ESP_ERR_NO_MEM); + ESP_RETURN_ON_ERROR(s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), TAG, "Setup dma buffer failed"); if (chan == SPI_SLAVE_CHAN_TX) { BaseType_t ret = xQueueSend(host->tx_trans_queue, &hd_priv_trans, timeout); @@ -890,12 +869,11 @@ esp_err_t spi_slave_hd_append_trans(spi_host_device_t host_id, spi_slave_chan_t SPIHD_CHECK(trans->len <= SPI_MAX_DMA_LEN, "Currently we only support transaction with data length within 4092 bytes", ESP_ERR_INVALID_ARG); SPIHD_CHECK(host->append_mode == 1, "This API should be used for SPI Slave HD Append Mode", ESP_ERR_INVALID_STATE); - SPIHD_CHECK(esp_ptr_dma_capable(trans->data), "The buffer should be DMA capable.", ESP_ERR_INVALID_ARG); SPIHD_CHECK(trans->len <= host->bus_attr->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG); SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG); spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans}; - SPIHD_CHECK(ESP_OK == s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), "No mem to allocate new cache buffer", ESP_ERR_NO_MEM); + ESP_RETURN_ON_ERROR(s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), TAG, "Setup dma buffer failed"); if (chan == SPI_SLAVE_CHAN_TX) { BaseType_t ret = xSemaphoreTake(host->tx_cnting_sem, timeout); diff --git a/components/esp_driver_spi/test_apps/param/main/test_spi_param.c b/components/esp_driver_spi/test_apps/param/main/test_spi_param.c index 020df8e860..c412d23b43 100644 --- a/components/esp_driver_spi/test_apps/param/main/test_spi_param.c +++ b/components/esp_driver_spi/test_apps/param/main/test_spi_param.c @@ -1567,15 +1567,18 @@ static void test_slave_hd_dma(void) test_fill_random_to_buffers_dualboard(985 + mode + speed_level + i, slave_expect, slave_send, TEST_STEP_LEN); uint32_t test_trans_len = TEST_STEP_LEN; - spi_slave_hd_data_t *ret_trans, slave_trans = { + spi_slave_hd_data_t *ret_trans, slave_tx = { .data = slave_send, .len = test_trans_len, .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, + }, slave_rx = { + .data = slave_receive, + .len = test_trans_len, + .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, }; unity_send_signal("Slave ready"); - TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_trans, portMAX_DELAY)); - slave_trans.data = slave_receive; - TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY)); + TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_tx, portMAX_DELAY)); + TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_rx, portMAX_DELAY)); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY)); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY)); @@ -1677,16 +1680,19 @@ static void test_slave_hd_no_dma(void) test_fill_random_to_buffers_dualboard(911 + mode + speed_level + i, slave_expect, slave_send, SOC_SPI_MAXIMUM_BUFFER_SIZE); uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE; - spi_slave_hd_data_t *ret_trans, slave_trans = { + spi_slave_hd_data_t *ret_trans, slave_tx = { .data = slave_send, .len = test_trans_len, .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, + }, slave_rx = { + .data = slave_receive, + .len = test_trans_len, + .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, }; - TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_trans, portMAX_DELAY)); + TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_tx, portMAX_DELAY)); unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY)); - slave_trans.data = slave_receive; - TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY)); + TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_rx, portMAX_DELAY)); unity_send_signal("Slave ready"); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY)); diff --git a/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c b/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c index 4ff0817d02..9bf1b7061e 100644 --- a/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c +++ b/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -1085,3 +1085,66 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]") #endif } #endif //SOC_LIGHT_SLEEP_SUPPORTED + +#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE +// function pointers for segment and append mode +static esp_err_t (*hd_trans[2])(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t *trans, uint32_t timeout) = { + spi_slave_hd_queue_trans, spi_slave_hd_append_trans +}; +static esp_err_t (*hd_get_trans_res[2])(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout) = { + spi_slave_hd_get_trans_res, spi_slave_hd_get_append_trans_res +}; + +#define TEST_PSRAM_TRANS_LEN 1000 +TEST_CASE("test slave hd edma segment and append mode", "[spi]") +{ + uint8_t *mst_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_DEFAULT); + uint8_t *mst_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_DEFAULT); + uint8_t *slv_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM); + uint8_t *slv_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM); + spi_slave_hd_data_t *ret_trans, tx_data = { + .data = slv_tx, + .len = TEST_PSRAM_TRANS_LEN, + }, rx_data = { + .data = slv_rx, + .len = TEST_PSRAM_TRANS_LEN, + .flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, + }; + + for (int i = 0; i < 2; i++) { + printf("\ntest slave hd edma %s mode\n", i ? "append" : "segment"); + test_fill_random_to_buffers_dualboard(i + 1, mst_tx, slv_tx, TEST_PSRAM_TRANS_LEN); + + spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG(); + bus_cfg.max_transfer_sz = 4092 * 4; // append mode require at least 2 for tx and 2 for rx dma descs + bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; + spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); + slave_hd_cfg.flags |= i ? SPI_SLAVE_HD_APPEND_MODE : 0; + TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg)); + same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num); + vTaskDelay(1); + + TEST_ESP_OK(hd_trans[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &tx_data, portMAX_DELAY)); + TEST_ESP_OK(hd_trans[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &rx_data, portMAX_DELAY)); + + // tx append transaction + printf("tx %d bytes\n", TEST_PSRAM_TRANS_LEN); + essl_sspi_hd_dma_trans_seg(bus_cfg, slave_hd_cfg.spics_io_num, 0, false, mst_tx, TEST_PSRAM_TRANS_LEN, -1); + TEST_ESP_OK(hd_get_trans_res[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY)); + + // rx append transaction + printf("rx %d bytes\n", TEST_PSRAM_TRANS_LEN); + essl_sspi_hd_dma_trans_seg(bus_cfg, slave_hd_cfg.spics_io_num, 0, true, mst_rx, TEST_PSRAM_TRANS_LEN, -1); + TEST_ESP_OK(hd_get_trans_res[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY)); + + spitest_cmp_or_dump(slv_rx, mst_tx, TEST_PSRAM_TRANS_LEN); + spitest_cmp_or_dump(mst_rx, slv_tx, TEST_PSRAM_TRANS_LEN); + spi_slave_hd_deinit(TEST_SLAVE_HOST); + printf("test done\n"); + } + free(mst_tx); + free(mst_rx); + free(slv_tx); + free(slv_rx); +} +#endif //CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE diff --git a/components/esp_hal_gpspi/include/hal/spi_slave_hd_hal.h b/components/esp_hal_gpspi/include/hal/spi_slave_hd_hal.h index b718a8be6d..225e2f283a 100644 --- a/components/esp_hal_gpspi/include/hal/spi_slave_hd_hal.h +++ b/components/esp_hal_gpspi/include/hal/spi_slave_hd_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -138,6 +138,15 @@ void spi_slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_h */ bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev); +/** + * @brief Check and clear the interrupt by mask + * + * @param hal Context of the HAL layer + * @param mask Mask of the interrupt bits to check + * @return True if the masked interrupts are set, false otherwise + */ +bool spi_slave_hd_hal_check_clear_intr(spi_slave_hd_hal_context_t *hal, uint32_t mask); + /** * @brief Check and clear the interrupt of one event. * diff --git a/components/esp_hal_gpspi/spi_slave_hd_hal.c b/components/esp_hal_gpspi/spi_slave_hd_hal.c index 6da050fedc..f0cf35403c 100644 --- a/components/esp_hal_gpspi/spi_slave_hd_hal.c +++ b/components/esp_hal_gpspi/spi_slave_hd_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -154,16 +154,20 @@ static spi_ll_intr_t get_event_intr(spi_slave_hd_hal_context_t *hal, spi_event_t return intr; } -bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev) +bool spi_slave_hd_hal_check_clear_intr(spi_slave_hd_hal_context_t *hal, uint32_t mask) { - spi_ll_intr_t intr = get_event_intr(hal, ev); - if (spi_ll_get_intr(hal->dev, intr)) { - spi_ll_clear_intr(hal->dev, intr); + if (spi_ll_get_intr(hal->dev, mask)) { + spi_ll_clear_intr(hal->dev, mask); return true; } return false; } +bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev) +{ + return spi_slave_hd_hal_check_clear_intr(hal, get_event_intr(hal, ev)); +} + bool spi_slave_hd_hal_check_disable_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev) { //The trans_done interrupt is used for the workaround when some interrupt is not writable diff --git a/docs/en/api-reference/peripherals/spi_slave_hd.rst b/docs/en/api-reference/peripherals/spi_slave_hd.rst index e849c700fe..9453a3c952 100644 --- a/docs/en/api-reference/peripherals/spi_slave_hd.rst +++ b/docs/en/api-reference/peripherals/spi_slave_hd.rst @@ -63,6 +63,12 @@ Send/Receive Data by DMA Channels To send data to the master through the sending DMA channel, the application should properly wrap the data in an :cpp:type:`spi_slave_hd_data_t` descriptor structure before calling :cpp:func:`spi_slave_hd_queue_trans` with the data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. The pointers to descriptors are stored in the queue, and the data is sent to the master in the same order they are enqueued using :cpp:func:`spi_slave_hd_queue_trans`, upon receiving the master's ``Rd_DMA`` command. +.. only:: SOC_PSRAM_DMA_CAPABLE + + The driver supports using PSRAM for DMA transfer. Directly passing a PSRAM address as :cpp:member:`spi_slave_hd_data_t::data` is supported. For the DMA receive channel, its memory address and transfer length have alignment requirements, using :cpp:func:`heap_caps_malloc` to allocate memory can automatically handle the alignment requirements. For the buffers that you can not control, you can also use the :c:macro:`SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO` flag to enable driver to automatically align the buffer from PSRAM. + + Note that this feature shares the MSPI bus bandwidth (bus frequency * bus width), so the transmission bandwidth of the host to this device should be less than the PSRAM bandwidth, otherwise **data may be lost**, and then getting the transmission result will return the :c:macro:`ESP_ERR_INVALID_STATE` error. + The application should check the result of data sending by calling :cpp:func:`spi_slave_hd_get_trans_res` with the channel set as :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. This function blocks until the transaction with the command ``Rd_DMA`` from the master successfully completes (or timeout). The ``out_trans`` argument of the function outputs the pointer of the data descriptor which is just finished, providing information about the sending. Receiving data from the master through the receiving DMA channel is quite similar. The application calls :cpp:func:`spi_slave_hd_queue_trans` with proper data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_RX`. And the application calls the :cpp:func:`spi_slave_hd_get_trans_res` later to get the descriptor to the receiving buffer before it handles the data in the receiving buffer. diff --git a/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst b/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst index e1ce48cb33..bb7b79e18e 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst @@ -63,6 +63,12 @@ SPI 从机半双工模式 要通过 DMA 通道向主设备发送数据,应用程序需要先将数据正确地封装在 :cpp:type:`spi_slave_hd_data_t` 描述符结构体中,然后再将数据描述符和通道参数 :cpp:enumerator:`SPI_SLAVE_CHAN_TX` 传递给 :cpp:func:`spi_slave_hd_queue_trans`。数据描述符的指针存储在队列中,一旦接收到主设备的 Rd_DMA 命令,就会按照调用 :cpp:func:`spi_slave_hd_queue_trans` 时数据进入队列的顺序,依次将数据发送给主设备。 +.. only:: SOC_PSRAM_DMA_CAPABLE + + 驱动程序支持使用 PSRAM 进行传输。直接传入 PSRAM 地址作为 :cpp:member:`spi_slave_hd_data_t::data` 即可。对于 DMA 接收通道,其内存地址和传输长度有对齐要求,使用 :cpp:func:`heap_caps_malloc` 分配内存可以自动处理对齐要求。对于不能控制的内存,也可以使用 :c:macro:`SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO` 标志位,驱动会自动从 PSRAM 重新分配满足要求的内存。 + + 请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此主机对该设备的传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**,此时获取传输结果会返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。 + 应用程序需要检查数据发送的结果。为此,应用程序可以调用 :cpp:func:`spi_slave_hd_get_trans_res`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_TX`。该函数将阻塞程序,直到主设备发起的 Rd_DMA 命令事务成功完成或超时。函数中的参数 ``out_trans`` 将输出刚刚完成的数据描述符的指针,从而提供有关已完成的发送操作的信息。 通过 DMA 通道从主设备接收数据的操作与发送数据类似。应用程序需要使用正确的数据描述符调用 :cpp:func:`spi_slave_hd_queue_trans`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_RX`。随后,应用程序调用 :cpp:func:`spi_slave_hd_get_trans_res` 获取接收 buffer 的描述符,然后处理接收 buffer 中的数据。 From 21b37a672a2ee4b27410f7bb007e0b745bc6a8f7 Mon Sep 17 00:00:00 2001 From: wanckl Date: Wed, 21 Jan 2026 15:55:02 +0800 Subject: [PATCH 4/6] fix(driver_spi): fixes psram dma rx buffer memory barrier issue --- .../include/esp_private/spi_common_internal.h | 8 ++++++++ components/esp_driver_spi/src/gpspi/spi_common.c | 14 ++++++++++++++ components/esp_driver_spi/src/gpspi/spi_master.c | 2 ++ components/esp_driver_spi/src/gpspi/spi_slave.c | 3 ++- components/esp_driver_spi/src/gpspi/spi_slave_hd.c | 6 +++--- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/components/esp_driver_spi/include/esp_private/spi_common_internal.h b/components/esp_driver_spi/include/esp_private/spi_common_internal.h index 1a0df7fbd7..2b1c6e82af 100644 --- a/components/esp_driver_spi/include/esp_private/spi_common_internal.h +++ b/components/esp_driver_spi/include/esp_private/spi_common_internal.h @@ -154,6 +154,14 @@ void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, in */ esp_err_t spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer); +/** + * @brief Memory barrier for DMA RX buffer + * + * @param host_id SPI host ID + * @param rx_buffer RX buffer + */ +void spicommon_dma_rx_mb(spi_host_device_t host_id, void *rx_buffer); + /** * @brief Free DMA for SPI * diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index b4b83ead0c..3903a52314 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -30,6 +30,9 @@ #if CONFIG_IDF_TARGET_ESP32 #include "soc/dport_reg.h" #endif +#if CONFIG_SPIRAM +#include "esp_private/esp_psram_mspi.h" +#endif #if SOC_PERIPH_CLK_CTRL_SHARED #define SPI_COMMON_PERI_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC() @@ -438,6 +441,17 @@ esp_err_t SPI_COMMON_ISR_ATTR spicommon_dma_setup_priv_buffer(spi_host_device_t return ESP_OK; } +void SPI_COMMON_ISR_ATTR spicommon_dma_rx_mb(spi_host_device_t host_id, void *rx_buffer) +{ + (void)host_id; +#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM + if (esp_ptr_external_ram(rx_buffer)) { + // dma rx data to psram need memory barrier fix + esp_psram_mspi_mb(); + } +#endif +} + //----------------------------------------------------------free dma periph-------------------------------------------------------// esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id) { diff --git a/components/esp_driver_spi/src/gpspi/spi_master.c b/components/esp_driver_spi/src/gpspi/spi_master.c index 2e4794ad9a..99915cc009 100644 --- a/components/esp_driver_spi/src/gpspi/spi_master.c +++ b/components/esp_driver_spi/src/gpspi/spi_master.c @@ -1002,6 +1002,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) //This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same spicommon_dmaworkaround_idle(dma_ctx->tx_dma_chan.chan_id); #endif //#if CONFIG_IDF_TARGET_ESP32 + spicommon_dma_rx_mb(host->id, host->cur_trans_buf.buffer_to_rcv); spi_trans_dma_error_check(host); } @@ -1429,6 +1430,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle, return ESP_ERR_TIMEOUT; } } + spicommon_dma_rx_mb(host->id, host->cur_trans_buf.buffer_to_rcv); spi_trans_dma_error_check(host); uint32_t trans_flags = host->cur_trans_buf.trans->flags; // save the flags before bus_lock release diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index bcdab9516e..e3e2bbb7b6 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -654,6 +654,7 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg) // When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready. if (use_dma) { freeze_cs(host); + spicommon_dma_rx_mb(host->id, host->cur_trans.rx_buffer); spi_slave_trans_dma_error_check(host); } diff --git a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c index 0fc06fa117..e61b84fa91 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c @@ -8,6 +8,7 @@ #include "esp_compiler.h" #include "esp_log.h" #include "esp_check.h" +#include "esp_cache.h" #include "esp_memory_utils.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -19,9 +20,6 @@ #include "esp_private/esp_cache_private.h" #include "driver/spi_slave_hd.h" #include "hal/spi_slave_hd_hal.h" -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE -#include "esp_cache.h" -#endif #ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM #define SPI_SLAVE_ISR_ATTR IRAM_ATTR @@ -476,6 +474,7 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg) host->tx_curr_trans.trans = NULL; } if (rx_done) { + spicommon_dma_rx_mb(host->host_id, host->rx_curr_trans.aligned_buffer); spi_slave_hd_rx_dma_error_check(host, host->rx_curr_trans); bool ret_queue = true; host->rx_curr_trans.trans->trans_len = spi_slave_hd_hal_rxdma_seg_get_len(hal); @@ -617,6 +616,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg) ret_priv_trans.trans->trans_len = trans_len; bool ret_queue = true; + spicommon_dma_rx_mb(host->host_id, ret_priv_trans.aligned_buffer); spi_slave_hd_rx_dma_error_check(host, ret_priv_trans); if (callback->cb_recv) { spi_slave_hd_event_t ev = { From fbc049ed698db9f74f1f8b694ec28a747d017573 Mon Sep 17 00:00:00 2001 From: wanckl Date: Mon, 12 Jan 2026 11:47:38 +0800 Subject: [PATCH 5/6] fix(driver_spi): fixed spi still got input after re-install --- .../include/esp_private/spi_common_internal.h | 7 ++- .../esp_driver_spi/src/gpspi/spi_common.c | 50 +++++++++++++------ .../esp_driver_spi/src/gpspi/spi_slave.c | 2 +- .../esp_driver_spi/src/gpspi/spi_slave_hd.c | 2 +- .../test_apps/master/main/test_spi_bus_lock.c | 2 +- components/esp_hal_gpio/gpio_hal.c | 4 +- 6 files changed, 43 insertions(+), 24 deletions(-) diff --git a/components/esp_driver_spi/include/esp_private/spi_common_internal.h b/components/esp_driver_spi/include/esp_private/spi_common_internal.h index 2b1c6e82af..e5583f8f8c 100644 --- a/components/esp_driver_spi/include/esp_private/spi_common_internal.h +++ b/components/esp_driver_spi/include/esp_private/spi_common_internal.h @@ -52,7 +52,7 @@ typedef enum { /// Attributes of an SPI bus typedef struct { spi_bus_config_t bus_cfg; ///< Config used to initialize the bus - uint64_t gpio_reserve; ///< reserved output gpio bit mask + uint64_t gpio_reserve; ///< reserved gpio matrix output pins and all iomux pins bit mask uint32_t flags; ///< Flags (SPICOMMON_BUSFLAG_* flag combination of bus abilities) of the bus int max_transfer_sz; ///< Maximum length of bytes available to send bool dma_enabled; ///< To enable DMA or not @@ -211,14 +211,13 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf /** * @brief Free the IO used by a SPI peripheral * - * @param bus_cfg Bus config struct which defines which pins to be used. - * @param io_reserved Bitmap indicate which pin is reserved + * @param host SPI peripheral * * @return * - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_OK on success */ -esp_err_t spicommon_bus_free_io_cfg(const spi_bus_config_t *bus_cfg, uint64_t *io_reserved); +esp_err_t spicommon_bus_free_io_cfg(spi_host_device_t host); /** * @brief Initialize a Chip Select pin for a specific SPI peripheral diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index 3903a52314..b279a9c587 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,7 +12,6 @@ #include "esp_attr.h" #include "esp_check.h" #include "esp_cache.h" -#include "esp_rom_gpio.h" #include "esp_heap_caps.h" #include "esp_memory_utils.h" #include "driver/spi_master.h" @@ -607,14 +606,13 @@ static void s_spi_common_bus_via_gpio(gpio_num_t gpio_num, int in_sig, int out_s { assert(GPIO_IS_VALID_GPIO(gpio_num)); //coverity check if (in_sig != -1) { - gpio_input_enable(gpio_num); - esp_rom_gpio_connect_in_signal(gpio_num, in_sig, false); + gpio_matrix_input(gpio_num, in_sig, false); } if (out_sig != -1) { // For gpio_matrix, reserve output pins, see 'esp_gpio_reserve.h' *io_mask |= BIT64(gpio_num); s_spi_common_gpio_check_reserve(gpio_num); - esp_rom_gpio_connect_out_signal(gpio_num, out_sig, false, false); + gpio_matrix_output(gpio_num, out_sig, false, false); } gpio_func_sel(gpio_num, PIN_FUNC_GPIO); } @@ -682,9 +680,9 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf SPI_CHECK_PIN(bus_config->miso_io_num, "miso", !(flags & SPICOMMON_BUSFLAG_MASTER) || (temp_flag & SPICOMMON_BUSFLAG_DUAL)); } //set flags for DUAL mode according to output-capability of MOSI and MISO pins. - if ((bus_config->mosi_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num)) && - (bus_config->miso_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->miso_io_num)) && - (bus_config->miso_io_num != bus_config->mosi_io_num)) { + //DUAL mode requires both MOSI and MISO to able to input and output. + if (GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num) && GPIO_IS_VALID_OUTPUT_GPIO(bus_config->miso_io_num) && + bus_config->miso_io_num != bus_config->mosi_io_num) { temp_flag |= SPICOMMON_BUSFLAG_DUAL; } @@ -751,7 +749,7 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf } else { //Use GPIO matrix if (bus_config->mosi_io_num >= 0) { - int in_sig = (!(flags & SPICOMMON_BUSFLAG_MASTER) || (temp_flag & SPICOMMON_BUSFLAG_DUAL)) ? spi_periph_signal[host].spid_in : -1; + int in_sig = spi_periph_signal[host].spid_in; // always connect input in case sio mode device is used int out_sig = ((flags & SPICOMMON_BUSFLAG_MASTER) || (temp_flag & SPICOMMON_BUSFLAG_DUAL)) ? spi_periph_signal[host].spid_out : -1; s_spi_common_bus_via_gpio(bus_config->mosi_io_num, in_sig, out_sig, &gpio_reserv); } @@ -800,20 +798,41 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf return ESP_OK; } -esp_err_t spicommon_bus_free_io_cfg(const spi_bus_config_t *bus_cfg, uint64_t *io_reserved) +esp_err_t spicommon_bus_free_io_cfg(spi_host_device_t host) { + spi_bus_attr_t *bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host); + assert(bus_attr); + spi_bus_config_t *bus_cfg = &bus_attr->bus_cfg; + for (uint8_t i = 0; i < sizeof(bus_cfg->iocfg) / sizeof(bus_cfg->iocfg[0]); i++) { #if !SOC_SPI_SUPPORT_OCT if (i > 4) { break; } #endif - if (GPIO_IS_VALID_GPIO(bus_cfg->iocfg[i]) && (*io_reserved & BIT64(bus_cfg->iocfg[i]))) { - *io_reserved &= ~BIT64(bus_cfg->iocfg[i]); + if (GPIO_IS_VALID_GPIO(bus_cfg->iocfg[i]) && (bus_attr->gpio_reserve & BIT64(bus_cfg->iocfg[i]))) { + bus_attr->gpio_reserve &= ~BIT64(bus_cfg->iocfg[i]); + // all reserved pins (even iomux input pins) is harmless to be disabled here. gpio_output_disable(bus_cfg->iocfg[i]); esp_gpio_revoke(BIT64(bus_cfg->iocfg[i])); } } + + // disconnect all input signals anyway + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spics_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spiclk_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spid_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spiq_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spiwp_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spihd_in, false); +#if SOC_SPI_SUPPORT_OCT + if (host == SPI2_HOST) { // only gpspi2 supports octal pins (data4 ~ data7) + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spid4_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spid5_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spid6_in, false); + gpio_matrix_input(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host].spid7_in, false); + } +#endif return ESP_OK; } @@ -831,12 +850,11 @@ void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_id, i if (GPIO_IS_VALID_OUTPUT_GPIO(cs_io_num)) { out_mask |= BIT64(cs_io_num); s_spi_common_gpio_check_reserve(cs_io_num); - esp_rom_gpio_connect_out_signal(cs_io_num, spi_periph_signal[host].spics_out[cs_id], false, false); + gpio_matrix_output(cs_io_num, spi_periph_signal[host].spics_out[cs_id], false, false); } // cs_id 0 is always used by slave for input if (cs_id == 0) { - gpio_input_enable(cs_io_num); - esp_rom_gpio_connect_in_signal(cs_io_num, spi_periph_signal[host].spics_in, false); + gpio_matrix_input(cs_io_num, spi_periph_signal[host].spics_in, false); } gpio_func_sel(cs_io_num, PIN_FUNC_GPIO); } @@ -1026,7 +1044,7 @@ esp_err_t spi_bus_free(spi_host_device_t host_id) return err; } } - spicommon_bus_free_io_cfg(&bus_attr->bus_cfg, &bus_attr->gpio_reserve); + spicommon_bus_free_io_cfg(host_id); #if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP const periph_retention_module_t retention_id = spi_reg_retention_info[host_id - 1].module_id; diff --git a/components/esp_driver_spi/src/gpspi/spi_slave.c b/components/esp_driver_spi/src/gpspi/spi_slave.c index e3e2bbb7b6..c8387289b2 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave.c @@ -324,7 +324,7 @@ esp_err_t spi_slave_free(spi_host_device_t host) if (spihost[host]->bus_attr->dma_enabled) { spicommon_dma_chan_free(host); } - spicommon_bus_free_io_cfg(&spihost[host]->bus_attr->bus_cfg, &spihost[host]->bus_attr->gpio_reserve); + spicommon_bus_free_io_cfg(host); if (spihost[host]->cfg.spics_io_num >= 0) { spicommon_cs_free_io(spihost[host]->cfg.spics_io_num, &spihost[host]->bus_attr->gpio_reserve); } diff --git a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c index e61b84fa91..22ebfe7cd5 100644 --- a/components/esp_driver_spi/src/gpspi/spi_slave_hd.c +++ b/components/esp_driver_spi/src/gpspi/spi_slave_hd.c @@ -337,7 +337,7 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id) } #endif - spicommon_bus_free_io_cfg(&host->bus_attr->bus_cfg, &host->bus_attr->gpio_reserve); + spicommon_bus_free_io_cfg(host_id); spicommon_cs_free_io(host->cs_io_num, &host->bus_attr->gpio_reserve); free(host->hal.dmadesc_tx); free(host->hal.dmadesc_rx); diff --git a/components/esp_driver_spi/test_apps/master/main/test_spi_bus_lock.c b/components/esp_driver_spi/test_apps/master/main/test_spi_bus_lock.c index 7038eee3fd..0b52beda80 100644 --- a/components/esp_driver_spi/test_apps/master/main/test_spi_bus_lock.c +++ b/components/esp_driver_spi/test_apps/master/main/test_spi_bus_lock.c @@ -283,7 +283,7 @@ static void test_bus_lock(bool test_flash) #if CONFIG_IDF_TARGET_ESP32 // no need this case in other target, only esp32 need buslock to split MSPI and GPSPI2 action -TEST_CASE("spi bus lock, with flash", "[spi][test_env=external_flash]") +TEST_CASE("spi bus lock, with flash", "[external_flash][test_env=external_flash]") { test_bus_lock(true); } diff --git a/components/esp_hal_gpio/gpio_hal.c b/components/esp_hal_gpio/gpio_hal.c index 43eacf3037..deeeefbd4f 100644 --- a/components/esp_hal_gpio/gpio_hal.c +++ b/components/esp_hal_gpio/gpio_hal.c @@ -47,7 +47,9 @@ void gpio_hal_iomux_out(gpio_hal_context_t *hal, uint32_t gpio_num, int func) void gpio_hal_matrix_in(gpio_hal_context_t *hal, uint32_t gpio_num, uint32_t signal_idx, bool in_inv) { - gpio_ll_input_enable(hal->dev, gpio_num); + if (gpio_num < GPIO_NUM_MAX) { // skip const_0/1 io num from enabling input + gpio_ll_input_enable(hal->dev, gpio_num); + } #if HAL_CONFIG(GPIO_USE_ROM_API) esp_rom_gpio_connect_in_signal(gpio_num, signal_idx, in_inv); #else From 19131099ea43fd55d57eda266b742aa40d591730 Mon Sep 17 00:00:00 2001 From: wanckl Date: Sat, 28 Feb 2026 19:09:39 +0800 Subject: [PATCH 6/6] 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 b279a9c587..2c5d912163 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -419,9 +419,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 c65747ed7d..3a04bed4bc 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 @@ -880,6 +880,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 37b45a9264..1697ffcb77 100644 --- a/docs/en/api-reference/peripherals/spi_master.rst +++ b/docs/en/api-reference/peripherals/spi_master.rst @@ -832,6 +832,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 74c29fc196..73e4cc7493 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_master.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_master.rst @@ -832,6 +832,10 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_interface_con 4. 全双工传输事务模式中,命令阶段和地址阶段与 ``cs_ena_pretrans`` 不兼容。 + .. only:: esp32 + + 5. 若启用了 DMA,则 RX 缓冲区应该以字对齐(从 32 位边界开始,字节长度为 4 的倍数)。否则 DMA 可能覆盖未对齐部分的数据。 + 应用示例 -------------------