forked from espressif/esp-idf
feat(parlio_rx): add test for parlio rx driver
This commit is contained in:
@@ -22,7 +22,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
size_t trans_queue_depth; /*!< Depth of internal transaction queue */
|
||||
size_t max_recv_size; /*!< Maximum receive size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
|
||||
size_t data_width; /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't bigger than PARLIO_RX_UNIT_MAX_DATA_WIDTH */
|
||||
size_t data_width; /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't be greater than PARLIO_RX_UNIT_MAX_DATA_WIDTH */
|
||||
parlio_clock_source_t clk_src; /*!< Parallel IO clock source */
|
||||
uint32_t clk_freq_hz; /*!< The source clock frequency for external when the clock source is selected as PARLIO_CLK_SRC_EXTERNAL;
|
||||
The expected output clock frequency when the clock source is from internal */
|
||||
|
@@ -48,6 +48,7 @@ typedef struct {
|
||||
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 */
|
||||
} flags;
|
||||
} parlio_rx_transaction_t;
|
||||
|
||||
@@ -144,6 +145,7 @@ static IRAM_ATTR size_t s_parlio_mount_transaction_buffer(parlio_rx_unit_handle_
|
||||
}
|
||||
size_t mount_size = 0;
|
||||
size_t offset = 0;
|
||||
/* Loop the descriptors to assign the data */
|
||||
for (int i = 0; i < desc_num; i++) {
|
||||
size_t rest_size = trans->size - offset;
|
||||
if (rest_size >= 2 * DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) {
|
||||
@@ -235,13 +237,14 @@ static esp_err_t s_parlio_rx_unit_set_gpio(parlio_rx_unit_handle_t rx_unit, cons
|
||||
{
|
||||
int group_id = rx_unit->group->group_id;
|
||||
int unit_id = rx_unit->unit_id;
|
||||
|
||||
/* Default GPIO configuration */
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.pull_down_en = false,
|
||||
.pull_up_en = true,
|
||||
};
|
||||
|
||||
/* When the source clock comes from external, enable the gpio input direction and connect to the clock input signal */
|
||||
if (config->clk_src == PARLIO_CLK_SRC_EXTERNAL) {
|
||||
ESP_RETURN_ON_FALSE(config->clk_gpio_num >= 0, ESP_ERR_INVALID_ARG, TAG, "clk_gpio_num must be set while the clock input from external");
|
||||
/* Connect the clock in signal to the GPIO matrix if it is set */
|
||||
@@ -249,17 +252,17 @@ static esp_err_t s_parlio_rx_unit_set_gpio(parlio_rx_unit_handle_t rx_unit, cons
|
||||
gpio_conf.mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_INPUT;
|
||||
gpio_conf.pin_bit_mask = BIT64(config->clk_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed");
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_gpio_num], PIN_FUNC_GPIO);
|
||||
}
|
||||
esp_rom_gpio_connect_in_signal(config->clk_gpio_num,
|
||||
parlio_periph_signals.groups[group_id].rx_units[unit_id].clk_in_sig, false);
|
||||
}
|
||||
/* When the source clock comes from internal and supported to output the internal clock,
|
||||
* enable the gpio output direction and connect to the clock output signal */
|
||||
else if (config->clk_gpio_num >= 0) {
|
||||
#if SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
|
||||
gpio_conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_conf.mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_OUTPUT;
|
||||
gpio_conf.pin_bit_mask = BIT64(config->clk_gpio_num);
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed");
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_gpio_num], PIN_FUNC_GPIO);
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk out GPIO failed");
|
||||
esp_rom_gpio_connect_out_signal(config->clk_gpio_num,
|
||||
parlio_periph_signals.groups[group_id].rx_units[unit_id].clk_out_sig, false, false);
|
||||
#else
|
||||
@@ -267,16 +270,17 @@ static esp_err_t s_parlio_rx_unit_set_gpio(parlio_rx_unit_handle_t rx_unit, cons
|
||||
#endif // SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
|
||||
}
|
||||
|
||||
gpio_conf.mode = GPIO_MODE_INPUT;
|
||||
gpio_conf.mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_INPUT;
|
||||
/* Initialize the valid GPIO as input */
|
||||
if (config->valid_gpio_num >= 0) {
|
||||
if (!config->flags.io_no_init) {
|
||||
gpio_conf.pin_bit_mask = BIT64(config->valid_gpio_num);
|
||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->valid_gpio_num], PIN_FUNC_GPIO);
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config data GPIO failed");
|
||||
}
|
||||
/* Not connect the signal here, the signal is lazy connected until the delimiter takes effect */
|
||||
}
|
||||
|
||||
/* Initialize the data GPIO as input and bind them to the corresponding data line signals */
|
||||
for (int i = 0; i < config->data_width; i++) {
|
||||
/* Loop the data_gpio_nums to connect data and valid signals via GPIO matrix */
|
||||
if (config->data_gpio_nums[i] >= 0) {
|
||||
@@ -295,22 +299,30 @@ static esp_err_t s_parlio_rx_unit_set_gpio(parlio_rx_unit_handle_t rx_unit, cons
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool s_parlio_rx_default_trans_done_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
static IRAM_ATTR bool s_parlio_rx_default_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
|
||||
BaseType_t high_task_woken = pdFALSE;
|
||||
bool need_yield = false;
|
||||
|
||||
/* If configured on_receive_done callback and transaction just done */
|
||||
if (rx_unit->cbs.on_receive_done) {
|
||||
parlio_rx_event_data_t evt_data = {
|
||||
.delimiter = rx_unit->curr_trans.delimiter,
|
||||
.data = rx_unit->usr_recv_buf,
|
||||
.size = rx_unit->curr_trans.size,
|
||||
.recv_bytes = rx_unit->curr_trans.recv_bytes,
|
||||
};
|
||||
|
||||
if (event_data->flags.abnormal_eof) {
|
||||
/* If received an abnormal EOF, it's a timeout event on parlio RX */
|
||||
if (rx_unit->cbs.on_timeout) {
|
||||
need_yield |= rx_unit->cbs.on_timeout(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
} else {
|
||||
/* If received a normal EOF, it's a receive done event on parlio RX */
|
||||
if (rx_unit->cbs.on_receive_done) {
|
||||
evt_data.data = rx_unit->usr_recv_buf;
|
||||
evt_data.size = rx_unit->curr_trans.size;
|
||||
evt_data.recv_bytes = rx_unit->curr_trans.recv_bytes;
|
||||
need_yield |= rx_unit->cbs.on_receive_done(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (rx_unit->curr_trans.flags.infinite) {
|
||||
/* For infinite transactions, reset the receiving bytes when the transaction is done */
|
||||
@@ -333,7 +345,6 @@ static IRAM_ATTR bool s_parlio_rx_default_trans_done_callback(gdma_channel_handl
|
||||
parlio_ll_rx_start(rx_unit->group->hal.regs, true);
|
||||
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, true);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* No more transaction pending to receive, clear the current transaction */
|
||||
rx_unit->curr_trans.delimiter->under_using = false;
|
||||
@@ -355,6 +366,7 @@ static IRAM_ATTR bool s_parlio_rx_default_desc_done_callback(gdma_channel_handle
|
||||
dma_descriptor_t *finished_desc = rx_unit->curr_desc;
|
||||
parlio_rx_event_data_t evt_data = {
|
||||
.delimiter = rx_unit->curr_trans.delimiter,
|
||||
// TODO: The current descriptor is not able to access when error EOF occur
|
||||
.data = finished_desc->buffer,
|
||||
.size = finished_desc->dw0.size,
|
||||
.recv_bytes = finished_desc->dw0.length,
|
||||
@@ -363,7 +375,7 @@ static IRAM_ATTR bool s_parlio_rx_default_desc_done_callback(gdma_channel_handle
|
||||
need_yield |= rx_unit->cbs.on_partial_receive(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
/* For the infinite transaction, need to copy the data in DMA buffer to the user receiving buffer */
|
||||
if (rx_unit->curr_trans.flags.infinite) {
|
||||
if (rx_unit->curr_trans.flags.infinite && rx_unit->curr_trans.flags.indirect_mount) {
|
||||
memcpy(rx_unit->usr_recv_buf + rx_unit->curr_trans.recv_bytes, evt_data.data, evt_data.recv_bytes);
|
||||
} else {
|
||||
rx_unit->curr_trans.delimiter->under_using = false;
|
||||
@@ -379,20 +391,6 @@ static IRAM_ATTR bool s_parlio_rx_default_desc_done_callback(gdma_channel_handle
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool s_parlio_rx_default_timeout_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = (parlio_rx_unit_handle_t )user_data;
|
||||
bool need_yield = false;
|
||||
parlio_rx_delimiter_handle_t deli = rx_unit->curr_trans.delimiter;
|
||||
if (rx_unit->cbs.on_timeout && deli) {
|
||||
parlio_rx_event_data_t evt_data = {
|
||||
.delimiter = deli,
|
||||
};
|
||||
need_yield |= rx_unit->cbs.on_timeout(rx_unit, &evt_data, rx_unit->user_data);
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t s_parlio_rx_create_dma_descriptors(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");
|
||||
@@ -431,11 +429,8 @@ static esp_err_t s_parlio_rx_unit_init_dma(parlio_rx_unit_handle_t rx_unit)
|
||||
|
||||
/* Register callbacks */
|
||||
gdma_rx_event_callbacks_t cbs = {
|
||||
.on_recv_eof = s_parlio_rx_default_trans_done_callback,
|
||||
.on_recv_eof = s_parlio_rx_default_eof_callback,
|
||||
.on_recv_done = s_parlio_rx_default_desc_done_callback,
|
||||
// TODO: not on_err_desc, wait for GDMA supports on_err_eof
|
||||
// .on_err_eof = s_parlio_rx_default_timeout_callback,
|
||||
.on_descr_err = s_parlio_rx_default_timeout_callback,
|
||||
};
|
||||
gdma_register_rx_event_callbacks(rx_unit->dma_chan, &cbs, rx_unit);
|
||||
|
||||
@@ -808,6 +803,7 @@ static esp_err_t s_parlio_rx_unit_do_transaction(parlio_rx_unit_handle_t rx_unit
|
||||
s_parlio_mount_transaction_buffer(rx_unit, trans);
|
||||
gdma_start(rx_unit->dma_chan, (intptr_t)rx_unit->curr_desc);
|
||||
if (rx_unit->cfg.flags.free_clk) {
|
||||
printf("enable start\n");
|
||||
parlio_ll_rx_start(rx_unit->group->hal.regs, true);
|
||||
parlio_ll_rx_enable_clock(rx_unit->group->hal.regs, true);
|
||||
}
|
||||
@@ -863,6 +859,7 @@ esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit,
|
||||
.size = payload_size,
|
||||
.recv_bytes = 0,
|
||||
.flags.infinite = recv_cfg->flags.is_infinite,
|
||||
.flags.indirect_mount = recv_cfg->flags.indirect_mount,
|
||||
};
|
||||
rx_unit->usr_recv_buf = payload;
|
||||
|
||||
|
@@ -1,8 +1,14 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_parlio_rx.c"
|
||||
"test_parlio_tx.c")
|
||||
|
||||
# TODO: IDF-7840, semaphore in 'spi_bus_lock.c' is not IRAM safe
|
||||
if(CONFIG_PARLIO_ISR_IRAM_SAFE)
|
||||
list(REMOVE_ITEM srcs "test_parlio_rx")
|
||||
endif()
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity driver
|
||||
PRIV_REQUIRES unity driver soc
|
||||
WHOLE_ARCHIVE)
|
||||
|
@@ -11,8 +11,17 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_PARLIO_ISR_IRAM_SAFE
|
||||
#define TEST_PARLIO_CALLBACK_ATTR IRAM_ATTR
|
||||
#define TEST_PARLIO_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define TEST_PARLIO_CALLBACK_ATTR
|
||||
#define TEST_PARLIO_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C6
|
||||
#define TEST_CLK_GPIO 10
|
||||
#define TEST_VALID_GPIO 11
|
||||
#define TEST_DATA0_GPIO 0
|
||||
#define TEST_DATA1_GPIO 1
|
||||
#define TEST_DATA2_GPIO 2
|
||||
@@ -23,6 +32,7 @@ extern "C" {
|
||||
#define TEST_DATA7_GPIO 7
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define TEST_CLK_GPIO 10
|
||||
#define TEST_VALID_GPIO 11
|
||||
#define TEST_DATA0_GPIO 0
|
||||
#define TEST_DATA1_GPIO 1
|
||||
#define TEST_DATA2_GPIO 2
|
||||
|
513
components/driver/test_apps/parlio/main/test_parlio_rx.c
Normal file
513
components/driver/test_apps/parlio/main/test_parlio_rx.c
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "driver/parlio_rx.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/i2s_periph.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "soc/parlio_periph.h"
|
||||
#include "esp_attr.h"
|
||||
#include "test_board.h"
|
||||
|
||||
#define TEST_SPI_HOST SPI2_HOST
|
||||
#define TEST_I2S_PORT I2S_NUM_0
|
||||
#define TEST_VALID_SIG (PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1)
|
||||
#define TEST_DEFAULT_UNIT_CONFIG(_clk_src, _clk_freq) { \
|
||||
.trans_queue_depth = 10, \
|
||||
.max_recv_size = 10 * 1024, \
|
||||
.data_width = 1, \
|
||||
.clk_src = _clk_src, \
|
||||
.clk_freq_hz = _clk_freq, \
|
||||
.clk_gpio_num = _clk_src == PARLIO_CLK_SRC_EXTERNAL ? TEST_CLK_GPIO : -1, \
|
||||
.valid_gpio_num = TEST_VALID_GPIO, \
|
||||
.data_gpio_nums = { \
|
||||
[0] = TEST_DATA0_GPIO, \
|
||||
[1 ... (PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1)] = -1, \
|
||||
}, \
|
||||
.flags = { \
|
||||
.clk_gate_en = false, \
|
||||
.io_loop_back = true, \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TASK_DATA_READY_BIT 0x01
|
||||
#define TEST_TASK_FINISHED_BIT 0x02
|
||||
|
||||
TEST_CASE("parallel_rx_unit_install_uninstall", "[parlio_rx]")
|
||||
{
|
||||
printf("install rx units exhaustively\r\n");
|
||||
parlio_rx_unit_handle_t units[SOC_PARLIO_GROUPS * SOC_PARLIO_RX_UNITS_PER_GROUP];
|
||||
int k = 0;
|
||||
parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_DEFAULT, 1000000);
|
||||
for (int i = 0; i < SOC_PARLIO_GROUPS; i++) {
|
||||
for (int j = 0; j < SOC_PARLIO_RX_UNITS_PER_GROUP; j++) {
|
||||
TEST_ESP_OK(parlio_new_rx_unit(&config, &units[k++]));
|
||||
}
|
||||
}
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, parlio_new_rx_unit(&config, &units[0]));
|
||||
|
||||
for (int i = 0; i < k; i++) {
|
||||
TEST_ESP_OK(parlio_del_rx_unit(units[i]));
|
||||
}
|
||||
|
||||
// clock from external
|
||||
config.clk_src = PARLIO_CLK_SRC_EXTERNAL;
|
||||
// clock gpio must be set when the clock is input from external
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, parlio_new_rx_unit(&config, &units[0]));
|
||||
|
||||
// clock from internal
|
||||
config.clk_src = PARLIO_CLK_SRC_DEFAULT;
|
||||
config.clk_gpio_num = TEST_CLK_GPIO;
|
||||
#if SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT
|
||||
TEST_ESP_OK(parlio_new_rx_unit(&config, &units[0]));
|
||||
TEST_ESP_OK(parlio_del_rx_unit(units[0]));
|
||||
#else
|
||||
// failed because of not support output the clock to a gpio
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, parlio_new_rx_unit(&config, &units[0]));
|
||||
config.clk_gpio_num = -1;
|
||||
#endif
|
||||
config.data_width = 3;
|
||||
// data width should be power of 2
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, parlio_new_rx_unit(&config, &units[0]));
|
||||
|
||||
config.data_width = 4;
|
||||
TEST_ESP_OK(parlio_new_rx_unit(&config, &units[0]));
|
||||
TEST_ESP_OK(parlio_rx_unit_enable(units[0], true));
|
||||
// delete unit before it's disabled is not allowed
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, parlio_del_rx_unit(units[0]));
|
||||
TEST_ESP_OK(parlio_rx_unit_disable(units[0]));
|
||||
TEST_ESP_OK(parlio_del_rx_unit(units[0]));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t partial_recv_cnt;
|
||||
uint32_t recv_done_cnt;
|
||||
uint32_t timeout_cnt;
|
||||
} test_data_t;
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_rx_partial_recv_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_data_t *test_data = (test_data_t *)user_data;
|
||||
test_data->partial_recv_cnt++;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_rx_done_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_data_t *test_data = (test_data_t *)user_data;
|
||||
test_data->recv_done_cnt++;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_rx_timeout_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_data)
|
||||
{
|
||||
test_data_t *test_data = (test_data_t *)user_data;
|
||||
test_data->timeout_cnt++;
|
||||
return false;
|
||||
}
|
||||
|
||||
#define TEST_PAYLOAD_SIZE 5000
|
||||
|
||||
// This test case uses soft delimiter
|
||||
TEST_CASE("parallel_rx_unit_receive_transaction_test", "[parlio_rx]")
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = NULL;
|
||||
parlio_rx_delimiter_handle_t deli = NULL;
|
||||
parlio_rx_delimiter_handle_t timeout_deli = NULL;
|
||||
|
||||
parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_DEFAULT, 1000000);
|
||||
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 = TEST_PAYLOAD_SIZE,
|
||||
.timeout_ticks = 0,
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_rx_delimiter(&sft_deli_cfg, &deli));
|
||||
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,
|
||||
.eof_data_len = TEST_PAYLOAD_SIZE,
|
||||
.timeout_ticks = 5,
|
||||
.flags = {
|
||||
.active_level = 1,
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_rx_delimiter(&lvl_deli_cfg, &timeout_deli));
|
||||
|
||||
parlio_rx_event_callbacks_t cbs = {
|
||||
.on_partial_receive = test_parlio_rx_partial_recv_callback,
|
||||
.on_receive_done = test_parlio_rx_done_callback,
|
||||
.on_timeout = test_parlio_rx_timeout_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));
|
||||
|
||||
parlio_receive_config_t recv_config = {
|
||||
.delimiter = deli,
|
||||
.flags.is_infinite = false,
|
||||
};
|
||||
uint8_t *payload = heap_caps_calloc(1, TEST_PAYLOAD_SIZE, TEST_PARLIO_MEM_ALLOC_CAPS);
|
||||
TEST_ASSERT(payload);
|
||||
|
||||
printf("Testing one normal transaction...\n");
|
||||
TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, true));
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, TEST_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_ASSERT_EQUAL_UINT32(2, test_data.partial_recv_cnt);
|
||||
TEST_ASSERT_EQUAL_UINT32(1, test_data.recv_done_cnt);
|
||||
memset(&test_data, 0, sizeof(test_data_t));
|
||||
|
||||
printf("Testing normal transactions in queue...\n");
|
||||
TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, true));
|
||||
// push 5 repeated transactions to the queue
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, TEST_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_ASSERT_EQUAL_UINT32(10, test_data.partial_recv_cnt);
|
||||
TEST_ASSERT_EQUAL_UINT32(5, test_data.recv_done_cnt);
|
||||
memset(&test_data, 0, sizeof(test_data_t));
|
||||
|
||||
printf("Testing the infinite transaction...\n");
|
||||
recv_config.flags.is_infinite = true;
|
||||
TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, true));
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
// Won't receive done semaphore in infinite transaction
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, parlio_rx_unit_wait_all_done(rx_unit, 500));
|
||||
TEST_ESP_OK(parlio_rx_soft_delimiter_start_stop(rx_unit, deli, false));
|
||||
TEST_ASSERT_GREATER_THAN(6, test_data.partial_recv_cnt);
|
||||
TEST_ASSERT_GREATER_THAN(3, test_data.recv_done_cnt);
|
||||
memset(&test_data, 0, sizeof(test_data_t));
|
||||
|
||||
// printf("Testing the timeout callback...\n");
|
||||
// recv_config.flags.is_infinite = false;
|
||||
// recv_config.delimiter = timeout_deli;
|
||||
// // push 5 repeated transactions to the queue
|
||||
// for (int i = 0; i < 5; i++) {
|
||||
// TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
// gpio_set_level(TEST_VALID_GPIO, 1);
|
||||
// vTaskDelay(pdMS_TO_TICKS(100));
|
||||
// gpio_set_level(TEST_VALID_GPIO, 0);
|
||||
// vTaskDelay(pdMS_TO_TICKS(50));
|
||||
// }
|
||||
// TEST_ASSERT_TRUE(test_data.timeout_cnt);
|
||||
|
||||
TEST_ESP_OK(parlio_rx_unit_disable(rx_unit));
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(timeout_deli));
|
||||
TEST_ESP_OK(parlio_del_rx_unit(rx_unit));
|
||||
free(payload);
|
||||
};
|
||||
|
||||
static void connect_signal_internally(uint32_t gpio, uint32_t sigo, uint32_t sigi)
|
||||
{
|
||||
gpio_config_t gpio_conf = {
|
||||
.pin_bit_mask = BIT64(gpio),
|
||||
.mode = GPIO_MODE_INPUT_OUTPUT,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.pull_down_en = false,
|
||||
.pull_up_en = false,
|
||||
};
|
||||
gpio_config(&gpio_conf);
|
||||
esp_rom_gpio_connect_out_signal(gpio, sigo, false, false);
|
||||
esp_rom_gpio_connect_in_signal(gpio, sigi, false);
|
||||
}
|
||||
|
||||
#define TEST_EOF_DATA_LEN 64
|
||||
|
||||
static void pulse_delimiter_sender_task_i2s(void *args)
|
||||
{
|
||||
uint32_t *task_flags = (uint32_t *)args;
|
||||
i2s_chan_handle_t tx_chan = NULL;
|
||||
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(TEST_I2S_PORT, I2S_ROLE_MASTER);
|
||||
TEST_ESP_OK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));
|
||||
i2s_tdm_config_t tx_tdm_cfg = {
|
||||
.clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_TDM_PCM_SHORT_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO,
|
||||
I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = TEST_CLK_GPIO,
|
||||
.ws = TEST_VALID_GPIO,
|
||||
.dout = TEST_DATA0_GPIO,
|
||||
.din = -1,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_init_tdm_mode(tx_chan, &tx_tdm_cfg));
|
||||
|
||||
uint32_t buf_size = 2048;
|
||||
uint16_t *w_buf = (uint16_t *)calloc(1, buf_size);
|
||||
assert(w_buf); // Check if w_buf allocation success
|
||||
|
||||
for (int i = 0; i < buf_size / 2; i += 2) {
|
||||
w_buf[i] = 0x1234;
|
||||
w_buf[i + 1] = 0x5678;
|
||||
}
|
||||
|
||||
size_t w_bytes = buf_size;
|
||||
|
||||
// Preload the data into DMA buffer
|
||||
while (w_bytes == buf_size) {
|
||||
TEST_ESP_OK(i2s_channel_preload_data(tx_chan, w_buf, buf_size, &w_bytes));
|
||||
}
|
||||
// Transmission will start after enable the tx channel
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_chan));
|
||||
|
||||
// Connect GPIO signals
|
||||
connect_signal_internally(TEST_CLK_GPIO,
|
||||
i2s_periph_signal[TEST_I2S_PORT].m_tx_bck_sig,
|
||||
parlio_periph_signals.groups[0].rx_units[0].clk_in_sig);
|
||||
connect_signal_internally(TEST_VALID_GPIO,
|
||||
i2s_periph_signal[TEST_I2S_PORT].m_tx_ws_sig,
|
||||
parlio_periph_signals.groups[0].rx_units[0].data_sigs[TEST_VALID_SIG]);
|
||||
connect_signal_internally(TEST_DATA0_GPIO,
|
||||
i2s_periph_signal[TEST_I2S_PORT].data_out_sig,
|
||||
parlio_periph_signals.groups[0].rx_units[0].data_sigs[0]);
|
||||
|
||||
while (!((*task_flags) & TEST_TASK_FINISHED_BIT)) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
*task_flags |= TEST_TASK_DATA_READY_BIT;
|
||||
}
|
||||
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_chan));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_chan));
|
||||
free(w_buf);
|
||||
|
||||
*task_flags = 0;
|
||||
while (1) {
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void cs_high(spi_transaction_t *trans)
|
||||
{
|
||||
gpio_set_level(TEST_VALID_GPIO, 1);
|
||||
}
|
||||
|
||||
static void cs_low(spi_transaction_t *trans)
|
||||
{
|
||||
gpio_set_level(TEST_VALID_GPIO, 0);
|
||||
}
|
||||
|
||||
#define TEST_SPI_CLK_FREQ 100000
|
||||
|
||||
static void level_delimiter_sender_task_spi(void *args)
|
||||
{
|
||||
uint32_t *task_flags = (uint32_t *)args;
|
||||
spi_device_handle_t dev_handle;
|
||||
|
||||
spi_bus_config_t bus_cfg = {
|
||||
.miso_io_num = -1,
|
||||
.mosi_io_num = TEST_DATA0_GPIO,
|
||||
.sclk_io_num = TEST_CLK_GPIO,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = 2048,
|
||||
};
|
||||
spi_device_interface_config_t dev_cfg = {
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.clock_speed_hz = TEST_SPI_CLK_FREQ,
|
||||
.mode = 0,
|
||||
.duty_cycle_pos = 128,
|
||||
.spics_io_num = TEST_VALID_GPIO,
|
||||
.queue_size = 5,
|
||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_POSITIVE_CS,
|
||||
.pre_cb = cs_high,
|
||||
.post_cb = cs_low,
|
||||
};
|
||||
//Initialize the SPI bus and add device
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO));
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &dev_cfg, &dev_handle));
|
||||
|
||||
// Initialize CS gpio
|
||||
gpio_set_level(TEST_VALID_GPIO, 0);
|
||||
gpio_config_t cs_cfg = {
|
||||
.pin_bit_mask = BIT64(TEST_VALID_GPIO),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
};
|
||||
gpio_config(&cs_cfg);
|
||||
|
||||
// Connect SPI signals to parlio rx signals
|
||||
connect_signal_internally(TEST_CLK_GPIO,
|
||||
spi_periph_signal[TEST_SPI_HOST].spiclk_out,
|
||||
parlio_periph_signals.groups[0].rx_units[0].clk_in_sig);
|
||||
connect_signal_internally(TEST_VALID_GPIO,
|
||||
spi_periph_signal[TEST_SPI_HOST].spics_out[0],
|
||||
parlio_periph_signals.groups[0].rx_units[0].data_sigs[TEST_VALID_SIG]);
|
||||
connect_signal_internally(TEST_DATA0_GPIO,
|
||||
spi_periph_signal[TEST_SPI_HOST].spid_out,
|
||||
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) {
|
||||
data[i] = 0x12;
|
||||
data[i + 1] = 0x34;
|
||||
data[i + 2] = 0x56;
|
||||
data[i + 3] = 0x78;
|
||||
}
|
||||
spi_transaction_t t = {
|
||||
.cmd = 0,
|
||||
.length = TEST_EOF_DATA_LEN * 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;
|
||||
}
|
||||
|
||||
// Remove the SPI device and free the bus
|
||||
TEST_ESP_OK(spi_bus_remove_device(dev_handle));
|
||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||
// Free data buffer
|
||||
free(data);
|
||||
|
||||
// Reset the flag to indicate the sending loop has quit
|
||||
*task_flags = 0;
|
||||
// Waiting to be deleted
|
||||
while (1) {
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static bool test_delimiter(parlio_rx_delimiter_handle_t deli, void (*sender_task_thread)(void *args))
|
||||
{
|
||||
parlio_rx_unit_handle_t rx_unit = NULL;
|
||||
|
||||
parlio_rx_unit_config_t config = TEST_DEFAULT_UNIT_CONFIG(PARLIO_CLK_SRC_EXTERNAL, 1000000);
|
||||
if (sender_task_thread == pulse_delimiter_sender_task_i2s) {
|
||||
// I2S offers free-running clock
|
||||
config.flags.free_clk = 1;
|
||||
}
|
||||
TEST_ESP_OK(parlio_new_rx_unit(&config, &rx_unit));
|
||||
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 = 0;
|
||||
xTaskCreate(sender_task_thread, "sender task", 4096, &task_flags, 5, &sender_task);
|
||||
// Waiting for the data ready on line
|
||||
while ((task_flags & TEST_TASK_DATA_READY_BIT)) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
parlio_receive_config_t recv_config = {
|
||||
.delimiter = deli,
|
||||
.flags.is_infinite = false,
|
||||
};
|
||||
uint8_t recv_buff[TEST_EOF_DATA_LEN];
|
||||
bool is_success = false;
|
||||
// sample 5 times
|
||||
for (int i = 0; i < 5 && !is_success; i++) {
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, recv_buff, TEST_EOF_DATA_LEN, &recv_config));
|
||||
TEST_ESP_OK(parlio_rx_unit_wait_all_done(rx_unit, 5000));
|
||||
for (int k = 0; k < TEST_EOF_DATA_LEN; k++) {
|
||||
printf("%x ", recv_buff[k]);
|
||||
if ((k & 0xf) == 0xf) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
for (int j = 0; j < TEST_EOF_DATA_LEN; j++) {
|
||||
// Check if 0x12 0x34 0x56 0x78 appeared in the buffer
|
||||
if (recv_buff[j] == 0x12 && recv_buff[j+1] == 0x34 &&
|
||||
recv_buff[j+2] == 0x56 && recv_buff[j+3] == 0x78) {
|
||||
is_success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Indicate the test finished, no need to send data
|
||||
task_flags |= TEST_TASK_FINISHED_BIT;
|
||||
// Waiting for the sender task quit
|
||||
while (task_flags) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
// Delete the sender task
|
||||
vTaskDelete(sender_task);
|
||||
|
||||
TEST_ESP_OK(parlio_rx_unit_disable(rx_unit));
|
||||
TEST_ESP_OK(parlio_del_rx_unit(rx_unit));
|
||||
return is_success;
|
||||
}
|
||||
|
||||
// This test case uses level delimiter
|
||||
TEST_CASE("parallel_rx_unit_level_delimiter_test_via_spi", "[parlio_rx]")
|
||||
{
|
||||
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,
|
||||
.eof_data_len = TEST_EOF_DATA_LEN,
|
||||
.timeout_ticks = 0,
|
||||
.flags = {
|
||||
.active_level = 1,
|
||||
},
|
||||
};
|
||||
parlio_rx_delimiter_handle_t deli = NULL;
|
||||
TEST_ESP_OK(parlio_new_rx_delimiter(&lvl_deli_cfg, &deli));
|
||||
bool is_success = test_delimiter(deli, level_delimiter_sender_task_spi);
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
TEST_ASSERT(is_success);
|
||||
}
|
||||
|
||||
// This test case uses pulse delimiter
|
||||
TEST_CASE("parallel_rx_unit_pulse_delimiter_test_via_i2s", "[parlio_rx]")
|
||||
{
|
||||
parlio_rx_pulse_delimiter_config_t pls_deli_cfg = {
|
||||
.valid_sig_line_id = TEST_VALID_SIG,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB,
|
||||
.eof_data_len = TEST_EOF_DATA_LEN,
|
||||
.timeout_ticks = 0,
|
||||
.flags = {
|
||||
.start_bit_included = 0,
|
||||
.end_bit_included = 0,
|
||||
.has_end_pulse = 0,
|
||||
.pulse_invert = 0,
|
||||
},
|
||||
};
|
||||
parlio_rx_delimiter_handle_t deli = NULL;
|
||||
TEST_ESP_OK(parlio_new_rx_delimiter(&pls_deli_cfg, &deli));
|
||||
bool is_success = test_delimiter(deli, pulse_delimiter_sender_task_i2s);
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
TEST_ASSERT(is_success);
|
||||
}
|
@@ -15,12 +15,6 @@
|
||||
#include "esp_attr.h"
|
||||
#include "test_board.h"
|
||||
|
||||
#if CONFIG_PARLIO_ISR_IRAM_SAFE
|
||||
#define TEST_PARLIO_CALLBACK_ATTR IRAM_ATTR
|
||||
#else
|
||||
#define TEST_PARLIO_CALLBACK_ATTR
|
||||
#endif
|
||||
|
||||
TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]")
|
||||
{
|
||||
printf("install tx units exhaustively\r\n");
|
||||
|
@@ -17,4 +17,4 @@ from pytest_embedded import Dut
|
||||
indirect=True,
|
||||
)
|
||||
def test_parlio(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
dut.run_all_single_board_cases(reset=True)
|
||||
|
Reference in New Issue
Block a user