feat(parlio_rx): add test for parlio rx driver

This commit is contained in:
laokaiyao
2023-07-11 23:54:06 +08:00
parent 04d267b023
commit ce9110dbf0
7 changed files with 567 additions and 47 deletions

View File

@@ -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 */

View File

@@ -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");
}
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,21 +299,29 @@ 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,
};
need_yield |= rx_unit->cbs.on_receive_done(rx_unit, &evt_data, rx_unit->user_data);
parlio_rx_event_data_t evt_data = {
.delimiter = rx_unit->curr_trans.delimiter,
};
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) {
@@ -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;

View File

@@ -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)

View File

@@ -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

View 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);
}

View File

@@ -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");

View File

@@ -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)