Skip to content

Commit 486fd18

Browse files
committed
Merge branch 'refactor/cp_dma_use_gdma_link' into 'master'
refactor(cp_dma): to use gdma_link driver for descriptor config See merge request espressif/esp-idf!35348
2 parents e41746e + 364bbbd commit 486fd18

File tree

3 files changed

+88
-112
lines changed

3 files changed

+88
-112
lines changed

components/esp_hw_support/debug_probe/debug_probe.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ esp_err_t debug_probe_new_unit(const debug_probe_unit_config_t *config, debug_pr
9191
}
9292
// reserve the GPIO output path, because we don't expect another peripheral to signal to the same GPIO
9393
uint64_t old_gpio_rsv_mask = esp_gpio_reserve(pin_bit_mask);
94-
// check if the GPIO is already used by others, RMT TX channel only uses the output path of the GPIO
94+
// check if the GPIO is already used by others
9595
if (old_gpio_rsv_mask & pin_bit_mask) {
9696
ESP_LOGW(TAG, "GPIO conflict with others");
9797
}

components/esp_hw_support/dma/async_memcpy_cp_dma.c

Lines changed: 86 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include <sys/param.h>
1111
#include "sdkconfig.h"
1212
#include "freertos/FreeRTOS.h"
13-
#include "soc/soc_caps.h"
1413
#include "soc/interrupts.h"
1514
#include "esp_log.h"
1615
#include "esp_check.h"
@@ -20,40 +19,36 @@
2019
#include "esp_memory_utils.h"
2120
#include "esp_async_memcpy.h"
2221
#include "esp_async_memcpy_priv.h"
22+
#include "esp_private/gdma_link.h"
2323
#include "hal/cp_dma_hal.h"
2424
#include "hal/cp_dma_ll.h"
25-
#include "hal/dma_types.h"
2625

2726
static const char *TAG = "async_mcp.cpdma";
2827

28+
#define MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE 4095
29+
2930
/// @brief Transaction object for async memcpy
30-
/// @note - the DMA descriptors to be 4-byte aligned
31-
/// @note - The DMA descriptor link list is allocated dynamically from DMA-able memory
32-
/// @note - Because of the eof_node, the transaction object should also be allocated from DMA-able memory
3331
typedef struct async_memcpy_transaction_t {
34-
dma_descriptor_align4_t eof_node; // this is the DMA node which act as the EOF descriptor (RX path only)
35-
dma_descriptor_align4_t *tx_desc_link; // descriptor link list, the length of the link is determined by the copy buffer size
36-
dma_descriptor_align4_t *rx_desc_link; // descriptor link list, the length of the link is determined by the copy buffer size
37-
intptr_t tx_start_desc_addr; // TX start descriptor address
38-
intptr_t rx_start_desc_addr; // RX start descriptor address
39-
async_memcpy_isr_cb_t cb; // user callback
40-
void *cb_args; // user callback args
32+
gdma_link_list_handle_t tx_link_list; // DMA link list for TX direction
33+
gdma_link_list_handle_t rx_link_list; // DMA link list for RX direction
34+
async_memcpy_isr_cb_t cb; // user callback
35+
void *cb_args; // user callback args
4136
STAILQ_ENTRY(async_memcpy_transaction_t) idle_queue_entry; // Entry for the idle queue
4237
STAILQ_ENTRY(async_memcpy_transaction_t) ready_queue_entry; // Entry for the ready queue
4338
} async_memcpy_transaction_t;
4439

4540
/// @brief Context of async memcpy driver
4641
/// @note - It saves two queues, one for idle transaction objects, one for ready transaction objects
47-
/// @note - Transaction objects are allocated from DMA-able memory
4842
/// @note - Number of transaction objects are determined by the backlog parameter
4943
typedef struct {
5044
async_memcpy_context_t parent; // Parent IO interface
51-
size_t max_single_dma_buffer; // max DMA buffer size by a single descriptor
5245
cp_dma_hal_context_t hal; // CPDMA hal
5346
intr_handle_t intr; // CPDMA interrupt handle
54-
portMUX_TYPE spin_lock; // spin lock to avoid threads and isr from accessing the same resource simultaneously
55-
_Atomic async_memcpy_fsm_t fsm; // driver state machine, changing state should be atomic
56-
async_memcpy_transaction_t *transaction_pool; // transaction object pool
47+
portMUX_TYPE spin_lock; // spin lock to avoid threads and isr from accessing the same resource simultaneously
48+
_Atomic async_memcpy_fsm_t fsm;// driver state machine, changing state should be atomic
49+
size_t num_trans_objs; // number of transaction objects
50+
async_memcpy_transaction_t *transaction_pool; // transaction object pool
51+
async_memcpy_transaction_t *current_transaction; // current transaction object
5752
STAILQ_HEAD(, async_memcpy_transaction_t) idle_queue_head; // Head of the idle queue
5853
STAILQ_HEAD(, async_memcpy_transaction_t) ready_queue_head; // Head of the ready queue
5954
} async_memcpy_cpdma_context_t;
@@ -65,6 +60,15 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
6560
static esp_err_t mcp_cpdma_destroy(async_memcpy_cpdma_context_t *mcp_dma)
6661
{
6762
if (mcp_dma->transaction_pool) {
63+
for (size_t i = 0; i < mcp_dma->num_trans_objs; i++) {
64+
async_memcpy_transaction_t* trans = &mcp_dma->transaction_pool[i];
65+
if (trans->tx_link_list) {
66+
gdma_del_link_list(trans->tx_link_list);
67+
}
68+
if (trans->rx_link_list) {
69+
gdma_del_link_list(trans->rx_link_list);
70+
}
71+
}
6872
free(mcp_dma->transaction_pool);
6973
}
7074
if (mcp_dma->intr) {
@@ -83,13 +87,12 @@ esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, as
8387
esp_err_t ret = ESP_OK;
8488
async_memcpy_cpdma_context_t *mcp_dma = NULL;
8589
ESP_RETURN_ON_FALSE(config && mcp, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
86-
// allocate memory of driver context from internal memory
90+
// allocate memory of driver context from internal memory (because it contains atomic variable)
8791
mcp_dma = heap_caps_calloc(1, sizeof(async_memcpy_cpdma_context_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
8892
ESP_GOTO_ON_FALSE(mcp_dma, ESP_ERR_NO_MEM, err, TAG, "no mem for driver context");
8993
uint32_t trans_queue_len = config->backlog ? config->backlog : DEFAULT_TRANSACTION_QUEUE_LENGTH;
90-
// allocate memory for transaction pool, aligned to 4 because the trans->eof_node requires that alignment
91-
mcp_dma->transaction_pool = heap_caps_aligned_calloc(4, trans_queue_len, sizeof(async_memcpy_transaction_t),
92-
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
94+
// allocate memory for transaction pool from internal memory
95+
mcp_dma->transaction_pool = heap_caps_calloc(trans_queue_len, sizeof(async_memcpy_transaction_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
9396
ESP_GOTO_ON_FALSE(mcp_dma->transaction_pool, ESP_ERR_NO_MEM, err, TAG, "no mem for transaction pool");
9497

9598
// Init hal context
@@ -110,8 +113,8 @@ esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, as
110113
// initialize other members
111114
portMUX_INITIALIZE(&mcp_dma->spin_lock);
112115
atomic_init(&mcp_dma->fsm, MCP_FSM_IDLE);
113-
size_t trans_align = config->dma_burst_size;
114-
mcp_dma->max_single_dma_buffer = trans_align ? ALIGN_DOWN(DMA_DESCRIPTOR_BUFFER_MAX_SIZE, trans_align) : DMA_DESCRIPTOR_BUFFER_MAX_SIZE;
116+
mcp_dma->num_trans_objs = trans_queue_len;
117+
115118
mcp_dma->parent.del = mcp_cpdma_del;
116119
mcp_dma->parent.memcpy = mcp_cpdma_memcpy;
117120
// return driver object
@@ -138,55 +141,6 @@ static esp_err_t mcp_cpdma_del(async_memcpy_context_t *ctx)
138141
return mcp_cpdma_destroy(mcp_dma);
139142
}
140143

141-
static void mount_tx_buffer_to_dma(dma_descriptor_align4_t *desc_array, int num_desc,
142-
uint8_t *buf, size_t buf_sz, size_t max_single_dma_buffer)
143-
{
144-
uint32_t prepared_length = 0;
145-
size_t len = buf_sz;
146-
for (int i = 0; i < num_desc - 1; i++) {
147-
desc_array[i].buffer = &buf[prepared_length];
148-
desc_array[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
149-
desc_array[i].dw0.suc_eof = 0;
150-
desc_array[i].dw0.size = max_single_dma_buffer;
151-
desc_array[i].dw0.length = max_single_dma_buffer;
152-
desc_array[i].next = &desc_array[i + 1];
153-
prepared_length += max_single_dma_buffer;
154-
len -= max_single_dma_buffer;
155-
}
156-
// take special care to the EOF descriptor
157-
desc_array[num_desc - 1].buffer = &buf[prepared_length];
158-
desc_array[num_desc - 1].next = NULL;
159-
desc_array[num_desc - 1].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
160-
desc_array[num_desc - 1].dw0.suc_eof = 1;
161-
desc_array[num_desc - 1].dw0.size = len;
162-
desc_array[num_desc - 1].dw0.length = len;
163-
}
164-
165-
static void mount_rx_buffer_to_dma(dma_descriptor_align4_t *desc_array, int num_desc, dma_descriptor_align4_t *eof_desc,
166-
uint8_t *buf, size_t buf_sz, size_t max_single_dma_buffer)
167-
{
168-
uint32_t prepared_length = 0;
169-
size_t len = buf_sz;
170-
if (desc_array) {
171-
assert(num_desc > 0);
172-
for (int i = 0; i < num_desc; i++) {
173-
desc_array[i].buffer = &buf[prepared_length];
174-
desc_array[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
175-
desc_array[i].dw0.size = max_single_dma_buffer;
176-
desc_array[i].dw0.length = max_single_dma_buffer;
177-
desc_array[i].next = &desc_array[i + 1];
178-
prepared_length += max_single_dma_buffer;
179-
len -= max_single_dma_buffer;
180-
}
181-
desc_array[num_desc - 1].next = eof_desc;
182-
}
183-
eof_desc->buffer = &buf[prepared_length];
184-
eof_desc->next = NULL;
185-
eof_desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
186-
eof_desc->dw0.size = len;
187-
eof_desc->dw0.length = len;
188-
}
189-
190144
/// @brief help function to get one transaction from the ready queue
191145
/// @note this function is allowed to be called in ISR
192146
static async_memcpy_transaction_t *try_pop_trans_from_ready_queue(async_memcpy_cpdma_context_t *mcp_dma)
@@ -211,7 +165,10 @@ static void try_start_pending_transaction(async_memcpy_cpdma_context_t *mcp_dma)
211165
trans = try_pop_trans_from_ready_queue(mcp_dma);
212166
if (trans) {
213167
atomic_store(&mcp_dma->fsm, MCP_FSM_RUN);
214-
cp_dma_hal_set_desc_base_addr(&mcp_dma->hal, trans->tx_start_desc_addr, trans->rx_start_desc_addr);
168+
mcp_dma->current_transaction = trans;
169+
cp_dma_hal_set_desc_base_addr(&mcp_dma->hal,
170+
gdma_link_get_head_addr(trans->tx_link_list),
171+
gdma_link_get_head_addr(trans->rx_link_list));
215172
cp_dma_hal_start(&mcp_dma->hal); // enable DMA and interrupt
216173
} else {
217174
atomic_store(&mcp_dma->fsm, MCP_FSM_IDLE);
@@ -244,33 +201,67 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
244201
// check if we get the transaction object successfully
245202
ESP_RETURN_ON_FALSE(trans, ESP_ERR_INVALID_STATE, TAG, "no free node in the idle queue");
246203

247-
// calculate how many descriptors we want
248-
size_t max_single_dma_buffer = mcp_dma->max_single_dma_buffer;
249-
uint32_t num_desc_per_path = (n + max_single_dma_buffer - 1) / max_single_dma_buffer;
250-
// allocate DMA descriptors, descriptors need a strict alignment
251-
trans->tx_desc_link = heap_caps_aligned_calloc(4, num_desc_per_path, sizeof(dma_descriptor_align4_t),
252-
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
253-
ESP_GOTO_ON_FALSE(trans->tx_desc_link, ESP_ERR_NO_MEM, err, TAG, "no mem for DMA descriptors");
254-
// don't have to allocate the EOF descriptor, we will use trans->eof_node as the RX EOF descriptor
255-
if (num_desc_per_path > 1) {
256-
trans->rx_desc_link = heap_caps_aligned_calloc(4, num_desc_per_path - 1, sizeof(dma_descriptor_align4_t),
257-
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
258-
ESP_GOTO_ON_FALSE(trans->rx_desc_link, ESP_ERR_NO_MEM, err, TAG, "no mem for DMA descriptors");
259-
} else {
260-
// small copy buffer, use the trans->eof_node is sufficient
261-
trans->rx_desc_link = NULL;
204+
// clean up the transaction configuration comes from the last one
205+
if (trans->tx_link_list) {
206+
gdma_del_link_list(trans->tx_link_list);
207+
trans->tx_link_list = NULL;
208+
}
209+
if (trans->rx_link_list) {
210+
gdma_del_link_list(trans->rx_link_list);
211+
trans->rx_link_list = NULL;
262212
}
263213

264-
// (preload) mount src data to the TX descriptor
265-
mount_tx_buffer_to_dma(trans->tx_desc_link, num_desc_per_path, src, n, max_single_dma_buffer);
266-
// (preload) mount dst data to the RX descriptor
267-
mount_rx_buffer_to_dma(trans->rx_desc_link, num_desc_per_path - 1, &trans->eof_node, dst, n, max_single_dma_buffer);
214+
// allocate gdma TX link
215+
gdma_link_list_config_t tx_link_cfg = {
216+
.buffer_alignment = 1, // CP_DMA doesn't have alignment requirement for internal memory
217+
.item_alignment = 4, // CP_DMA requires 4 bytes alignment for each descriptor
218+
.num_items = n / MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1,
219+
.flags = {
220+
.check_owner = true,
221+
.items_in_ext_mem = false,
222+
},
223+
};
224+
ESP_GOTO_ON_ERROR(gdma_new_link_list(&tx_link_cfg, &trans->tx_link_list), err, TAG, "failed to create TX link list");
225+
// mount the source buffer to the TX link list
226+
gdma_buffer_mount_config_t tx_buf_mount_config[1] = {
227+
[0] = {
228+
.buffer = src,
229+
.length = n,
230+
.flags = {
231+
.mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item
232+
.mark_final = true, // using singly list, so terminate the link here
233+
}
234+
}
235+
};
236+
gdma_link_mount_buffers(trans->tx_link_list, 0, tx_buf_mount_config, 1, NULL);
237+
238+
// allocate gdma RX link
239+
gdma_link_list_config_t rx_link_cfg = {
240+
.buffer_alignment = 1, // CP_DMA doesn't have alignment requirement for internal memory
241+
.item_alignment = 4, // CP_DMA requires 4 bytes alignment for each descriptor
242+
.num_items = n / MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1,
243+
.flags = {
244+
.check_owner = true,
245+
.items_in_ext_mem = false,
246+
},
247+
};
248+
ESP_GOTO_ON_ERROR(gdma_new_link_list(&rx_link_cfg, &trans->rx_link_list), err, TAG, "failed to create RX link list");
249+
// mount the destination buffer to the RX link list
250+
gdma_buffer_mount_config_t rx_buf_mount_config[1] = {
251+
[0] = {
252+
.buffer = dst,
253+
.length = n,
254+
.flags = {
255+
.mark_eof = false, // EOF is set by TX side
256+
.mark_final = true, // using singly list, so terminate the link here
257+
}
258+
}
259+
};
260+
gdma_link_mount_buffers(trans->rx_link_list, 0, rx_buf_mount_config, 1, NULL);
268261

269262
// save other transaction context
270263
trans->cb = cb_isr;
271264
trans->cb_args = cb_args;
272-
trans->tx_start_desc_addr = (intptr_t)trans->tx_desc_link;
273-
trans->rx_start_desc_addr = trans->rx_desc_link ? (intptr_t)trans->rx_desc_link : (intptr_t)&trans->eof_node;
274265

275266
portENTER_CRITICAL(&mcp_dma->spin_lock);
276267
// insert the trans to ready queue
@@ -284,14 +275,6 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
284275

285276
err:
286277
if (trans) {
287-
if (trans->tx_desc_link) {
288-
free(trans->tx_desc_link);
289-
trans->tx_desc_link = NULL;
290-
}
291-
if (trans->rx_desc_link) {
292-
free(trans->rx_desc_link);
293-
trans->rx_desc_link = NULL;
294-
}
295278
// return back the trans to idle queue
296279
portENTER_CRITICAL(&mcp_dma->spin_lock);
297280
STAILQ_INSERT_TAIL(&mcp_dma->idle_queue_head, trans, idle_queue_entry);
@@ -310,9 +293,7 @@ static void mcp_default_isr_handler(void *args)
310293

311294
// End-Of-Frame on RX side
312295
if (status & CP_DMA_LL_EVENT_RX_EOF) {
313-
dma_descriptor_align4_t *eof_desc = (dma_descriptor_align4_t *)cp_dma_ll_get_rx_eof_descriptor_address(mcp_dma->hal.dev);
314-
// get the transaction object address by the EOF descriptor address
315-
async_memcpy_transaction_t *trans = __containerof(eof_desc, async_memcpy_transaction_t, eof_node);
296+
async_memcpy_transaction_t *trans = mcp_dma->current_transaction;
316297

317298
// switch driver state from RUN to IDLE
318299
async_memcpy_fsm_t expected_fsm = MCP_FSM_RUN;
@@ -325,11 +306,6 @@ static void mcp_default_isr_handler(void *args)
325306
};
326307
need_yield = cb(&mcp_dma->parent, &e, trans->cb_args);
327308
}
328-
// recycle descriptor memory
329-
free(trans->tx_desc_link);
330-
free(trans->rx_desc_link);
331-
trans->tx_desc_link = NULL;
332-
trans->rx_desc_link = NULL;
333309
trans->cb = NULL;
334310

335311
portENTER_CRITICAL_ISR(&mcp_dma->spin_lock);

components/esp_hw_support/include/esp_async_memcpy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ typedef struct {
5353
uint32_t backlog; /*!< Maximum number of transactions that can be prepared in the background */
5454
size_t sram_trans_align __attribute__((deprecated)); /*!< DMA transfer alignment (both in size and address) for SRAM memory */
5555
union {
56-
size_t psram_trans_align; /*!< DMA transfer alignment (both in size and address) for PSRAM memory */
56+
size_t psram_trans_align __attribute__((deprecated)); /*!< DMA transfer alignment (both in size and address) for PSRAM memory */
5757
size_t dma_burst_size; /*!< DMA transfer burst size, in bytes */
5858
};
5959
uint32_t flags; /*!< Extra flags to control async memcpy feature */

0 commit comments

Comments
 (0)