From 2baee4fb0fb81c811515e286e8f0fff78c0d40cb Mon Sep 17 00:00:00 2001 From: wanlei Date: Thu, 28 Dec 2023 19:58:54 +0800 Subject: [PATCH] refactor(spi_master): replace dma_ll in spi hal layer (part 2.1) --- components/esp_driver_spi/Kconfig | 1 + .../include/esp_private/spi_common_internal.h | 10 ++ components/esp_driver_spi/linker.lf | 8 +- .../esp_driver_spi/src/gpspi/spi_common.c | 38 +++++ .../esp_driver_spi/src/gpspi/spi_master.c | 77 +++++++--- components/hal/esp32/include/hal/spi_ll.h | 8 + components/hal/esp32s2/include/hal/spi_ll.h | 6 + components/hal/include/hal/spi_hal.h | 89 +++++------- components/hal/spi_hal.c | 10 +- components/hal/spi_hal_iram.c | 137 +++--------------- .../include/esp32c2/idf_performance_target.h | 10 +- .../include/esp32h2/idf_performance_target.h | 6 +- 12 files changed, 189 insertions(+), 211 deletions(-) diff --git a/components/esp_driver_spi/Kconfig b/components/esp_driver_spi/Kconfig index 6ef7191211..9fdb735b7f 100644 --- a/components/esp_driver_spi/Kconfig +++ b/components/esp_driver_spi/Kconfig @@ -26,6 +26,7 @@ menu "ESP-Driver:SPI Configurations" select PERIPH_CTRL_FUNC_IN_IRAM select HAL_SPI_MASTER_FUNC_IN_IRAM select ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM + select GDMA_CTRL_FUNC_IN_IRAM if SOC_GDMA_SUPPORTED help Place the SPI master ISR in to IRAM to avoid possible cache miss. diff --git a/components/esp_driver_spi/include/esp_private/spi_common_internal.h b/components/esp_driver_spi/include/esp_private/spi_common_internal.h index 4652347593..f8820171a0 100644 --- a/components/esp_driver_spi/include/esp_private/spi_common_internal.h +++ b/components/esp_driver_spi/include/esp_private/spi_common_internal.h @@ -102,6 +102,16 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma */ esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *actual_max_sz); +/** + * Setupt/Configure dma descriptor link list + * + * @param dmadesc start of dma descriptor memory + * @param data start of data buffer to be configured in + * @param len length of data buffer, in byte + * @param is_rx if descriptor is for rx/receive direction + */ +void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx); + /** * @brief Free DMA for SPI * diff --git a/components/esp_driver_spi/linker.lf b/components/esp_driver_spi/linker.lf index fe92562a6b..f3c420c122 100644 --- a/components/esp_driver_spi/linker.lf +++ b/components/esp_driver_spi/linker.lf @@ -1,7 +1,7 @@ [mapping:gpspi_driver] archive: libesp_driver_spi.a entries: - if SOC_GDMA_SUPPORTED = n: - if SPI_MASTER_ISR_IN_IRAM = y || SPI_SLAVE_ISR_IN_IRAM = y: - spi_dma: spi_dma_reset (noflash) - spi_dma: spi_dma_start (noflash) + # SOC_GDMA_SUPPORTED is undeclared if GDMA not support, so use `SOC_GDMA_SUPPORTED != y` instead + if SOC_GDMA_SUPPORTED != y && (SPI_MASTER_ISR_IN_IRAM = y || SPI_SLAVE_ISR_IN_IRAM = y): + spi_dma: spi_dma_reset (noflash) + spi_dma: spi_dma_start (noflash) diff --git a/components/esp_driver_spi/src/gpspi/spi_common.c b/components/esp_driver_spi/src/gpspi/spi_common.c index 981cd19ab5..b10c9a9b8e 100644 --- a/components/esp_driver_spi/src/gpspi/spi_common.c +++ b/components/esp_driver_spi/src/gpspi/spi_common.c @@ -14,6 +14,7 @@ #include "esp_rom_gpio.h" #include "esp_heap_caps.h" #include "soc/spi_periph.h" +#include "soc/ext_mem_defs.h" #include "driver/gpio.h" #include "driver/spi_master.h" #include "esp_private/periph_ctrl.h" @@ -304,6 +305,43 @@ esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int * *actual_max_sz = dma_desc_ct * DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; return ESP_OK; } + +#if SOC_NON_CACHEABLE_OFFSET +#define ADDR_DMA_2_CPU(addr) ((typeof(addr))((uint32_t)(addr) + SOC_NON_CACHEABLE_OFFSET)) +#define ADDR_CPU_2_DMA(addr) ((typeof(addr))((uint32_t)(addr) - SOC_NON_CACHEABLE_OFFSET)) +#else +#define ADDR_DMA_2_CPU(addr) (addr) +#define ADDR_CPU_2_DMA(addr) (addr) +#endif + +void SPI_MASTER_ISR_ATTR spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx) +{ + dmadesc = ADDR_DMA_2_CPU(dmadesc); + int n = 0; + while (len) { + int dmachunklen = len; + if (dmachunklen > DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) { + dmachunklen = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; + } + if (is_rx) { + //Receive needs DMA length rounded to next 32-bit boundary + dmadesc[n].dw0.size = (dmachunklen + 3) & (~3); + } else { + dmadesc[n].dw0.size = dmachunklen; + dmadesc[n].dw0.length = dmachunklen; + } + dmadesc[n].buffer = (uint8_t *)data; + dmadesc[n].dw0.suc_eof = 0; + dmadesc[n].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; + dmadesc[n].next = ADDR_CPU_2_DMA(&dmadesc[n + 1]); + len -= dmachunklen; + data += dmachunklen; + n++; + } + dmadesc[n - 1].dw0.suc_eof = 1; //Mark last DMA desc as end of stream. + dmadesc[n - 1].next = NULL; +} + //----------------------------------------------------------free dma periph-------------------------------------------------------// esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx) { diff --git a/components/esp_driver_spi/src/gpspi/spi_master.c b/components/esp_driver_spi/src/gpspi/spi_master.c index 5ae4d1a803..421aac0613 100644 --- a/components/esp_driver_spi/src/gpspi/spi_master.c +++ b/components/esp_driver_spi/src/gpspi/spi_master.c @@ -261,29 +261,10 @@ static esp_err_t spi_master_init_driver(spi_host_device_t host_id) } } - //assign the SPI, RX DMA and TX DMA peripheral registers beginning address - spi_hal_config_t hal_config = { .dma_enabled = bus_attr->dma_enabled, }; - if (bus_attr->dma_enabled && dma_ctx) { - hal_config.dmadesc_tx = dma_ctx->dmadesc_tx; - hal_config.dmadesc_rx = dma_ctx->dmadesc_rx; - hal_config.dmadesc_n = dma_ctx->dma_desc_num; -#if SOC_GDMA_SUPPORTED - //temporary used for gdma_ll alias in hal layer - gdma_get_channel_id(dma_ctx->tx_dma_chan, (int *)&hal_config.tx_dma_chan); - gdma_get_channel_id(dma_ctx->rx_dma_chan, (int *)&hal_config.rx_dma_chan); -#else - //On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. Pass the registers of SPI peripheral to control it. - hal_config.dma_in = SPI_LL_GET_HW(host_id); - hal_config.dma_out = SPI_LL_GET_HW(host_id); - hal_config.tx_dma_chan = dma_ctx->tx_dma_chan.chan_id; - hal_config.rx_dma_chan = dma_ctx->rx_dma_chan.chan_id; -#endif - } - SPI_MASTER_PERI_CLOCK_ATOMIC() { spi_ll_enable_clock(host_id, true); } - spi_hal_init(&host->hal, host_id, &hal_config); + spi_hal_init(&host->hal, host_id); if (host_id != SPI1_HOST) { //SPI1 attributes are already initialized at start up. @@ -625,6 +606,56 @@ static void SPI_MASTER_ISR_ATTR spi_bus_intr_disable(void *host) esp_intr_disable(((spi_host_t*)host)->intr); } +#if SOC_GDMA_SUPPORTED // AHB_DMA_V1 and AXI_DMA +// dma is provided by gdma driver on these targets +#define spi_dma_reset gdma_reset +#define spi_dma_start(chan, addr) gdma_start(chan, (intptr_t)(addr)) +#endif + +static void SPI_MASTER_ISR_ATTR s_spi_dma_prepare_data(spi_host_t *host, spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans) +{ + const spi_dma_ctx_t *dma_ctx = host->dma_ctx; + + if (trans->rcv_buffer) { + spicommon_dma_desc_setup_link(dma_ctx->dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true); + + spi_dma_reset(dma_ctx->rx_dma_chan); + spi_hal_hw_prepare_rx(hal->hw); + spi_dma_start(dma_ctx->rx_dma_chan, dma_ctx->dmadesc_rx); + } +#if CONFIG_IDF_TARGET_ESP32 + else if (!dev->half_duplex) { + //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon + spi_ll_dma_rx_enable(hal->hw, 1); + spi_dma_start(dma_ctx->rx_dma_chan, NULL); + } +#endif + if (trans->send_buffer) { + spicommon_dma_desc_setup_link(dma_ctx->dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false); + + spi_dma_reset(dma_ctx->tx_dma_chan); + spi_hal_hw_prepare_tx(hal->hw); + spi_dma_start(dma_ctx->tx_dma_chan, dma_ctx->dmadesc_tx); + } +} + +static void SPI_MASTER_ISR_ATTR s_spi_prepare_data(spi_device_t *dev, const spi_hal_trans_config_t *hal_trans) +{ + spi_host_t *host = dev->host; + spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); + spi_hal_context_t *hal = &(host->hal); + + if (host->bus_attr->dma_enabled) { + s_spi_dma_prepare_data(host, hal, hal_dev, hal_trans); + } else { + //Need to copy data to registers manually + spi_hal_push_tx_buffer(hal, hal_trans); + } + + //in ESP32 these registers should be configured after the DMA is set + spi_hal_enable_data_line(hal->hw, (!hal_dev->half_duplex && hal_trans->rcv_buffer) || hal_trans->send_buffer, !!hal_trans->rcv_buffer); +} + // The function is called to send a new transaction, in ISR or in the task. // Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used) static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf) @@ -677,7 +708,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_ } spi_hal_setup_trans(hal, hal_dev, &hal_trans); - spi_hal_prepare_data(hal, hal_dev, &hal_trans); + s_spi_prepare_data(dev, &hal_trans); //Call pre-transmission callback, if any if (dev->cfg.pre_cb) { @@ -693,7 +724,9 @@ static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host) { spi_transaction_t *cur_trans = host->cur_trans_buf.trans; - spi_hal_fetch_result(&host->hal); + if (!host->bus_attr->dma_enabled) { + spi_hal_fetch_result(&host->hal); + } //Call post-transaction callback, if any spi_device_t* dev = host->device[host->cur_cs]; if (dev->cfg.post_cb) { diff --git a/components/hal/esp32/include/hal/spi_ll.h b/components/hal/esp32/include/hal/spi_ll.h index c82725e481..1b8eea8ed7 100644 --- a/components/hal/esp32/include/hal/spi_ll.h +++ b/components/hal/esp32/include/hal/spi_ll.h @@ -288,6 +288,7 @@ static inline void spi_ll_dma_rx_fifo_reset(spi_dev_t *hw) * * @param hw Beginning address of the peripheral registers. */ +__attribute__((always_inline)) static inline void spi_ll_infifo_full_clr(spi_dev_t *hw) { //This is not used in esp32 @@ -298,6 +299,7 @@ static inline void spi_ll_infifo_full_clr(spi_dev_t *hw) * * @param hw Beginning address of the peripheral registers. */ +__attribute__((always_inline)) static inline void spi_ll_outfifo_empty_clr(spi_dev_t *hw) { //This is not used in esp32 @@ -313,6 +315,7 @@ static inline void spi_ll_outfifo_empty_clr(spi_dev_t *hw) * @param hw Beginning address of the peripheral registers. * @param enable 1: enable; 2: disable */ +__attribute__((always_inline)) static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable) { //This is not used in esp32 @@ -324,6 +327,7 @@ static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable) * @param hw Beginning address of the peripheral registers. * @param enable 1: enable; 2: disable */ +__attribute__((always_inline)) static inline void spi_ll_dma_tx_enable(spi_dev_t *hw, bool enable) { //This is not used in esp32 @@ -1090,6 +1094,7 @@ static inline void spi_dma_ll_reset_register(spi_host_device_t host_id) { * @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM. * @param channel DMA channel, for chip version compatibility, not used. */ +__attribute__((always_inline)) static inline void spi_dma_ll_rx_reset(spi_dma_dev_t *dma_in, uint32_t channel) { //Reset RX DMA peripheral @@ -1104,6 +1109,7 @@ static inline void spi_dma_ll_rx_reset(spi_dma_dev_t *dma_in, uint32_t channel) * @param channel DMA channel, for chip version compatibility, not used. * @param addr Address of the beginning DMA descriptor. */ +__attribute__((always_inline)) static inline void spi_dma_ll_rx_start(spi_dma_dev_t *dma_in, uint32_t channel, lldesc_t *addr) { dma_in->dma_in_link.addr = (int) addr & 0xFFFFF; @@ -1140,6 +1146,7 @@ static inline void spi_dma_ll_rx_enable_burst_desc(spi_dma_dev_t *dma_in, uint32 * @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral. * @param channel DMA channel, for chip version compatibility, not used. */ +__attribute__((always_inline)) static inline void spi_dma_ll_tx_reset(spi_dma_dev_t *dma_out, uint32_t channel) { //Reset TX DMA peripheral @@ -1154,6 +1161,7 @@ static inline void spi_dma_ll_tx_reset(spi_dma_dev_t *dma_out, uint32_t channel) * @param channel DMA channel, for chip version compatibility, not used. * @param addr Address of the beginning DMA descriptor. */ +__attribute__((always_inline)) static inline void spi_dma_ll_tx_start(spi_dma_dev_t *dma_out, uint32_t channel, lldesc_t *addr) { dma_out->dma_out_link.addr = (int) addr & 0xFFFFF; diff --git a/components/hal/esp32s2/include/hal/spi_ll.h b/components/hal/esp32s2/include/hal/spi_ll.h index 032a907e15..9dc462cfd5 100644 --- a/components/hal/esp32s2/include/hal/spi_ll.h +++ b/components/hal/esp32s2/include/hal/spi_ll.h @@ -388,6 +388,7 @@ static inline void spi_ll_outfifo_empty_clr(spi_dev_t *hw) * @param hw Beginning address of the peripheral registers. * @param enable 1: enable; 2: disable */ +__attribute__((always_inline)) static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable) { //This is not used in esp32s2 @@ -399,6 +400,7 @@ static inline void spi_ll_dma_rx_enable(spi_dev_t *hw, bool enable) * @param hw Beginning address of the peripheral registers. * @param enable 1: enable; 2: disable */ +__attribute__((always_inline)) static inline void spi_ll_dma_tx_enable(spi_dev_t *hw, bool enable) { //This is not used in esp32s2 @@ -1266,6 +1268,7 @@ static inline void spi_dma_ll_reset_register(spi_host_device_t host_id) { * @param dma_in Beginning address of the DMA peripheral registers which stores the data received from a peripheral into RAM. * @param channel DMA channel, for chip version compatibility, not used. */ +__attribute__((always_inline)) static inline void spi_dma_ll_rx_reset(spi_dma_dev_t *dma_in, uint32_t channel) { dma_in->dma_conf.in_rst = 1; @@ -1279,6 +1282,7 @@ static inline void spi_dma_ll_rx_reset(spi_dma_dev_t *dma_in, uint32_t channel) * @param channel DMA channel, for chip version compatibility, not used. * @param addr Address of the beginning DMA descriptor. */ +__attribute__((always_inline)) static inline void spi_dma_ll_rx_start(spi_dma_dev_t *dma_in, uint32_t channel, lldesc_t *addr) { dma_in->dma_in_link.addr = (int) addr & 0xFFFFF; @@ -1340,6 +1344,7 @@ static inline uint32_t spi_dma_ll_get_in_suc_eof_desc_addr(spi_dma_dev_t *dma_in * @param dma_out Beginning address of the DMA peripheral registers which transmits the data from RAM to a peripheral. * @param channel DMA channel, for chip version compatibility, not used. */ +__attribute__((always_inline)) static inline void spi_dma_ll_tx_reset(spi_dma_dev_t *dma_out, uint32_t channel) { //Reset TX DMA peripheral @@ -1354,6 +1359,7 @@ static inline void spi_dma_ll_tx_reset(spi_dma_dev_t *dma_out, uint32_t channel) * @param channel DMA channel, for chip version compatibility, not used. * @param addr Address of the beginning DMA descriptor. */ +__attribute__((always_inline)) static inline void spi_dma_ll_tx_start(spi_dma_dev_t *dma_out, uint32_t channel, lldesc_t *addr) { dma_out->dma_out_link.addr = (int) addr & 0xFFFFF; diff --git a/components/hal/include/hal/spi_hal.h b/components/hal/include/hal/spi_hal.h index 9fbff296ec..5937cd2952 100644 --- a/components/hal/include/hal/spi_hal.h +++ b/components/hal/include/hal/spi_hal.h @@ -12,16 +12,15 @@ // The HAL layer for SPI master (common part) -// SPI HAL usages: +// SPI HAL usages (without DMA): // 1. initialize the bus -// 2. initialize the DMA descriptors if DMA used -// 3. setup the clock speed (since this takes long time) -// 4. call setup_device to update parameters for the specific device -// 5. call setup_trans to update parameters for the specific transaction -// 6. prepare data to send, and prepare the receiving buffer -// 7. trigger user defined SPI transaction to start -// 8. wait until the user transaction is done -// 9. fetch the received data +// 2. setup the clock speed (since this takes long time) +// 3. call setup_device to update parameters for the specific device +// 4. call setup_trans to update parameters for the specific transaction +// 5. prepare data to send into hw registers +// 6. trigger user defined SPI transaction to start +// 7. wait until the user transaction is done +// 8. fetch the received data // Parameter to be updated only during ``setup_device`` will be highlighted in the // field comments. @@ -75,27 +74,6 @@ typedef struct { int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing } spi_hal_timing_conf_t; -/** - * DMA configuration structure - * Should be set by driver at initialization - */ -typedef struct { - spi_dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address - spi_dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address - bool dma_enabled; ///< Whether the DMA is enabled, do not update after initialization - spi_dma_desc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - spi_dma_desc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - uint32_t tx_dma_chan; ///< TX DMA channel - uint32_t rx_dma_chan; ///< RX DMA channel - int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use. -} spi_hal_config_t; - /** * Transaction configuration structure, this should be assigned by driver each time. * All these parameters will be updated to the peripheral every transaction. @@ -118,25 +96,9 @@ typedef struct { * Context that should be maintained by both the driver and the HAL. */ typedef struct { - /* These two need to be malloced by the driver first */ - spi_dma_desc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - spi_dma_desc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - /* Configured by driver at initialization, don't touch */ spi_dev_t *hw; ///< Beginning address of the peripheral registers. - spi_dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM (DMA -> RAM). - spi_dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral (RAM -> DMA). bool dma_enabled; ///< Whether the DMA is enabled, do not update after initialization - uint32_t tx_dma_chan; ///< TX DMA channel - uint32_t rx_dma_chan; ///< RX DMA channel - int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use. - /* Internal parameters, don't touch */ spi_hal_trans_config_t trans_config; ///< Transaction configuration } spi_hal_context_t; @@ -172,9 +134,8 @@ typedef struct { * * @param hal Context of the HAL layer. * @param host_id Index of the SPI peripheral. 0 for SPI1, 1 for SPI2 and 2 for SPI3. - * @param hal_config Configuration of the hal defined by the upper layer. */ -void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_config_t *hal_config); +void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id); /** * Deinit the peripheral (and the context if needed). @@ -201,13 +162,27 @@ void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *ha void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans); /** - * Prepare the data for the current transaction. + * Enable/Disable miso/mosi signals on peripheral side * - * @param hal Context of the HAL layer. - * @param hal_dev Device configuration - * @param hal_trans Transaction configuration + * @param hw Beginning address of the peripheral registers. + * @param mosi_ena enable/disable mosi line + * @param miso_ena enable/disable miso line */ -void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans); +void spi_hal_enable_data_line(spi_dev_t *hw, bool mosi_ena, bool miso_ena); + +/** + * Prepare tx hardware for a new DMA trans + * + * @param hw Beginning address of the peripheral registers. + */ +void spi_hal_hw_prepare_rx(spi_dev_t *hw); + +/** + * Prepare tx hardware for a new DMA trans + * + * @param hw Beginning address of the peripheral registers. + */ +void spi_hal_hw_prepare_tx(spi_dev_t *hw); /** * Trigger start a user-defined transaction. @@ -223,6 +198,14 @@ void spi_hal_user_start(const spi_hal_context_t *hal); */ bool spi_hal_usr_is_done(const spi_hal_context_t *hal); +/** + * Setup transaction operations, write tx buffer to HW registers + * + * @param hal Context of the HAL layer. + * @param hal_trans Transaction configuration. + */ +void spi_hal_push_tx_buffer(const spi_hal_context_t *hal, const spi_hal_trans_config_t *hal_trans); + /** * Post transaction operations, mainly fetch data from the buffer. * diff --git a/components/hal/spi_hal.c b/components/hal/spi_hal.c index ee58edae03..aa090a71b8 100644 --- a/components/hal/spi_hal.c +++ b/components/hal/spi_hal.c @@ -21,19 +21,11 @@ static const __attribute__((unused)) char SPI_HAL_TAG[] = "spi_hal"; return (ret_val); \ } -void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_config_t *config) +void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id) { memset(hal, 0, sizeof(spi_hal_context_t)); spi_dev_t *hw = SPI_LL_GET_HW(host_id); hal->hw = hw; - hal->dma_in = config->dma_in; - hal->dma_out = config->dma_out; - hal->dma_enabled = config->dma_enabled; - hal->dmadesc_tx = config->dmadesc_tx; - hal->dmadesc_rx = config->dmadesc_rx; - hal->tx_dma_chan = config->tx_dma_chan; - hal->rx_dma_chan = config->rx_dma_chan; - hal->dmadesc_n = config->dmadesc_n; #if SPI_LL_MOSI_FREE_LEVEL // Change default data line level to low which same as esp32 diff --git a/components/hal/spi_hal_iram.c b/components/hal/spi_hal_iram.c index 790cbc7ad0..f7b3526bf8 100644 --- a/components/hal/spi_hal_iram.c +++ b/components/hal/spi_hal_iram.c @@ -12,37 +12,6 @@ #include "soc/ext_mem_defs.h" #include "soc/soc_caps.h" -//This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros. -#if SOC_GDMA_SUPPORTED -#if (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB) && (SOC_AHB_GDMA_VERSION == 1) -#include "soc/gdma_struct.h" -#include "hal/gdma_ll.h" -#define spi_dma_ll_rx_reset(dev, chan) gdma_ll_rx_reset_channel(&GDMA, chan) -#define spi_dma_ll_tx_reset(dev, chan) gdma_ll_tx_reset_channel(&GDMA, chan); -#define spi_dma_ll_rx_start(dev, chan, addr) do {\ - gdma_ll_rx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\ - gdma_ll_rx_start(&GDMA, chan);\ - } while (0) -#define spi_dma_ll_tx_start(dev, chan, addr) do {\ - gdma_ll_tx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\ - gdma_ll_tx_start(&GDMA, chan);\ - } while (0) - -#elif (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI) //TODO: IDF-6152, refactor spi hal layer -#include "hal/axi_dma_ll.h" -#define spi_dma_ll_rx_reset(dev, chan) axi_dma_ll_rx_reset_channel(&AXI_DMA, chan) -#define spi_dma_ll_tx_reset(dev, chan) axi_dma_ll_tx_reset_channel(&AXI_DMA, chan); -#define spi_dma_ll_rx_start(dev, chan, addr) do {\ - axi_dma_ll_rx_set_desc_addr(&AXI_DMA, chan, (uint32_t)addr);\ - axi_dma_ll_rx_start(&AXI_DMA, chan);\ - } while (0) -#define spi_dma_ll_tx_start(dev, chan, addr) do {\ - axi_dma_ll_tx_set_desc_addr(&AXI_DMA, chan, (uint32_t)addr);\ - axi_dma_ll_tx_start(&AXI_DMA, chan);\ - } while (0) -#endif -#endif //SOC_GDMA_SUPPORTED - void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev) { //Configure clock settings @@ -145,94 +114,24 @@ void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t)); } -#if SOC_NON_CACHEABLE_OFFSET -#define ADDR_DMA_2_CPU(addr) ((typeof(addr))((uint32_t)(addr) + SOC_NON_CACHEABLE_OFFSET)) -#define ADDR_CPU_2_DMA(addr) ((typeof(addr))((uint32_t)(addr) - SOC_NON_CACHEABLE_OFFSET)) -#else -#define ADDR_DMA_2_CPU(addr) (addr) -#define ADDR_CPU_2_DMA(addr) (addr) -#endif -//TODO: IDF-6152, refactor spi hal layer -static void s_spi_hal_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx) +void spi_hal_enable_data_line(spi_dev_t *hw, bool mosi_ena, bool miso_ena) { - dmadesc = ADDR_DMA_2_CPU(dmadesc); - int n = 0; - while (len) { - int dmachunklen = len; - if (dmachunklen > DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) { - dmachunklen = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; - } - if (is_rx) { - //Receive needs DMA length rounded to next 32-bit boundary - dmadesc[n].dw0.size = (dmachunklen + 3) & (~3); - dmadesc[n].dw0.length = (dmachunklen + 3) & (~3); - } else { - dmadesc[n].dw0.size = dmachunklen; - dmadesc[n].dw0.length = dmachunklen; - } - dmadesc[n].buffer = (uint8_t *)data; - dmadesc[n].dw0.suc_eof = 0; - dmadesc[n].dw0.owner = 1; - dmadesc[n].next = ADDR_CPU_2_DMA(&dmadesc[n + 1]); - len -= dmachunklen; - data += dmachunklen; - n++; - } - dmadesc[n - 1].dw0.suc_eof = 1; //Mark last DMA desc as end of stream. - dmadesc[n - 1].next = NULL; + spi_ll_enable_mosi(hw, mosi_ena); + spi_ll_enable_miso(hw, miso_ena); } -void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans) +void spi_hal_hw_prepare_rx(spi_dev_t *hw) { - spi_dev_t *hw = hal->hw; + spi_ll_dma_rx_fifo_reset(hw); + spi_ll_infifo_full_clr(hw); + spi_ll_dma_rx_enable(hw, 1); +} - //Fill DMA descriptors - if (trans->rcv_buffer) { - if (!hal->dma_enabled) { - //No need to setup anything; we'll copy the result out of the work registers directly later. - } else { - s_spi_hal_dma_desc_setup_link(hal->dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true); - - spi_dma_ll_rx_reset(hal->dma_in, hal->rx_dma_chan); - spi_ll_dma_rx_fifo_reset(hal->hw); - spi_ll_infifo_full_clr(hal->hw); - spi_ll_dma_rx_enable(hal->hw, 1); - spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, (lldesc_t *)hal->dmadesc_rx); - } - - } -#if CONFIG_IDF_TARGET_ESP32 - else { - //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon - if (hal->dma_enabled && !dev->half_duplex) { - spi_ll_dma_rx_enable(hal->hw, 1); - spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, 0); - } - } -#endif - - if (trans->send_buffer) { - if (!hal->dma_enabled) { - //Need to copy data to registers manually - spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen); - } else { - s_spi_hal_dma_desc_setup_link(hal->dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false); - - spi_dma_ll_tx_reset(hal->dma_out, hal->tx_dma_chan); - spi_ll_dma_tx_fifo_reset(hal->hw); - spi_ll_outfifo_empty_clr(hal->hw); - spi_ll_dma_tx_enable(hal->hw, 1); - spi_dma_ll_tx_start(hal->dma_out, hal->tx_dma_chan, (lldesc_t *)hal->dmadesc_tx); - } - } - - //in ESP32 these registers should be configured after the DMA is set - if ((!dev->half_duplex && trans->rcv_buffer) || trans->send_buffer) { - spi_ll_enable_mosi(hw, 1); - } else { - spi_ll_enable_mosi(hw, 0); - } - spi_ll_enable_miso(hw, (trans->rcv_buffer) ? 1 : 0); +void spi_hal_hw_prepare_tx(spi_dev_t *hw) +{ + spi_ll_dma_tx_fifo_reset(hw); + spi_ll_outfifo_empty_clr(hw); + spi_ll_dma_tx_enable(hw, 1); } void spi_hal_user_start(const spi_hal_context_t *hal) @@ -246,11 +145,19 @@ bool spi_hal_usr_is_done(const spi_hal_context_t *hal) return spi_ll_usr_is_done(hal->hw); } +void spi_hal_push_tx_buffer(const spi_hal_context_t *hal, const spi_hal_trans_config_t *hal_trans) +{ + if (hal_trans->send_buffer) { + spi_ll_write_buffer(hal->hw, hal_trans->send_buffer, hal_trans->tx_bitlen); + } + //No need to setup anything for RX, we'll copy the result out of the work registers directly later. +} + void spi_hal_fetch_result(const spi_hal_context_t *hal) { const spi_hal_trans_config_t *trans = &hal->trans_config; - if (trans->rcv_buffer && !hal->dma_enabled) { + if (trans->rcv_buffer) { //Need to copy from SPI regs to result buffer. spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen); } diff --git a/components/idf_test/include/esp32c2/idf_performance_target.h b/components/idf_test/include/esp32c2/idf_performance_target.h index 711c4e5c62..916caf4eca 100644 --- a/components/idf_test/include/esp32c2/idf_performance_target.h +++ b/components/idf_test/include/esp32c2/idf_performance_target.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -28,10 +28,10 @@ #define IDF_PERFORMANCE_MAX_ECDSA_P256_VERIFY_OP 49000 #define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 40*1000*1000 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 20 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 16 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 45 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 41 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 23 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 18 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 47 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 42 // floating point instructions per divide and per sqrt (configured for worst-case with PSRAM workaround) #define IDF_PERFORMANCE_MAX_CYCLES_PER_DIV 70 diff --git a/components/idf_test/include/esp32h2/idf_performance_target.h b/components/idf_test/include/esp32h2/idf_performance_target.h index 9bc935c895..7d89bdb1d8 100644 --- a/components/idf_test/include/esp32h2/idf_performance_target.h +++ b/components/idf_test/include/esp32h2/idf_performance_target.h @@ -27,9 +27,9 @@ #define IDF_PERFORMANCE_MAX_ECDSA_P256_VERIFY_OP 67000 #define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 26*1000*1000 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 28 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 24 -#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 58 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 32 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 25 +#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 61 #define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 54 // floating point instructions per divide and per sqrt (configured for worst-case with PSRAM workaround)