Skip to content

Commit 7d7f533

Browse files
committed
Merge branch 'feat/support_lp_dma_on_s31' into 'master'
feat(gdma): support lp ahb dma on esp32s31 Closes IDF-14761 See merge request espressif/esp-idf!45229
2 parents b8664ef + 7656e09 commit 7d7f533

File tree

18 files changed

+400
-54
lines changed

18 files changed

+400
-54
lines changed

components/esp_driver_dma/include/esp_async_memcpy.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -97,6 +97,21 @@ esp_err_t esp_async_memcpy_install_gdma_ahb(const async_memcpy_config_t *config,
9797
esp_err_t esp_async_memcpy_install_gdma_axi(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp);
9898
#endif // SOC_HAS(AXI_GDMA)
9999

100+
#if SOC_HAS(LP_AHB_GDMA)
101+
/**
102+
* @brief Install async memcpy driver, with LP AHB-GDMA as the backend
103+
*
104+
* @param[in] config Configuration of async memcpy
105+
* @param[out] mcp Returned driver handle
106+
* @return
107+
* - ESP_OK: Install async memcpy driver successfully
108+
* - ESP_ERR_INVALID_ARG: Install async memcpy driver failed because of invalid argument
109+
* - ESP_ERR_NO_MEM: Install async memcpy driver failed because out of memory
110+
* - ESP_FAIL: Install async memcpy driver failed because of other error
111+
*/
112+
esp_err_t esp_async_memcpy_install_gdma_lp_ahb(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp);
113+
#endif // SOC_HAS(LP_AHB_GDMA)
114+
100115
#if SOC_CP_DMA_SUPPORTED
101116
/**
102117
* @brief Install async memcpy driver, with CPDMA as the backend

components/esp_driver_dma/include/esp_private/gdma.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -143,6 +143,26 @@ esp_err_t gdma_new_ahb_channel(const gdma_channel_alloc_config_t *config, gdma_c
143143
*/
144144
esp_err_t gdma_new_axi_channel(const gdma_channel_alloc_config_t *config, gdma_channel_handle_t *ret_tx_chan, gdma_channel_handle_t *ret_rx_chan);
145145

146+
/**
147+
* @brief Create LP-AHB-GDMA channel(s)
148+
* @note This API won't install interrupt service for the allocated channel.
149+
* If interrupt service is needed, user has to register GDMA event callback by `gdma_register_tx_event_callbacks` or `gdma_register_rx_event_callbacks`.
150+
* @note To allocate both TX and RX channels in a pair, pass non-NULL pointers for both ret_tx_chan and ret_rx_chan.
151+
* To allocate only one direction, pass NULL for the unwanted direction.
152+
* @note Allocation is atomic: if any channel fails to allocate, all partially allocated resources are cleaned up automatically.
153+
* Either all requested channels are successfully allocated, or the function fails with no channels allocated.
154+
*
155+
* @param[in] config Pointer to a collection of configurations for allocating GDMA channel
156+
* @param[out] ret_tx_chan Returned TX channel handle. Pass NULL if TX channel is not needed. Must not be NULL if ret_rx_chan is also NULL.
157+
* @param[out] ret_rx_chan Returned RX channel handle. Pass NULL if RX channel is not needed. Must not be NULL if ret_tx_chan is also NULL.
158+
* @return
159+
* - ESP_OK: Create DMA channel(s) successfully
160+
* - ESP_ERR_INVALID_ARG: Create DMA channel failed because both ret_tx_chan and ret_rx_chan are NULL
161+
* - ESP_ERR_NO_MEM: Create DMA channel failed because out of memory
162+
* - ESP_FAIL: Create DMA channel failed because of other error
163+
*/
164+
esp_err_t gdma_new_lp_ahb_channel(const gdma_channel_alloc_config_t *config, gdma_channel_handle_t *ret_tx_chan, gdma_channel_handle_t *ret_rx_chan);
165+
146166
/**
147167
* @brief Connect GDMA channel to trigger peripheral
148168
*

components/esp_driver_dma/src/async_memcpy_gdma.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,13 @@ esp_err_t esp_async_memcpy_install_gdma_axi(const async_memcpy_config_t *config,
207207
}
208208
#endif // SOC_HAS(AXI_GDMA)
209209

210+
#if SOC_HAS(LP_AHB_GDMA)
211+
esp_err_t esp_async_memcpy_install_gdma_lp_ahb(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp)
212+
{
213+
return esp_async_memcpy_install_gdma_template(config, mcp, gdma_new_lp_ahb_channel, SOC_GDMA_BUS_LP);
214+
}
215+
#endif // SOC_HAS(LP_AHB_GDMA)
216+
210217
#if SOC_HAS(AHB_GDMA)
211218
/// default installation falls back to use the AHB GDMA
212219
esp_err_t esp_async_memcpy_install(const async_memcpy_config_t *config, async_memcpy_handle_t *asmcp)
@@ -320,6 +327,14 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
320327
dma_link_item_alignment = GDMA_LL_AXI_DESC_ALIGNMENT;
321328
}
322329
#endif // SOC_HAS(AXI_GDMA)
330+
#if SOC_HAS(LP_AHB_GDMA)
331+
if (mcp_gdma->gdma_bus_id == SOC_GDMA_BUS_LP) {
332+
#if !GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
333+
ESP_RETURN_ON_FALSE(esp_ptr_internal(src) && esp_ptr_internal(dst), ESP_ERR_INVALID_ARG, TAG, "LP AHB GDMA can only access SRAM");
334+
#endif // !GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
335+
dma_link_item_alignment = GDMA_LL_AHB_DESC_ALIGNMENT;
336+
}
337+
#endif // SOC_HAS(LP_AHB_GDMA)
323338
// alignment check
324339
ESP_RETURN_ON_FALSE(check_buffer_alignment(mcp_gdma, src, dst, n), ESP_ERR_INVALID_ARG, TAG, "address|size not aligned: %p -> %p, sz=%zu", src, dst, n);
325340

components/esp_driver_dma/src/gdma.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,21 @@ esp_err_t gdma_new_axi_channel(const gdma_channel_alloc_config_t *config, gdma_c
264264
}
265265
#endif // SOC_HAS(AXI_GDMA)
266266

267+
#if SOC_HAS(LP_AHB_GDMA)
268+
esp_err_t gdma_new_lp_ahb_channel(const gdma_channel_alloc_config_t *config, gdma_channel_handle_t *ret_tx_chan, gdma_channel_handle_t *ret_rx_chan)
269+
{
270+
gdma_channel_search_info_t search_info = {
271+
.bus_id = SOC_GDMA_BUS_LP,
272+
.start_group_id = GDMA_LL_LP_AHB_GROUP_START_ID,
273+
.end_group_id = GDMA_LL_LP_AHB_GROUP_START_ID + GDMA_LL_LP_AHB_NUM_GROUPS,
274+
.pairs_per_group = GDMA_LL_LP_AHB_PAIRS_PER_GROUP,
275+
.m2m_capable_mask = GDMA_LL_LP_AHB_M2M_CAPABLE_PAIR_MASK,
276+
.hal_init = gdma_lp_ahb_hal_init,
277+
};
278+
return do_allocate_gdma_channel(&search_info, config, ret_tx_chan, ret_rx_chan);
279+
}
280+
#endif // SOC_HAS(LP_AHB_GDMA)
281+
267282
esp_err_t gdma_del_channel(gdma_channel_handle_t dma_chan)
268283
{
269284
if (!dma_chan) {
@@ -400,7 +415,7 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf
400415
// burst size must be power of 2
401416
ESP_RETURN_ON_FALSE((max_data_burst_size & (max_data_burst_size - 1)) == 0, ESP_ERR_INVALID_ARG,
402417
TAG, "invalid max_data_burst_size: %"PRIu32, max_data_burst_size);
403-
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) || GDMA_LL_GET(AXI_PSRAM_CAPABLE)
418+
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) || GDMA_LL_GET(AXI_PSRAM_CAPABLE) || GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
404419
if (config->access_ext_mem) {
405420
ESP_RETURN_ON_FALSE(max_data_burst_size <= GDMA_LL_MAX_BURST_SIZE_PSRAM, ESP_ERR_INVALID_ARG,
406421
TAG, "max_data_burst_size must not exceed %d when accessing external memory", GDMA_LL_MAX_BURST_SIZE_PSRAM);

components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ TEST_CASE("memory copy the same buffer with different content", "[async mcp]")
141141
test_memory_copy_with_same_buffer(driver, &config);
142142
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
143143
#endif // SOC_CP_DMA_SUPPORTED
144+
145+
#if SOC_HAS(LP_AHB_GDMA)
146+
printf("Testing memcpy by LP AHB GDMA\r\n");
147+
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&config, &driver));
148+
test_memory_copy_with_same_buffer(driver, &config);
149+
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
150+
#endif // SOC_HAS(LP_AHB_GDMA)
144151
}
145152

146153
static bool test_async_memcpy_cb_v1(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)
@@ -206,6 +213,13 @@ TEST_CASE("memory copy by DMA (blocking)", "[async mcp]")
206213
test_memory_copy_blocking(driver);
207214
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
208215
#endif // SOC_CP_DMA_SUPPORTED
216+
217+
#if SOC_HAS(LP_AHB_GDMA)
218+
printf("Testing memcpy by LP AHB GDMA\r\n");
219+
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&config, &driver));
220+
test_memory_copy_blocking(driver);
221+
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
222+
#endif // SOC_HAS(LP_AHB_GDMA)
209223
}
210224

211225
[[maybe_unused]] static void test_memcpy_with_dest_addr_unaligned(async_memcpy_handle_t driver, bool src_in_psram, bool dst_in_psram)
@@ -273,6 +287,16 @@ TEST_CASE("memory copy with dest address unaligned", "[async mcp]")
273287
#endif // GDMA_LL_GET(AXI_PSRAM_CAPABLE) && SOC_HAS(SPIRAM)
274288
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
275289
#endif // SOC_HAS(AXI_GDMA)
290+
291+
#if SOC_HAS(LP_AHB_GDMA) && !CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION
292+
printf("Testing memcpy by LP AHB GDMA\r\n");
293+
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver));
294+
test_memcpy_with_dest_addr_unaligned(driver, false, false);
295+
#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE) && SOC_HAS(SPIRAM)
296+
test_memcpy_with_dest_addr_unaligned(driver, true, true);
297+
#endif // GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE) && SOC_HAS(SPIRAM)
298+
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
299+
#endif // SOC_HAS(LP_AHB_GDMA)
276300
}
277301

278302
#define TEST_ASYNC_MEMCPY_BENCH_COUNTS 16
@@ -362,6 +386,13 @@ TEST_CASE("memory copy performance 40KB: SRAM->SRAM", "[async mcp]")
362386
test_memcpy_performance(driver, 40 * 1024, false, false);
363387
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
364388
#endif // SOC_CP_DMA_SUPPORTED
389+
390+
#if SOC_HAS(LP_AHB_GDMA)
391+
printf("Testing memcpy by LP AHB GDMA\r\n");
392+
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver));
393+
test_memcpy_performance(driver, 40 * 1024, false, false);
394+
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
395+
#endif // SOC_HAS(LP_AHB_GDMA)
365396
}
366397

367398
#if SOC_SPIRAM_SUPPORTED
@@ -390,6 +421,15 @@ TEST_CASE("memory copy performance 40KB: PSRAM->PSRAM", "[async mcp]")
390421
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
391422
#endif
392423
#endif // SOC_HAS(AXI_GDMA)
424+
425+
#if SOC_HAS(LP_AHB_GDMA)
426+
#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
427+
printf("Testing memcpy by LP AHB GDMA\r\n");
428+
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver));
429+
test_memcpy_performance(driver, 40 * 1024, true, true);
430+
TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
431+
#endif // GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
432+
#endif // SOC_HAS(LP_AHB_GDMA)
393433
}
394434
#endif
395435

components/esp_driver_dma/test_apps/dma/main/test_gdma.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,64 @@ TEST_CASE("GDMA channel allocation", "[GDMA]")
147147
TEST_ESP_OK(gdma_del_channel(rx_channels[i]));
148148
}
149149
#endif // GDMA_LL_AXI_PAIRS_PER_GROUP >= 2
150+
151+
#if SOC_HAS(LP_AHB_GDMA)
152+
// install TX channels
153+
for (int i = 0; i < GDMA_LL_LP_AHB_PAIRS_PER_GROUP; i++) {
154+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&channel_config, &tx_channels[i], NULL));
155+
};
156+
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gdma_new_lp_ahb_channel(&channel_config, &tx_channels[0], NULL));
157+
158+
// Free interrupts before installing RX interrupts to ensure enough free interrupts
159+
for (int i = 0; i < GDMA_LL_LP_AHB_PAIRS_PER_GROUP; i++) {
160+
TEST_ESP_OK(gdma_del_channel(tx_channels[i]));
161+
}
162+
163+
// install RX channels
164+
for (int i = 0; i < GDMA_LL_LP_AHB_PAIRS_PER_GROUP; i++) {
165+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&channel_config, NULL, &rx_channels[i]));
166+
}
167+
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, gdma_new_lp_ahb_channel(&channel_config, NULL, &rx_channels[0]));
168+
169+
for (int i = 0; i < GDMA_LL_LP_AHB_PAIRS_PER_GROUP; i++) {
170+
TEST_ESP_OK(gdma_del_channel(rx_channels[i]));
171+
}
172+
#endif // SOC_HAS(LP_AHB_GDMA)
173+
174+
// install single and paired TX/RX channels
175+
#if GDMA_LL_LP_AHB_PAIRS_PER_GROUP >= 2
176+
// single tx channel
177+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&channel_config, &tx_channels[0], NULL));
178+
179+
// create tx and rx channel pair
180+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&channel_config, &tx_channels[1], &rx_channels[1]));
181+
// create single rx channel
182+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&channel_config, NULL, &rx_channels[0]));
183+
184+
gdma_trigger_t fake_lp_ahb_trigger1 = {
185+
.bus_id = SOC_GDMA_BUS_LP,
186+
.instance_id = 0,
187+
};
188+
gdma_trigger_t fake_lp_ahb_trigger2 = {
189+
.bus_id = SOC_GDMA_BUS_LP,
190+
.instance_id = 1,
191+
};
192+
TEST_ESP_OK(gdma_connect(tx_channels[0], fake_lp_ahb_trigger1));
193+
// can't connect multiple channels to the same peripheral
194+
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, gdma_connect(tx_channels[1], fake_lp_ahb_trigger1));
195+
TEST_ESP_OK(gdma_connect(tx_channels[1], fake_lp_ahb_trigger2));
196+
197+
// but rx and tx can connect to the same peripheral
198+
TEST_ESP_OK(gdma_connect(rx_channels[0], fake_lp_ahb_trigger1));
199+
TEST_ESP_OK(gdma_connect(rx_channels[1], fake_lp_ahb_trigger2));
200+
for (int i = 0; i < 2; i++) {
201+
TEST_ESP_OK(gdma_disconnect(tx_channels[i]));
202+
TEST_ESP_OK(gdma_disconnect(rx_channels[i]));
203+
TEST_ESP_OK(gdma_del_channel(tx_channels[i]));
204+
TEST_ESP_OK(gdma_del_channel(rx_channels[i]));
205+
}
206+
#endif // GDMA_LL_LP_AHB_PAIRS_PER_GROUP >= 2
207+
150208
}
151209

152210
static void test_gdma_config_link_list(gdma_channel_handle_t tx_chan, gdma_channel_handle_t rx_chan,
@@ -363,6 +421,15 @@ static void test_gdma_m2m_mode(bool trig_retention_backup)
363421
TEST_ESP_OK(gdma_del_channel(tx_chan));
364422
TEST_ESP_OK(gdma_del_channel(rx_chan));
365423
#endif // SOC_HAS(AXI_GDMA)
424+
425+
#if SOC_HAS(LP_AHB_GDMA)
426+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&chan_alloc_config, &tx_chan, &rx_chan));
427+
428+
test_gdma_m2m_transaction(tx_chan, rx_chan, false, trig_retention_backup);
429+
430+
TEST_ESP_OK(gdma_del_channel(tx_chan));
431+
TEST_ESP_OK(gdma_del_channel(rx_chan));
432+
#endif // SOC_HAS(LP_AHB_GDMA)
366433
}
367434

368435
TEST_CASE("GDMA M2M Mode", "[GDMA][M2M]")
@@ -677,5 +744,15 @@ TEST_CASE("GDMA memory copy SRAM->PSRAM->SRAM", "[GDMA][M2M]")
677744
TEST_ESP_OK(gdma_del_channel(rx_chan));
678745
#endif
679746
#endif // SOC_HAS(AXI_GDMA)
747+
748+
#if SOC_HAS(LP_AHB_GDMA)
749+
printf("Testing LP-AHB-GDMA memory copy SRAM->PSRAM->SRAM\n");
750+
TEST_ESP_OK(gdma_new_lp_ahb_channel(&chan_alloc_config, &tx_chan, &rx_chan));
751+
752+
test_gdma_memcpy_from_to_psram(tx_chan, rx_chan);
753+
754+
TEST_ESP_OK(gdma_del_channel(tx_chan));
755+
TEST_ESP_OK(gdma_del_channel(rx_chan));
756+
#endif // SOC_HAS(LP_AHB_GDMA)
680757
}
681758
#endif // SOC_SPIRAM_SUPPORTED

components/esp_hal_dma/esp32s31/gdma_periph.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ const gdma_signal_conn_t gdma_periph_signals = {
4747
.tx_irq_id = ETS_AXI_PDMA_OUT_CH2_INTR_SOURCE,
4848
}
4949
}
50+
},
51+
[2] = {
52+
.pairs = {
53+
[0] = {
54+
.rx_irq_id = ETS_LP_AHB_PDMA_IN_CH0_INTR_SOURCE,
55+
.tx_irq_id = ETS_LP_AHB_PDMA_OUT_CH0_INTR_SOURCE,
56+
},
57+
[1] = {
58+
.rx_irq_id = ETS_LP_AHB_PDMA_IN_CH1_INTR_SOURCE,
59+
.tx_irq_id = ETS_LP_AHB_PDMA_OUT_CH1_INTR_SOURCE,
60+
}
61+
}
5062
}
5163
}
5264
};

components/esp_hal_dma/esp32s31/include/hal/ahb_dma_ll.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
extern "C" {
1919
#endif
2020

21-
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : NULL)
21+
#define AHB_DMA_LL_GET_HW(id) (((id) == 0) ? (&AHB_DMA) : (((id) == 2) ? ((ahb_dma_dev_t *)&LP_AHB_DMA) : NULL))
2222

2323
// any "dummy" peripheral ID can be used for M2M mode
2424
#define AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0x8200)
25+
#define LP_AHB_DMA_LL_M2M_FREE_PERIPH_ID_MASK (0xFFFC)
2526

2627
///////////////////////////////////// Common /////////////////////////////////////////
2728

@@ -67,6 +68,17 @@ static inline void ahb_dma_ll_set_default_memory_range(ahb_dma_dev_t *dev)
6768
dev->intr_mem_end_addr.val = 0x53FFFFFF;
6869
}
6970

71+
/**
72+
* @brief Preset valid memory range for LP_AHB-DMA
73+
* @param dev DMA register base address
74+
*/
75+
static inline void lp_ahb_dma_ll_set_default_memory_range(ahb_dma_dev_t *dev)
76+
{
77+
// LP_AHB-DMA can access LPMEM, SRAM, ROM, MSPI Flash, MSPI PSRAM
78+
dev->intr_mem_start_addr.val = 0x2E000000;
79+
dev->intr_mem_end_addr.val = 0x53FFFFFF;
80+
}
81+
7082
///////////////////////////////////// RX /////////////////////////////////////////
7183
/**
7284
* @brief Get DMA RX channel interrupt status word

0 commit comments

Comments
 (0)