feat(gdma): add burst size validation check during config

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Chen Chen
2026-08-20 15:51:10 +08:00
parent dce5812654
commit 1dfce4409a
18 changed files with 154 additions and 16 deletions

View File

@@ -202,7 +202,7 @@ esp_err_t gdma_disconnect(gdma_channel_handle_t dma_chan);
typedef struct {
uint32_t max_data_burst_size; /*!< Set the max burst size when DMA read/write the data buffer.
Set to 0 means to disable the data burst.
Other value must be power of 2, e.g., 4/8/16/32/64 */
Other values must be powers of 2 or supported by the selected GDMA bus. */
bool access_ext_mem; /*!< Set this if the DMA transfer will access external memory */
} gdma_transfer_config_t;

View File

@@ -421,6 +421,9 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf
return ESP_ERR_INVALID_ARG;
}
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
uint32_t max_data_burst_size = config->max_data_burst_size;
size_t int_mem_alignment = 1;
size_t ext_enc_mem_alignment = 1;
@@ -443,8 +446,7 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf
#endif
}
if (max_data_burst_size) {
// burst size must be power of 2
ESP_RETURN_ON_FALSE((max_data_burst_size & (max_data_burst_size - 1)) == 0, ESP_ERR_INVALID_ARG,
ESP_RETURN_ON_FALSE(gdma_hal_check_burst_size(hal, max_data_burst_size), ESP_ERR_INVALID_ARG,
TAG, "invalid max_data_burst_size: %"PRIu32, max_data_burst_size);
}
@@ -477,10 +479,6 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf
ext_no_enc_mem_alignment = BIT(31);
}
gdma_pair_t *pair = dma_chan->pair;
gdma_group_t *group = pair->group;
gdma_hal_context_t *hal = &group->hal;
// always enable descriptor burst as the descriptor is always word aligned and is in the internal SRAM
bool en_desc_burst = true;
gdma_hal_enable_burst(hal, pair->pair_id, dma_chan->direction, en_data_burst, en_desc_burst);

View File

@@ -921,3 +921,66 @@ TEST_CASE("GDMA interrupt priority configuration", "[GDMA]")
TEST_ESP_OK(gdma_del_channel(tx_chan));
TEST_ESP_OK(gdma_del_channel(rx_chan));
}
#if SOC_HAS(AHB_GDMA)
TEST_CASE("GDMA rejects invalid AHB burst sizes", "[GDMA]")
{
gdma_channel_handle_t tx_chan = NULL;
gdma_channel_alloc_config_t channel_config = {};
TEST_ESP_OK(gdma_new_ahb_channel(&channel_config, &tx_chan, NULL));
gdma_transfer_config_t transfer_config = {
.max_data_burst_size = 16,
.access_ext_mem = false,
};
TEST_ESP_OK(gdma_config_transfer(tx_chan, &transfer_config));
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
// Non power-of-two must be rejected when the burst size is programmable.
transfer_config.max_data_burst_size = 3;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config));
// 8 is a power of two but is outside every current AHB supported-burst mask.
transfer_config.max_data_burst_size = 8;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config));
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE)
transfer_config.access_ext_mem = true;
transfer_config.max_data_burst_size = GDMA_LL_MAX_BURST_SIZE_PSRAM * 2;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config));
#endif // GDMA_LL_GET(AHB_PSRAM_CAPABLE)
#endif // GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
TEST_ESP_OK(gdma_del_channel(tx_chan));
}
#endif // SOC_HAS(AHB_GDMA)
#if SOC_HAS(AXI_GDMA)
TEST_CASE("GDMA rejects invalid AXI burst sizes", "[GDMA]")
{
gdma_channel_handle_t tx_chan = NULL;
gdma_channel_alloc_config_t channel_config = {};
TEST_ESP_OK(gdma_new_axi_channel(&channel_config, &tx_chan, NULL));
gdma_transfer_config_t transfer_config = {
.max_data_burst_size = 16,
.access_ext_mem = false,
};
TEST_ESP_OK(gdma_config_transfer(tx_chan, &transfer_config));
transfer_config.max_data_burst_size = 3;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config));
// 4 is a power of two but is outside the AXI supported-burst mask.
transfer_config.max_data_burst_size = 4;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config));
#if GDMA_LL_GET(AXI_PSRAM_CAPABLE)
transfer_config.access_ext_mem = true;
transfer_config.max_data_burst_size = GDMA_LL_MAX_BURST_SIZE_PSRAM * 2;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gdma_config_transfer(tx_chan, &transfer_config));
#endif // GDMA_LL_GET(AXI_PSRAM_CAPABLE)
TEST_ESP_OK(gdma_del_channel(tx_chan));
}
#endif // SOC_HAS(AXI_GDMA)

View File

@@ -267,7 +267,7 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel
#endif
static esp_err_t resolve_dma_burst_size(uint32_t requested, uint32_t *out_burst_size)
static uint32_t resolve_dma_burst_size(uint32_t requested)
{
uint32_t burst_size = requested;
if (burst_size == 0) {
@@ -280,8 +280,7 @@ static esp_err_t resolve_dma_burst_size(uint32_t requested, uint32_t *out_burst_
}
burst_size = SPI_DMA_DEFAULT_BURST_SIZE;
#endif
*out_burst_size = burst_size;
return ESP_OK;
return burst_size;
}
static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_chan, uint32_t dma_burst_size, spi_dma_ctx_t *dma_ctx)
@@ -291,8 +290,7 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
esp_err_t ret = ESP_OK;
if (dma_chan == SPI_DMA_CH_AUTO) {
uint32_t burst_size = SPI_DMA_DEFAULT_BURST_SIZE;
ESP_RETURN_ON_ERROR(resolve_dma_burst_size(dma_burst_size, &burst_size), SPI_TAG, "invalid dma_burst_size");
uint32_t burst_size = resolve_dma_burst_size(dma_burst_size);
gdma_channel_alloc_config_t alloc_config = {
#if CONFIG_SPI_MASTER_ISR_IN_IRAM

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -21,6 +21,11 @@ extern "C" {
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : NULL)
#define AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_4 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64)
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
#define GDMA_LL_CHANNEL_MAX_WEIGHT 15 // supported weight levels: [0,15]

View File

@@ -21,6 +21,10 @@ extern "C" {
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : NULL)
#define AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_4 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32)
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
#define GDMA_LL_CHANNEL_MAX_WEIGHT 15 // supported weight levels: [0,15]

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -21,6 +21,11 @@ extern "C" {
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : NULL)
#define AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_4 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64)
#define GDMA_LL_CHANNEL_MAX_PRIORITY 5 // supported priority levels: [0,5]
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,6 +22,11 @@ extern "C" {
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : NULL)
#define AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_4 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64)
// any "dummy" peripheral ID can be used for M2M mode
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFAC2)
#define AHB_DMA_LL_RX_EVENT_MASK (0x1F)

View File

@@ -24,6 +24,12 @@ extern "C" {
#define AXI_DMA_LL_GET_HW(id) (((id) == 0) ? (&AXI_DMA) : NULL)
#define AXI_DMA_LL_SUPPORT(_feat) AXI_DMA_LL_SUPPORT_ ## _feat
#define AXI_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_8 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64 | \
GDMA_BURST_SIZE_SUPPORT_128)
// any "dummy" peripheral ID can be used for M2M mode
#define AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFC0)
#define AXI_DMA_LL_RX_EVENT_MASK (0x1F)

View File

@@ -19,6 +19,10 @@
#define GDMA_LL_INST_NUM 1
#define GDMA_LL_PAIRS_PER_INST GDMA_LL_AHB_PAIRS_PER_GROUP
#define GDMA_LL_AHB_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64)
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -20,6 +20,11 @@ extern "C" {
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : (((id) == 2) ? ((ahb_dma_dev_t *)&LP_AHB_DMA) : NULL))
#define AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_4 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64)
// any "dummy" peripheral ID can be used for M2M mode
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0x8200)
#define AHB_DMA_LL_RX_EVENT_MASK (0x7F)

View File

@@ -23,6 +23,12 @@ extern "C" {
#define AXI_DMA_LL_GET_HW(id) (((id) == 0) ? (&AXI_DMA) : NULL)
#define AXI_DMA_LL_SUPPORT(_feat) AXI_DMA_LL_SUPPORT_ ## _feat
#define AXI_DMA_LL_SUPPORTED_BURST_SIZE_MASK (GDMA_BURST_SIZE_SUPPORT_8 | \
GDMA_BURST_SIZE_SUPPORT_16 | \
GDMA_BURST_SIZE_SUPPORT_32 | \
GDMA_BURST_SIZE_SUPPORT_64 | \
GDMA_BURST_SIZE_SUPPORT_128)
// any "dummy" peripheral ID can be used for M2M mode
#define AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFC0)
#define AXI_DMA_LL_RX_EVENT_MASK (0x7F)

View File

@@ -13,6 +13,9 @@ static gdma_hal_priv_data_t gdma_ahb_hal_priv_data = {
.m2m_free_periph_mask = GDMA_LL_M2M_FREE_PERIPH_ID_MASK,
.tx_event_mask = GDMA_LL_TX_EVENT_MASK,
.rx_event_mask = GDMA_LL_RX_EVENT_MASK,
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
.supported_burst_size_mask = GDMA_LL_AHB_SUPPORTED_BURST_SIZE_MASK,
#endif
};
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)

View File

@@ -14,6 +14,9 @@ static gdma_hal_priv_data_t gdma_ahb_hal_priv_data = {
.m2m_free_periph_mask = AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
.tx_event_mask = AHB_DMA_LL_TX_EVENT_MASK,
.rx_event_mask = AHB_DMA_LL_RX_EVENT_MASK,
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
.supported_burst_size_mask = AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK,
#endif
};
void gdma_ahb_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)
@@ -315,6 +318,9 @@ static gdma_hal_priv_data_t gdma_lp_ahb_hal_priv_data = {
.m2m_free_periph_mask = LP_AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
.tx_event_mask = AHB_DMA_LL_TX_EVENT_MASK,
.rx_event_mask = AHB_DMA_LL_RX_EVENT_MASK,
#if GDMA_LL_GET(AHB_BURST_SIZE_ADJUSTABLE)
.supported_burst_size_mask = AHB_DMA_LL_SUPPORTED_BURST_SIZE_MASK,
#endif
};
void gdma_lp_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config)

View File

@@ -14,6 +14,7 @@ static gdma_hal_priv_data_t gdma_axi_hal_priv_data = {
.m2m_free_periph_mask = AXI_DMA_LL_M2M_FREE_PERIPH_ID_MASK,
.tx_event_mask = AXI_DMA_LL_TX_EVENT_MASK,
.rx_event_mask = AXI_DMA_LL_RX_EVENT_MASK,
.supported_burst_size_mask = AXI_DMA_LL_SUPPORTED_BURST_SIZE_MASK,
};
void gdma_axi_hal_start_with_desc(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, intptr_t desc_base_addr)

View File

@@ -65,6 +65,19 @@ void gdma_hal_set_burst_size(gdma_hal_context_t *hal, int chan_id, gdma_channel_
}
}
bool gdma_hal_check_burst_size(gdma_hal_context_t *hal, uint32_t burst_sz)
{
if (burst_sz & (burst_sz - 1)) {
// Not a power of 2
return false;
}
if (!hal->set_burst_size) {
// When a specific burst size cannot be set (using fixed burst size)
return true;
}
return (hal->priv_data->supported_burst_size_mask & burst_sz) != 0;
}
void gdma_hal_set_strategy(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_owner_check, bool en_desc_write_back, bool eof_till_popped)
{
hal->set_strategy(hal, chan_id, dir, en_owner_check, en_desc_write_back, eof_till_popped);

View File

@@ -55,6 +55,8 @@ typedef struct {
// Supported interrupt events can vary across DMA instances (e.g. AHB vs AXI)
uint32_t tx_event_mask;
uint32_t rx_event_mask;
// Bitmap of supported configurable data burst sizes, using gdma_burst_size_support_t
uint32_t supported_burst_size_mask;
} gdma_hal_priv_data_t;
/**
@@ -128,6 +130,8 @@ void gdma_hal_enable_burst(gdma_hal_context_t *hal, int chan_id, gdma_channel_di
void gdma_hal_set_burst_size(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t burst_sz);
bool gdma_hal_check_burst_size(gdma_hal_context_t *hal, uint32_t burst_sz);
void gdma_hal_set_strategy(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, bool en_owner_check, bool en_desc_write_back, bool eof_till_popped);
void gdma_hal_enable_intr(gdma_hal_context_t *hal, int chan_id, gdma_channel_direction_t dir, uint32_t intr_event_mask, bool en_or_dis);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -22,6 +22,18 @@ typedef enum {
GDMA_CHANNEL_DIRECTION_RX, /*!< GDMA channel direction: RX */
} gdma_channel_direction_t;
/**
* @brief Bit definitions used by the LL layer to describe supported data burst sizes
*/
typedef enum {
GDMA_BURST_SIZE_SUPPORT_4 = 1U << 2,
GDMA_BURST_SIZE_SUPPORT_8 = 1U << 3,
GDMA_BURST_SIZE_SUPPORT_16 = 1U << 4,
GDMA_BURST_SIZE_SUPPORT_32 = 1U << 5,
GDMA_BURST_SIZE_SUPPORT_64 = 1U << 6,
GDMA_BURST_SIZE_SUPPORT_128 = 1U << 7,
} gdma_burst_size_support_t;
/**
* @brief GDMA channel events that supported by the ETM module
*/