feat(spi_master): support dma transfer with psram directly

This commit is contained in:
Wan Lei
2025-12-15 15:59:04 +08:00
committed by wanckl
parent 6759fbfa52
commit 4adf8aecce
20 changed files with 328 additions and 123 deletions
+1 -1
View File
@@ -27,6 +27,6 @@ idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS ${public_include}
REQUIRES esp_pm
PRIV_REQUIRES esp_timer esp_mm esp_driver_gpio
PRIV_REQUIRES esp_timer esp_mm esp_driver_gpio spi_flash esp_psram #For CONFIG_SPIRAM_SPEED
LDFRAGMENTS "linker.lf"
)
+1 -1
View File
@@ -44,7 +44,7 @@ menu "ESP-Driver:SPI Configurations"
help
Normally only the ISR of SPI slave is placed in the IRAM, so that it
can work without the flash when interrupt is triggered.
For other functions, there's some possibility that the flash cache
For other functions, there is some possibility that the flash cache
miss when running inside and out of SPI functions, which may increase
the interval of SPI transactions.
Enable this to put ``queue_trans``, ``get_trans_result`` and
@@ -103,6 +103,7 @@ typedef struct {
*/
} spi_device_interface_config_t;
// Input flags
#define SPI_TRANS_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode
#define SPI_TRANS_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode
#define SPI_TRANS_USE_RXDATA (1<<2) ///< Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer.
@@ -116,6 +117,11 @@ typedef struct {
#define SPI_TRANS_MODE_OCT (1<<10) ///< Transmit/receive data in 8-bit mode
#define SPI_TRANS_MULTILINE_ADDR SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)
#define SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL (1<<11) ///< By default driver will automatically re-alloc dma buffer if it doesn't meet hardware alignment or dma_capable requirements, this flag is for you to disable this feature, you will need to take care of the alignment otherwise driver will return you error ESP_ERR_INVALID_ARG
#define SPI_TRANS_DMA_USE_PSRAM (1<<12) ///< Use PSRAM for DMA buffer directly, has speed limit, but no temp buffer and save memory
// Output flags
#define SPI_TRANS_DMA_RX_FAIL (1<<30) ///< RX transaction data lose flag, indicate DMA RX overflow
#define SPI_TRANS_DMA_TX_FAIL (1<<31) ///< TX transaction data lose flag, indicate DMA TX underflow
/**
* This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes.
+105 -54
View File
@@ -116,6 +116,7 @@ We have two bits to control the interrupt:
#include "esp_private/spi_common_internal.h"
#include "esp_private/spi_master_internal.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/cache_utils.h"
#include "driver/spi_master.h"
#include "clk_ctrl_os.h"
#include "esp_log.h"
@@ -131,10 +132,6 @@ We have two bits to control the interrupt:
#include "hal/spi_hal.h"
#include "hal/spi_ll.h"
#include "hal/hal_utils.h"
#include "esp_heap_caps.h"
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_cache.h"
#endif
#ifdef CONFIG_SPI_MASTER_ISR_IN_IRAM
#define SPI_MASTER_ISR_ATTR IRAM_ATTR
@@ -162,7 +159,25 @@ We have two bits to control the interrupt:
#define SPI_PERIPH_SRC_FREQ_MAX (80*1000*1000) //peripheral hardware limitation for clock source into peripheral
static const char *SPI_TAG = "spi_master";
/**
* The approx time for dma setup and pop data into peripheral
* This time is theoretically inverse proportion to the PSRAM speed(bandwidth), and direct proportion to the SPI speed, but hard to accurately calculated
* Below is an engineering value based on experience test result, e.g. delay 5us for 20MHz PSRAM speed, 1us for 80M
* Then the formula is: Delay_time = K * (SPI_SPEED / PSRAM_SPEED) + B
* delay
* ▲
* │ x
* │ x
* │ x
* │ x x
* ─┼─────────────────►
* │ psram speed
*/
#define K_EDMA_SETUP_RATIO 1 / 50000
#define B_EDMA_SETUP_TIME_US 1
#define SPI_EDMA_SETUP_TIME_US(spi_speed) ((spi_speed) * K_EDMA_SETUP_RATIO / CONFIG_SPIRAM_SPEED + B_EDMA_SETUP_TIME_US)
ESP_LOG_ATTR_TAG_DRAM(SPI_TAG, "spi_master");
#define SPI_CHECK(a, str, ret_val, ...) ESP_RETURN_ON_FALSE_ISR(a, ret_val, SPI_TAG, str, ##__VA_ARGS__)
typedef struct spi_device_t spi_device_t;
@@ -835,6 +850,13 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_
if (dev->cfg.pre_cb) {
dev->cfg.pre_cb(trans);
}
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
spi_hal_clear_intr_mask(hal, SPI_LL_INTR_IN_FULL | SPI_LL_INTR_OUT_EMPTY);
if (esp_ptr_dma_ext_capable(hal_trans.send_buffer)) {
// ! Delay here is required for EDMA to pass data from PSRAM to GPSPI
esp_rom_delay_us(SPI_EDMA_SETUP_TIME_US(hal_dev->timing_conf.real_freq));
}
#endif
//Kick off transfer
spi_hal_user_start(hal);
}
@@ -932,6 +954,26 @@ static void SPI_MASTER_ISR_ATTR spi_post_sct_trans(spi_host_t *host)
}
#endif //#if SOC_SPI_SCT_SUPPORTED
static void SPI_MASTER_ISR_ATTR spi_trans_dma_error_check(spi_host_t *host)
{
#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma
if (!host->sct_mode_enabled) {
if (esp_ptr_external_ram(host->cur_trans_buf.buffer_to_rcv) && spi_hal_get_intr_mask(&host->hal, SPI_LL_INTR_IN_FULL)) {
host->cur_trans_buf.trans->flags |= SPI_TRANS_DMA_RX_FAIL;
ESP_DRAM_LOGE(SPI_TAG, "DMA RX overflow detected");
} else {
host->cur_trans_buf.trans->flags &= ~SPI_TRANS_DMA_RX_FAIL;
}
if (esp_ptr_external_ram(host->cur_trans_buf.buffer_to_send) && spi_hal_get_intr_mask(&host->hal, SPI_LL_INTR_OUT_EMPTY)) {
host->cur_trans_buf.trans->flags |= SPI_TRANS_DMA_TX_FAIL;
ESP_DRAM_LOGE(SPI_TAG, "DMA TX underflow detected");
} else {
host->cur_trans_buf.trans->flags &= ~SPI_TRANS_DMA_TX_FAIL;
}
}
#endif
}
// This is run in interrupt context.
static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
{
@@ -981,6 +1023,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
(void)ret;
}
#endif
spi_trans_dma_error_check(host);
}
#if SOC_SPI_SCT_SUPPORTED
@@ -1132,7 +1175,9 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
SPI_CHECK(trans_desc->length <= SPI_LL_CPU_MAX_BIT_LEN, "txdata transfer > hardware max supported len", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->rxlength <= SPI_LL_CPU_MAX_BIT_LEN, "rxdata transfer > hardware max supported len", ESP_ERR_INVALID_ARG);
}
if (esp_ptr_external_ram(trans_desc->tx_buffer) || esp_ptr_external_ram(trans_desc->rx_buffer)){
SPI_CHECK(spi_flash_cache_enabled(), "Using PSRAM must when cache is enabled", ESP_ERR_INVALID_STATE);
}
return ESP_OK;
}
@@ -1154,6 +1199,45 @@ 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(uint32_t *buffer, uint32_t len, uint32_t alignment, bool is_tx, uint32_t flags, uint32_t **ret_buffer)
{
bool unaligned = ((((uint32_t)buffer) | len) & (alignment - 1));
#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
if (!((flags & SPI_TRANS_DMA_USE_PSRAM) && esp_ptr_dma_ext_capable(buffer))) {
// tx don't need align on addr or length on those chips
unaligned = is_tx ? false : (((uint32_t)buffer) & (alignment - 1));
}
#endif
#if SOC_PSRAM_DMA_CAPABLE
if ((flags & SPI_TRANS_DMA_USE_PSRAM) && esp_ptr_dma_ext_capable(buffer)) {
// dma psram don't do additional copy, check only
ESP_RETURN_ON_FALSE_ISR(!unaligned, ESP_ERR_INVALID_ARG, SPI_TAG, "%s buffer addr and length should align to %ld Byte to use PSRAM, use heap_caps_aligned_calloc() may helpful", is_tx ? "TX" : "RX", alignment);
ESP_RETURN_ON_ERROR_ISR(esp_cache_msync((void *)buffer, len, is_tx ? ESP_CACHE_MSYNC_FLAG_DIR_C2M : ESP_CACHE_MSYNC_FLAG_DIR_M2C), SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
*ret_buffer = buffer;
return ESP_OK;
}
#endif
if ((!esp_ptr_dma_capable(buffer) || unaligned)) {
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);
//if buf in the desc not DMA-capable, or not bytes aligned to alignment, malloc a new one
ESP_EARLY_LOGD(SPI_TAG, "Allocate %s buffer for DMA", is_tx ? "TX" : "RX");
len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
uint32_t *temp = heap_caps_aligned_alloc(alignment, len, MALLOC_CAP_DMA);
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;
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
ESP_RETURN_ON_ERROR_ISR(esp_cache_msync((void *)buffer, len, is_tx ? ESP_CACHE_MSYNC_FLAG_DIR_C2M : ESP_CACHE_MSYNC_FLAG_DIR_M2C), SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
#endif
*ret_buffer = buffer;
return ESP_OK;
}
static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans_priv_t* priv_desc)
{
spi_transaction_t *trans_desc = priv_desc->trans;
@@ -1170,70 +1254,36 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans
}
// tx memory assign
const uint32_t *send_ptr;
uint32_t *send_ptr;
if (trans_desc->flags & SPI_TRANS_USE_TXDATA) {
send_ptr = (uint32_t *)&trans_desc->tx_data[0];
} else {
//if not use TXDATA neither tx_buffer, tx data assigned to NULL
send_ptr = trans_desc->tx_buffer ;
send_ptr = (uint32_t *)trans_desc->tx_buffer ;
}
uint32_t tx_byte_len = (trans_desc->length + 7) / 8;
uint32_t rx_byte_len = (trans_desc->rxlength + 7) / 8;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
bool tx_unaligned = ((((uint32_t)send_ptr) | tx_byte_len) & (alignment - 1));
bool rx_unaligned = ((((uint32_t)rcv_ptr) | rx_byte_len) & (alignment - 1));
#else
bool tx_unaligned = false; //tx don't need align on addr or length, for other chips
bool rx_unaligned = (((uint32_t)rcv_ptr) & (alignment - 1));
#endif
esp_err_t ret = ESP_OK;
if (send_ptr && bus_attr->dma_enabled) {
if ((!esp_ptr_dma_capable(send_ptr) || tx_unaligned)) {
ESP_RETURN_ON_FALSE(!(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but TX buffer addr&len not align to %d byte, or not dma_capable", alignment);
//if txbuf in the desc not DMA-capable, or not bytes aligned to alignment, malloc a new one
ESP_EARLY_LOGD(SPI_TAG, "Allocate TX buffer for DMA");
tx_byte_len = (tx_byte_len + alignment - 1) & (~(alignment - 1)); // up align alignment
uint32_t *temp = heap_caps_aligned_alloc(alignment, tx_byte_len, MALLOC_CAP_DMA);
if (temp == NULL) {
goto clean_up;
}
memcpy(temp, send_ptr, (trans_desc->length + 7) / 8);
send_ptr = temp;
ret = setup_dma_priv_buffer(send_ptr, (trans_desc->length + 7) / 8, alignment, true, trans_desc->flags, &send_ptr);
if (ret != ESP_OK) {
goto clean_up;
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
esp_err_t ret = esp_cache_msync((void *)send_ptr, tx_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
(void)ret;
#endif
}
if (rcv_ptr && bus_attr->dma_enabled) {
if ((!esp_ptr_dma_capable(rcv_ptr) || rx_unaligned)) {
ESP_RETURN_ON_FALSE(!(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but RX buffer addr&len not align to %d byte, or not dma_capable", alignment);
//if rxbuf in the desc not DMA-capable, or not aligned to alignment, malloc a new one
ESP_EARLY_LOGD(SPI_TAG, "Allocate RX buffer for DMA");
rx_byte_len = (rx_byte_len + alignment - 1) & (~(alignment - 1)); // up align alignment
rcv_ptr = heap_caps_aligned_alloc(alignment, rx_byte_len, MALLOC_CAP_DMA);
if (rcv_ptr == NULL) {
goto clean_up;
}
ret = setup_dma_priv_buffer(rcv_ptr, (trans_desc->rxlength + 7) / 8, alignment, false, trans_desc->flags, &rcv_ptr);
if (ret != ESP_OK) {
goto clean_up;
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
// do invalid here to hold on cache status to avoid hardware auto write back during dma transaction
esp_err_t ret = esp_cache_msync((void *)rcv_ptr, rx_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
(void)ret;
#endif
}
priv_desc->buffer_to_send = send_ptr;
priv_desc->buffer_to_rcv = rcv_ptr;
return ESP_OK;
clean_up:
uninstall_priv_desc(priv_desc);
return ESP_ERR_NO_MEM;
return ret;
}
esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, uint32_t ticks_to_wait)
@@ -1312,7 +1362,7 @@ esp_err_t SPI_MASTER_ATTR spi_device_get_trans_result(spi_device_handle_t handle
}
(*trans_desc) = trans_buf.trans;
return ESP_OK;
return (trans_buf.trans->flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)) ? ESP_ERR_INVALID_STATE : ESP_OK;
}
//Porcelain to do one blocking transmission.
@@ -1461,6 +1511,8 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle,
return ESP_ERR_TIMEOUT;
}
}
spi_trans_dma_error_check(host);
uint32_t trans_flags = host->cur_trans_buf.trans->flags; // save the flags before bus_lock release
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
const spi_bus_attr_t *bus_attr = host->bus_attr;
@@ -1474,7 +1526,6 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle,
}
}
#endif
ESP_LOGV(SPI_TAG, "polling trans done");
//deal with the in-flight transaction
spi_post_trans(host);
@@ -1490,7 +1541,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle,
spi_bus_lock_acquire_end(handle->dev_lock);
}
return ESP_OK;
return (trans_flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)) ? ESP_ERR_INVALID_STATE : ESP_OK;
}
esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_transmit(spi_device_handle_t handle, spi_transaction_t* trans_desc)
@@ -16,6 +16,6 @@ endif()
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(
SRCS ${srcs}
PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio
PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm
WHOLE_ARCHIVE
)
@@ -25,6 +25,7 @@
#include "esp_clk_tree.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "esp_cache.h"
#include "test_utils.h"
#include "test_spi_utils.h"
#include "spi_performance.h"
@@ -775,24 +776,27 @@ TEST_CASE("SPI Master DMA test, TX and RX in different regions", "[spi]")
//connect MOSI to two devices breaks the output, fix it.
spitest_gpio_output_sel(buscfg.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SPI_HOST].spid_out);
#define TEST_REGION_SIZE 2
#define TEST_REGION_SIZE 3
static spi_transaction_t trans[TEST_REGION_SIZE];
int x;
memset(trans, 0, sizeof(trans));
trans[0].length = 320 * 8,
trans[0].tx_buffer = data_malloc + 2;
trans[0].length = 320 * 8;
trans[0].tx_buffer = data_malloc + 2;
trans[0].rx_buffer = data_dram;
trans[1].length = 4 * 8,
trans[1].flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;
trans[1].length = 4 * 8;
trans[1].flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;
uint32_t *ptr = (uint32_t *)trans[1].rx_data;
*ptr = 0x54545454;
ptr = (uint32_t *)trans[1].tx_data;
*ptr = 0xbc124960;
trans[2].length = 64 * 8;
trans[2].tx_buffer = data_drom;
trans[2].rx_buffer = data_malloc;
//Queue all transactions.
for (x = 0; x < TEST_REGION_SIZE; x++) {
for (int x = 0; x < TEST_REGION_SIZE; x++) {
ESP_LOGI(TAG, "transmitting %d...", x);
ret = spi_device_transmit(spi, &trans[x]);
TEST_ASSERT(ret == ESP_OK);
@@ -1967,3 +1971,95 @@ TEST_CASE("test_spi_master_auto_sleep_retention", "[spi]")
}
#endif //CONFIG_PM_ENABLE
#endif //SOC_LIGHT_SLEEP_SUPPORTED
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
#define TEST_EDMA_PSRAM_TRANS_NUM 5
#define TEST_EDMA_TRANS_LEN 20480 //Use PSRAM need a 16/32/64 aligned buffer len
#define TEST_EDMA_BUFFER_SZ (TEST_EDMA_PSRAM_TRANS_NUM * TEST_EDMA_TRANS_LEN)
void test_spi_psram_trans(spi_device_handle_t dev_handle, void *tx, void *rx)
{
spi_transaction_t trans_cfg = {
.length = TEST_EDMA_TRANS_LEN * 8,
.flags = SPI_TRANS_DMA_USE_PSRAM,
};
for (uint8_t cnt = 0; cnt < TEST_EDMA_PSRAM_TRANS_NUM; cnt ++) {
trans_cfg.tx_buffer = tx + TEST_EDMA_TRANS_LEN * cnt;
trans_cfg.rx_buffer = rx + TEST_EDMA_TRANS_LEN * cnt;
// To use psram, hardware will pass data through MSPI and GDMA to GPSPI, which need some time
// GPSPI bandwidth(speed * line_num) should always no more than PSRAM bandwidth
trans_cfg.override_freq_hz = (CONFIG_SPIRAM_SPEED / 4) * 1000 * 1000;
printf("%d TX %p RX %p len %d @%ld kHz\n", cnt, trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN, trans_cfg.override_freq_hz / 1000);
TEST_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg));
TEST_ASSERT(!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)));
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN);
}
}
TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]")
{
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.miso_io_num = buscfg.mosi_io_num; // set spi "self-loopback"
buscfg.max_transfer_sz = TEST_EDMA_BUFFER_SZ;
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
spi_device_handle_t dev_handle;
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
devcfg.clock_speed_hz = 80 * 1000 * 1000; // Test error case on highest freq first
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev_handle));
int real_freq_khz;
spi_device_get_actual_freq(dev_handle, &real_freq_khz);
uint32_t cache_width = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
uint8_t *internal_1 = heap_caps_aligned_calloc(cache_width, 1, TEST_EDMA_BUFFER_SZ, MALLOC_CAP_INTERNAL);
uint8_t *external_1 = heap_caps_aligned_calloc(cache_width, 1, TEST_EDMA_BUFFER_SZ, MALLOC_CAP_SPIRAM);
uint8_t *external_2 = heap_caps_aligned_calloc(cache_width, 1, TEST_EDMA_BUFFER_SZ, MALLOC_CAP_SPIRAM);
test_fill_random_to_buffers_dualboard(1001, internal_1, external_2, TEST_EDMA_BUFFER_SZ);
printf("Test error case: High freq @%d kHz\n", real_freq_khz);
spi_transaction_t trans_cfg = {
.length = TEST_EDMA_TRANS_LEN * 8,
.tx_buffer = external_2,
.rx_buffer = external_1,
};
for (uint8_t i = 0; i < 2; i++) {
trans_cfg.flags = i ? SPI_TRANS_DMA_USE_PSRAM : 0;
uint32_t before = esp_get_free_heap_size();
spi_device_polling_start(dev_handle, &trans_cfg, portMAX_DELAY);
uint32_t after = esp_get_free_heap_size();
printf("\n==== %s ====\n", i ? "EDMA" : "Auto Malloc");
printf("before: %ld, after: %ld, diff: %ld\n", before, after, after - before);
spi_device_polling_end(dev_handle, portMAX_DELAY);
printf("RX fail: %d, TX fail: %d\n", !!(trans_cfg.flags & SPI_TRANS_DMA_RX_FAIL), !!(trans_cfg.flags & SPI_TRANS_DMA_TX_FAIL));
TEST_ASSERT(i ? (before - after) < TEST_EDMA_TRANS_LEN : (before - after) > 2 * TEST_EDMA_TRANS_LEN);
TEST_ASSERT((!!i) == !!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)));
}
printf("\nTest unaligned tx psram buffer\n");
trans_cfg.tx_buffer ++;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, spi_device_transmit(dev_handle, &trans_cfg));
printf("\nTest trans: internal -> psram\n");
memset(external_1, 0, TEST_EDMA_BUFFER_SZ);
TEST_ESP_OK(esp_cache_msync((void *)external_1, TEST_EDMA_BUFFER_SZ, ESP_CACHE_MSYNC_FLAG_DIR_C2M));
test_spi_psram_trans(dev_handle, internal_1, external_1);
printf("\nTest trans: psram -> psram\n");
memset(external_2, 0, TEST_EDMA_BUFFER_SZ);
TEST_ESP_OK(esp_cache_msync((void *)external_2, TEST_EDMA_BUFFER_SZ, ESP_CACHE_MSYNC_FLAG_DIR_C2M));
test_spi_psram_trans(dev_handle, external_1, external_2);
printf("\nTest trans: psram -> internal\n");
memset(internal_1, 0, TEST_EDMA_BUFFER_SZ);
test_spi_psram_trans(dev_handle, external_2, internal_1);
free(internal_1);
free(external_1);
free(external_2);
spi_bus_remove_device(dev_handle);
spi_bus_free(TEST_SPI_HOST);
}
#endif
@@ -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