refactor(parlio_rx): refactor to support unaligned user payload buffer

Closes https://github.com/espressif/esp-idf/issues/17581
This commit is contained in:
laokaiyao
2025-11-28 15:37:28 +08:00
parent 0b0306a13f
commit 047ea940bf
9 changed files with 179 additions and 115 deletions
+45 -42
View File
@@ -29,7 +29,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
{
esp_err_t ret = ESP_OK;
uint8_t* stash_buffer = NULL;
ESP_RETURN_ON_FALSE(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// 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
size_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
@@ -41,80 +41,83 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
} else if (esp_ptr_internal(rx_buffer)) {
split_line_size = int_mem_cache_line_size;
}
ESP_LOGV(TAG, "split_line_size:%zu", split_line_size);
bool align_required = split_line_size > 0;
ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size);
// allocate the stash buffer from internal RAM
// Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine
stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer");
if (*ret_stash_buffer == NULL) {
// If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM
// Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine
stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE_ISR(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer");
} else {
// If the stash buffer is offered by the caller, check if it is aligned
ESP_RETURN_ON_FALSE_ISR(split_line_size == 0 || (uintptr_t)(*ret_stash_buffer) % split_line_size == 0,
ESP_ERR_INVALID_ARG, TAG, "the offered stash buffer is not aligned");
// If the stash buffer is offered by the caller, use it
stash_buffer = *ret_stash_buffer;
}
// clear align_array to avoid garbage data
memset(align_buf_array, 0, sizeof(dma_buffer_split_array_t));
bool need_cache_sync[3] = {false};
// if split_line_size is non-zero, split the buffer into head, body and tail
if (split_line_size > 0) {
// if align_required, split the buffer into head, body and tail
if (align_required) {
// calculate head_overflow_len
size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size;
head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0;
ESP_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len);
ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len);
// calculate tail_overflow_len
size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size;
ESP_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len);
uint8_t extra_buf_count = 0;
uint8_t* input_buffer = (uint8_t*)rx_buffer;
align_buf_array->buf.head.recovery_address = input_buffer;
align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
align_buf_array->buf.head.length = head_overflow_len;
need_cache_sync[0] = int_mem_cache_line_size > 0;
align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len;
align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len;
align_buf_array->buf.body.length = buffer_len - head_overflow_len - tail_overflow_len;
need_cache_sync[1] = true;
align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len;
align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
align_buf_array->buf.tail.length = tail_overflow_len;
need_cache_sync[2] = int_mem_cache_line_size > 0;
ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len);
// special handling when input_buffer length is no more than buffer alignment
if (head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len) {
align_buf_array->buf.head.length = buffer_len ;
align_buf_array->buf.body.length = 0 ;
align_buf_array->buf.tail.length = 0 ;
bool is_small_buf = head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len;
uint8_t extra_buf_count = 0;
uint8_t* input_buffer = (uint8_t*)rx_buffer;
if (head_overflow_len || is_small_buf) {
align_buf_array->buf.head.recovery_address = input_buffer;
align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
align_buf_array->buf.head.length = is_small_buf ? buffer_len : head_overflow_len;
need_cache_sync[0] = int_mem_cache_line_size > 0;
}
int body_len = (int)buffer_len - (int)head_overflow_len - (int)tail_overflow_len;
if (body_len > 0) {
align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len;
align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len;
align_buf_array->buf.body.length = body_len;
need_cache_sync[1] = true;
}
if (tail_overflow_len && !is_small_buf) {
align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len;
align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
align_buf_array->buf.tail.length = tail_overflow_len;
need_cache_sync[2] = int_mem_cache_line_size > 0;
}
} else {
align_buf_array->buf.body.aligned_buffer = rx_buffer;
align_buf_array->buf.body.recovery_address = rx_buffer;
align_buf_array->buf.body.length = buffer_len;
need_cache_sync[1] = false;
}
for (int i = 0; i < 3; i++) {
if (align_buf_array->aligned_buffer[i].length == 0) {
align_buf_array->aligned_buffer[i].aligned_buffer = NULL;
align_buf_array->aligned_buffer[i].recovery_address = NULL;
need_cache_sync[i] = false;
}
}
// invalidate the aligned buffer if necessary
for (int i = 0; i < 3; i++) {
if (need_cache_sync[i]) {
size_t sync_size = align_buf_array->aligned_buffer[i].length;
size_t sync_size = align_buf_array->aligned_buffer[i].length;
if (need_cache_sync[i] && sync_size > 0) {
if (sync_size < split_line_size) {
// If the buffer is smaller than the cache line size, we need to sync the whole buffer
sync_size = split_line_size;
}
esp_err_t res = esp_cache_msync(align_buf_array->aligned_buffer[i].aligned_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
ESP_GOTO_ON_ERROR(res, err, TAG, "failed to do cache sync");
ESP_GOTO_ON_ERROR_ISR(res, err, TAG, "failed to do cache sync");
}
}
*ret_stash_buffer = stash_buffer;
return ESP_OK;
err:
if (stash_buffer) {
// Only free the stash buffer if it is not offered by the caller
if (stash_buffer && *ret_stash_buffer == NULL) {
free(stash_buffer);
}
return ret;
+18 -14
View File
@@ -203,20 +203,24 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
lli_nc->dw0.size = lli_nc->dw0.length;
// mark the EOF node
lli_nc->dw0.suc_eof = (config->flags.mark_eof == 1) && (i == num_items_need - 1);
// mark the final node
switch (config->flags.mark_final) {
case GDMA_FINAL_LINK_TO_NULL:
lli_nc->next = NULL;
break;
case GDMA_FINAL_LINK_TO_HEAD:
lli_nc->next = (gdma_link_list_item_t *)(list->items);
break;
case GDMA_FINAL_LINK_TO_START:
lli_nc->next = (gdma_link_list_item_t *)(list->items + begin_item_idx * item_size);
break;
default:
lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size);
break;
if (i == num_items_need - 1) {
// mark the final node
switch (config->flags.mark_final) {
case GDMA_FINAL_LINK_TO_NULL:
lli_nc->next = NULL;
break;
case GDMA_FINAL_LINK_TO_HEAD:
lli_nc->next = (gdma_link_list_item_t *)(list->items);
break;
case GDMA_FINAL_LINK_TO_START:
lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size);
break;
default:
lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size);
break;
}
} else {
lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size);
}
lli_nc->dw0.owner = GDMA_LLI_OWNER_DMA;
buf += max_buffer_mount_length;
@@ -62,7 +62,7 @@ esp_err_t gdma_del_link_list(gdma_link_list_handle_t list);
*/
typedef enum {
GDMA_FINAL_LINK_TO_DEFAULT = 0, /*!< The next node is linked to the default next item in the link list */
GDMA_FINAL_LINK_TO_NULL = 1, /*!< The next node is linked to the final item in the link list */
GDMA_FINAL_LINK_TO_NULL = 1, /*!< No next node is linked */
GDMA_FINAL_LINK_TO_HEAD = 2, /*!< The next node is linked to the head item in the link list */
GDMA_FINAL_LINK_TO_START = 3, /*!< The next node is linked to the start item in the link list */
} gdma_final_node_link_type_t;
@@ -77,9 +77,11 @@ typedef struct {
struct gdma_buffer_mount_flags {
uint32_t mark_eof: 1; /*!< Whether to mark the list item as the "EOF" item.
Note, an "EOF" descriptor can be interrupted differently by peripheral.
But it doesn't mean to terminate a DMA link (use `mark_final` instead).
But it doesn't mean to terminate a DMA link (set `mark_final` to GDMA_FINAL_LINK_TO_NULL instead).
EOF link list item can also trigger an interrupt. */
gdma_final_node_link_type_t mark_final: 2; /*!< The next node of the final item in the link list */
gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount.
For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect.
Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */
uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check.
Only enable it when you know what you are doing. */
} flags; //!< Flags for buffer mount configurations