feat(i2s): support i2s etm event and task

This commit is contained in:
laokaiyao
2024-07-09 19:44:52 +08:00
parent 5c3964c9ea
commit 86a552e4e2
23 changed files with 895 additions and 61 deletions

View File

@@ -18,6 +18,9 @@ if(CONFIG_SOC_I2S_SUPPORTED)
if(CONFIG_SOC_I2S_SUPPORTS_TDM)
list(APPEND srcs "i2s_tdm.c")
endif()
if(CONFIG_SOC_I2S_SUPPORTS_ETM)
list(APPEND srcs "i2s_etm.c")
endif()
endif()
idf_component_register(SRCS ${srcs}

View File

@@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include <stdlib.h>
#include <sys/cdefs.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "driver/i2s_etm.h"
#include "i2s_private.h"
#include "hal/i2s_ll.h"
#include "esp_private/etm_interface.h"
#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
static const char *TAG = "i2s-etm";
static esp_err_t i2s_del_etm_event(esp_etm_event_t *event)
{
free(event);
return ESP_OK;
}
static esp_err_t i2s_del_etm_task(esp_etm_task_t *task)
{
free(task);
return ESP_OK;
}
esp_err_t i2s_new_etm_event(i2s_chan_handle_t handle, const i2s_etm_event_config_t *config, esp_etm_event_handle_t *out_event)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(handle && config && out_event, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config->event_type < I2S_ETM_EVENT_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid event type");
esp_etm_event_t *event = heap_caps_calloc(1, sizeof(esp_etm_event_t), ETM_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(event, ESP_ERR_NO_MEM, TAG, "no memory for ETM event");
uint32_t event_id = I2S_LL_ETM_EVENT_TABLE(handle->controller->id, handle->dir, config->event_type);
if (config->event_type == I2S_ETM_EVENT_REACH_THRESH) {
ESP_GOTO_ON_FALSE(config->threshold <= I2S_LL_ETM_MAX_THRESH_NUM, ESP_ERR_INVALID_ARG, err, TAG,
"exceed the max threshold %"PRIu32, (uint32_t)I2S_LL_ETM_MAX_THRESH_NUM);
if (handle->dir == I2S_DIR_TX) {
i2s_ll_tx_set_etm_threshold(handle->controller->hal.dev, config->threshold);
} else {
i2s_ll_rx_set_etm_threshold(handle->controller->hal.dev, config->threshold);
}
}
// fill the ETM event object
event->event_id = event_id;
event->trig_periph = ETM_TRIG_PERIPH_I2S;
event->del = i2s_del_etm_event;
*out_event = event;
return ret;
err:
free(event);
return ret;
}
esp_err_t i2s_new_etm_task(i2s_chan_handle_t handle, const i2s_etm_task_config_t *config, esp_etm_task_handle_t *out_task)
{
ESP_RETURN_ON_FALSE(handle && config && out_task, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config->task_type < I2S_ETM_TASK_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid task type");
esp_etm_task_t *task = heap_caps_calloc(1, sizeof(esp_etm_task_t), ETM_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(task, ESP_ERR_NO_MEM, TAG, "no memory for ETM task");
uint32_t task_id = I2S_LL_ETM_TASK_TABLE(handle->controller->id, handle->dir, config->task_type);
// fill the ETM task object
task->task_id = task_id;
task->trig_periph = ETM_TRIG_PERIPH_I2S;
task->del = i2s_del_etm_task;
*out_task = task;
return ESP_OK;
}

View File

@@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/i2s_types.h"
#include "hal/i2s_types.h"
#include "esp_etm.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_I2S_SUPPORTS_ETM
/**
* @brief I2S ETM event configuration
*/
typedef struct {
i2s_etm_event_type_t event_type; /*!< I2S ETM event type */
uint32_t threshold; /*!< The threshold word number that triggers `I2S_ETM_EVENT_REACH_THRESH` event,
only take effect when the event type is `I2S_ETM_EVENT_REACH_THRESH`
Unit is in word (4 bytes) */
} i2s_etm_event_config_t;
/**
* @brief Register the ETM event for I2S channel
*
* @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
*
* @param[in] handle I2S channel handle, allocated by `i2s_new_channel`
* @param[in] config I2S ETM event configuration
* @param[out] out_event Returned ETM event handle
* @return
* - ESP_OK: Get ETM event successfully
* - ESP_ERR_INVALID_ARG: Get ETM event failed because of invalid argument
* - ESP_ERR_NOT_SUPPORTED: Get ETM event failed because the I2S hardware doesn't support ETM event
* - ESP_FAIL: Get ETM event failed because of other error
*/
esp_err_t i2s_new_etm_event(i2s_chan_handle_t handle, const i2s_etm_event_config_t *config, esp_etm_event_handle_t *out_event);
/**
* @brief I2S ETM task configuration
*/
typedef struct {
i2s_etm_task_type_t task_type; /*!< I2S ETM task type */
} i2s_etm_task_config_t;
/**
* @brief Register the ETM task for I2S channel
*
* @note The created ETM task object can be deleted later by calling `esp_etm_del_task`
*
* @param[in] handle I2S channel handle, allocated by `i2s_new_channel`
* @param[in] config I2S ETM task configuration
* @param[out] out_task Returned ETM task handle
* @return
* - ESP_OK: Get ETM task successfully
* - ESP_ERR_INVALID_ARG: Get ETM task failed because of invalid argument
* - ESP_ERR_NOT_SUPPORTED: Get ETM task failed because the i2s hardware doesn't support ETM task
* - ESP_FAIL: Get ETM task failed because of other error
*/
esp_err_t i2s_new_etm_task(i2s_chan_handle_t handle, const i2s_etm_task_config_t *config, esp_etm_task_handle_t *out_task);
#endif // SOC_I2S_SUPPORTS_ETM
#ifdef __cplusplus
}
#endif

View File

@@ -2,6 +2,10 @@ set(srcs "test_app_main.c"
"test_i2s.c"
"test_i2s_iram.c")
if(CONFIG_SOC_I2S_SUPPORTS_ETM AND CONFIG_SOC_GPIO_SUPPORT_ETM)
set(srcs ${srcs} "test_i2s_etm.c")
endif()
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES unity esp_driver_pcnt driver spi_flash
PRIV_REQUIRES unity esp_driver_pcnt driver spi_flash esp_driver_gpio
WHOLE_ARCHIVE)

View File

@@ -41,32 +41,6 @@
#define I2S_TEST_MODE_MASTER_TO_SLAVE 1
#define I2S_TEST_MODE_LOOPBACK 2
#define I2S_TEST_MASTER_DEFAULT_PIN { \
.mclk = MASTER_MCK_IO, \
.bclk = MASTER_BCK_IO, \
.ws = MASTER_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
#define I2S_TEST_SLAVE_DEFAULT_PIN { \
.mclk = -1, \
.bclk = SLAVE_BCK_IO, \
.ws = SLAVE_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
// mode: 0, master rx, slave tx. mode: 1, master tx, slave rx. mode: 2, master tx rx loop-back
// Since ESP32-S2 has only one I2S, only loop back test can be tested.
static void i2s_test_io_config(int mode)

View File

@@ -0,0 +1,215 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_attr.h"
#include "driver/gpio_etm.h"
#include "driver/gpio.h"
#include "driver/i2s_std.h"
#include "driver/i2s_etm.h"
#include "hal/i2s_ll.h"
#include "hal/etm_ll.h"
#include "../../test_inc/test_i2s.h"
#include "soc/soc_etm_struct.h"
#define TEST_DESC_NUM 4
#define TEST_FRAME_NUM 256
#define TEST_BUFF_SIZE (TEST_DESC_NUM * TEST_FRAME_NUM)
#define TEST_GPIO_ETM_NUM DATA_IN_IO
static i2s_chan_handle_t s_tx_handle = NULL;
static i2s_chan_handle_t s_rx_handle = NULL;
#if ETM_LL_SUPPORT_STATUS
static void s_i2s_etm_check_status(void)
{
i2s_dev_t *hw = I2S_LL_GET_HW(0);
bool is_tx_done = i2s_ll_get_etm_tx_done_event_status(hw);
bool is_rx_done = i2s_ll_get_etm_rx_done_event_status(hw);
bool is_tx_reach_thresh = i2s_ll_get_etm_tx_threshold_event_status(hw);
bool is_rx_reach_thresh = i2s_ll_get_etm_rx_threshold_event_status(hw);
printf("tx done st %d, rx done st %d, tx x st %d, rx x st %d\n",
is_tx_done, is_rx_done, is_tx_reach_thresh, is_rx_reach_thresh);
// TODO: IDF-10512 enable the TX_DONE and RX_DONE check after refactor. Currently not support
// TEST_ASSERT(is_tx_done);
// TEST_ASSERT(is_rx_done);
TEST_ASSERT(is_tx_reach_thresh);
TEST_ASSERT(is_rx_reach_thresh);
}
#endif // ETM_LL_SUPPORT_STATUS
static void s_i2s_init(uint8_t *buf)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = TEST_DESC_NUM;
chan_cfg.dma_frame_num = TEST_FRAME_NUM;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO),
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
};
std_cfg.gpio_cfg.mclk = -1; // no need mclk
std_cfg.gpio_cfg.din = DATA_OUT_IO; // data loopback
/* I2S channels init */
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &s_tx_handle, &s_rx_handle));
TEST_ESP_OK(i2s_channel_init_std_mode(s_tx_handle, &std_cfg));
TEST_ESP_OK(i2s_channel_init_std_mode(s_rx_handle, &std_cfg));
// TODO: IDF-10512 rx_stop_mode is necessary for rx_done event, enable it when supported
// I2S0.rx_conf.rx_stop_mode = 1;
size_t w_bytes = TEST_BUFF_SIZE;
while (w_bytes == TEST_BUFF_SIZE) {
TEST_ESP_OK(i2s_channel_preload_data(s_tx_handle, buf, TEST_BUFF_SIZE, &w_bytes));
}
}
static void s_i2s_deinit(void)
{
TEST_ESP_OK(i2s_del_channel(s_rx_handle));
TEST_ESP_OK(i2s_del_channel(s_tx_handle));
s_tx_handle = NULL;
s_rx_handle = NULL;
}
static void s_gpio_init(void)
{
gpio_config_t gpio_cfg = {
.mode = GPIO_MODE_INPUT_OUTPUT,
.pin_bit_mask = (1ULL << TEST_GPIO_ETM_NUM),
};
TEST_ESP_OK(gpio_config(&gpio_cfg));
TEST_ESP_OK(gpio_set_level(TEST_GPIO_ETM_NUM, 0));
}
TEST_CASE("i2s_etm_event_test", "[etm]")
{
uint8_t *buf = calloc(1, TEST_BUFF_SIZE);
assert(buf);
memset(buf, 0x3C, TEST_BUFF_SIZE);
/* I2S init */
s_i2s_init(buf);
/* GPIO init */
s_gpio_init();
/* GPIO ETM task */
gpio_etm_task_config_t gpio_task_cfg = {
.action = GPIO_ETM_TASK_ACTION_SET,
};
esp_etm_task_handle_t gpio_task_handle;
TEST_ESP_OK(gpio_new_etm_task(&gpio_task_cfg, &gpio_task_handle));
TEST_ESP_OK(gpio_etm_task_add_gpio(gpio_task_handle, TEST_GPIO_ETM_NUM));
/* I2S Event init */
i2s_etm_event_config_t i2s_evt_cfg = {
.event_type = I2S_ETM_EVENT_REACH_THRESH,
.threshold = 64,
};
esp_etm_event_handle_t i2s_evt_handle;
TEST_ESP_OK(i2s_new_etm_event(s_rx_handle, &i2s_evt_cfg, &i2s_evt_handle));
/* ETM connect */
esp_etm_channel_config_t etm_config = {};
esp_etm_channel_handle_t etm_channel = NULL;
TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel));
TEST_ESP_OK(esp_etm_channel_connect(etm_channel, i2s_evt_handle, gpio_task_handle));
TEST_ESP_OK(esp_etm_channel_enable(etm_channel));
esp_etm_dump(stdout);
TEST_ESP_OK(i2s_channel_enable(s_tx_handle));
TEST_ESP_OK(i2s_channel_enable(s_rx_handle));
TEST_ESP_OK(i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, portMAX_DELAY));
#if ETM_LL_SUPPORT_STATUS
s_i2s_etm_check_status();
#else
TEST_ASSERT(gpio_get_level(TEST_GPIO_ETM_NUM));
#endif // ETM_LL_SUPPORT_STATUS
/* Test finished, free the resources */
TEST_ESP_OK(i2s_channel_disable(s_rx_handle));
TEST_ESP_OK(i2s_channel_disable(s_tx_handle));
free(buf);
TEST_ESP_OK(esp_etm_channel_disable(etm_channel));
TEST_ESP_OK(esp_etm_del_event(i2s_evt_handle));
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task_handle, TEST_GPIO_ETM_NUM));
TEST_ESP_OK(esp_etm_del_task(gpio_task_handle));
TEST_ESP_OK(esp_etm_del_channel(etm_channel));
s_i2s_deinit();
}
TEST_CASE("i2s_etm_task_test", "[etm]")
{
uint8_t *buf = calloc(1, TEST_BUFF_SIZE);
assert(buf);
memset(buf, 0x3C, TEST_BUFF_SIZE);
/* I2S init */
s_i2s_init(buf);
/* GPIO init */
s_gpio_init();
/* GPIO ETM event */
gpio_etm_event_config_t gpio_event_cfg = {
.edge = GPIO_ETM_EVENT_EDGE_POS,
};
esp_etm_event_handle_t gpio_event_handle;
TEST_ESP_OK(gpio_new_etm_event(&gpio_event_cfg, &gpio_event_handle));
TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_handle, TEST_GPIO_ETM_NUM));
/* I2S Task init */
i2s_etm_task_config_t i2s_task_cfg = {
.task_type = I2S_ETM_TASK_STOP,
};
esp_etm_task_handle_t i2s_task_handle;
TEST_ESP_OK(i2s_new_etm_task(s_tx_handle, &i2s_task_cfg, &i2s_task_handle));
/* ETM connect */
esp_etm_channel_config_t etm_config = {};
esp_etm_channel_handle_t etm_channel = NULL;
TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel));
TEST_ESP_OK(esp_etm_channel_connect(etm_channel, gpio_event_handle, i2s_task_handle));
TEST_ESP_OK(esp_etm_channel_enable(etm_channel));
esp_etm_dump(stdout);
TEST_ESP_OK(i2s_channel_enable(s_tx_handle));
TEST_ESP_OK(i2s_channel_enable(s_rx_handle));
/* Test */
// receive normally
i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, portMAX_DELAY);
// Set the GPIO to stop the I2S TX via ETM
TEST_ESP_OK(gpio_set_level(TEST_GPIO_ETM_NUM, 1));
esp_err_t ret = ESP_OK;
// Receive will timeout after TX stopped
for (int i = 0; i < 20 && ret == ESP_OK; i++) {
ret = i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, 1000);
}
TEST_ESP_ERR(ESP_ERR_TIMEOUT, ret);
/* Test finished, free the resources */
TEST_ESP_OK(i2s_channel_disable(s_rx_handle));
TEST_ESP_OK(i2s_channel_disable(s_tx_handle));
free(buf);
TEST_ESP_OK(esp_etm_channel_disable(etm_channel));
TEST_ESP_OK(esp_etm_del_event(gpio_event_handle));
TEST_ESP_OK(esp_etm_del_task(i2s_task_handle));
TEST_ESP_OK(esp_etm_del_channel(etm_channel));
s_i2s_deinit();
}

View File

@@ -72,6 +72,32 @@ extern "C" {
#define DATA_OUT_IO 7
#endif
#define I2S_TEST_MASTER_DEFAULT_PIN { \
.mclk = MASTER_MCK_IO, \
.bclk = MASTER_BCK_IO, \
.ws = MASTER_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
#define I2S_TEST_SLAVE_DEFAULT_PIN { \
.mclk = -1, \
.bclk = SLAVE_BCK_IO, \
.ws = SLAVE_WS_IO, \
.dout = DATA_OUT_IO, \
.din = DATA_IN_IO, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
#ifdef __cplusplus
}
#endif