Merge branch 'feat/spi_slave_edma_psram_support' into 'master'

feat(driver_spi): spi slave edma psram support

Closes IDFCI-7326 and IDF-15125

See merge request espressif/esp-idf!44317
This commit is contained in:
Wan Lei
2026-02-09 15:22:46 +08:00
30 changed files with 645 additions and 467 deletions
@@ -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 //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); 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, // Connect master and slave to the same pin
//then the cs_num of the 1st and 2nd devices are 0 and 1 respectively. // master_id and slave_id are the IDs of the master and slave devices, set 0 for each to use soft master/slave.
//Enable `soft_master` to connect to soft spi master instead of hardware 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);
void same_pin_func_sel(spi_bus_config_t bus, uint8_t cs_pin, uint8_t cs_dev_id, bool soft_master);
// Soft simulated spi master host for slave testing // Soft simulated spi master host for slave testing
// `speed_hz` max 500kHz // `speed_hz` max 500kHz
@@ -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); 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_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, spi_periph_signal[TEST_SLAVE_HOST].spid_in); 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, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spiq_in);
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_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 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, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spics_out[cs_dev_id]); 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, spi_periph_signal[TEST_SLAVE_HOST].spics_in); 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_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, spi_periph_signal[TEST_SLAVE_HOST].spiclk_in); 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) #define GPIO_MAX_FREQ 500*1000 //max of soft spi clock at delay(0)
+1 -1
View File
@@ -23,8 +23,8 @@ typedef struct {
gdma_channel_handle_t gdma_chan; gdma_channel_handle_t gdma_chan;
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2
//On ESP32S2, there is no gdma, so use SPI DMA to transmit data //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; spi_dev_t *adc_spi_dev;
uint32_t dma_chan_id;
#elif CONFIG_IDF_TARGET_ESP32 #elif CONFIG_IDF_TARGET_ESP32
//On ESP32, there is no gdma, so use I2S DMA to transmit data //On ESP32, there is no gdma, so use I2S DMA to transmit data
i2s_dev_t *adc_i2s_dev; i2s_dev_t *adc_i2s_dev;
+7 -6
View File
@@ -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); bool conversion_finish = spi_ll_get_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
if (conversion_finish) { if (conversion_finish) {
spi_ll_clear_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK); 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; ctx->rx_eof_desc_addr = desc_addr;
need_yield = ctx->adc_intr_func(ctx); 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) { if (spi_success != true) {
return ESP_FAIL; 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) { if (ret != ESP_OK) {
return ret; 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); adc_dma->adc_spi_dev = SPI_LL_GET_HW(ADC_DMA_SPI_HOST);
return ESP_OK; 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_err_t adc_dma_deinit(adc_dma_t adc_dma)
{ {
esp_intr_free(adc_dma.dma_intr_hdl); 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); spicommon_periph_free(ADC_DMA_SPI_HOST);
return ESP_OK; 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_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_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; 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_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_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; return ESP_OK;
} }
esp_err_t adc_dma_reset(adc_dma_t adc_dma) 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; return ESP_OK;
} }
+3 -5
View File
@@ -40,7 +40,6 @@
typedef struct { typedef struct {
void *periph_dev; /* DMA peripheral device address */ void *periph_dev; /* DMA peripheral device address */
uint32_t dma_chan; uint32_t dma_chan;
spi_dma_ctx_t *spi_dma_ctx; /* spi_dma context */
intr_handle_t intr_handle; /* Interrupt handle */ intr_handle_t intr_handle; /* Interrupt handle */
bool use_apll; /* Whether use APLL as digital controller clock source */ bool use_apll; /* Whether use APLL as digital controller clock source */
} dac_dma_periph_spi_t; } 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 */ /* 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); 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(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"); err, TAG, "Failed to allocate dma peripheral channel");
s_ddp->dma_chan = spi_bus_get_dma_ctx(DAC_DMA_PERIPH_SPI_HOST)->rx_dma_chan.chan_id;
s_ddp->dma_chan = s_ddp->spi_dma_ctx->rx_dma_chan.chan_id;
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF); 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); dac_ll_digi_set_convert_mode(is_alternate);
return ret; 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 != 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"); 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) { 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"); 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); spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
@@ -185,6 +185,7 @@ esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transact
* @return * @return
* - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_ERR_NOT_SUPPORTED if flag `SPI_SLAVE_NO_RETURN_RESULT` is set * - 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_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); 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. * out.
* @return * @return
* - ESP_ERR_INVALID_ARG if parameter is invalid * - 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_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); esp_err_t spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait);
@@ -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_OK: on success
* - ESP_ERR_INVALID_ARG: Function is not valid * - ESP_ERR_INVALID_ARG: Function is not valid
* - ESP_ERR_TIMEOUT: There's no transaction done before timeout * - 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); 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_OK: on success
* - ESP_ERR_INVALID_ARG: Function is not valid * - ESP_ERR_INVALID_ARG: Function is not valid
* - ESP_ERR_TIMEOUT: There's no transaction done before timeout * - 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); 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);
@@ -104,19 +104,18 @@ esp_err_t spicommon_bus_free(spi_host_device_t host_id);
* *
* @param host_id SPI host ID * @param host_id SPI host ID
* @param dma_chan DMA channel to be used * @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 * @return
* - ESP_OK: On success * - ESP_OK: On success
* - ESP_ERR_NO_MEM: No enough memory * - ESP_ERR_NO_MEM: No enough memory
* - ESP_ERR_NOT_FOUND: There is no available DMA channel * - 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 * @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[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. * @param[out] actual_max_sz Actual max transfer size one transaction can be, in bytes.
* *
@@ -124,7 +123,7 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma
* - ESP_OK: On success * - ESP_OK: On success
* - ESP_ERR_NO_MEM: No enough memory * - 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 * Setupt/Configure dma descriptor link list
@@ -136,15 +135,40 @@ esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *
*/ */
void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx); 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 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 * @brief Free DMA for SPI
* *
* @param dma_ctx spi_dma_ctx_t struct pointer * @param host_id SPI host ID
* *
* @return * @return
* - ESP_OK: On success * - 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 * @brief Connect a SPI peripheral to GPIO pins
@@ -180,7 +204,7 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx);
* - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success * - 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 * @brief Free the IO used by a SPI peripheral
@@ -292,7 +316,7 @@ void spicommon_dmaworkaround_transfer_active(int dmachan);
* @param host_id The specified host to get attribute * @param host_id The specified host to get attribute
* @return (Const) Pointer to the attributes * @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. * @brief Get the dma context of a specified SPI bus.
@@ -300,7 +324,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 * @param host_id The specified host to get attribute
* @return (Const) Pointer to the dma context * @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. * @brief Register a function to a initialized bus to make it called when deinitializing the bus.
+144 -86
View File
@@ -13,6 +13,7 @@
#include "esp_check.h" #include "esp_check.h"
#include "esp_cache.h" #include "esp_cache.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_memory_utils.h"
#include "driver/spi_master.h" #include "driver/spi_master.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "esp_private/gpio.h" #include "esp_private/gpio.h"
@@ -28,6 +29,9 @@
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
#endif #endif
#if CONFIG_SPIRAM
#include "esp_private/esp_psram_mspi.h"
#endif
#if CONFIG_SPI_MASTER_ISR_IN_IRAM || CONFIG_SPI_SLAVE_ISR_IN_IRAM #if CONFIG_SPI_MASTER_ISR_IN_IRAM || CONFIG_SPI_SLAVE_ISR_IN_IRAM
#define SPI_COMMON_ISR_ATTR IRAM_ATTR #define SPI_COMMON_ISR_ATTR IRAM_ATTR
@@ -62,11 +66,11 @@ typedef struct {
spi_destroy_func_t destroy_func; spi_destroy_func_t destroy_func;
void* destroy_arg; void* destroy_arg;
spi_bus_attr_t bus_attr; spi_bus_attr_t bus_attr;
spi_dma_ctx_t *dma_ctx;
} spicommon_bus_context_t; } spicommon_bus_context_t;
static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT(); 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 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 #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 /* The lock for the share SPI1 bus is registered here in a constructor due to need to access the context
@@ -90,6 +94,10 @@ esp_err_t spicommon_bus_alloc(spi_host_device_t host_id, const char *name)
PERIPH_RCC_ATOMIC() { PERIPH_RCC_ATOMIC() {
spi_ll_enable_clock(host_id, true); 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; ctx->host_id = host_id;
bus_ctx[host_id] = ctx; bus_ctx[host_id] = ctx;
return ESP_OK; return ESP_OK;
@@ -107,20 +115,6 @@ esp_err_t spicommon_bus_free(spi_host_device_t host_id)
return ESP_OK; 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) static inline bool is_valid_host(spi_host_device_t host)
{ {
#if (SOC_SPI_PERIPH_NUM == 2) #if (SOC_SPI_PERIPH_NUM == 2)
@@ -142,44 +136,52 @@ int spicommon_irqdma_source_for_host(spi_host_device_t host)
//----------------------------------------------------------alloc dma periph-------------------------------------------------------// //----------------------------------------------------------alloc dma periph-------------------------------------------------------//
#if !SOC_GDMA_SUPPORTED #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 void _spicommon_dma_rcc_clock_ctrl(spi_dma_chan_t dma_chan, bool enable)
static inline shared_periph_module_t get_dma_periph(int dma_chan)
{ {
assert(dma_chan >= 1 && dma_chan <= SPI_LL_DMA_CHANNEL_NUM); #if SPI_LL_DMA_SHARED
shared_periph_module_t dma_periph;
if (dma_chan == 1) { if (dma_chan == 1) {
return PERIPH_SPI2_DMA_MODULE; dma_periph = PERIPH_SPI2_DMA_MODULE;
} else if (dma_chan == 2) { } else if (dma_chan == 2) {
return PERIPH_SPI3_DMA_MODULE; dma_periph = PERIPH_SPI3_DMA_MODULE;
} else { } else {
abort(); 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
PERIPH_RCC_ATOMIC() {
spi_dma_ll_enable_bus_clock(dma_chan, enable);
spi_dma_ll_reset_register(dma_chan);
}
#endif #endif
}
static bool claim_dma_chan(int dma_chan, uint32_t *out_actual_dma_chan) static bool claim_dma_chan(int dma_chan, uint32_t *out_actual_dma_chan)
{ {
bool ret = false; bool ret = false;
portENTER_CRITICAL(&spi_dma_spinlock); portENTER_CRITICAL(&spi_dma_spinlock);
bool is_used = (BIT(dma_chan) & spi_dma_chan_enabled); if (!(BIT(dma_chan) & spi_dma_chan_enabled)) {
if (!is_used) {
spi_dma_chan_enabled |= BIT(dma_chan); spi_dma_chan_enabled |= BIT(dma_chan);
#if SPI_LL_DMA_SHARED _spicommon_dma_rcc_clock_ctrl(dma_chan, true);
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
PERIPH_RCC_ATOMIC() {
//esp32: have only one spi_dma
spi_dma_ll_enable_bus_clock(dma_chan, true);
spi_dma_ll_reset_register(dma_chan);
}
#endif
*out_actual_dma_chan = dma_chan; *out_actual_dma_chan = dma_chan;
ret = true; ret = true;
} }
@@ -249,6 +251,12 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
} }
#else //SOC_GDMA_SUPPORTED #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) 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)
{ {
@@ -291,7 +299,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 #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)); assert(is_valid_host(host_id));
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
@@ -311,7 +319,7 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma
if (ret != ESP_OK) { if (ret != ESP_OK) {
goto cleanup; goto cleanup;
} }
*out_dma_ctx = dma_ctx; spi_dma_ctx[host_id] = dma_ctx;
return ret; return ret;
cleanup: cleanup:
@@ -319,13 +327,17 @@ cleanup:
return ret; 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; 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) { if (dma_desc_ct == 0) {
dma_desc_ct = 1; //default to 4k when max is not given 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_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); 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) { if (dma_ctx->dmadesc_tx == NULL || dma_ctx->dmadesc_rx == NULL) {
@@ -372,10 +384,71 @@ void SPI_COMMON_ISR_ATTR spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc,
dmadesc[n - 1].next = NULL; dmadesc[n - 1].next = NULL;
} }
//----------------------------------------------------------free dma periph-------------------------------------------------------// 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)
esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx)
{ {
assert(dma_ctx); 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;
}
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)
{
spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id);
#if !SOC_GDMA_SUPPORTED #if !SOC_GDMA_SUPPORTED
//On ESP32S2, each SPI controller has its own DMA channel //On ESP32S2, each SPI controller has its own DMA channel
@@ -384,17 +457,7 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx)
portENTER_CRITICAL(&spi_dma_spinlock); portENTER_CRITICAL(&spi_dma_spinlock);
spi_dma_chan_enabled &= ~BIT(dma_chan); spi_dma_chan_enabled &= ~BIT(dma_chan);
#if SPI_LL_DMA_SHARED _spicommon_dma_rcc_clock_ctrl(dma_chan, false);
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
PERIPH_RCC_ATOMIC() {
spi_dma_ll_enable_bus_clock(dma_ctx->tx_dma_chan.host_id, false);
}
#endif
portEXIT_CRITICAL(&spi_dma_spinlock); portEXIT_CRITICAL(&spi_dma_spinlock);
#else //SOC_GDMA_SUPPORTED #else //SOC_GDMA_SUPPORTED
@@ -408,7 +471,14 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx)
} }
#endif #endif
if (dma_ctx->dmadesc_tx) {
free(dma_ctx->dmadesc_tx);
}
if (dma_ctx->dmadesc_rx) {
free(dma_ctx->dmadesc_rx);
}
free(dma_ctx); free(dma_ctx);
spi_dma_ctx[host_id] = NULL;
return ESP_OK; return ESP_OK;
} }
@@ -546,7 +616,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, 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. 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 #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 // 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
@@ -837,18 +907,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->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
if (bus_attr->dma_enabled) { 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) { if (err != ESP_OK) {
goto cleanup; 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) { if (err != ESP_OK) {
goto cleanup; 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 = { spi_bus_lock_config_t lock_config = {
@@ -902,7 +968,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
} }
#endif //CONFIG_PM_ENABLE #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) { if (err != ESP_OK) {
goto cleanup; goto cleanup;
} }
@@ -917,12 +983,10 @@ cleanup:
if (bus_attr->lock) { if (bus_attr->lock) {
spi_bus_deinit_lock(bus_attr->lock); spi_bus_deinit_lock(bus_attr->lock);
} }
if (ctx->dma_ctx) { }
free(ctx->dma_ctx->dmadesc_tx); if (bus_attr->dma_enabled) {
free(ctx->dma_ctx->dmadesc_rx); // free dma channel and descriptors
spicommon_dma_chan_free(ctx->dma_ctx); spicommon_dma_chan_free(host_id);
ctx->dma_ctx = NULL;
}
} }
spicommon_bus_free(host_id); spicommon_bus_free(host_id);
return err; return err;
@@ -931,19 +995,20 @@ cleanup:
void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps) 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_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; size_t alignment = 1; // return 1 anyway if dma not used but user use it.
// detailed alignment requirement is not available for slave bus, so use 16 bytes as default if (dma_ctx) {
if (bus_ctx[host_id]->bus_attr.flags & SPICOMMON_BUSFLAG_MASTER) {
// As don't know the buffer will used for TX or RX, so use the max alignment requirement // 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) ? \ 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(dma_ctx->dma_align_tx_ext, 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_int, dma_ctx->dma_align_rx_int);
} }
return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA); 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_COMMON_ISR_ATTR spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
{ {
if (bus_ctx[host_id] == NULL) { if (bus_ctx[host_id] == NULL) {
return NULL; return NULL;
@@ -952,13 +1017,9 @@ const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
return &bus_ctx[host_id]->bus_attr; return &bus_ctx[host_id]->bus_attr;
} }
const 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)
{ {
if (bus_ctx[host_id] == NULL) { return spi_dma_ctx[host_id];
return NULL;
}
return bus_ctx[host_id]->dma_ctx;
} }
esp_err_t spi_bus_free(spi_host_device_t host_id) esp_err_t spi_bus_free(spi_host_device_t host_id)
@@ -998,11 +1059,8 @@ esp_err_t spi_bus_free(spi_host_device_t host_id)
#endif #endif
spi_bus_deinit_lock(bus_attr->lock); spi_bus_deinit_lock(bus_attr->lock);
if (ctx->dma_ctx) { if (bus_attr->dma_enabled) {
free(ctx->dma_ctx->dmadesc_tx); spicommon_dma_chan_free(host_id);
free(ctx->dma_ctx->dmadesc_rx);
spicommon_dma_chan_free(ctx->dma_ctx);
ctx->dma_ctx = NULL;
} }
spicommon_bus_free(host_id); spicommon_bus_free(host_id);
return err; return err;
@@ -124,10 +124,10 @@ We have two bits to control the interrupt:
#include "esp_ipc.h" #include "esp_ipc.h"
#include "esp_cache.h" #include "esp_cache.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_memory_utils.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/queue.h" #include "freertos/queue.h"
#include "soc/soc_memory_layout.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "hal/spi_hal.h" #include "hal/spi_hal.h"
#include "hal/spi_ll.h" #include "hal/spi_ll.h"
@@ -992,6 +992,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 //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); spicommon_dmaworkaround_idle(dma_ctx->tx_dma_chan.chan_id);
#endif //#if CONFIG_IDF_TARGET_ESP32 #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); spi_trans_dma_error_check(host);
} }
@@ -1170,47 +1171,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) 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; spi_transaction_t *trans_desc = priv_desc->trans;
@@ -1220,25 +1180,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; 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 // 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; uint32_t *send_ptr = (trans_desc->flags & SPI_TRANS_USE_TXDATA) ? (uint32_t *)trans_desc->tx_data : (uint32_t *)trans_desc->tx_buffer;
if (!bus_attr->dma_enabled) {
esp_err_t ret = ESP_OK; priv_desc->buffer_to_send = send_ptr;
if (send_ptr && bus_attr->dma_enabled) { priv_desc->buffer_to_rcv = rcv_ptr;
ret = setup_dma_priv_buffer(host, send_ptr, (trans_desc->length + 7) / 8, true, trans_desc->flags, &send_ptr); return ESP_OK;
if (ret != ESP_OK) {
goto clean_up;
}
} }
if (rcv_ptr && bus_attr->dma_enabled) { bool use_psram = trans_desc->flags & SPI_TRANS_DMA_USE_PSRAM;
ret = setup_dma_priv_buffer(host, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, trans_desc->flags, &rcv_ptr); bool auto_malloc = !(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL);
if (ret != ESP_OK) { 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);
goto clean_up; if (ret != ESP_OK) {
} goto clean_up;
} }
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);
priv_desc->buffer_to_send = send_ptr; if (ret != ESP_OK) {
priv_desc->buffer_to_rcv = rcv_ptr; goto clean_up;
return ESP_OK; }
return ret;
clean_up: clean_up:
uninstall_priv_desc(priv_desc); uninstall_priv_desc(priv_desc);
@@ -1467,6 +1425,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle,
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
} }
} }
spicommon_dma_rx_mb(host->id, host->cur_trans_buf.buffer_to_rcv);
spi_trans_dma_error_check(host); spi_trans_dma_error_check(host);
uint32_t trans_flags = host->cur_trans_buf.trans->flags; // save the flags before bus_lock release uint32_t trans_flags = host->cur_trans_buf.trans->flags; // save the flags before bus_lock release
+63 -121
View File
@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -54,12 +54,13 @@ static const char *SPI_TAG = "spi_slave";
/// struct to hold private transaction data (like tx and rx buffer for DMA). /// struct to hold private transaction data (like tx and rx buffer for DMA).
typedef struct { typedef struct {
spi_slave_transaction_t *trans; //original trans spi_slave_transaction_t *trans; //original trans
void *tx_buffer; //actually tx buffer (re-malloced if needed) uint32_t *tx_buffer; //actually tx buffer (re-malloced if needed)
void *rx_buffer; //actually rx 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; } spi_slave_trans_priv_t;
typedef struct { typedef struct {
int id; spi_host_device_t id;
_Atomic spi_bus_fsm_t fsm; _Atomic spi_bus_fsm_t fsm;
spi_bus_attr_t* bus_attr; spi_bus_attr_t* bus_attr;
spi_dma_ctx_t *dma_ctx; spi_dma_ctx_t *dma_ctx;
@@ -69,13 +70,10 @@ typedef struct {
spi_slave_trans_priv_t cur_trans; spi_slave_trans_priv_t cur_trans;
uint32_t flags; uint32_t flags;
uint32_t intr_flags; uint32_t intr_flags;
int max_transfer_sz;
QueueHandle_t trans_queue; QueueHandle_t trans_queue;
QueueHandle_t ret_queue; QueueHandle_t ret_queue;
bool dma_enabled;
bool cs_iomux; bool cs_iomux;
uint8_t cs_in_signal; uint8_t cs_in_signal;
uint16_t internal_mem_align_size;
#ifdef CONFIG_PM_ENABLE #ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; esp_pm_lock_handle_t pm_lock;
#endif #endif
@@ -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); atomic_store(&spihost[host]->fsm, SPI_BUS_FSM_ENABLED);
spi_slave_hal_context_t *hal = &spihost[host]->hal; spi_slave_hal_context_t *hal = &spihost[host]->hal;
spihost[host]->dma_enabled = (dma_chan != SPI_DMA_DISABLED); spihost[host]->bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
if (spihost[host]->dma_enabled) { spihost[host]->bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
ret = spicommon_dma_chan_alloc(host, dma_chan, &spihost[host]->dma_ctx); if (spihost[host]->bus_attr->dma_enabled) {
ret = spicommon_dma_chan_alloc(host, dma_chan);
if (ret != ESP_OK) { if (ret != ESP_OK) {
goto cleanup; 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) { if (ret != ESP_OK) {
goto cleanup; goto cleanup;
} }
@@ -199,20 +199,9 @@ 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_tx = spihost[host]->dma_ctx->dmadesc_tx;
hal->dmadesc_rx = spihost[host]->dma_ctx->dmadesc_rx; hal->dmadesc_rx = spihost[host]->dma_ctx->dmadesc_rx;
hal->dmadesc_n = spihost[host]->dma_ctx->dma_desc_num; 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
} 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) { if (err != ESP_OK) {
ret = err; ret = err;
goto cleanup; goto cleanup;
@@ -226,7 +215,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 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. // 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]); freeze_cs(spihost[host]);
} }
@@ -313,7 +302,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->rx_lsbfirst = (slave_config->flags & SPI_SLAVE_RXBIT_LSBFIRST) ? 1 : 0;
hal->tx_lsbfirst = (slave_config->flags & SPI_SLAVE_TXBIT_LSBFIRST) ? 1 : 0; hal->tx_lsbfirst = (slave_config->flags & SPI_SLAVE_TXBIT_LSBFIRST) ? 1 : 0;
hal->mode = slave_config->mode; 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); spi_slave_hal_setup_device(hal);
return ESP_OK; return ESP_OK;
@@ -332,10 +321,8 @@ esp_err_t spi_slave_free(spi_host_device_t host)
if (spihost[host]->ret_queue) { if (spihost[host]->ret_queue) {
vQueueDelete(spihost[host]->ret_queue); vQueueDelete(spihost[host]->ret_queue);
} }
if (spihost[host]->dma_enabled) { if (spihost[host]->bus_attr->dma_enabled) {
free(spihost[host]->dma_ctx->dmadesc_tx); spicommon_dma_chan_free(host);
free(spihost[host]->dma_ctx->dmadesc_rx);
spicommon_dma_chan_free(spihost[host]->dma_ctx);
} }
spicommon_bus_free_io_cfg(host); spicommon_bus_free_io_cfg(host);
if (spihost[host]->cfg.spics_io_num >= 0) { if (spihost[host]->cfg.spics_io_num >= 0) {
@@ -409,12 +396,11 @@ 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; __attribute__((unused)) spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
#if CONFIG_IDF_TARGET_ESP32 #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"); ESP_EARLY_LOGW(SPI_TAG, "Use DMA but real trans_len is not 4 bytes aligned, slave may loss data");
} }
#endif #endif
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE if (spihost[host]->bus_attr->dma_enabled) {
if (spihost[host]->dma_enabled) {
if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) { if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) {
free(priv_trans->tx_buffer); free(priv_trans->tx_buffer);
} }
@@ -423,54 +409,28 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t
free(priv_trans->rx_buffer); 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; spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
if (!spihost[host]->bus_attr->dma_enabled) {
priv_trans->tx_buffer = (void *)trans->tx_buffer; priv_trans->tx_buffer = (uint32_t *)trans->tx_buffer;
priv_trans->rx_buffer = trans->rx_buffer; priv_trans->rx_buffer = (uint32_t *)trans->rx_buffer;
return ESP_OK;
#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]->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]->dma_enabled && trans->rx_buffer) {
if ((!esp_ptr_dma_capable(trans->rx_buffer) || ((((uint32_t)trans->rx_buffer) | (trans->length + 7) / 8) & (alignment - 1)))) { bool auto_malloc = (trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO);
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); 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 rxbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one if (ret != ESP_OK) {
ESP_EARLY_LOGD(SPI_TAG, "Allocate RX buffer for DMA"); spi_slave_uninstall_priv_trans(host, priv_trans);
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment" return ret;
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");
} }
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE 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);
return ESP_OK; 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) 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)
@@ -478,20 +438,7 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi
BaseType_t r; BaseType_t r;
SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG); 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], "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(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
"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]->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_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc}; 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); SPI_CHECK(ESP_OK == spi_slave_setup_priv_trans(host, &priv_trans), "slave setup priv_trans failed", ESP_ERR_NO_MEM);
@@ -543,30 +490,18 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c
BaseType_t do_yield = pdFALSE; 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(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(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"); 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]->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 = { spi_slave_trans_priv_t priv_trans = {
.trans = (spi_slave_transaction_t *)trans_desc, .trans = (spi_slave_transaction_t *)trans_desc,
.tx_buffer = (void *)trans_desc->tx_buffer, .tx_buffer = (void *)trans_desc->tx_buffer,
.rx_buffer = trans_desc->rx_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); r = xQueueSendFromISR(spihost[host]->trans_queue, (void *)&priv_trans, &do_yield);
if (!r) { if (!r) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
@@ -617,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); spi_slave_uninstall_priv_trans(host, &priv_trans);
*trans_desc = priv_trans.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) 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)
@@ -665,7 +600,7 @@ static void SPI_SLAVE_ISR_ATTR s_spi_slave_prepare_data(spi_slave_t *host)
{ {
spi_slave_hal_context_t *hal = &host->hal; 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); s_spi_slave_dma_prepare_data(host->dma_ctx, &host->hal);
} else { } else {
//No DMA. Copy data to transmit buffers. //No DMA. Copy data to transmit buffers.
@@ -688,6 +623,20 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_restart_after_dmareset(void *arg)
} }
#endif //#if CONFIG_IDF_TARGET_ESP32 #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 //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 //touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are
//no muxes in this code. //no muxes in this code.
@@ -700,11 +649,13 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
assert(spi_slave_hal_usr_is_done(hal)); 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) { if (host->cur_trans.trans) {
// When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready. // When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready.
if (use_dma) { if (use_dma) {
freeze_cs(host); freeze_cs(host);
spicommon_dma_rx_mb(host->id, host->cur_trans.rx_buffer);
spi_slave_trans_dma_error_check(host);
} }
spi_slave_hal_store_result(hal); spi_slave_hal_store_result(hal);
@@ -718,17 +669,6 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
} }
#endif //#if CONFIG_IDF_TARGET_ESP32 #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) { if (host->cfg.post_trans_cb) {
host->cfg.post_trans_cb(host->cur_trans.trans); host->cfg.post_trans_cb(host->cur_trans.trans);
} }
@@ -789,7 +729,9 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
if (use_dma) { if (use_dma) {
restore_cs(host); 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 //Kick off transfer
spi_slave_hal_user_start(hal); spi_slave_hal_user_start(hal);
if (host->cfg.post_setup_cb) { if (host->cfg.post_setup_cb) {
@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -8,6 +8,7 @@
#include "esp_compiler.h" #include "esp_compiler.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h" #include "esp_check.h"
#include "esp_cache.h"
#include "esp_memory_utils.h" #include "esp_memory_utils.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
@@ -19,9 +20,6 @@
#include "esp_private/esp_cache_private.h" #include "esp_private/esp_cache_private.h"
#include "driver/spi_slave_hd.h" #include "driver/spi_slave_hd.h"
#include "hal/spi_slave_hd_hal.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 #ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM
#define SPI_SLAVE_ISR_ATTR IRAM_ATTR #define SPI_SLAVE_ISR_ATTR IRAM_ATTR
@@ -40,6 +38,7 @@
typedef struct { typedef struct {
spi_slave_hd_data_t *trans; //original trans spi_slave_hd_data_t *trans; //original trans
void *aligned_buffer; //actually trans buffer (re-malloced if needed) 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; } spi_slave_hd_trans_priv_t;
typedef struct { typedef struct {
@@ -48,9 +47,6 @@ typedef struct {
spi_bus_attr_t* bus_attr; spi_bus_attr_t* bus_attr;
_Atomic spi_bus_fsm_t fsm; _Atomic spi_bus_fsm_t fsm;
spi_dma_ctx_t *dma_ctx; spi_dma_ctx_t *dma_ctx;
uint16_t internal_mem_align_size;
int max_transfer_sz;
uint32_t flags;
portMUX_TYPE int_spinlock; portMUX_TYPE int_spinlock;
intr_handle_t intr; intr_handle_t intr;
intr_handle_t intr_dma; intr_handle_t intr_dma;
@@ -131,10 +127,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->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id);
host->cs_io_num = config->spics_io_num; 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) { if (ret != ESP_OK) {
goto cleanup; goto cleanup;
} }
host->dma_ctx = spi_bus_get_dma_ctx(host_id);
#if SOC_GDMA_SUPPORTED #if SOC_GDMA_SUPPORTED
gdma_strategy_config_t dma_strategy = { gdma_strategy_config_t dma_strategy = {
.auto_update_desc = true, .auto_update_desc = true,
@@ -145,7 +142,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_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); 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 #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) { if (ret != ESP_OK) {
goto cleanup; goto cleanup;
} }
@@ -163,20 +160,11 @@ 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]; host->hal.dmadesc_rx[i].desc = &host->dma_ctx->dmadesc_rx[i];
} }
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL);
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, NULL);
if (ret != ESP_OK) { if (ret != ESP_OK) {
goto cleanup; goto cleanup;
} }
spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), NULL); 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 = { spi_slave_hd_hal_config_t hal_config = {
.host_id = host_id, .host_id = host_id,
@@ -351,12 +339,10 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id)
spicommon_bus_free_io_cfg(host_id); spicommon_bus_free_io_cfg(host_id);
spicommon_cs_free_io(host->cs_io_num, &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_tx);
free(host->hal.dmadesc_rx); 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); free(host);
spihost[host_id] = NULL; spihost[host_id] = NULL;
@@ -426,6 +412,25 @@ static inline SPI_SLAVE_ISR_ATTR BaseType_t intr_check_clear_callback(spi_slave_
} }
return cb_awoken; 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) 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; spi_slave_hd_slot_t *host = (spi_slave_hd_slot_t *)arg;
@@ -450,6 +455,7 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg)
portEXIT_CRITICAL_ISR(&host->int_spinlock); portEXIT_CRITICAL_ISR(&host->int_spinlock);
if (tx_done) { if (tx_done) {
spi_slave_hd_tx_dma_error_check(host, host->tx_curr_trans);
bool ret_queue = true; bool ret_queue = true;
if (callback->cb_sent) { if (callback->cb_sent) {
spi_slave_hd_event_t ev = { spi_slave_hd_event_t ev = {
@@ -468,15 +474,10 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg)
host->tx_curr_trans.trans = NULL; host->tx_curr_trans.trans = NULL;
} }
if (rx_done) { 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; bool ret_queue = true;
host->rx_curr_trans.trans->trans_len = spi_slave_hd_hal_rxdma_seg_get_len(hal); 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) { if (callback->cb_recv) {
spi_slave_hd_event_t ev = { spi_slave_hd_event_t ev = {
.event = SPI_EV_RECV, .event = SPI_EV_RECV,
@@ -557,7 +558,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_tx_isr(void *arg)
BaseType_t awoken = pdFALSE; BaseType_t awoken = pdFALSE;
BaseType_t ret __attribute__((unused)); 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) { while (1) {
bool trans_finish = false; 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); trans_finish = spi_slave_hd_hal_get_tx_finished_trans(hal, (void **)&ret_priv_trans.trans, &ret_priv_trans.aligned_buffer);
@@ -569,6 +570,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_tx_isr(void *arg)
portEXIT_CRITICAL_ISR(&host->int_spinlock); portEXIT_CRITICAL_ISR(&host->int_spinlock);
bool ret_queue = true; bool ret_queue = true;
spi_slave_hd_tx_dma_error_check(host, ret_priv_trans);
if (callback->cb_sent) { if (callback->cb_sent) {
spi_slave_hd_event_t ev = { spi_slave_hd_event_t ev = {
.event = SPI_EV_SEND, .event = SPI_EV_SEND,
@@ -600,7 +602,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg)
BaseType_t awoken = pdFALSE; BaseType_t awoken = pdFALSE;
BaseType_t ret __attribute__((unused)); 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; size_t trans_len;
while (1) { while (1) {
bool trans_finish = false; bool trans_finish = false;
@@ -613,14 +615,9 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg)
portEXIT_CRITICAL_ISR(&host->int_spinlock); portEXIT_CRITICAL_ISR(&host->int_spinlock);
ret_priv_trans.trans->trans_len = trans_len; 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; 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) { if (callback->cb_recv) {
spi_slave_hd_event_t ev = { spi_slave_hd_event_t ev = {
.event = SPI_EV_RECV, .event = SPI_EV_RECV,
@@ -689,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) 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; spi_slave_hd_data_t *orig_trans = priv_trans->trans;
if (priv_trans->aligned_buffer != orig_trans->data) { if (priv_trans->aligned_buffer != orig_trans->data) {
if (chan == SPI_SLAVE_CHAN_RX) { if (chan == SPI_SLAVE_CHAN_RX) {
@@ -697,39 +693,19 @@ static void s_spi_slave_hd_destroy_priv_trans(spi_host_device_t host, spi_slave_
} }
free(priv_trans->aligned_buffer); 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) 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; spi_slave_hd_data_t *orig_trans = priv_trans->trans;
priv_trans->aligned_buffer = orig_trans->data; bool auto_malloc = (orig_trans->flags & SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO);
bool is_tx = (chan == SPI_SLAVE_CHAN_TX);
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE 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);
uint16_t alignment = spihost[host]->internal_mem_align_size; if (ret != ESP_OK) {
uint32_t byte_len = orig_trans->len; s_spi_slave_hd_destroy_priv_trans(host, priv_trans, chan);
return ret;
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;
}
} }
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; return ESP_OK;
} }
@@ -750,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); s_spi_slave_hd_destroy_priv_trans(host_id, &hd_priv_trans, chan);
*out_trans = hd_priv_trans.trans; *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) esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data, size_t len, void *arg)
@@ -841,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]; 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(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(trans->len <= host->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); 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}; 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) { if (chan == SPI_SLAVE_CHAN_TX) {
BaseType_t ret = xQueueSend(host->tx_trans_queue, &hd_priv_trans, timeout); BaseType_t ret = xQueueSend(host->tx_trans_queue, &hd_priv_trans, timeout);
@@ -894,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(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(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(trans->len <= host->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); 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}; 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) { if (chan == SPI_SLAVE_CHAN_TX) {
BaseType_t ret = xSemaphoreTake(host->tx_cnting_sem, timeout); BaseType_t ret = xSemaphoreTake(host->tx_cnting_sem, timeout);
@@ -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 = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin, .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 .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_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); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
ESP_LOGI(TAG, "test 4 iomux output pins..."); ESP_LOGI(TAG, "test 4 iomux output pins...");
@@ -513,9 +513,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, .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 .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_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); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
ESP_LOGI(TAG, "test 6 output pins..."); ESP_LOGI(TAG, "test 6 output pins...");
@@ -525,9 +525,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, .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 .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_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); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
ESP_LOGI(TAG, "test 4 output pins..."); ESP_LOGI(TAG, "test 4 output pins...");
@@ -537,9 +537,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, .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 .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_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); 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. #if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
@@ -549,7 +549,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, .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 .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_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
ESP_LOGI(TAG, "test slave 5 output pins and MISO on input-only pin..."); ESP_LOGI(TAG, "test slave 5 output pins and MISO on input-only pin...");
@@ -558,7 +558,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, .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 .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); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
ESP_LOGI(TAG, "test master 3 output pins and MOSI on input-only pin..."); ESP_LOGI(TAG, "test master 3 output pins and MOSI on input-only pin...");
@@ -568,7 +568,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, .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 .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_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
ESP_LOGI(TAG, "test slave 3 output pins and MISO on input-only pin..."); ESP_LOGI(TAG, "test slave 3 output pins and MISO on input-only pin...");
@@ -577,7 +577,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, .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 .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); TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
//There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored. //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
#endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS #endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS
@@ -589,8 +589,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, .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 .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_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, NULL)); 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..."); ESP_LOGI(TAG, "check native flag for 4 output pins...");
flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS; flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS;
@@ -599,8 +599,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, .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 .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_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, NULL)); 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. #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..."); ESP_LOGI(TAG, "check dual flag for master 5 output pins and MISO/MOSI on input-only pin...");
@@ -609,14 +609,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, .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 .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_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, NULL)); 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) { 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, .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 .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_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, NULL)); 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..."); 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; flags_expected = SPICOMMON_BUSFLAG_DUAL | SPICOMMON_BUSFLAG_GPIO_PINS;
@@ -624,14 +624,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, .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 .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_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, NULL)); 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) { 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, .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 .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_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, NULL)); 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. //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
#endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS #endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS
@@ -641,8 +641,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, .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 .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_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, NULL)); 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..."); ESP_LOGI(TAG, "check mosi flag...");
flags_expected = SPICOMMON_BUSFLAG_MOSI; flags_expected = SPICOMMON_BUSFLAG_MOSI;
@@ -650,8 +650,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, .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 .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_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, NULL)); 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..."); ESP_LOGI(TAG, "check miso flag...");
flags_expected = SPICOMMON_BUSFLAG_MISO; flags_expected = SPICOMMON_BUSFLAG_MISO;
@@ -659,8 +659,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, .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 .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_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, NULL)); 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..."); ESP_LOGI(TAG, "check quad flag...");
flags_expected = SPICOMMON_BUSFLAG_QUAD; flags_expected = SPICOMMON_BUSFLAG_QUAD;
@@ -668,14 +668,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, .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 .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_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, NULL)); 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) { 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, .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 .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_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, NULL)); 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]") TEST_CASE("SPI Master no response when switch from host1 (SPI2) to host2 (SPI3)", "[spi]")
@@ -912,7 +912,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)); TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
//connecting pins to two peripherals breaks the output, fix it. //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++) { for (int i = 0; i < 8; i++) {
//prepare slave tx data //prepare slave tx data
@@ -1097,7 +1097,7 @@ TEST_CASE("SPI master variable dummy test", "[spi]")
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); 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)); 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}; uint8_t data_to_send[] = {0x12, 0x34, 0x56, 0x78};
@@ -1140,7 +1140,7 @@ TEST_CASE("SPI master hd dma TX without RX test", "[spi]")
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); 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)); 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; uint32_t buf_size = 32;
uint8_t *mst_send_buf = spi_bus_dma_memory_alloc(TEST_SPI_HOST, buf_size, 0); uint8_t *mst_send_buf = spi_bus_dma_memory_alloc(TEST_SPI_HOST, buf_size, 0);
@@ -2076,11 +2076,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 // 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 // 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); 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_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg));
TEST_ASSERT(!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL))); 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.tx_buffer += trans_len;
trans_cfg.rx_buffer += trans_len; trans_cfg.rx_buffer += trans_len;
trans_len ++; trans_len ++;
@@ -2125,9 +2125,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); 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); 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)); 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))); 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 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);
} }
} }
@@ -75,7 +75,7 @@ TEST_CASE("SPI Single Board Test SIO", "[spi]")
spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); 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)); 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); inner_connect(bus_cfg);
WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320]; WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320];
@@ -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); 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; 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, .data = slave_send,
.len = test_trans_len, .len = test_trans_len,
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, .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"); unity_send_signal("Slave ready");
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));
slave_trans.data = slave_receive; 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_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, 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_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)); 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); 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; 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, .data = slave_send,
.len = test_trans_len, .len = test_trans_len,
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, .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"); 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)); 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_rx, portMAX_DELAY));
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY));
unity_send_signal("Slave ready"); 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)); TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
@@ -8,6 +8,7 @@
*/ */
#include <string.h> #include <string.h>
#include <sys/param.h>
#include "sdkconfig.h" #include "sdkconfig.h"
#include "unity.h" #include "unity.h"
#include "test_utils.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 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 }; 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) static spi_host_device_t master_slave_ids[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_device_handle_t spi; 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 //Initialize buffers
memset(master_txbuf, 0, sizeof(master_txbuf)); 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_txbuf, 0, sizeof(slave_txbuf));
memset(slave_rxbuf, 0, sizeof(slave_rxbuf)); memset(slave_rxbuf, 0, sizeof(slave_rxbuf));
//Initialize SPI Master
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
buscfg.max_transfer_sz = 40960;
spi_device_interface_config_t devcfg = { spi_device_interface_config_t devcfg = {
.clock_speed_hz = 4 * 1000 * 1000, //currently only up to 4MHz for internal connect .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 .spics_io_num = PIN_NUM_CS, //CS pin
.queue_size = 7, //We want to be able to queue 7 transactions at a time .queue_size = 7, //We want to be able to queue 7 transactions at a time
.pre_cb = NULL,
.cs_ena_posttrans = 5, .cs_ena_posttrans = 5,
.cs_ena_pretrans = 1, .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 //Initialize SPI Master
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); if (master_id) {
//Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected. //Initialize the SPI bus
gpio_set_pull_mode(PIN_NUM_MOSI, GPIO_PULLUP_ONLY); TEST_ESP_OK(spi_bus_initialize(master_id, &buscfg, SPI_DMA_CH_AUTO));
gpio_set_pull_mode(PIN_NUM_CLK, GPIO_PULLUP_ONLY); //Attach the device to the SPI bus
gpio_set_pull_mode(PIN_NUM_CS, GPIO_PULLUP_ONLY); TEST_ESP_OK(spi_bus_add_device(master_id, &devcfg, &spi));
//Initialize SPI slave interface }
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
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 //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)); if (master_slave_ids[1]) {
TEST_ESP_OK(spi_bus_remove_device(spi)); TEST_ESP_OK(spi_slave_free(master_slave_ids[1]));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); 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]") 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)); 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]") 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)); 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)); 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 //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]; 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++) { 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_remove_device(spidev0));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
} }
#endif // !CONFIG_SPIRAM
#endif // #if (TEST_SPI_PERIPH_NUM >= 2) #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]") TEST_CASE("test slave send unaligned", "[spi]")
{ {
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS; buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); 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)); 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(master_txbuf, master_send, sizeof(master_send));
memcpy(slave_txbuf, slave_send, sizeof(slave_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; buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG(); 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)); 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 ++) { 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"); printf("Going into sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold");
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -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. //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_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_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_wrbuf_sig(ctx, 0);
wait_rdbuf_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. //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_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_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; const int send_buf_size = 1024;
WORD_ALIGNED_ATTR uint8_t* slave_send_buf = malloc(send_buf_size * 2); 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; bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG(); 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)); 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); vTaskDelay(1);
for (uint8_t cnt = 0; cnt < 3; cnt ++) { 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(); spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE; slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg)); 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); vTaskDelay(1);
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
@@ -1085,3 +1085,66 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]")
#endif #endif
} }
#endif //SOC_LIGHT_SLEEP_SUPPORTED #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
@@ -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); 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. * Post transaction operations, fetch data from the buffer and recorded the length.
* *
@@ -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 * 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); 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. * @brief Check and clear the interrupt of one event.
* *
@@ -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 #endif // SPI_LL_SLAVE_NEEDS_RESET_WORKAROUND
return ret; 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
+9 -5
View File
@@ -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 * 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; 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, mask)) {
if (spi_ll_get_intr(hal->dev, intr)) { spi_ll_clear_intr(hal->dev, mask);
spi_ll_clear_intr(hal->dev, intr);
return true; return true;
} }
return false; 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) 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 //The trans_done interrupt is used for the workaround when some interrupt is not writable
@@ -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`. 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 Driver Usage
------------ ------------
@@ -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. 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. 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. 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.
@@ -79,6 +79,14 @@ SPI 传输事务
主机应在从机设备准备好接收数据之后再进行传输事务。建议使用另外一个 GPIO 管脚作为握手信号来同步设备。更多细节,请参阅 :ref:`transaction_interval` 主机应在从机设备准备好接收数据之后再进行传输事务。建议使用另外一个 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` 错误。
使用驱动程序 使用驱动程序
------------ ------------
@@ -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` 时数据进入队列的顺序,依次将数据发送给主设备。 要通过 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`` 将输出刚刚完成的数据描述符的指针,从而提供有关已完成的发送操作的信息。 应用程序需要检查数据发送的结果。为此,应用程序可以调用 :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 中的数据。 通过 DMA 通道从主设备接收数据的操作与发送数据类似。应用程序需要使用正确的数据描述符调用 :cpp:func:`spi_slave_hd_queue_trans`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_RX`。随后,应用程序调用 :cpp:func:`spi_slave_hd_get_trans_res` 获取接收 buffer 的描述符,然后处理接收 buffer 中的数据。