mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(parlio_rx): support to force trigger eof
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 Force to trigger the EOF interrupt
|
||||
* @note This function is a workaround for the case that level delimiter needs to receive more than 64KB data in one transaction.
|
||||
*
|
||||
* @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_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -419,7 +419,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 */
|
||||
@@ -479,6 +481,10 @@ static esp_err_t parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit, size_t
|
||||
};
|
||||
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");
|
||||
#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;
|
||||
#endif
|
||||
|
||||
/* Register callbacks */
|
||||
gdma_rx_event_callbacks_t cbs = {
|
||||
@@ -1077,3 +1083,50 @@ err:
|
||||
xSemaphoreGive(rx_unit->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t parlio_rx_unit_force_trigger_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;
|
||||
/* 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);
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -25,6 +25,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
|
||||
@@ -57,6 +58,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;
|
||||
@@ -177,8 +182,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);
|
||||
@@ -194,6 +197,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 = {
|
||||
@@ -210,11 +214,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));
|
||||
@@ -240,8 +244,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;
|
||||
@@ -249,17 +259,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(2));
|
||||
}
|
||||
} else {
|
||||
while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) {
|
||||
TEST_ESP_OK(spi_device_transmit(dev_handle, &t));
|
||||
vTaskDelay(pdMS_TO_TICKS(2));
|
||||
*task_flags |= TEST_TASK_DATA_READY_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the SPI device and free the bus
|
||||
@@ -275,7 +299,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))
|
||||
{
|
||||
@@ -592,3 +615,122 @@ TEST_CASE("parallel_rx_unit_receive_timeout_test", "[parlio_rx]")
|
||||
TEST_ESP_OK(gpio_reset_pin(TEST_VALID_GPIO));
|
||||
free(payload);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t partial_recv_cnt;
|
||||
uint32_t recv_done_cnt;
|
||||
uint32_t timeout_cnt;
|
||||
uint32_t isr_send_cnt;
|
||||
uint32_t isr_send_success_cnt;
|
||||
parlio_rx_unit_handle_t rx_unit;
|
||||
parlio_rx_delimiter_handle_t delimiter;
|
||||
uint8_t *isr_payload;
|
||||
size_t isr_payload_size;
|
||||
bool enable_isr_send;
|
||||
} test_isr_data_t;
|
||||
|
||||
/**
|
||||
* @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_force_trigger_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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user