From 0b0306a13f39e3dc2fc78f7e5d2a51f6a7ea0766 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Mon, 21 Jul 2025 22:07:35 +0800 Subject: [PATCH] refactor(parlio_rx): use gdma_link for better gdma link management --- components/esp_driver_parlio/src/parlio_rx.c | 166 +++++++++---------- 1 file changed, 74 insertions(+), 92 deletions(-) diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index b413cc21f4..aa6b2da262 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -19,12 +19,6 @@ #define PARLIO_MAX_ALIGNED_DMA_BUF_SIZE DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED #endif -#if defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AHB) -typedef dma_descriptor_align4_t parlio_dma_desc_t; -#elif defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS == SOC_GDMA_BUS_AXI) -typedef dma_descriptor_align8_t parlio_dma_desc_t; -#endif - #define PARLIO_DMA_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA) /** @@ -32,13 +26,13 @@ typedef dma_descriptor_align8_t parlio_dma_desc_t; */ typedef struct { parlio_rx_delimiter_handle_t delimiter; /*!< Delimiter of this transaction */ - void *payload; /*!< The payload of this transaction, will be mounted to DMA descriptor */ + void *payload; /*!< The payload of this transaction, will be mounted to DMA link list node */ size_t size; /*!< The payload size in byte */ size_t recv_bytes; /*!< The received bytes of this transaction will be reset when all data filled in the infinite transaction */ struct { uint32_t infinite : 1; /*!< Whether this is an infinite transaction */ - uint32_t indirect_mount : 1; /*!< Whether the user payload mount to the descriptor indirectly via an internal DMA buffer */ + uint32_t indirect_mount : 1; /*!< Whether the user payload mount to the link list node indirectly via an internal DMA buffer */ } flags; } parlio_rx_transaction_t; @@ -70,11 +64,12 @@ typedef struct parlio_rx_unit_t { /* DMA Resources */ gdma_channel_handle_t dma_chan; /*!< DMA channel */ size_t max_recv_size; /*!< Maximum receive size for a normal transaction */ - size_t desc_num; /*!< DMA descriptor number */ - size_t desc_size; /*!< DMA descriptors total size */ - parlio_dma_desc_t **dma_descs; /*!< DMA descriptor array pointer */ - parlio_dma_desc_t *curr_desc; /*!< The pointer of the current descriptor */ - void *usr_recv_buf; /*!< The pointe to the user's receiving buffer */ + gdma_link_list_handle_t dma_link; /*!< DMA link list handle */ + uint32_t node_num; /*!< The number of nodes in the DMA link list */ + size_t int_mem_align; /*!< Alignment for internal memory */ + size_t ext_mem_align; /*!< Alignment for external memory */ + uint32_t curr_node_id; /*!< The index of the current node in the DMA link list */ + void *usr_recv_buf; /*!< The point to the user's receiving buffer */ /* Infinite transaction specific */ void *dma_buf; /*!< Additional internal DMA buffer only for infinite transactions */ @@ -122,7 +117,6 @@ static portMUX_TYPE s_rx_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parlio_rx_transaction_t *trans) { - parlio_dma_desc_t **p_desc = rx_unit->dma_descs; /* Update the current transaction to the next one, and declare the delimiter is under using of the rx unit */ memcpy(&rx_unit->curr_trans, trans, sizeof(parlio_rx_transaction_t)); portENTER_CRITICAL_SAFE(&s_rx_spinlock); @@ -131,50 +125,51 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli } portEXIT_CRITICAL_SAFE(&s_rx_spinlock); - uint32_t desc_num = trans->size / PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; - uint32_t remain_num = trans->size % PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; - /* If there are still data remained, need one more descriptor */ - desc_num += remain_num ? 1 : 0; - if (trans->flags.infinite && desc_num < 2) { - /* At least 2 descriptors needed */ - desc_num = 2; + uint32_t required_node_num = trans->size / PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; + uint32_t remain_node_num = trans->size % PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; + /* If there are still data remained, need one more node */ + required_node_num += remain_node_num ? 1 : 0; + if (trans->flags.infinite && required_node_num < 2) { + /* At least 2 nodes needed */ + required_node_num = 2; } + rx_unit->node_num = required_node_num; size_t mount_size = 0; size_t offset = 0; + // TODO: update DMA alignment #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE uint32_t alignment = rx_unit->base.group->dma_align; #else uint32_t alignment = 4; #endif - /* Loop the descriptors to assign the data */ - for (int i = 0; i < desc_num; i++) { - size_t rest_size = trans->size - offset; + gdma_buffer_mount_config_t mount_config[required_node_num]; + /* Loop the nodes to assign the data */ + for (int i = 0; i < required_node_num; i++) { + size_t rest_size = trans->size - offset; if (rest_size >= 2 * PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = PRALIO_RX_MOUNT_SIZE_CALC(trans->size, desc_num, alignment); + mount_size = PARLIO_RX_MOUNT_SIZE_CALC(trans->size, required_node_num, alignment); } else if (rest_size <= PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = (desc_num == 2) && (i == 0) ? PRALIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment) : rest_size; + mount_size = (required_node_num == 2) && (i == 0) ? PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment) : rest_size; } else { mount_size = PRALIO_RX_MOUNT_SIZE_CALC(rest_size, 2, alignment); } - p_desc[i]->buffer = (void *)((uint8_t *)trans->payload + offset); - p_desc[i]->dw0.size = mount_size; - p_desc[i]->dw0.length = mount_size; - p_desc[i]->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; - // Link the descriptor - if (i < desc_num - 1) { - p_desc[i]->next = p_desc[i + 1]; - } else { - /* For infinite transaction, link the descriptor as a ring */ - p_desc[i]->next = trans->flags.infinite ? p_desc[0] : NULL; + mount_config[i].buffer = (void *)((uint8_t *)trans->payload + offset); + mount_config[i].length = mount_size; + mount_config[i].flags.bypass_buffer_align_check = false; + mount_config[i].flags.mark_eof = false; + mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT; + // Link the node + if (i == required_node_num - 1) { + /* For infinite transaction, link the node as a ring */ + mount_config[i].flags.mark_final = !trans->flags.infinite ? GDMA_FINAL_LINK_TO_NULL : GDMA_FINAL_LINK_TO_HEAD; + mount_config[i].flags.mark_eof = true; } offset += mount_size; -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(p_desc[i], rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); -#endif } + gdma_link_mount_buffers(rx_unit->dma_link, 0, mount_config, required_node_num, NULL); /* Reset the current DMA node */ - rx_unit->curr_desc = p_desc[0]; + rx_unit->curr_node_id = 0; return offset; } @@ -350,7 +345,7 @@ static bool parlio_rx_default_eof_callback(gdma_channel_handle_t dma_chan, gdma_ } /* Mount the new transaction buffer and start the new transaction */ parlio_rx_mount_transaction_buffer(rx_unit, &next_trans); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->dma_descs[0]); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(rx_unit->base.group->hal.regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -381,20 +376,20 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, return false; } - /* Get the finished descriptor from the current descriptor */ - parlio_dma_desc_t *finished_desc = rx_unit->curr_desc; + /* Get the finished node from the current node */ + void *finished_buffer = gdma_link_get_buffer(rx_unit->dma_link, rx_unit->curr_node_id); + size_t finished_length = gdma_link_get_length(rx_unit->dma_link, rx_unit->curr_node_id); #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE esp_err_t ret = ESP_OK; - ret |= esp_cache_msync((void *)finished_desc, rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - ret |= esp_cache_msync((void *)(finished_desc->buffer), finished_desc->dw0.size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + ret = esp_cache_msync(finished_buffer, finished_length, ESP_CACHE_MSYNC_FLAG_DIR_M2C); if (ret != ESP_OK) { ESP_EARLY_LOGW(TAG, "failed to sync dma buffer from memory to cache"); } #endif parlio_rx_event_data_t evt_data = { .delimiter = rx_unit->curr_trans.delimiter, - .data = finished_desc->buffer, - .recv_bytes = finished_desc->dw0.length, + .data = finished_buffer, + .recv_bytes = finished_length, }; if (rx_unit->cbs.on_partial_receive) { need_yield |= rx_unit->cbs.on_partial_receive(rx_unit, &evt_data, rx_unit->user_data); @@ -412,49 +407,32 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, rx_unit->curr_trans.recv_bytes = 0; } rx_unit->curr_trans.recv_bytes += evt_data.recv_bytes; - /* Move to the next DMA descriptor */ - rx_unit->curr_desc = rx_unit->curr_desc->next; + /* Move to the next DMA node */ + rx_unit->curr_node_id++; + rx_unit->curr_node_id %= rx_unit->node_num; return need_yield; } -static esp_err_t parlio_rx_create_dma_descriptors(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size) +static esp_err_t parlio_rx_create_dma_link(parlio_rx_unit_handle_t rx_unit, uint32_t max_recv_size) { ESP_RETURN_ON_FALSE(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid param"); esp_err_t ret = ESP_OK; - uint32_t desc_num = max_recv_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED + 1; - /* set at least 2 descriptors */ - if (desc_num < 2) { - desc_num = 2; - } - rx_unit->desc_num = desc_num; - /* Allocated and link the descriptor nodes */ - rx_unit->dma_descs = heap_caps_calloc(desc_num, sizeof(parlio_dma_desc_t *), MALLOC_CAP_DMA); - ESP_RETURN_ON_FALSE(rx_unit->dma_descs, ESP_ERR_NO_MEM, TAG, "no memory for DMA descriptor array"); - uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - size_t alignment = MAX(cache_line_size, PARLIO_DMA_DESC_ALIGNMENT); - rx_unit->desc_size = ALIGN_UP(sizeof(parlio_dma_desc_t), alignment); - for (int i = 0; i < desc_num; i++) { - rx_unit->dma_descs[i] = heap_caps_aligned_calloc(alignment, 1, rx_unit->desc_size, PARLIO_DMA_MEM_ALLOC_CAPS); - ESP_GOTO_ON_FALSE(rx_unit->dma_descs[i], ESP_ERR_NO_MEM, err, TAG, "no memory for DMA descriptors"); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_cache_msync(rx_unit->dma_descs[i], rx_unit->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); -#endif - } + // create DMA link list + size_t buffer_alignment = MAX(rx_unit->int_mem_align, rx_unit->ext_mem_align); + size_t tot_node_num = esp_dma_calculate_node_count(max_recv_size, buffer_alignment, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE); + gdma_link_list_config_t dma_link_config = { + .buffer_alignment = buffer_alignment, + .item_alignment = PARLIO_DMA_DESC_ALIGNMENT, + .num_items = tot_node_num, + }; + + // throw the error to the caller + ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &rx_unit->dma_link), TAG, "create DMA link list failed"); rx_unit->max_recv_size = max_recv_size; - return ret; -err: - for (int i = 0; i < desc_num; i++) { - if (rx_unit->dma_descs[i]) { - free(rx_unit->dma_descs[i]); - rx_unit->dma_descs[i] = NULL; - } - } - free(rx_unit->dma_descs); - rx_unit->dma_descs = NULL; return ret; } @@ -477,6 +455,15 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit) }; gdma_apply_strategy(rx_unit->dma_chan, &gdma_strategy_conf); + // configure DMA transfer parameters + gdma_transfer_config_t trans_cfg = { + // TODO: add burst size + .max_data_burst_size = 4, // Enable DMA burst transfer for better performance, + .access_ext_mem = true, // support transmit PSRAM buffer + }; + ESP_RETURN_ON_ERROR(gdma_config_transfer(rx_unit->dma_chan, &trans_cfg), TAG, "config DMA transfer failed"); + ESP_RETURN_ON_ERROR(gdma_get_alignment_constraints(rx_unit->dma_chan, &rx_unit->int_mem_align, &rx_unit->ext_mem_align), TAG, "get alignment constraints failed"); + /* Register callbacks */ gdma_rx_event_callbacks_t cbs = { .on_recv_eof = parlio_rx_default_eof_callback, @@ -575,16 +562,10 @@ static esp_err_t parlio_destroy_rx_unit(parlio_rx_unit_handle_t rx_unit) ESP_RETURN_ON_ERROR(gdma_disconnect(rx_unit->dma_chan), TAG, "disconnect dma channel failed"); ESP_RETURN_ON_ERROR(gdma_del_channel(rx_unit->dma_chan), TAG, "delete dma channel failed"); } - /* Free the DMA descriptors */ - if (rx_unit->dma_descs) { - for (int i = 0; i < rx_unit->desc_num; i++) { - if (rx_unit->dma_descs[i]) { - free(rx_unit->dma_descs[i]); - rx_unit->dma_descs[i] = NULL; - } - } - free(rx_unit->dma_descs); - rx_unit->dma_descs = NULL; + /* Free the DMA link list */ + if (rx_unit->dma_link) { + gdma_del_link_list(rx_unit->dma_link); + rx_unit->dma_link = NULL; } /* Free the internal DMA buffer */ if (rx_unit->dma_buf) { @@ -638,7 +619,6 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un unit->trans_que = xQueueCreateWithCaps(config->trans_queue_depth, sizeof(parlio_rx_transaction_t), PARLIO_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(unit->trans_que, ESP_ERR_NO_MEM, err, TAG, "no memory for transaction queue"); - ESP_GOTO_ON_ERROR(parlio_rx_create_dma_descriptors(unit, config->max_recv_size), err, TAG, "create dma descriptor failed"); /* Register and attach the rx unit to the group */ ESP_GOTO_ON_ERROR(parlio_register_unit_to_group(&unit->base), err, TAG, "failed to register the rx unit to the group"); memcpy(&unit->cfg, config, sizeof(parlio_rx_unit_config_t)); @@ -653,6 +633,8 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un ESP_GOTO_ON_ERROR(parlio_rx_unit_set_gpio(unit, config), err, TAG, "failed to set GPIO"); /* Install DMA service */ ESP_GOTO_ON_ERROR(parlio_rx_unit_init_dma(unit), err, TAG, "install rx DMA failed"); + ESP_GOTO_ON_ERROR(parlio_rx_create_dma_link(unit, config->max_recv_size), err, TAG, "create dma link list failed"); + /* Reset RX module */ PARLIO_RCC_ATOMIC() { parlio_ll_rx_reset_clock(hal->regs); @@ -746,7 +728,7 @@ esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queu assert(trans.delimiter); parlio_rx_set_delimiter_config(rx_unit, trans.delimiter); parlio_rx_mount_transaction_buffer(rx_unit, &trans); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(hal->regs, true); PARLIO_CLOCK_SRC_ATOMIC() { @@ -935,7 +917,7 @@ static esp_err_t parlio_rx_unit_do_transaction(parlio_rx_unit_handle_t rx_unit, parlio_rx_mount_transaction_buffer(rx_unit, trans); // Take semaphore without block time here, only indicate there are transactions on receiving xSemaphoreTake(rx_unit->trans_sem, 0); - gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc); + gdma_start(rx_unit->dma_chan, gdma_link_get_head_addr(rx_unit->dma_link)); if (rx_unit->cfg.flags.free_clk) { parlio_ll_rx_start(rx_unit->base.group->hal.regs, true); PARLIO_CLOCK_SRC_ATOMIC() {