Skip to content

Commit

Permalink
Merge branch 'feat/gdma_set_burst_size' into 'master'
Browse files Browse the repository at this point in the history
feat(gdma): return alignment constraints required by the GDMA channel

Closes IDF-9848

See merge request espressif/esp-idf!30748
  • Loading branch information
suda-morris committed May 27, 2024
2 parents 332bb9b + 69ef3b6 commit e659675
Show file tree
Hide file tree
Showing 49 changed files with 663 additions and 491 deletions.
11 changes: 6 additions & 5 deletions components/esp_driver_spi/src/gpspi/spi_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,13 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
gdma_connect(dma_ctx->rx_dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 3));
}
#endif
gdma_transfer_ability_t ability = {
.psram_trans_align = 0, // fall back to use the same size of the psram data cache line size
.sram_trans_align = 4,
// TODO: add support to allow SPI transfer PSRAM buffer
gdma_transfer_config_t trans_cfg = {
.max_data_burst_size = 16,
.access_ext_mem = false,
};
ESP_RETURN_ON_ERROR(gdma_set_transfer_ability(dma_ctx->tx_dma_chan, &ability), SPI_TAG, "set gdma tx transfer ability failed");
ESP_RETURN_ON_ERROR(gdma_set_transfer_ability(dma_ctx->rx_dma_chan, &ability), SPI_TAG, "set gdma rx transfer ability failed");
ESP_RETURN_ON_ERROR(gdma_config_transfer(dma_ctx->tx_dma_chan, &trans_cfg), SPI_TAG, "config gdma tx transfer failed");
ESP_RETURN_ON_ERROR(gdma_config_transfer(dma_ctx->rx_dma_chan, &trans_cfg), SPI_TAG, "config gdma rx transfer failed");
}
return ret;
}
Expand Down
5 changes: 4 additions & 1 deletion components/esp_hw_support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,16 @@ if(NOT BOOTLOADER_BUILD)
endif()

if(CONFIG_SOC_GDMA_SUPPORTED)
list(APPEND srcs "dma/gdma.c")
list(APPEND srcs "dma/gdma.c" "deprecated/gdma_legacy.c")
if(CONFIG_SOC_GDMA_SUPPORT_SLEEP_RETENTION)
list(APPEND srcs "dma/gdma_sleep_retention.c")
endif()
if(CONFIG_SOC_GDMA_SUPPORT_ETM)
list(APPEND srcs "dma/gdma_etm.c")
endif()
if(CONFIG_SOC_GDMA_SUPPORT_CRC)
list(APPEND srcs "dma/gdma_crc.c")
endif()
endif()

if(CONFIG_SOC_GP_LDO_SUPPORTED)
Expand Down
62 changes: 62 additions & 0 deletions components/esp_hw_support/deprecated/gdma_legacy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_check.h"
#include "../dma/gdma_priv.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"

static const char *TAG = "gdma";

esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_transfer_ability_t *ability)
{
ESP_RETURN_ON_FALSE(dma_chan && ability, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;

size_t int_mem_alignment = ability->sram_trans_align;
size_t ext_mem_alignment = ability->psram_trans_align;
// alignment should be 2^n
ESP_RETURN_ON_FALSE((int_mem_alignment & (int_mem_alignment - 1)) == 0, ESP_ERR_INVALID_ARG,
TAG, "invalid sram alignment: %zu", int_mem_alignment);

uint32_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
if (ext_mem_alignment == 0) {
// fall back to use the same size of the psram data cache line size
ext_mem_alignment = ext_mem_cache_line_size;
}
if ((ext_mem_cache_line_size > 0) && (ext_mem_alignment > ext_mem_cache_line_size)) {
ESP_RETURN_ON_FALSE(((ext_mem_alignment % ext_mem_cache_line_size) == 0), ESP_ERR_INVALID_ARG,
TAG, "ext_mem_alignment(%d) should be multiple of the ext_mem_cache_line_size(%"PRIu32")",
ext_mem_alignment, ext_mem_cache_line_size);
}

// if the DMA can't access the PSRAM, this HAL function is no-op
gdma_hal_set_burst_size(hal, pair->pair_id, dma_chan->direction, ext_mem_alignment);

// TX channel can always enable burst mode, no matter data alignment
bool en_burst = true;
if (dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) {
// RX channel burst mode depends on specific data alignment
en_burst = int_mem_alignment >= 4;
}
gdma_hal_enable_burst(hal, pair->pair_id, dma_chan->direction, en_burst, en_burst);

dma_chan->int_mem_alignment = int_mem_alignment;
dma_chan->ext_mem_alignment = ext_mem_alignment;
ESP_LOGD(TAG, "%s channel (%d,%d), (%u:%u) bytes aligned, burst %s", dma_chan->direction == GDMA_CHANNEL_DIRECTION_TX ? "tx" : "rx",
group->group_id, pair->pair_id, int_mem_alignment, ext_mem_alignment, en_burst ? "enabled" : "disabled");

return ESP_OK;
}
18 changes: 5 additions & 13 deletions components/esp_hw_support/dma/async_memcpy_cp_dma.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -48,7 +48,6 @@ typedef struct async_memcpy_transaction_t {
/// @note - Number of transaction objects are determined by the backlog parameter
typedef struct {
async_memcpy_context_t parent; // Parent IO interface
size_t sram_trans_align; // DMA transfer alignment (both in size and address) for SRAM memory
size_t max_single_dma_buffer; // max DMA buffer size by a single descriptor
cp_dma_hal_context_t hal; // CPDMA hal
intr_handle_t intr; // CPDMA interrupt handle
Expand Down Expand Up @@ -90,7 +89,7 @@ esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, as
uint32_t trans_queue_len = config->backlog ? config->backlog : DEFAULT_TRANSACTION_QUEUE_LENGTH;
// allocate memory for transaction pool, aligned to 4 because the trans->eof_node requires that alignment
mcp_dma->transaction_pool = heap_caps_aligned_calloc(4, trans_queue_len, sizeof(async_memcpy_transaction_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(mcp_dma->transaction_pool, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction pool");

// Init hal context
Expand All @@ -111,8 +110,7 @@ esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, as
// initialize other members
portMUX_INITIALIZE(&mcp_dma->spin_lock);
atomic_init(&mcp_dma->fsm, MCP_FSM_IDLE);
mcp_dma->sram_trans_align = config->sram_trans_align;
size_t trans_align = config->sram_trans_align;
size_t trans_align = config->dma_burst_size;
mcp_dma->max_single_dma_buffer = trans_align ? ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, trans_align) : DMA_DESCRIPTOR_BUFFER_MAX_SIZE;
mcp_dma->parent.del = mcp_cpdma_del;
mcp_dma->parent.memcpy = mcp_cpdma_memcpy;
Expand Down Expand Up @@ -240,12 +238,6 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
esp_err_t ret = ESP_OK;
async_memcpy_cpdma_context_t *mcp_dma = __containerof(ctx, async_memcpy_cpdma_context_t, parent);
ESP_RETURN_ON_FALSE(esp_ptr_internal(src) && esp_ptr_internal(dst), ESP_ERR_INVALID_ARG, TAG, "CP_DMA can only access SRAM");
// alignment check
if (mcp_dma->sram_trans_align) {
ESP_RETURN_ON_FALSE((((intptr_t)dst & (mcp_dma->sram_trans_align - 1)) == 0), ESP_ERR_INVALID_ARG, TAG, "buffer address not aligned: %p -> %p", src, dst);
ESP_RETURN_ON_FALSE(((n & (mcp_dma->sram_trans_align - 1)) == 0), ESP_ERR_INVALID_ARG, TAG,
"copy size should align to %d bytes", mcp_dma->sram_trans_align);
}
async_memcpy_transaction_t *trans = NULL;
// pick one transaction node from idle queue
trans = try_pop_trans_from_idle_queue(mcp_dma);
Expand All @@ -257,12 +249,12 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
uint32_t num_desc_per_path = (n + max_single_dma_buffer - 1) / max_single_dma_buffer;
// allocate DMA descriptors, descriptors need a strict alignment
trans->tx_desc_link = heap_caps_aligned_calloc(4, num_desc_per_path, sizeof(dma_descriptor_align4_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(trans->tx_desc_link, ESP_ERR_NO_MEM, err, TAG, "no mem for DMA descriptors");
// don't have to allocate the EOF descriptor, we will use trans->eof_node as the RX EOF descriptor
if (num_desc_per_path > 1) {
trans->rx_desc_link = heap_caps_aligned_calloc(4, num_desc_per_path - 1, sizeof(dma_descriptor_align4_t),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(trans->rx_desc_link, ESP_ERR_NO_MEM, err, TAG, "no mem for DMA descriptors");
} else {
// small copy buffer, use the trans->eof_node is sufficient
Expand Down
56 changes: 24 additions & 32 deletions components/esp_hw_support/dma/async_memcpy_gdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ typedef struct async_memcpy_transaction_t {
typedef struct {
async_memcpy_context_t parent; // Parent IO interface
size_t descriptor_align; // DMA descriptor alignment
size_t sram_trans_align; // DMA buffer alignment (both in size and address) for SRAM memory
size_t psram_trans_align; // DMA buffer alignment (both in size and address) for PSRAM memory
size_t rx_int_mem_alignment; // DMA buffer alignment (both in size and address) for internal RX memory
size_t rx_ext_mem_alignment; // DMA buffer alignment (both in size and address) for external RX memory
size_t tx_int_mem_alignment; // DMA buffer alignment (both in size and address) for internal TX memory
size_t tx_ext_mem_alignment; // DMA buffer alignment (both in size and address) for external TX memory
size_t max_single_dma_buffer; // max DMA buffer size by a single descriptor
int gdma_bus_id; // GDMA bus id (AHB, AXI, etc.)
gdma_channel_handle_t tx_channel; // GDMA TX channel handle
Expand Down Expand Up @@ -146,12 +148,12 @@ static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_confi
ESP_GOTO_ON_ERROR(gdma_connect(mcp_gdma->rx_channel, m2m_trigger), err, TAG, "GDMA rx connect failed");
ESP_GOTO_ON_ERROR(gdma_connect(mcp_gdma->tx_channel, m2m_trigger), err, TAG, "GDMA tx connect failed");

gdma_transfer_ability_t transfer_ability = {
.sram_trans_align = config->sram_trans_align,
.psram_trans_align = config->psram_trans_align,
gdma_transfer_config_t transfer_cfg = {
.max_data_burst_size = config->dma_burst_size ? config->dma_burst_size : 16,
.access_ext_mem = true, // allow to do memory copy from/to external memory
};
ESP_GOTO_ON_ERROR(gdma_set_transfer_ability(mcp_gdma->tx_channel, &transfer_ability), err, TAG, "set tx trans ability failed");
ESP_GOTO_ON_ERROR(gdma_set_transfer_ability(mcp_gdma->rx_channel, &transfer_ability), err, TAG, "set rx trans ability failed");
ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->tx_channel, &transfer_cfg), err, TAG, "config transfer for tx channel failed");
ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->rx_channel, &transfer_cfg), err, TAG, "config transfer for rx channel failed");

// register rx eof callback
gdma_rx_event_callbacks_t cbs = {
Expand All @@ -172,15 +174,13 @@ static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_confi
atomic_init(&mcp_gdma->fsm, MCP_FSM_IDLE);
mcp_gdma->gdma_bus_id = gdma_bus_id;

uint32_t psram_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
uint32_t sram_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
// if the psram_trans_align is configured to zero, we should fall back to use the data cache line size
size_t psram_trans_align = MAX(psram_cache_line_size, config->psram_trans_align);
size_t sram_trans_align = MAX(sram_cache_line_size, config->sram_trans_align);
size_t trans_align = MAX(sram_trans_align, psram_trans_align);
mcp_gdma->max_single_dma_buffer = ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, trans_align);
mcp_gdma->psram_trans_align = psram_trans_align;
mcp_gdma->sram_trans_align = sram_trans_align;
// get the buffer alignment required by the GDMA channel
gdma_get_alignment_constraints(mcp_gdma->rx_channel, &mcp_gdma->rx_int_mem_alignment, &mcp_gdma->rx_ext_mem_alignment);
gdma_get_alignment_constraints(mcp_gdma->tx_channel, &mcp_gdma->tx_int_mem_alignment, &mcp_gdma->tx_ext_mem_alignment);

size_t buf_align = MAX(MAX(mcp_gdma->rx_int_mem_alignment, mcp_gdma->rx_ext_mem_alignment),
MAX(mcp_gdma->tx_int_mem_alignment, mcp_gdma->tx_ext_mem_alignment));
mcp_gdma->max_single_dma_buffer = ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, buf_align);
mcp_gdma->parent.del = mcp_gdma_del;
mcp_gdma->parent.memcpy = mcp_gdma_memcpy;
#if SOC_GDMA_SUPPORT_ETM
Expand Down Expand Up @@ -335,29 +335,21 @@ static async_memcpy_transaction_t *try_pop_trans_from_idle_queue(async_memcpy_gd
static bool check_buffer_alignment(async_memcpy_gdma_context_t *mcp_gdma, void *src, void *dst, size_t n)
{
bool valid = true;
uint32_t psram_align_mask = 0;
uint32_t sram_align_mask = 0;
if (mcp_gdma->psram_trans_align) {
psram_align_mask = mcp_gdma->psram_trans_align - 1;
}
if (mcp_gdma->sram_trans_align) {
sram_align_mask = mcp_gdma->sram_trans_align - 1;
}

if (esp_ptr_external_ram(dst)) {
valid = valid && (((uint32_t)dst & psram_align_mask) == 0);
valid = valid && ((n & psram_align_mask) == 0);
valid = valid && (((uint32_t)dst & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0);
} else {
valid = valid && (((uint32_t)dst & sram_align_mask) == 0);
valid = valid && ((n & sram_align_mask) == 0);
valid = valid && (((uint32_t)dst & (mcp_gdma->rx_int_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->rx_int_mem_alignment - 1)) == 0);
}

if (esp_ptr_external_ram(src)) {
valid = valid && (((uint32_t)src & psram_align_mask) == 0);
valid = valid && ((n & psram_align_mask) == 0);
valid = valid && (((uint32_t)src & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0);
} else {
valid = valid && (((uint32_t)src & sram_align_mask) == 0);
valid = valid && ((n & sram_align_mask) == 0);
valid = valid && (((uint32_t)src & (mcp_gdma->tx_int_mem_alignment - 1)) == 0);
valid = valid && ((n & (mcp_gdma->tx_int_mem_alignment - 1)) == 0);
}

return valid;
Expand Down
Loading

0 comments on commit e659675

Please sign in to comment.