From b3e48fd0019534602d7988736e0c11166d85b3ea Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 10 Nov 2025 17:58:24 +0800 Subject: [PATCH] test(parlio): fix sleep test issue --- .../include/esp_private/parlio_rx_private.h | 16 +++++++-- .../{parlio_private.h => parlio_tx_private.h} | 0 components/esp_driver_parlio/src/parlio_rx.c | 35 ++++++++++--------- .../test_apps/parlio/main/test_parlio_rx.c | 6 ++-- .../test_apps/parlio/main/test_parlio_sleep.c | 2 +- .../esp_lcd/parl/esp_lcd_panel_io_parl.c | 2 +- 6 files changed, 38 insertions(+), 23 deletions(-) rename components/esp_driver_parlio/include/esp_private/{parlio_private.h => parlio_tx_private.h} (100%) diff --git a/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h b/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h index 657237642b..9e74c61368 100644 --- a/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h +++ b/components/esp_driver_parlio/include/esp_private/parlio_rx_private.h @@ -13,17 +13,27 @@ extern "C" { #endif /** - * @brief Force to trigger the EOF interrupt + * @brief Trigger the fake EOF interrupt * @note This function is a workaround for the case that level delimiter needs to receive more than 64KB data in one transaction. + * The hardware can't generate the EOF interrupt when the data length is greater than 64KB due to the limitation of the hardware, + * so this function is used to trigger the fake EOF interrupt. + * @note This function will reset the whole parlio module, + * If the pair tx unit is in using, + * the reset operation will affect the TX unit and lead to unknown behavior + * @usage If the application needs to receive more than 64KB data in one transaction, you can follow the steps below: + * 1. Create a level delimiter with a length greater than 64KB + * 2. Register the interrupt of the end edge on the valid GPIO + * 3. Call this function to trigger the fake EOF interrupt in the GPIO interrupt handler + * 4. Receive the transaction that is greater than 64KB * * @param rx_unit Parallel IO RX unit that created by `parlio_new_rx_unit` * @param need_yield Pointer to a status flag to record whether a task switch is needed if this API is being called in an ISR * @return - * - ESP_OK: Force to trigger the EOF interrupt successfully + * - ESP_OK: Trigger the fake EOF interrupt successfully * - ESP_ERR_INVALID_ARG: Invalid argument like NULL pointer * - ESP_ERR_INVALID_STATE: Tx unit is in using, can't be called when pair tx unit is in using */ -esp_err_t parlio_rx_unit_force_trigger_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield); +esp_err_t parlio_rx_unit_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield); #ifdef __cplusplus } diff --git a/components/esp_driver_parlio/include/esp_private/parlio_private.h b/components/esp_driver_parlio/include/esp_private/parlio_tx_private.h similarity index 100% rename from components/esp_driver_parlio/include/esp_private/parlio_private.h rename to components/esp_driver_parlio/include/esp_private/parlio_tx_private.h diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index 43909ca10b..818a14834a 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -67,7 +67,8 @@ typedef struct parlio_rx_unit_t { size_t dma_burst_size; /*!< DMA burst size, in bytes */ 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 dma_mem_align; /*!< Alignment for DMA memory */ + 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 */ @@ -163,7 +164,7 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli if (rest_size >= 2 * PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { mount_size = PARLIO_MAX_ALIGNED_DMA_BUF_SIZE; } else if (rest_size <= PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) { - mount_size = (required_node_num - tail_node_num == 2) && (i == 0) ? PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment) : rest_size; + mount_size = ((required_node_num - tail_node_num) == 2) && (i == 0) ? PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment) : rest_size; } else { mount_size = PARLIO_RX_MOUNT_SIZE_CALC(rest_size, 2, trans->alignment); } @@ -381,7 +382,7 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan, /* The sych length should be the cache line size for the un-aligned head and tail part */ for (int i = 0; i < 2; i++) { if (finished_buffer == rx_unit->stash_buf[i]) { - sync_size = rx_unit->dma_mem_align; + sync_size = rx_unit->int_mem_align; break; } } @@ -426,7 +427,7 @@ static esp_err_t parlio_rx_create_dma_link(parlio_rx_unit_handle_t rx_unit, uint esp_err_t ret = ESP_OK; // calculated the total node number, add 2 for the aligned stash buffer - size_t tot_node_num = esp_dma_calculate_node_count(max_recv_size, rx_unit->dma_mem_align, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) + 2; + size_t tot_node_num = esp_dma_calculate_node_count(max_recv_size, rx_unit->int_mem_align, PARLIO_DMA_DESCRIPTOR_BUFFER_MAX_SIZE) + 2; gdma_link_list_config_t dma_link_config = { .num_items = tot_node_num, .item_alignment = PARLIO_DMA_DESC_ALIGNMENT, @@ -462,13 +463,16 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit, size_t rx_unit->dma_burst_size = dma_burst_size ? dma_burst_size : 16; gdma_transfer_config_t trans_cfg = { .max_data_burst_size = rx_unit->dma_burst_size, // Enable DMA burst transfer for better performance, + .access_ext_mem = true, }; 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->dma_mem_align, NULL), TAG, "get alignment constraints 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"); #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE uint32_t cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - rx_unit->dma_mem_align = rx_unit->dma_mem_align > cache_line_size ? rx_unit->dma_mem_align : cache_line_size; + rx_unit->int_mem_align = rx_unit->int_mem_align > cache_line_size ? rx_unit->int_mem_align : cache_line_size; #endif + uint32_t ext_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); + rx_unit->ext_mem_align = rx_unit->ext_mem_align > ext_cache_line_size ? rx_unit->ext_mem_align : ext_cache_line_size; /* Register callbacks */ gdma_rx_event_callbacks_t cbs = { @@ -650,7 +654,8 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un ESP_GOTO_ON_ERROR(parlio_rx_create_dma_link(unit, config->max_recv_size), err, TAG, "create dma link list failed"); for (uint8_t i = 0; i < 2; i++) { - unit->stash_buf[i] = heap_caps_aligned_calloc(unit->dma_mem_align, 2, unit->dma_mem_align, PARLIO_MEM_ALLOC_CAPS | MALLOC_CAP_DMA); + uint32_t max_alignment = unit->int_mem_align > unit->ext_mem_align ? unit->int_mem_align : unit->ext_mem_align; + unit->stash_buf[i] = heap_caps_aligned_calloc(max_alignment, 2, max_alignment, PARLIO_MEM_ALLOC_CAPS | MALLOC_CAP_DMA); ESP_GOTO_ON_FALSE(unit->stash_buf[i], ESP_ERR_NO_MEM, err, TAG, "no memory for stash buffer"); } @@ -745,7 +750,7 @@ esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queu assert(res == pdTRUE); if (trans.flags.indirect_mount && trans.flags.infinite && rx_unit->dma_buf == NULL) { - rx_unit->dma_buf = heap_caps_aligned_calloc(rx_unit->dma_mem_align, 1, trans.aligned_payload.buf.body.length, PARLIO_DMA_MEM_ALLOC_CAPS); + rx_unit->dma_buf = heap_caps_aligned_calloc(rx_unit->int_mem_align, 1, trans.aligned_payload.buf.body.length, PARLIO_DMA_MEM_ALLOC_CAPS); ESP_GOTO_ON_FALSE(rx_unit->dma_buf, ESP_ERR_NO_MEM, err, TAG, "No memory for the internal DMA buffer"); trans.aligned_payload.buf.body.aligned_buffer = rx_unit->dma_buf; trans.aligned_payload.buf.body.recovery_address = rx_unit->dma_buf; @@ -972,7 +977,7 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, ESP_RETURN_ON_FALSE(rx_unit && payload && recv_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(recv_cfg->delimiter, ESP_ERR_INVALID_ARG, TAG, "no delimiter specified"); ESP_RETURN_ON_FALSE(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG, TAG, "trans length too large"); - size_t alignment = rx_unit->dma_mem_align; + size_t alignment = rx_unit->int_mem_align; if (recv_cfg->flags.partial_rx_en) { ESP_RETURN_ON_FALSE(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG, TAG, "The payload size should greater than %"PRIu32, 2 * alignment); } @@ -1039,15 +1044,11 @@ esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit, PARLIO_RX_CHECK_ISR(payload_size <= rx_unit->max_recv_size, ESP_ERR_INVALID_ARG); // Can only be called from ISR PARLIO_RX_CHECK_ISR(xPortInIsrContext() == pdTRUE, ESP_ERR_INVALID_STATE); - size_t alignment = rx_unit->dma_mem_align; + size_t alignment = rx_unit->int_mem_align; if (recv_cfg->flags.partial_rx_en) { PARLIO_RX_CHECK_ISR(payload_size >= 2 * alignment, ESP_ERR_INVALID_ARG); } -#if CONFIG_PARLIO_RX_ISR_CACHE_SAFE - PARLIO_RX_CHECK_ISR(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG); -#else - PARLIO_RX_CHECK_ISR(recv_cfg->flags.indirect_mount || esp_ptr_internal(payload), ESP_ERR_INVALID_ARG); -#endif + if (recv_cfg->delimiter->eof_data_len) { PARLIO_RX_CHECK_ISR(payload_size >= recv_cfg->delimiter->eof_data_len, ESP_ERR_INVALID_ARG); } @@ -1138,7 +1139,7 @@ err: return ret; } -esp_err_t parlio_rx_unit_force_trigger_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield) +esp_err_t parlio_rx_unit_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield) { ESP_RETURN_ON_FALSE_ISR(rx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); @@ -1154,6 +1155,7 @@ esp_err_t parlio_rx_unit_force_trigger_eof(parlio_rx_unit_handle_t rx_unit, bool ESP_RETURN_ON_ERROR_ISR(gdma_reset(rx_unit->dma_chan), TAG, "reset DMA channel failed"); parlio_hal_context_t *hal = &rx_unit->base.group->hal; + portENTER_CRITICAL_SAFE(&s_rx_spinlock); /* Save the current register values */ parl_io_dev_t save_curr_regs = *(parl_io_dev_t *)hal->regs; /* Reset the hardware FSM of the parlio module */ @@ -1164,6 +1166,7 @@ esp_err_t parlio_rx_unit_force_trigger_eof(parlio_rx_unit_handle_t rx_unit, bool PARLIO_CLOCK_SRC_ATOMIC() { parlio_ll_rx_set_clock_source(hal->regs, PARLIO_CLK_SRC_DEFAULT); } + portEXIT_CRITICAL_SAFE(&s_rx_spinlock); /* Restore the register values and clock source*/ memcpy(hal->regs, &save_curr_regs, sizeof(parl_io_dev_t)); parlio_ll_rx_update_config(hal->regs); diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c index be15c465c5..e5604a0c07 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_rx.c @@ -547,6 +547,7 @@ TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]") free(payload); }; +#if SOC_PSRAM_DMA_CAPABLE TEST_CASE("parallel_rx_unit_receive_external_memory_test", "[parlio_rx]") { parlio_rx_unit_handle_t rx_unit = NULL; @@ -587,6 +588,7 @@ TEST_CASE("parallel_rx_unit_receive_external_memory_test", "[parlio_rx]") TEST_ESP_OK(parlio_del_rx_unit(rx_unit)); free(payload); } +#endif // SOC_PSRAM_DMA_CAPABLE TEST_CASE("parallel_rx_unit_receive_timeout_test", "[parlio_rx]") { @@ -939,7 +941,7 @@ static void test_gpio_neg_edge_intr(void *arg) { parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t)arg; bool need_yield = false; - parlio_rx_unit_force_trigger_eof(rx_unit, &need_yield); + parlio_rx_unit_trigger_fake_eof(rx_unit, &need_yield); if (need_yield) { portYIELD_FROM_ISR(); } @@ -992,7 +994,7 @@ TEST_CASE("parallel_rx_unit_force_trigger_eof_test", "[parlio_rx]") uint32_t alignment = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); alignment = alignment < 4 ? 4 : alignment; size_t buff_size = ALIGN_UP(TEST_TASK_LARGE_TRANS_SIZE, alignment); - recv_buff = heap_caps_aligned_calloc(alignment, 1, buff_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); + recv_buff = heap_caps_aligned_calloc(alignment, 1, buff_size, TEST_PARLIO_DMA_MEM_ALLOC_CAPS); TEST_ASSERT_NOT_NULL(recv_buff); gpio_set_intr_type(TEST_VALID_GPIO, GPIO_INTR_NEGEDGE); diff --git a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c index 5631fa7076..783a5cfd92 100644 --- a/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c +++ b/components/esp_driver_parlio/test_apps/parlio/main/test_parlio_sleep.c @@ -94,7 +94,7 @@ static void test_parlio_sleep_retention(bool allow_pd) parlio_rx_level_delimiter_config_t lvl_deli_cfg = { .valid_sig_line_id = PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1, - .sample_edge = PARLIO_SAMPLE_EDGE_POS, + .sample_edge = PARLIO_SAMPLE_EDGE_NEG, // opposite to tx unit in case of timing issue .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, .eof_data_len = TEST_PAYLOAD_SIZE, .timeout_ticks = 0, diff --git a/components/esp_lcd/parl/esp_lcd_panel_io_parl.c b/components/esp_lcd/parl/esp_lcd_panel_io_parl.c index 3037f981a3..9fb4e63842 100644 --- a/components/esp_lcd/parl/esp_lcd_panel_io_parl.c +++ b/components/esp_lcd/parl/esp_lcd_panel_io_parl.c @@ -33,7 +33,7 @@ #include "driver/parlio_tx.h" #include "driver/parlio_types.h" #include "esp_private/gpio.h" -#include "esp_private/parlio_private.h" +#include "esp_private/parlio_tx_private.h" #include "esp_lcd_panel_io_interface.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_common.h"