mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
fix(driver_spi): fixed master dma unaligned trans error
This commit is contained in:
@@ -1204,12 +1204,13 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uin
|
|||||||
alignment = MAX(host->dma_ctx->dma_align_rx_int, host->bus_attr->cache_align_int);
|
alignment = MAX(host->dma_ctx->dma_align_rx_int, host->bus_attr->cache_align_int);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
need_malloc |= (((uint32_t)buffer | len) & (alignment - 1));
|
// length also must be aligned if cache sync is required, otherwise don't need
|
||||||
|
need_malloc |= (use_psram || host->bus_attr->cache_align_int > 1) ? (((uint32_t)buffer | len) & (alignment - 1)) : (((uint32_t)buffer) & (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");
|
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");
|
||||||
|
uint32_t align_len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
|
||||||
if (need_malloc) {
|
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);
|
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, align_len, mem_cap);
|
||||||
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");
|
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) {
|
if (is_tx) {
|
||||||
@@ -1219,7 +1220,8 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uin
|
|||||||
}
|
}
|
||||||
*ret_buffer = buffer;
|
*ret_buffer = buffer;
|
||||||
if (use_psram || (host->bus_attr->cache_align_int > 1)) {
|
if (use_psram || (host->bus_attr->cache_align_int > 1)) {
|
||||||
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);
|
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_RETURN_ON_FALSE_ISR(ret == ESP_OK, ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
|
ESP_RETURN_ON_FALSE_ISR(ret == ESP_OK, ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ TEST_CASE("SPI Master clockdiv calculation routines", "[spi]")
|
|||||||
|
|
||||||
// Test All clock source
|
// Test All clock source
|
||||||
#define TEST_CLK_BYTE_LEN 10000
|
#define TEST_CLK_BYTE_LEN 10000
|
||||||
#define TEST_TRANS_TIME_BIAS_RATIO (float)8.0/100 // think 8% transfer time bias as acceptable
|
#define TEST_TRANS_TIME_BIAS_RATIO (float)10.0/100 // think 8% transfer time bias as acceptable
|
||||||
TEST_CASE("SPI Master clk_source and divider accuracy", "[spi]")
|
TEST_CASE("SPI Master clk_source and divider accuracy", "[spi]")
|
||||||
{
|
{
|
||||||
int64_t start = 0, end = 0;
|
int64_t start = 0, end = 0;
|
||||||
@@ -893,6 +893,37 @@ TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]")
|
|||||||
TEST_ASSERT(spi_bus_free(TEST_SPI_HOST) == ESP_OK);
|
TEST_ASSERT(spi_bus_free(TEST_SPI_HOST) == ESP_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE // targets who need cache sync don't support unaligned trans
|
||||||
|
TEST_CASE("SPI Master DMA manually unaligned RX test", "[spi]")
|
||||||
|
{
|
||||||
|
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
|
buscfg.miso_io_num = buscfg.mosi_io_num;
|
||||||
|
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||||
|
|
||||||
|
spi_device_handle_t dev0;
|
||||||
|
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||||
|
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
|
||||||
|
|
||||||
|
WORD_ALIGNED_ATTR uint8_t tx_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
||||||
|
WORD_ALIGNED_ATTR uint8_t rx_data[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
|
||||||
|
spi_transaction_t trans_cfg = {
|
||||||
|
.tx_buffer = tx_data,
|
||||||
|
.rx_buffer = rx_data,
|
||||||
|
.length = 5 * 8,
|
||||||
|
.flags = SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL,
|
||||||
|
};
|
||||||
|
printf("Sending %d bytes\n", trans_cfg.length / 8);
|
||||||
|
TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg));
|
||||||
|
ESP_LOG_BUFFER_HEX("rx", rx_data, 8);
|
||||||
|
#if !CONFIG_IDF_TARGET_ESP32 // esp32 dma not work with unaligned RX
|
||||||
|
TEST_ASSERT_EQUAL(rx_data[6], 0xAA);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_ESP_OK(spi_bus_remove_device(dev0));
|
||||||
|
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if (TEST_SPI_PERIPH_NUM >= 2)
|
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||||
//These will only be enabled on chips with 2 or more SPI peripherals
|
//These will only be enabled on chips with 2 or more SPI peripherals
|
||||||
|
|
||||||
|
|||||||
@@ -852,6 +852,10 @@ Please note that the ISR is disabled during flash operation by default. To keep
|
|||||||
|
|
||||||
4. ``cs_ena_pretrans`` is not compatible with the Command and Address phases of full-duplex transactions.
|
4. ``cs_ena_pretrans`` is not compatible with the Command and Address phases of full-duplex transactions.
|
||||||
|
|
||||||
|
.. only:: esp32
|
||||||
|
|
||||||
|
5. If DMA is enabled, the RX buffer should be word-aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes). Otherwise, DMA may overwrite the data in the unaligned part.
|
||||||
|
|
||||||
|
|
||||||
Application Examples
|
Application Examples
|
||||||
--------------------
|
--------------------
|
||||||
|
|||||||
@@ -852,6 +852,10 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_interface_con
|
|||||||
|
|
||||||
4. 全双工传输事务模式中,命令阶段和地址阶段与 ``cs_ena_pretrans`` 不兼容。
|
4. 全双工传输事务模式中,命令阶段和地址阶段与 ``cs_ena_pretrans`` 不兼容。
|
||||||
|
|
||||||
|
.. only:: esp32
|
||||||
|
|
||||||
|
5. 若启用了 DMA,则 RX 缓冲区应该以字对齐(从 32 位边界开始,字节长度为 4 的倍数)。否则 DMA 可能覆盖未对齐部分的数据。
|
||||||
|
|
||||||
|
|
||||||
应用示例
|
应用示例
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user