Merge branch 'feature/support_force_eof_workaround_for_parlio_rx' into 'master'

feat(parlio_rx): support to force trigger eof

Closes IDF-14143, IDFCI-2958, IDFCI-6027, IDFCI-2929, and IDFCI-6242

See merge request espressif/esp-idf!43116
This commit is contained in:
Kevin (Lao Kaiyao)
2025-11-17 17:28:21 +08:00
6 changed files with 309 additions and 37 deletions
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/parlio_rx.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @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: 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_trigger_fake_eof(parlio_rx_unit_handle_t rx_unit, bool *need_yield);
#ifdef __cplusplus
}
#endif
+82 -22
View File
@@ -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 */
@@ -158,12 +159,12 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli
/* Mount body buffer */
size_t mount_size = 0;
size_t offset = 0;
size_t rest_size = trans->aligned_payload.buf.body.length;
for (int i = head_node_num; i < required_node_num - tail_node_num; i++) {
size_t rest_size = trans->aligned_payload.buf.body.length - offset;
if (rest_size >= 2 * PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) {
mount_size = PARLIO_RX_MOUNT_SIZE_CALC(trans->aligned_payload.buf.body.length, body_node_num, trans->alignment);
mount_size = PARLIO_MAX_ALIGNED_DMA_BUF_SIZE;
} else if (rest_size <= PARLIO_MAX_ALIGNED_DMA_BUF_SIZE) {
mount_size = (required_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);
}
@@ -174,6 +175,7 @@ size_t parlio_rx_mount_transaction_buffer(parlio_rx_unit_handle_t rx_unit, parli
mount_config[i].flags.mark_eof = false;
mount_config[i].flags.mark_final = GDMA_FINAL_LINK_TO_DEFAULT;
offset += mount_size;
rest_size -= mount_size;
}
/* Mount tail buffer */
if (tail_node_num) {
@@ -376,7 +378,15 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan,
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(finished_buffer, finished_length, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
size_t sync_size = finished_length;
/* 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->int_mem_align;
break;
}
}
ret = esp_cache_msync(finished_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
if (ret != ESP_OK) {
ESP_EARLY_LOGW(TAG, "failed to sync dma buffer from memory to cache");
}
@@ -394,7 +404,9 @@ static bool parlio_rx_default_desc_done_callback(gdma_channel_handle_t dma_chan,
memcpy(rx_unit->usr_recv_buf + rx_unit->curr_trans.recv_bytes, evt_data.data, evt_data.recv_bytes);
} else {
portENTER_CRITICAL_ISR(&s_rx_spinlock);
rx_unit->curr_trans.delimiter->under_using = false;
if (rx_unit->curr_trans.delimiter) {
rx_unit->curr_trans.delimiter->under_using = false;
}
portEXIT_CRITICAL_ISR(&s_rx_spinlock);
}
/* Update received bytes */
@@ -415,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,
@@ -451,9 +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->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 = {
@@ -635,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");
}
@@ -730,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;
@@ -957,16 +977,11 @@ 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);
}
#if CONFIG_PARLIO_RX_ISR_CACHE_SAFE
ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM");
#else
ESP_RETURN_ON_FALSE(recv_cfg->flags.indirect_mount || esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM");
#endif
if (recv_cfg->delimiter->eof_data_len) {
ESP_RETURN_ON_FALSE(payload_size >= recv_cfg->delimiter->eof_data_len, ESP_ERR_INVALID_ARG,
TAG, "payload size should be greater than eof_data_len");
@@ -1029,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);
}
@@ -1127,3 +1138,52 @@ err:
xSemaphoreGive(rx_unit->mutex);
return ret;
}
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");
int uint_id = rx_unit->base.unit_id;
parlio_unit_base_handle_t pair_tx_unit = rx_unit->base.group->tx_units[uint_id];
/* 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 */
ESP_RETURN_ON_FALSE_ISR(!pair_tx_unit, ESP_ERR_INVALID_STATE, TAG, "can't be called when pair tx unit is in using");
/* Stop and reset the DMA channel first */
ESP_RETURN_ON_ERROR_ISR(gdma_stop(rx_unit->dma_chan), TAG, "stop DMA channel failed");
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 */
PARLIO_RCC_ATOMIC() {
parlio_ll_reset_register(rx_unit->base.group->group_id);
}
/* Switch to the default clock source to ensure the register values can be written back successfully */
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);
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_rx_set_clock_source(hal->regs, rx_unit->clk_src);
}
/* Force to trigger the EOF interrupt */
gdma_event_data_t event_data = {
.flags.normal_eof = 1
};
bool _need_yield = false;
_need_yield |= parlio_rx_default_desc_done_callback(rx_unit->dma_chan, &event_data, rx_unit);
_need_yield |= parlio_rx_default_eof_callback(rx_unit->dma_chan, &event_data, rx_unit);
if (need_yield) {
*need_yield |= _need_yield;
}
return ESP_OK;
}
@@ -24,6 +24,7 @@
#include "soc/parlio_periph.h"
#include "esp_attr.h"
#include "test_board.h"
#include "esp_private/parlio_rx_private.h"
#define TEST_SPI_HOST SPI2_HOST
#define TEST_I2S_PORT I2S_NUM_0
@@ -56,6 +57,10 @@
#define TEST_TASK_DATA_READY_BIT 0x01
#define TEST_TASK_FINISHED_BIT 0x02
#define TEST_TASK_RECV_READY_BIT 0x04
#define TEST_TASK_LARGE_TRANS_BIT 0x08
#define TEST_TASK_LARGE_TRANS_SIZE 155584 // Use an unaligned size to ensure the reliability
typedef struct {
uint32_t partial_recv_cnt;
@@ -176,8 +181,6 @@ static void pulse_delimiter_sender_task_i2s(void *args)
}
}
#if CONFIG_IDF_TARGET_ESP32C6 // TODO: IDF-9806 fix the bit shift issue in other target
static void cs_high(spi_transaction_t *trans)
{
gpio_set_level(TEST_VALID_GPIO, 1);
@@ -193,6 +196,7 @@ static void cs_low(spi_transaction_t *trans)
static void level_delimiter_sender_task_spi(void *args)
{
uint32_t *task_flags = (uint32_t *)args;
bool is_large_trans = *task_flags & TEST_TASK_LARGE_TRANS_BIT;
spi_device_handle_t dev_handle;
spi_bus_config_t bus_cfg = {
@@ -209,11 +213,11 @@ static void level_delimiter_sender_task_spi(void *args)
.clock_speed_hz = TEST_SPI_CLK_FREQ,
.mode = 0,
.duty_cycle_pos = 128,
.spics_io_num = TEST_VALID_GPIO,
.spics_io_num = is_large_trans ? -1 : TEST_VALID_GPIO,
.queue_size = 5,
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_POSITIVE_CS,
.pre_cb = cs_high,
.post_cb = cs_low,
.pre_cb = is_large_trans ? NULL : cs_high,
.post_cb = is_large_trans ? NULL : cs_low,
};
//Initialize the SPI bus and add device
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO));
@@ -239,8 +243,14 @@ static void level_delimiter_sender_task_spi(void *args)
parlio_periph_signals.groups[0].rx_units[0].data_sigs[0]);
// Prepare the data the be transmitted
uint8_t *data = (uint8_t *)calloc(1, TEST_EOF_DATA_LEN);
for (int i = 0; i < TEST_EOF_DATA_LEN; i += 4) {
uint8_t *data = NULL;
size_t data_size = TEST_EOF_DATA_LEN;
if (*task_flags & TEST_TASK_LARGE_TRANS_BIT) {
data_size = 1024;
}
data = (uint8_t *)calloc(1, data_size);
TEST_ASSERT_NOT_NULL(data);
for (int i = 0; i < data_size; i += 4) {
data[i] = 0x12;
data[i + 1] = 0x34;
data[i + 2] = 0x56;
@@ -248,17 +258,31 @@ static void level_delimiter_sender_task_spi(void *args)
}
spi_transaction_t t = {
.cmd = 0,
.length = TEST_EOF_DATA_LEN * 8,
.length = data_size * 8,
.flags = 0,
.tx_buffer = data,
.user = NULL,
};
// Transmit data every 1ms, until the main test thread finished receiving
while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) {
TEST_ESP_OK(spi_device_transmit(dev_handle, &t));
vTaskDelay(pdMS_TO_TICKS(1));
*task_flags |= TEST_TASK_DATA_READY_BIT;
if (is_large_trans) {
while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) {
if (!((*task_flags) & TEST_TASK_RECV_READY_BIT)) {
gpio_set_level(TEST_VALID_GPIO, 1);
for (int i = 0; i < 80; i++) {
TEST_ESP_OK(spi_device_transmit(dev_handle, &t));
}
gpio_set_level(TEST_VALID_GPIO, 0);
*task_flags |= TEST_TASK_DATA_READY_BIT;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
} else {
while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) {
TEST_ESP_OK(spi_device_transmit(dev_handle, &t));
vTaskDelay(pdMS_TO_TICKS(1));
*task_flags |= TEST_TASK_DATA_READY_BIT;
}
}
// Remove the SPI device and free the bus
@@ -274,7 +298,6 @@ static void level_delimiter_sender_task_spi(void *args)
vTaskDelay(portMAX_DELAY);
}
}
#endif
static bool test_delimiter(parlio_rx_delimiter_handle_t deli, bool free_running_clk, void (*sender_task_thread)(void *args))
{
@@ -524,6 +547,49 @@ 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;
parlio_rx_delimiter_handle_t deli = NULL;
size_t payload_size = 1000;
parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_DEFAULT, 1000000);
config.flags.free_clk = 1;
TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit));
parlio_rx_soft_delimiter_config_t sft_deli_cfg = {
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
.eof_data_len = payload_size,
.timeout_ticks = 0,
};
TEST_ESP_OK(parlio_new_rx_soft_delimiter(&sft_deli_cfg, &deli));
TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true));
parlio_receive_config_t recv_config = {
.delimiter = deli,
.flags.partial_rx_en = false,
};
/* Do not specify alignment, check if the driver can work correctly */
uint8_t *payload = heap_caps_calloc_prefer(1, payload_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM, TEST_PARLIO_DMA_MEM_ALLOC_CAPS);
printf("payload addr: %p size: %u\n", payload, payload_size);
TEST_ASSERT(payload);
printf("Testing the external memory receive functionality...\n");
TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, true));
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, payload_size, &recv_config));
TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000));
TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false));
TEST_ESP_OK(parlio_rx_unit_disable(rx_unit));
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
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]")
{
printf("init a gpio to simulate valid signal\r\n");
@@ -867,3 +933,109 @@ TEST_CASE("parallel_rx_unit_infinite_transaction_switch_test", "[parlio_rx]")
free(payload1);
free(payload2);
}
/**
* @brief This ISR is to indicate the SPI transaction finished
*/
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_trigger_fake_eof(rx_unit, &need_yield);
if (need_yield) {
portYIELD_FROM_ISR();
}
}
TEST_CASE("parallel_rx_unit_force_trigger_eof_test", "[parlio_rx]")
{
parlio_rx_unit_handle_t rx_unit = NULL;
parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_EXTERNAL, 1000000);
config.flags.free_clk = 0;
config.max_recv_size = TEST_TASK_LARGE_TRANS_SIZE;
TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit));
parlio_rx_level_delimiter_config_t lvl_deli_cfg = {
.valid_sig_line_id = TEST_VALID_SIG,
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB,
/* Normally the EOF won't be triggered for the level delimiter that eof_data_len larger than 64KB */
.eof_data_len = TEST_TASK_LARGE_TRANS_SIZE,
.timeout_ticks = 0,
.flags = {
.active_low_en = 0,
},
};
parlio_rx_delimiter_handle_t deli = NULL;
TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli));
parlio_rx_event_callbacks_t cbs = {
.on_receive_done = test_parlio_rx_done_callback,
};
test_data_t test_data = {
.partial_recv_cnt = 0,
.recv_done_cnt = 0,
};
TEST_ESP_OK(parlio_rx_unit_register_event_callbacks(rx_unit, &cbs, &test_data));
TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true));
TaskHandle_t sender_task;
/* The flag to transport finish information between main test thread and the sender thread
* Set it as static to make sure it'll be valid in another thread */
static uint32_t task_flags = TEST_TASK_LARGE_TRANS_BIT;
xTaskCreate(level_delimiter_sender_task_spi, "sender task", 4096, &task_flags, 5, &sender_task);
parlio_receive_config_t recv_config = {
.delimiter = deli,
.flags.partial_rx_en = false,
};
uint8_t *recv_buff = NULL;
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, TEST_PARLIO_DMA_MEM_ALLOC_CAPS);
TEST_ASSERT_NOT_NULL(recv_buff);
gpio_set_intr_type(TEST_VALID_GPIO, GPIO_INTR_NEGEDGE);
gpio_install_isr_service(0);
gpio_isr_handler_add(TEST_VALID_GPIO, test_gpio_neg_edge_intr, rx_unit);
gpio_intr_enable(TEST_VALID_GPIO);
uint32_t recv_cnt = 3;
for (int i = 0; i < recv_cnt; i++) {
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, recv_buff, buff_size, &recv_config));
printf("[%d] recv ready\n", i);
task_flags |= TEST_TASK_RECV_READY_BIT;
while (!task_flags & TEST_TASK_DATA_READY_BIT) {
vTaskDelay(1);
}
task_flags &= ~TEST_TASK_DATA_READY_BIT;
printf("[%d] send done\n", i);
TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 10000));
task_flags &= ~TEST_TASK_RECV_READY_BIT;
printf("[%d] recv done\n", i);
}
// Indicate the test finished, no need to send data
task_flags |= TEST_TASK_FINISHED_BIT;
bool is_success = true;
is_success &= test_data.recv_done_cnt == recv_cnt;
gpio_intr_disable(TEST_VALID_GPIO);
gpio_isr_handler_remove(TEST_VALID_GPIO);
gpio_uninstall_isr_service();
// Waiting for the sender task quit
while (task_flags) {
vTaskDelay(1);
}
// Delete the sender task
vTaskDelete(sender_task);
free(recv_buff);
TEST_ESP_OK(parlio_rx_unit_disable(rx_unit));
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
TEST_ESP_OK(parlio_del_rx_unit(rx_unit));
TEST_ASSERT(is_success);
}
@@ -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,
@@ -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"