Skip to content

Commit 33cc365

Browse files
committed
Merge branch 'feat/async_memcpy_any_alignment_v5.3' into 'release/v5.3'
async memcpy destination address doesn't have to be cache aligned (v5.3) See merge request espressif/esp-idf!36634
2 parents 57061d6 + 74615ed commit 33cc365

File tree

7 files changed

+706
-451
lines changed

7 files changed

+706
-451
lines changed

components/esp_hw_support/dma/async_memcpy_gdma.c

Lines changed: 141 additions & 185 deletions
Large diffs are not rendered by default.

components/esp_hw_support/dma/esp_async_memcpy_priv.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
#include "esp_async_memcpy.h"
1414
#include "soc/soc_caps.h"
1515

16-
#define ALIGN_DOWN(val, align) ((val) & ~((align) - 1))
17-
1816
#define DEFAULT_TRANSACTION_QUEUE_LENGTH 4
1917

2018
#ifdef __cplusplus

components/esp_hw_support/dma/esp_dma_utils.c

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -13,14 +13,118 @@
1313
#include "esp_heap_caps.h"
1414
#include "esp_memory_utils.h"
1515
#include "esp_dma_utils.h"
16+
#include "esp_private/esp_dma_utils.h"
1617
#include "esp_private/esp_cache_private.h"
1718
#include "soc/soc_caps.h"
1819
#include "hal/hal_utils.h"
20+
#include "hal/cache_hal.h"
21+
#include "hal/cache_ll.h"
22+
#include "esp_cache.h"
1923

2024
static const char *TAG = "dma_utils";
2125

2226
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
23-
#define ALIGN_DOWN_BY(num, align) ((num) & (~((align) - 1)))
27+
28+
esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffer_len, dma_buffer_split_array_t *align_buf_array, uint8_t** ret_stash_buffer)
29+
{
30+
ESP_RETURN_ON_FALSE(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
31+
32+
// read the cache line size of internal and external memory, we also use this information to check if a given memory is behind the cache
33+
size_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
34+
size_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
35+
36+
size_t split_line_size = 0;
37+
if (esp_ptr_external_ram(rx_buffer)) {
38+
split_line_size = ext_mem_cache_line_size;
39+
} else if (esp_ptr_internal(rx_buffer)) {
40+
split_line_size = int_mem_cache_line_size;
41+
}
42+
ESP_LOGV(TAG, "split_line_size:%zu", split_line_size);
43+
44+
// allocate the stash buffer from internal RAM
45+
// Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine
46+
uint8_t* stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
47+
ESP_RETURN_ON_FALSE(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer");
48+
49+
// clear align_array to avoid garbage data
50+
memset(align_buf_array, 0, sizeof(dma_buffer_split_array_t));
51+
bool need_cache_sync[3] = {false};
52+
53+
// if split_line_size is non-zero, split the buffer into head, body and tail
54+
if (split_line_size > 0) {
55+
// calculate head_overflow_len
56+
size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size;
57+
head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0;
58+
ESP_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len);
59+
// calculate tail_overflow_len
60+
size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size;
61+
ESP_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len);
62+
63+
uint8_t extra_buf_count = 0;
64+
uint8_t* input_buffer = (uint8_t*)rx_buffer;
65+
align_buf_array->buf.head.recovery_address = input_buffer;
66+
align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
67+
align_buf_array->buf.head.length = head_overflow_len;
68+
need_cache_sync[0] = int_mem_cache_line_size > 0;
69+
align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len;
70+
align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len;
71+
align_buf_array->buf.body.length = buffer_len - head_overflow_len - tail_overflow_len;
72+
need_cache_sync[1] = true;
73+
align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len;
74+
align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
75+
align_buf_array->buf.tail.length = tail_overflow_len;
76+
need_cache_sync[2] = int_mem_cache_line_size > 0;
77+
78+
// special handling when input_buffer length is no more than buffer alignment
79+
if (head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len) {
80+
align_buf_array->buf.head.length = buffer_len ;
81+
align_buf_array->buf.body.length = 0 ;
82+
align_buf_array->buf.tail.length = 0 ;
83+
}
84+
} else {
85+
align_buf_array->buf.body.aligned_buffer = rx_buffer;
86+
align_buf_array->buf.body.recovery_address = rx_buffer;
87+
align_buf_array->buf.body.length = buffer_len;
88+
need_cache_sync[1] = false;
89+
}
90+
91+
for (int i = 0; i < 3; i++) {
92+
if (align_buf_array->aligned_buffer[i].length == 0) {
93+
align_buf_array->aligned_buffer[i].aligned_buffer = NULL;
94+
align_buf_array->aligned_buffer[i].recovery_address = NULL;
95+
need_cache_sync[i] = false;
96+
}
97+
}
98+
99+
// invalidate the aligned buffer if necessary
100+
for (int i = 0; i < 3; i++) {
101+
if (need_cache_sync[i]) {
102+
size_t sync_size = align_buf_array->aligned_buffer[i].length;
103+
if (sync_size < split_line_size) {
104+
// If the size is smaller than the cache line, we need to sync the split buffer (must be cache line sized)
105+
sync_size = split_line_size;
106+
}
107+
esp_cache_msync(align_buf_array->aligned_buffer[i].aligned_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
108+
}
109+
}
110+
111+
*ret_stash_buffer = stash_buffer;
112+
return ESP_OK;
113+
}
114+
115+
esp_err_t esp_dma_merge_aligned_rx_buffers(dma_buffer_split_array_t *align_array)
116+
{
117+
ESP_RETURN_ON_FALSE_ISR(align_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
118+
119+
// only need to copy the head and tail buffer
120+
if (align_array->buf.head.length) {
121+
memcpy(align_array->buf.head.recovery_address, align_array->buf.head.aligned_buffer, align_array->buf.head.length);
122+
}
123+
if (align_array->buf.tail.length) {
124+
memcpy(align_array->buf.tail.recovery_address, align_array->buf.tail.aligned_buffer, align_array->buf.tail.length);
125+
}
126+
return ESP_OK;
127+
}
24128

25129
esp_err_t esp_dma_capable_malloc(size_t size, const esp_dma_mem_info_t *dma_mem_info, void **out_ptr, size_t *actual_size)
26130
{

components/esp_hw_support/dma/gdma_link.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@
66

77
#include <stdlib.h>
88
#include <string.h>
9-
#include <stdatomic.h>
109
#include <sys/cdefs.h>
11-
#include <sys/lock.h>
12-
#include "sdkconfig.h"
13-
#include "freertos/FreeRTOS.h"
14-
#include "freertos/task.h"
1510
#include "soc/soc_caps.h"
16-
#include "soc/ext_mem_defs.h"
1711
#include "esp_log.h"
1812
#include "esp_check.h"
1913
#include "esp_memory_utils.h"
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <stdbool.h>
10+
#include "esp_err.h"
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
/**
17+
* @brief DMA buffer information
18+
*/
19+
typedef struct {
20+
void *aligned_buffer; //!< Buffer address
21+
void *recovery_address; //!< Origin buffer address that aligned buffer should be recovered
22+
size_t length; //!< Buffer length
23+
} dma_buffer_split_info_t;
24+
25+
/**
26+
* @brief DMA buffer aligned array
27+
* The array contains three parts: head, body and tail.
28+
* Length of each part will be >=0, especially, length=0 means that there is no such part.
29+
*/
30+
typedef struct {
31+
union {
32+
struct {
33+
dma_buffer_split_info_t head; //!< Aligned head part. Corresponds to the part of the original buffer where the head is not aligned
34+
dma_buffer_split_info_t body; //!< Aligned body part. Corresponds to the part of the original aligned buffer
35+
dma_buffer_split_info_t tail; //!< Aligned tail part. Corresponds to the part of the original buffer where the tail is not aligned
36+
} buf;
37+
dma_buffer_split_info_t aligned_buffer[3]; //!< DMA aligned buffer array, consist of `head`, `body` and `tail`
38+
};
39+
} dma_buffer_split_array_t;
40+
41+
/**
42+
* @brief Split DMA RX buffer to cache aligned buffers
43+
*
44+
* @note After the original RX buffer is split into an array, caller should mount the buffer array to the DMA controller in scatter-gather mode.
45+
* Don't read/write the aligned buffers before the DMA finished using them.
46+
*
47+
* @param[in] rx_buffer The origin DMA buffer used for receiving data
48+
* @param[in] buffer_len rx_buffer length
49+
* @param[out] align_buf_array Aligned DMA buffer array
50+
* @param[out] ret_stash_buffer Allocated stash buffer (caller should free it after use)
51+
* @return
52+
* - ESP_OK: Split to aligned buffer successfully
53+
* - ESP_ERR_INVALID_ARG: Split to aligned buffer failed because of invalid argument
54+
*
55+
* brief sketch:
56+
* cache alignment delimiter cache alignment delimiter
57+
* │ │
58+
* Origin Buffer │ Origin Buffer │
59+
* │ │ │ │
60+
* │ ▼ ▼ ▼
61+
* │ ...---xxxxx|xxxxxxxxxxxxxxxxxxxxxxxxxxxxx|xxxxx----...
62+
* │ │ │ │
63+
* │ │ ▼ │
64+
* │ │ |xxxxxxxxxxxxxxxxxxxxxxxxxxxxx| │
65+
* │ │ ▲ │
66+
* ▼ │ │ │
67+
* Aligned buffers └──► Head Body Tail ◄──────┘
68+
* │ │
69+
* ▼ ▼
70+
* |xxxxx......| |xxxxx......|
71+
*/
72+
esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffer_len, dma_buffer_split_array_t *align_buf_array, uint8_t** ret_stash_buffer);
73+
74+
/**
75+
* @brief Merge aligned RX buffer array to origin buffer
76+
*
77+
* @note This function can be used in the ISR context.
78+
*
79+
* @param[in] align_buf_array Aligned DMA buffer array
80+
* @return
81+
* - ESP_OK: Merge aligned buffer to origin buffer successfully
82+
* - ESP_ERR_INVALID_ARG: Merge aligned buffer to origin buffer failed because of invalid argument
83+
*/
84+
esp_err_t esp_dma_merge_aligned_rx_buffers(dma_buffer_split_array_t *align_buf_array);
85+
86+
#ifdef __cplusplus
87+
}
88+
#endif

0 commit comments

Comments
 (0)