mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feature(driver_spi): slave driver support psram transfer
This commit is contained in:
@@ -185,6 +185,7 @@ esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transact
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_NOT_SUPPORTED if flag `SPI_SLAVE_NO_RETURN_RESULT` is set
|
||||
* - ESP_ERR_INVALID_STATE if dma over/underflow error occurs during psram transfer
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, uint32_t ticks_to_wait);
|
||||
@@ -204,6 +205,7 @@ esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transacti
|
||||
* out.
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_ERR_INVALID_STATE if dma over/underflow error occurs during psram transfer
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait);
|
||||
|
||||
@@ -137,6 +137,23 @@ esp_err_t spicommon_dma_desc_alloc(spi_host_device_t host_id, int cfg_max_sz, in
|
||||
*/
|
||||
void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx);
|
||||
|
||||
/**
|
||||
* @brief Setup private buffer for DMA transfer
|
||||
*
|
||||
* @param host_id SPI host ID to access the DMA context
|
||||
* @param buffer buffer to be setup
|
||||
* @param len length of buffer, in byte
|
||||
* @param is_tx if buffer is for tx/transmit direction
|
||||
* @param psram_prefer if psram is preferred
|
||||
* @param auto_malloc if auto malloc is enabled
|
||||
* @param ret_buffer return buffer, which is the buffer that is actually used for DMA transfer
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: On success
|
||||
* - ESP_ERR_NO_MEM: No enough memory
|
||||
*/
|
||||
esp_err_t spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer);
|
||||
|
||||
/**
|
||||
* @brief Free DMA for SPI
|
||||
*
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "esp_cache.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_private/gpio.h"
|
||||
@@ -388,6 +388,56 @@ void SPI_COMMON_ISR_ATTR spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc,
|
||||
dmadesc[n - 1].next = NULL;
|
||||
}
|
||||
|
||||
esp_err_t SPI_COMMON_ISR_ATTR spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer)
|
||||
{
|
||||
spi_bus_attr_t *bus_attr = spi_bus_get_attr(host_id);
|
||||
spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id);
|
||||
assert(bus_attr && dma_ctx);
|
||||
if ((buffer == NULL) || (len == 0)) {
|
||||
*ret_buffer = buffer;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool is_ptr_ext = esp_ptr_external_ram(buffer);
|
||||
bool use_psram = is_ptr_ext && psram_prefer;
|
||||
#if SOC_IS(ESP32S2)
|
||||
ESP_RETURN_ON_FALSE_ISR((host_id != SPI3_HOST) || !use_psram, ESP_ERR_NOT_SUPPORTED, SPI_TAG, "SPI3 does not support external memory");
|
||||
#endif
|
||||
bool need_malloc = is_ptr_ext ? (!use_psram || !esp_ptr_dma_ext_capable(buffer)) : !esp_ptr_dma_capable(buffer);
|
||||
// If psram is wanted, re-malloc also from psram.
|
||||
uint32_t mem_cap = MALLOC_CAP_DMA | (use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL);
|
||||
uint16_t alignment = 0;
|
||||
if (is_tx) {
|
||||
alignment = use_psram ? dma_ctx->dma_align_tx_ext : dma_ctx->dma_align_tx_int;
|
||||
} else {
|
||||
// RX cache sync still need consider the cache alignment requirement
|
||||
if (use_psram) {
|
||||
alignment = MAX(dma_ctx->dma_align_rx_ext, bus_attr->cache_align_ext);
|
||||
} else {
|
||||
alignment = MAX(dma_ctx->dma_align_rx_int, bus_attr->cache_align_int);
|
||||
}
|
||||
}
|
||||
need_malloc |= (((uint32_t)buffer | len) & (alignment - 1));
|
||||
uint32_t align_len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
|
||||
ESP_EARLY_LOGV(SPI_TAG, "SPI%d %s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", host_id + 1, is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal");
|
||||
if (need_malloc) {
|
||||
ESP_RETURN_ON_FALSE_ISR(auto_malloc, ESP_ERR_INVALID_STATE, SPI_TAG, "%s addr&len not align to %d, or not dma_capable, suggest use 'heap_caps_malloc' or enable auto_align", is_tx ? "TX" : "RX", alignment);
|
||||
uint32_t *temp = heap_caps_aligned_alloc(alignment, align_len, mem_cap);
|
||||
ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX");
|
||||
|
||||
if (is_tx) {
|
||||
memcpy(temp, buffer, len);
|
||||
}
|
||||
buffer = temp;
|
||||
}
|
||||
*ret_buffer = buffer;
|
||||
uint32_t sync_flags = is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C;
|
||||
esp_err_t ret = esp_cache_msync((void *)buffer, need_malloc ? align_len : len, sync_flags);
|
||||
// ESP_ERR_NOT_SUPPORTED stands for not cache sync required, it's allowed here
|
||||
ESP_RETURN_ON_FALSE_ISR((ret == ESP_OK) || (ret == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------free dma periph-------------------------------------------------------//
|
||||
esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id)
|
||||
{
|
||||
@@ -932,7 +982,7 @@ void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t
|
||||
return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA);
|
||||
}
|
||||
|
||||
spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
|
||||
SPI_COMMON_ISR_ATTR spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
|
||||
{
|
||||
if (bus_ctx[host_id] == NULL) {
|
||||
return NULL;
|
||||
@@ -941,7 +991,7 @@ spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
|
||||
return &bus_ctx[host_id]->bus_attr;
|
||||
}
|
||||
|
||||
spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id)
|
||||
SPI_COMMON_ISR_ATTR spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id)
|
||||
{
|
||||
return spi_dma_ctx[host_id];
|
||||
}
|
||||
|
||||
@@ -124,10 +124,10 @@ We have two bits to control the interrupt:
|
||||
#include "esp_ipc.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/spi_hal.h"
|
||||
#include "hal/spi_ll.h"
|
||||
@@ -1175,47 +1175,6 @@ static SPI_MASTER_ISR_ATTR void uninstall_priv_desc(spi_trans_priv_t* trans_buf)
|
||||
}
|
||||
}
|
||||
|
||||
static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uint32_t *buffer, uint32_t len, bool is_tx, uint32_t flags, uint32_t **ret_buffer)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
ESP_RETURN_ON_FALSE_ISR((host->id != SPI3_HOST) || !(flags & SPI_TRANS_DMA_USE_PSRAM), ESP_ERR_NOT_SUPPORTED, SPI_TAG, "SPI3 does not support external memory");
|
||||
#endif
|
||||
bool is_ptr_ext = esp_ptr_external_ram(buffer);
|
||||
bool use_psram = is_ptr_ext && (flags & SPI_TRANS_DMA_USE_PSRAM);
|
||||
bool need_malloc = is_ptr_ext ? (!use_psram || !esp_ptr_dma_ext_capable(buffer)) : !esp_ptr_dma_capable(buffer);
|
||||
uint16_t alignment = 0;
|
||||
// If psram is wanted, re-malloc also from psram.
|
||||
uint32_t mem_cap = MALLOC_CAP_DMA | (use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL);
|
||||
if (is_tx) {
|
||||
alignment = use_psram ? host->dma_ctx->dma_align_tx_ext : host->dma_ctx->dma_align_tx_int;
|
||||
} else {
|
||||
// RX cache sync still need consider the cache alignment requirement
|
||||
if (use_psram) {
|
||||
alignment = MAX(host->dma_ctx->dma_align_rx_ext, host->bus_attr->cache_align_ext);
|
||||
} else {
|
||||
alignment = MAX(host->dma_ctx->dma_align_rx_int, host->bus_attr->cache_align_int);
|
||||
}
|
||||
}
|
||||
need_malloc |= (((uint32_t)buffer | len) & (alignment - 1));
|
||||
ESP_EARLY_LOGV(SPI_TAG, "%s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal");
|
||||
if (need_malloc) {
|
||||
ESP_RETURN_ON_FALSE_ISR(!(flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but %s addr&len not align to %d, or not dma_capable", is_tx ? "TX" : "RX", alignment);
|
||||
len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
|
||||
uint32_t *temp = heap_caps_aligned_alloc(alignment, len, mem_cap);
|
||||
ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX");
|
||||
|
||||
if (is_tx) {
|
||||
memcpy(temp, buffer, len);
|
||||
}
|
||||
buffer = temp;
|
||||
}
|
||||
esp_err_t ret = esp_cache_msync((void *)buffer, len, is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
// ESP_ERR_NOT_SUPPORTED stands for not cache sync required, it's allowed here
|
||||
ESP_RETURN_ON_FALSE_ISR((ret == ESP_OK) || (ret == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
|
||||
*ret_buffer = buffer;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans_priv_t* priv_desc)
|
||||
{
|
||||
spi_transaction_t *trans_desc = priv_desc->trans;
|
||||
@@ -1225,25 +1184,23 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans
|
||||
uint32_t* rcv_ptr = (trans_desc->flags & SPI_TRANS_USE_RXDATA) ? (uint32_t *)trans_desc->rx_data : (uint32_t *)trans_desc->rx_buffer;
|
||||
// tx memory assign
|
||||
uint32_t *send_ptr = (trans_desc->flags & SPI_TRANS_USE_TXDATA) ? (uint32_t *)trans_desc->tx_data : (uint32_t *)trans_desc->tx_buffer;
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (send_ptr && bus_attr->dma_enabled) {
|
||||
ret = setup_dma_priv_buffer(host, send_ptr, (trans_desc->length + 7) / 8, true, trans_desc->flags, &send_ptr);
|
||||
if (ret != ESP_OK) {
|
||||
goto clean_up;
|
||||
}
|
||||
if (!bus_attr->dma_enabled) {
|
||||
priv_desc->buffer_to_send = send_ptr;
|
||||
priv_desc->buffer_to_rcv = rcv_ptr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (rcv_ptr && bus_attr->dma_enabled) {
|
||||
ret = setup_dma_priv_buffer(host, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, trans_desc->flags, &rcv_ptr);
|
||||
if (ret != ESP_OK) {
|
||||
goto clean_up;
|
||||
}
|
||||
bool use_psram = trans_desc->flags & SPI_TRANS_DMA_USE_PSRAM;
|
||||
bool auto_malloc = !(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL);
|
||||
esp_err_t ret = spicommon_dma_setup_priv_buffer(host->id, send_ptr, (trans_desc->length + 7) / 8, true, use_psram, auto_malloc, (void *)&priv_desc->buffer_to_send);
|
||||
if (ret != ESP_OK) {
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
priv_desc->buffer_to_send = send_ptr;
|
||||
priv_desc->buffer_to_rcv = rcv_ptr;
|
||||
return ESP_OK;
|
||||
ret = spicommon_dma_setup_priv_buffer(host->id, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, use_psram, auto_malloc, &priv_desc->buffer_to_rcv);
|
||||
if (ret != ESP_OK) {
|
||||
goto clean_up;
|
||||
}
|
||||
return ret;
|
||||
|
||||
clean_up:
|
||||
uninstall_priv_desc(priv_desc);
|
||||
|
||||
@@ -54,8 +54,9 @@ static const char *SPI_TAG = "spi_slave";
|
||||
/// struct to hold private transaction data (like tx and rx buffer for DMA).
|
||||
typedef struct {
|
||||
spi_slave_transaction_t *trans; //original trans
|
||||
void *tx_buffer; //actually tx buffer (re-malloced if needed)
|
||||
void *rx_buffer; //actually rx buffer (re-malloced if needed)
|
||||
uint32_t *tx_buffer; //actually tx buffer (re-malloced if needed)
|
||||
uint32_t *rx_buffer; //actually rx buffer (re-malloced if needed)
|
||||
bool dma_hw_error; //true if DMA hardware over/underflow occurred
|
||||
} spi_slave_trans_priv_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -73,7 +74,6 @@ typedef struct {
|
||||
QueueHandle_t ret_queue;
|
||||
bool cs_iomux;
|
||||
uint8_t cs_in_signal;
|
||||
uint16_t internal_mem_align_size;
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
esp_pm_lock_handle_t pm_lock;
|
||||
#endif
|
||||
@@ -199,14 +199,6 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
||||
hal->dmadesc_tx = spihost[host]->dma_ctx->dmadesc_tx;
|
||||
hal->dmadesc_rx = spihost[host]->dma_ctx->dmadesc_rx;
|
||||
hal->dmadesc_n = spihost[host]->dma_ctx->dma_desc_num;
|
||||
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
size_t alignment;
|
||||
esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment);
|
||||
spihost[host]->internal_mem_align_size = alignment;
|
||||
#else
|
||||
spihost[host]->internal_mem_align_size = 4;
|
||||
#endif
|
||||
}
|
||||
|
||||
err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL);
|
||||
@@ -408,7 +400,6 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t
|
||||
ESP_EARLY_LOGW(SPI_TAG, "Use DMA but real trans_len is not 4 bytes aligned, slave may loss data");
|
||||
}
|
||||
#endif
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
if (spihost[host]->bus_attr->dma_enabled) {
|
||||
if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) {
|
||||
free(priv_trans->tx_buffer);
|
||||
@@ -418,54 +409,28 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t
|
||||
free(priv_trans->rx_buffer);
|
||||
}
|
||||
}
|
||||
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
}
|
||||
|
||||
static esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_setup_priv_trans(spi_host_device_t host, spi_slave_trans_priv_t *priv_trans)
|
||||
static esp_err_t SPI_SLAVE_ATTR spi_slave_setup_priv_trans(spi_host_device_t host, spi_slave_trans_priv_t *priv_trans)
|
||||
{
|
||||
spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
|
||||
|
||||
priv_trans->tx_buffer = (void *)trans->tx_buffer;
|
||||
priv_trans->rx_buffer = trans->rx_buffer;
|
||||
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
uint16_t alignment = spihost[host]->internal_mem_align_size;
|
||||
uint32_t buffer_byte_len = (trans->length + 7) / 8;
|
||||
|
||||
if (spihost[host]->bus_attr->dma_enabled && trans->tx_buffer) {
|
||||
if ((!esp_ptr_dma_capable(trans->tx_buffer) || ((((uint32_t)trans->tx_buffer) | buffer_byte_len) & (alignment - 1)))) {
|
||||
ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "TX buffer addr&len not align to %d byte, or not dma_capable", alignment);
|
||||
//if txbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one
|
||||
ESP_EARLY_LOGD(SPI_TAG, "Allocate TX buffer for DMA");
|
||||
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment"
|
||||
uint32_t *temp = heap_caps_aligned_alloc(alignment, buffer_byte_len, MALLOC_CAP_DMA);
|
||||
if (temp == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
memcpy(temp, trans->tx_buffer, (trans->length + 7) / 8);
|
||||
priv_trans->tx_buffer = temp;
|
||||
}
|
||||
esp_err_t ret = esp_cache_msync((void *)priv_trans->tx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
||||
ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync c2m(writeback) fail");
|
||||
if (!spihost[host]->bus_attr->dma_enabled) {
|
||||
priv_trans->tx_buffer = (uint32_t *)trans->tx_buffer;
|
||||
priv_trans->rx_buffer = (uint32_t *)trans->rx_buffer;
|
||||
return ESP_OK;
|
||||
}
|
||||
if (spihost[host]->bus_attr->dma_enabled && trans->rx_buffer) {
|
||||
if ((!esp_ptr_dma_capable(trans->rx_buffer) || ((((uint32_t)trans->rx_buffer) | (trans->length + 7) / 8) & (alignment - 1)))) {
|
||||
ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "RX buffer addr&len not align to %d byte, or not dma_capable", alignment);
|
||||
//if rxbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one
|
||||
ESP_EARLY_LOGD(SPI_TAG, "Allocate RX buffer for DMA");
|
||||
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment"
|
||||
priv_trans->rx_buffer = heap_caps_aligned_alloc(alignment, buffer_byte_len, MALLOC_CAP_DMA);
|
||||
if (priv_trans->rx_buffer == NULL) {
|
||||
free(priv_trans->tx_buffer);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
esp_err_t ret = esp_cache_msync((void *)priv_trans->rx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync m2c(invalid) fail");
|
||||
|
||||
bool auto_malloc = (trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO);
|
||||
esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, (trans->length + 7) / 8, true, true, auto_malloc, &priv_trans->tx_buffer);
|
||||
if (ret != ESP_OK) {
|
||||
spi_slave_uninstall_priv_trans(host, priv_trans);
|
||||
return ret;
|
||||
}
|
||||
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
return ESP_OK;
|
||||
ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, (trans->length + 7) / 8, false, true, auto_malloc, &priv_trans->rx_buffer);
|
||||
if (ret != ESP_OK) {
|
||||
spi_slave_uninstall_priv_trans(host, priv_trans);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait)
|
||||
@@ -473,19 +438,6 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi
|
||||
BaseType_t r;
|
||||
SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(spihost[host]->bus_attr->dma_enabled == 0 || trans_desc->tx_buffer == NULL || esp_ptr_dma_capable(trans_desc->tx_buffer),
|
||||
"txdata not in DMA-capable memory", ESP_ERR_INVALID_ARG);
|
||||
|
||||
// We don't check length WORD alignment for rx when using DMA, seems break DMA requirement,
|
||||
// however peripheral can also stop DMA from over writing memory even if it not aligned (except esp32).
|
||||
// ATTENTION!: On esp32, peripheral can NOT stop DMA, if length not WORD aligned,
|
||||
// remain bytes in last word domain will overwritten by DMA HW, which may cause unexpected issues!
|
||||
// But driver already used for long time, to avoid breaking changes, we still don't add alignment limit.
|
||||
SPI_CHECK(spihost[host]->bus_attr->dma_enabled == 0 || trans_desc->rx_buffer == NULL ||
|
||||
(esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) &&
|
||||
(trans_desc->length % 8 == 0)),
|
||||
"rxdata not in DMA-capable memory or not BYTE aligned", ESP_ERR_INVALID_ARG);
|
||||
|
||||
SPI_CHECK(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
|
||||
|
||||
spi_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc};
|
||||
@@ -539,29 +491,17 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c
|
||||
ESP_RETURN_ON_FALSE_ISR(is_valid_host(host), ESP_ERR_INVALID_ARG, SPI_TAG, "invalid host");
|
||||
ESP_RETURN_ON_FALSE_ISR(spihost[host], ESP_ERR_INVALID_ARG, SPI_TAG, "host not slave");
|
||||
ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum");
|
||||
if (spihost[host]->bus_attr->dma_enabled) {
|
||||
uint16_t alignment = spihost[host]->internal_mem_align_size;
|
||||
(void) alignment;
|
||||
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
// For those targets length and addr alignment is still required from Cache side
|
||||
uint32_t buffer_byte_len = (trans_desc->length + 7) / 8;
|
||||
bool tx_aligned = (trans_desc->tx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->tx_buffer) && ((((uint32_t)trans_desc->tx_buffer | buffer_byte_len) & (alignment - 1)) == 0));
|
||||
bool rx_aligned = (trans_desc->rx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->rx_buffer) && ((((uint32_t)trans_desc->rx_buffer | buffer_byte_len) & (alignment - 1)) == 0));
|
||||
#else
|
||||
bool tx_aligned = (trans_desc->tx_buffer == NULL) || esp_ptr_dma_capable(trans_desc->tx_buffer);
|
||||
bool rx_aligned = (trans_desc->rx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) && (trans_desc->length % 8 == 0));
|
||||
#endif
|
||||
|
||||
ESP_RETURN_ON_FALSE_ISR(tx_aligned, ESP_ERR_INVALID_ARG, SPI_TAG, "txdata addr & len not align to %d bytes or not dma_capable", alignment);
|
||||
ESP_RETURN_ON_FALSE_ISR(rx_aligned, ESP_ERR_INVALID_ARG, SPI_TAG, "rxdata addr & len not align to %d bytes or not dma_capable", alignment);
|
||||
}
|
||||
|
||||
spi_slave_trans_priv_t priv_trans = {
|
||||
.trans = (spi_slave_transaction_t *)trans_desc,
|
||||
.tx_buffer = (void *)trans_desc->tx_buffer,
|
||||
.rx_buffer = trans_desc->rx_buffer,
|
||||
};
|
||||
if (spihost[host]->bus_attr->dma_enabled) {
|
||||
// isr api is not allowed to auto_malloc, so don't need to 'uninstall' anything here, return directly
|
||||
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->tx_buffer, (trans_desc->length + 7) / 8, true, true, false, &priv_trans.tx_buffer), SPI_TAG, "");
|
||||
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->rx_buffer, (trans_desc->length + 7) / 8, false, true, false, &priv_trans.rx_buffer), SPI_TAG, "");
|
||||
}
|
||||
r = xQueueSendFromISR(spihost[host]->trans_queue, (void *)&priv_trans, &do_yield);
|
||||
if (!r) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
@@ -612,7 +552,7 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_get_trans_result(spi_host_device_t host, spi_
|
||||
|
||||
spi_slave_uninstall_priv_trans(host, &priv_trans);
|
||||
*trans_desc = priv_trans.trans;
|
||||
return ESP_OK;
|
||||
return priv_trans.dma_hw_error ? ESP_ERR_INVALID_STATE : ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t SPI_SLAVE_ATTR spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait)
|
||||
@@ -683,6 +623,20 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_restart_after_dmareset(void *arg)
|
||||
}
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
static void SPI_SLAVE_ISR_ATTR spi_slave_trans_dma_error_check(spi_slave_t *host)
|
||||
{
|
||||
#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma
|
||||
if (esp_ptr_external_ram(host->cur_trans.rx_buffer) && spi_slave_hal_get_intr_status(&host->hal, SPI_LL_INTR_IN_FULL)) {
|
||||
host->cur_trans.dma_hw_error = true;
|
||||
ESP_DRAM_LOGE(SPI_TAG, "DMA RX overflow detected");
|
||||
}
|
||||
if (esp_ptr_external_ram(host->cur_trans.tx_buffer) && spi_slave_hal_get_intr_status(&host->hal, SPI_LL_INTR_OUT_EMPTY)) {
|
||||
host->cur_trans.dma_hw_error = true;
|
||||
ESP_DRAM_LOGE(SPI_TAG, "DMA TX underflow detected");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//This is run in interrupt context and apart from initialization and destruction, this is the only code
|
||||
//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are
|
||||
//no muxes in this code.
|
||||
@@ -700,6 +654,7 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
|
||||
// When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready.
|
||||
if (use_dma) {
|
||||
freeze_cs(host);
|
||||
spi_slave_trans_dma_error_check(host);
|
||||
}
|
||||
|
||||
spi_slave_hal_store_result(hal);
|
||||
@@ -713,17 +668,6 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
|
||||
}
|
||||
#endif //#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
|
||||
if (use_dma && host->cur_trans.rx_buffer) {
|
||||
uint16_t alignment = host->internal_mem_align_size;
|
||||
uint32_t buffer_byte_len = (host->cur_trans.trans->length + 7) / 8;
|
||||
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1));
|
||||
// invalidate priv_trans.buffer_to_rcv anyway, only user provide aligned buffer can rcv correct data in post_cb
|
||||
esp_err_t ret = esp_cache_msync((void *)host->cur_trans.rx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
assert(ret == ESP_OK);
|
||||
(void)ret;
|
||||
}
|
||||
#endif
|
||||
if (host->cfg.post_trans_cb) {
|
||||
host->cfg.post_trans_cb(host->cur_trans.trans);
|
||||
}
|
||||
@@ -784,7 +728,9 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
|
||||
if (use_dma) {
|
||||
restore_cs(host);
|
||||
}
|
||||
|
||||
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||
spi_slave_hal_clear_intr_status(hal, SPI_LL_INTR_IN_FULL | SPI_LL_INTR_OUT_EMPTY);
|
||||
#endif
|
||||
//Kick off transfer
|
||||
spi_slave_hal_user_start(hal);
|
||||
if (host->cfg.post_setup_cb) {
|
||||
|
||||
@@ -914,7 +914,7 @@ void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first)
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
|
||||
|
||||
//connecting pins to two peripherals breaks the output, fix it.
|
||||
same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
//prepare slave tx data
|
||||
@@ -1099,7 +1099,7 @@ TEST_CASE("SPI master variable dummy test", "[spi]")
|
||||
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_DISABLED));
|
||||
|
||||
same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num);
|
||||
|
||||
uint8_t data_to_send[] = {0x12, 0x34, 0x56, 0x78};
|
||||
|
||||
@@ -1142,7 +1142,7 @@ TEST_CASE("SPI master hd dma TX without RX test", "[spi]")
|
||||
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_CH_AUTO));
|
||||
|
||||
same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num);
|
||||
|
||||
uint32_t buf_size = 32;
|
||||
uint8_t *mst_send_buf = spi_bus_dma_memory_alloc(TEST_SPI_HOST, buf_size, 0);
|
||||
@@ -1993,11 +1993,11 @@ void test_spi_psram_trans(spi_device_handle_t dev_handle, void *tx, void *rx)
|
||||
|
||||
// To use psram, hardware will pass data through MSPI and GDMA to GPSPI, which need some time
|
||||
// GPSPI bandwidth(speed * line_num) should always no more than PSRAM bandwidth
|
||||
trans_cfg.override_freq_hz = (CONFIG_SPIRAM_SPEED / 4) * 1000 * 1000;
|
||||
trans_cfg.override_freq_hz = MIN(80000000, (CONFIG_SPIRAM_SPEED / 2) * 1000 * 1000);
|
||||
printf("%d TX %p RX %p len %d @%ld kHz\n", cnt, trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len, trans_cfg.override_freq_hz / 1000);
|
||||
TEST_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg));
|
||||
TEST_ASSERT(!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)));
|
||||
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len);
|
||||
trans_cfg.tx_buffer += trans_len;
|
||||
trans_cfg.rx_buffer += trans_len;
|
||||
trans_len ++;
|
||||
@@ -2042,9 +2042,11 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]")
|
||||
TEST_ASSERT(i ? (before - after) < 2 * TEST_EDMA_TRANS_LEN : (before - after) > 2 * TEST_EDMA_TRANS_LEN);
|
||||
spi_device_polling_end(dev_handle, portMAX_DELAY);
|
||||
printf("TX fail: %d, RX fail: %d\n", !!(trans_cfg.flags & SPI_TRANS_DMA_TX_FAIL), !!(trans_cfg.flags & SPI_TRANS_DMA_RX_FAIL));
|
||||
#if !SOC_IS(ESP32P4) // P4 can't reach error condition since it has powerful 16bits ddr psram
|
||||
TEST_ASSERT((!!i) == !!(trans_cfg.flags & (SPI_TRANS_DMA_TX_FAIL | SPI_TRANS_DMA_RX_FAIL)));
|
||||
#endif
|
||||
if (!i) { // data should be correct if using auto malloc
|
||||
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ TEST_CASE("SPI Single Board Test SIO", "[spi]")
|
||||
spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_DISABLED));
|
||||
|
||||
same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num);
|
||||
inner_connect(bus_cfg);
|
||||
|
||||
WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
@@ -32,13 +33,9 @@ static WORD_ALIGNED_ATTR uint8_t slave_rxbuf[320];
|
||||
static const uint8_t master_send[] = { 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43 };
|
||||
static const uint8_t slave_send[] = { 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0 };
|
||||
|
||||
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||
//These will only be enabled on chips with 2 or more SPI peripherals
|
||||
#ifndef CONFIG_SPIRAM
|
||||
//This test should be removed once the timing test is merged.
|
||||
|
||||
static spi_host_device_t master_slave_ids[2];
|
||||
static spi_device_handle_t spi;
|
||||
static void custom_setup(void)
|
||||
void custom_setup(spi_host_device_t master_id, spi_host_device_t slave_id)
|
||||
{
|
||||
//Initialize buffers
|
||||
memset(master_txbuf, 0, sizeof(master_txbuf));
|
||||
@@ -46,46 +43,60 @@ static void custom_setup(void)
|
||||
memset(slave_txbuf, 0, sizeof(slave_txbuf));
|
||||
memset(slave_rxbuf, 0, sizeof(slave_rxbuf));
|
||||
|
||||
//Initialize SPI Master
|
||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||
buscfg.max_transfer_sz = 40960;
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.clock_speed_hz = 4 * 1000 * 1000, //currently only up to 4MHz for internal connect
|
||||
.mode = 0, //SPI mode 0
|
||||
.spics_io_num = PIN_NUM_CS, //CS pin
|
||||
.queue_size = 7, //We want to be able to queue 7 transactions at a time
|
||||
.pre_cb = NULL,
|
||||
.cs_ena_posttrans = 5,
|
||||
.cs_ena_pretrans = 1,
|
||||
};
|
||||
//Initialize the SPI bus
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
//Attach the device to the SPI bus
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
|
||||
|
||||
//Configuration for the SPI slave interface
|
||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
//Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
|
||||
gpio_set_pull_mode(PIN_NUM_MOSI, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(PIN_NUM_CLK, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(PIN_NUM_CS, GPIO_PULLUP_ONLY);
|
||||
//Initialize SPI slave interface
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
||||
//Initialize SPI Master
|
||||
if (master_id) {
|
||||
//Initialize the SPI bus
|
||||
TEST_ESP_OK(spi_bus_initialize(master_id, &buscfg, SPI_DMA_CH_AUTO));
|
||||
//Attach the device to the SPI bus
|
||||
TEST_ESP_OK(spi_bus_add_device(master_id, &devcfg, &spi));
|
||||
}
|
||||
|
||||
if (slave_id) {
|
||||
//Configuration for the SPI slave interface
|
||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
//Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
|
||||
slave_pull_up(&buscfg, devcfg.spics_io_num);
|
||||
//Initialize SPI slave interface
|
||||
TEST_ESP_OK(spi_slave_initialize(slave_id, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
//Do internal connections
|
||||
same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(master_id, slave_id, buscfg, devcfg.spics_io_num);
|
||||
master_slave_ids[0] = master_id;
|
||||
master_slave_ids[1] = slave_id;
|
||||
}
|
||||
|
||||
static void custom_teardown(void)
|
||||
void custom_teardown(void)
|
||||
{
|
||||
TEST_ESP_OK(spi_slave_free(TEST_SLAVE_HOST));
|
||||
TEST_ESP_OK(spi_bus_remove_device(spi));
|
||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||
if (master_slave_ids[1]) {
|
||||
TEST_ESP_OK(spi_slave_free(master_slave_ids[1]));
|
||||
master_slave_ids[1] = 0;
|
||||
}
|
||||
if (spi != NULL) {
|
||||
TEST_ESP_OK(spi_bus_remove_device(spi));
|
||||
spi = NULL;
|
||||
}
|
||||
if (master_slave_ids[0]) {
|
||||
TEST_ESP_OK(spi_bus_free(master_slave_ids[0]));
|
||||
master_slave_ids[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||
TEST_CASE("test fullduplex slave with only RX direction", "[spi]")
|
||||
{
|
||||
custom_setup();
|
||||
custom_setup(TEST_SPI_HOST, TEST_SLAVE_HOST);
|
||||
|
||||
memcpy(master_txbuf, master_send, sizeof(master_send));
|
||||
|
||||
@@ -132,7 +143,7 @@ TEST_CASE("test fullduplex slave with only RX direction", "[spi]")
|
||||
|
||||
TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
|
||||
{
|
||||
custom_setup();
|
||||
custom_setup(TEST_SPI_HOST, TEST_SLAVE_HOST);
|
||||
|
||||
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
|
||||
|
||||
@@ -191,7 +202,7 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
|
||||
|
||||
//initialize master and slave on the same pins break some of the output configs, fix them
|
||||
same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num);
|
||||
|
||||
uint8_t master_tx[TEST_SLV_RX_BUF_LEN], slave_rx[TEST_SLV_RX_BUF_LEN];
|
||||
for (uint8_t i = 0; i < TEST_SLV_RX_BUF_LEN; i++) {
|
||||
@@ -249,16 +260,80 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]
|
||||
TEST_ESP_OK(spi_bus_remove_device(spidev0));
|
||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||
}
|
||||
#endif // !CONFIG_SPIRAM
|
||||
#endif // #if (TEST_SPI_PERIPH_NUM >= 2)
|
||||
|
||||
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||
#define PSRAM_TRANS_LEN 16000
|
||||
TEST_CASE("test slave using external ram", "[spi]")
|
||||
{
|
||||
custom_setup((TEST_SPI_PERIPH_NUM >= 2) ? TEST_SLAVE_HOST : 0, TEST_SPI_HOST);
|
||||
|
||||
uint8_t *slave_ext_tx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
|
||||
uint8_t *slave_ext_rx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
|
||||
uint8_t *master_tx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA);
|
||||
uint8_t *master_rx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA);
|
||||
|
||||
spi_slave_transaction_t slave_tans = {}, *out_trans;
|
||||
slave_tans.length = 8 * PSRAM_TRANS_LEN;
|
||||
slave_tans.tx_buffer = slave_ext_tx;
|
||||
slave_tans.rx_buffer = slave_ext_rx;
|
||||
slave_tans.flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO;
|
||||
|
||||
spi_transaction_t master_tans = {};
|
||||
master_tans.override_freq_hz = MIN(60000000, (CONFIG_SPIRAM_SPEED / 2) * 1000 * 1000);
|
||||
master_tans.tx_buffer = master_tx;
|
||||
master_tans.rx_buffer = master_rx;
|
||||
|
||||
for (int i = 0; i < 6; i ++) {
|
||||
test_fill_random_to_buffers_dualboard(7 + i, master_tx, slave_ext_tx, PSRAM_TRANS_LEN);
|
||||
slave_tans.length -= i * 8;
|
||||
master_tans.length = slave_tans.length;
|
||||
master_tans.rxlength = slave_tans.length;
|
||||
ESP_LOGI(SLAVE_TAG, "Test freq: %ld, tx: %p, rx: %p, len: %d", master_tans.override_freq_hz, slave_tans.tx_buffer, slave_tans.rx_buffer, slave_tans.length / 8);
|
||||
|
||||
uint32_t before = esp_get_free_heap_size();
|
||||
TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &slave_tans, portMAX_DELAY));
|
||||
uint32_t after = esp_get_free_heap_size();
|
||||
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||
spi_device_transmit(spi, &master_tans);
|
||||
#else
|
||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
spi_master_trans_impl_gpio(buscfg, PIN_NUM_CS, 0, (uint8_t *)master_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8, false);
|
||||
#endif
|
||||
ESP_LOGI(SLAVE_TAG, "slave malloc: %ld", after - before);
|
||||
TEST_ASSERT(i ? (before - after) > PSRAM_TRANS_LEN : (before - after) < PSRAM_TRANS_LEN);
|
||||
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY));
|
||||
|
||||
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(master_tans.tx_buffer, slave_tans.rx_buffer, master_tans.length / 8);
|
||||
ESP_LOGI(SLAVE_TAG, "ok\n");
|
||||
}
|
||||
|
||||
#if (TEST_SPI_PERIPH_NUM >= 2) && !SOC_IS(ESP32P4) // P4 can't reach error condition since it has powerful 16bits ddr psram
|
||||
master_tans.override_freq_hz = 61000000; // real freq will be 40 if just config 60M
|
||||
ESP_LOGI(SLAVE_TAG, "Testing over freq: %ld", master_tans.override_freq_hz);
|
||||
TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &slave_tans, portMAX_DELAY));
|
||||
spi_device_transmit(spi, (spi_transaction_t *)&master_tans);
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY));
|
||||
#endif
|
||||
|
||||
free(slave_ext_tx);
|
||||
free(slave_ext_rx);
|
||||
free(master_tx);
|
||||
free(master_rx);
|
||||
custom_teardown();
|
||||
ESP_LOGI(SLAVE_TAG, "test passed.");
|
||||
}
|
||||
#endif // CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||
|
||||
TEST_CASE("test slave send unaligned", "[spi]")
|
||||
{
|
||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
||||
same_pin_func_sel(buscfg, slvcfg.spics_io_num, 0, true);
|
||||
same_pin_func_sel(0, TEST_SLAVE_HOST, buscfg, slvcfg.spics_io_num);
|
||||
|
||||
memcpy(master_txbuf, master_send, sizeof(master_send));
|
||||
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
|
||||
@@ -678,7 +753,7 @@ TEST_CASE("test_spi_slave_sleep_retention", "[spi]")
|
||||
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
|
||||
same_pin_func_sel(buscfg, slvcfg.spics_io_num, 0, true);
|
||||
same_pin_func_sel(0, TEST_SLAVE_HOST, buscfg, slvcfg.spics_io_num);
|
||||
|
||||
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
|
||||
printf("Going into sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold");
|
||||
|
||||
@@ -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
|
||||
@@ -171,7 +171,7 @@ static void test_hd_start(spi_device_handle_t *spi, int freq, const spitest_para
|
||||
//when test with single board via same set of mosi, miso, clk and cs pins.
|
||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, false);
|
||||
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||
|
||||
wait_wrbuf_sig(ctx, 0);
|
||||
wait_rdbuf_sig(ctx, 0);
|
||||
@@ -529,7 +529,7 @@ TEST_CASE("test spi slave hd segment mode, master too long", "[spi][spi_slv_hd]"
|
||||
//Use GPIO matrix to connect signal of master and slave via same set of pins on one board.
|
||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true);
|
||||
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||
|
||||
const int send_buf_size = 1024;
|
||||
WORD_ALIGNED_ATTR uint8_t* slave_send_buf = malloc(send_buf_size * 2);
|
||||
@@ -955,7 +955,7 @@ TEST_CASE("test_spi_slave_hd_sleep_retention", "[spi]")
|
||||
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true);
|
||||
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||
vTaskDelay(1);
|
||||
|
||||
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
|
||||
@@ -1033,7 +1033,7 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]")
|
||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
|
||||
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true);
|
||||
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||
vTaskDelay(1);
|
||||
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
|
||||
Reference in New Issue
Block a user