Merge branch 'bugfix/add_unity_sig_sync_for_i2s_tdm_test' into 'master'

i2s_test: fixed rx_update stuck issue

Closes IDFCI-1527

See merge request espressif/esp-idf!21308
This commit is contained in:
Kevin (Lao Kaiyao)
2022-12-22 11:27:52 +08:00
10 changed files with 93 additions and 83 deletions

View File

@@ -747,15 +747,15 @@ static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_c
esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0); esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
// Test common sample rate // Test common sample rate
uint32_t test_freq[15] = {8000, 11025, 12000, 16000, 22050, 24000, uint32_t test_freq[16] = {8000, 10000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000, 32000, 44100, 48000, 64000, 88200, 96000,
128000, 144000, 196000}; 128000, 144000, 196000};
int real_pulse = 0; int real_pulse = 0;
int case_cnt = 15; int case_cnt = 16;
#if SOC_I2S_HW_VERSION_2 #if SOC_I2S_HW_VERSION_2
// Can't support a very high sample rate while using XTAL as clock source // Can't support a very high sample rate while using XTAL as clock source
if (clk_cfg->clk_src == I2S_CLK_SRC_XTAL) { if (clk_cfg->clk_src == I2S_CLK_SRC_XTAL) {
case_cnt = 9; case_cnt = 10;
} }
#endif #endif
for (int i = 0; i < case_cnt; i++) { for (int i = 0; i < case_cnt; i++) {

View File

@@ -2,5 +2,9 @@
# in this exact order for cmake to work correctly # in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS
"$ENV{IDF_PATH}/tools/unit-test-app/components"
)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_tdm_full_duplex_test) project(i2s_tdm_full_duplex_test)

View File

@@ -9,7 +9,7 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#define TEST_MEMORY_LEAK_THRESHOLD (-300) #define TEST_MEMORY_LEAK_THRESHOLD (-400)
static size_t before_free_8bit; static size_t before_free_8bit;
static size_t before_free_32bit; static size_t before_free_32bit;

View File

@@ -11,14 +11,16 @@
#include "esp_log.h" #include "esp_log.h"
#include "unity.h" #include "unity.h"
#include "unity_test_utils.h" #include "unity_test_utils.h"
#include "test_utils.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/i2s_tdm.h" #include "driver/i2s_tdm.h"
static const char *TAG = "i2s_tdm_full_duplex_test"; static const char *TAG = "i2s_tdm_full_duplex_test";
#define TEST_I2S_FRAME_SIZE (128) #define TEST_I2S_FRAME_SIZE (128) // Frame numbers in every writing / reading
#define TEST_I2S_PACKET_COUNT (512) #define TEST_I2S_ARRAY_LENGTH (1024) // The loop data length for verification
#define TEST_I2S_BAD_PACKET_THRESHOLD (10) #define TEST_I2S_MAX_FAIL_CNT (3) // Max broken packet count
#define TEST_I2S_FRAME_TIMEOUT_SEC (10.0f) // Timeout seconds of waiting for a correct frame
#define TEST_I2S_NUM (I2S_NUM_0) // ESP32-C3 has only I2S0 #define TEST_I2S_NUM (I2S_NUM_0) // ESP32-C3 has only I2S0
#define TEST_I2S_BCK_IO (GPIO_NUM_4) #define TEST_I2S_BCK_IO (GPIO_NUM_4)
@@ -52,20 +54,22 @@ static void test_i2s_tdm_master_write_task(void *args)
uint32_t data_cnt = 0; uint32_t data_cnt = 0;
size_t bytes_written = 0; size_t bytes_written = 0;
/* Block here waiting for the main thread receiving Slave Ready signals */
xTaskNotifyWait(0, ULONG_MAX, NULL, portMAX_DELAY);
ESP_LOGI(TAG, "I2S TDM master send start"); ESP_LOGI(TAG, "I2S TDM master send start");
TEST_ESP_OK(i2s_channel_enable(task_args->tx_channel_handle));
while (xTaskNotifyWait(0, ULONG_MAX, NULL, 0) == pdFALSE) { // if main task sends terminate signal, exit the loop while (xTaskNotifyWait(0, ULONG_MAX, NULL, 0) == pdFALSE) { // if main task sends terminate signal, exit the loop
/* Fill in the tx buffer */ /* Fill in the tx buffer */
for (uint32_t i = 0; i < tx_buffer_size / sizeof(uint32_t); i ++) { for (uint32_t i = 0; i < tx_buffer_size / sizeof(uint32_t); i ++) {
tx_buffer[i] = data_cnt; tx_buffer[i] = data_cnt;
data_cnt ++; data_cnt++;
data_cnt %= TEST_I2S_ARRAY_LENGTH;
} }
TEST_ESP_OK(i2s_channel_write(task_args->tx_channel_handle, tx_buffer, tx_buffer_size, TEST_ESP_OK(i2s_channel_write(task_args->tx_channel_handle, tx_buffer, tx_buffer_size,
&bytes_written, portMAX_DELAY)); &bytes_written, 1000));
TEST_ASSERT_EQUAL(tx_buffer_size, bytes_written);
} }
ESP_LOGI(TAG, "I2S TDM master send stop");
TEST_ESP_OK(i2s_channel_disable(task_args->tx_channel_handle));
ESP_LOGI(TAG, "Freeing I2S TDM master tx buffer"); ESP_LOGI(TAG, "Freeing I2S TDM master tx buffer");
free(tx_buffer); free(tx_buffer);
@@ -106,12 +110,12 @@ static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_w
/* Create TDM write task */ /* Create TDM write task */
TaskHandle_t subtask_handle = NULL; TaskHandle_t subtask_handle = NULL;
test_i2s_tdm_write_task_args_t task_args = { /* Make the variable static in case it become invalid in the write task */
.tx_channel_handle = i2s_tdm_tx_handle, static test_i2s_tdm_write_task_args_t task_args;
.maintask_handle = xTaskGetCurrentTaskHandle(), task_args.tx_channel_handle = i2s_tdm_tx_handle;
.tx_data_bit_width = bit_width, task_args.maintask_handle = xTaskGetCurrentTaskHandle();
.tdm_slot_mask = slot_mask task_args.tx_data_bit_width = bit_width;
}; task_args.tdm_slot_mask = slot_mask;
xTaskCreate(test_i2s_tdm_master_write_task, "I2S TDM Write Task", 4096, &task_args, 5, &subtask_handle); xTaskCreate(test_i2s_tdm_master_write_task, "I2S TDM Write Task", 4096, &task_args, 5, &subtask_handle);
/* Allocate I2S rx buffer */ /* Allocate I2S rx buffer */
@@ -121,42 +125,44 @@ static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_w
uint32_t *rx_buffer = malloc(rx_buffer_size); uint32_t *rx_buffer = malloc(rx_buffer_size);
TEST_ASSERT(rx_buffer); TEST_ASSERT(rx_buffer);
uint8_t is_packet_valid = 0; uint32_t count = 1;
uint32_t good_packet_cnt = 0; bool is_start = false;
uint8_t fail_cnt = 0;
size_t bytes_read = 0; size_t bytes_read = 0;
ESP_LOGI(TAG, "I2S TDM master receive start"); float time = 0;
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle));
for(uint32_t packet_cnt = 0; packet_cnt < TEST_I2S_PACKET_COUNT; packet_cnt ++) { TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle));
TEST_ESP_OK(i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size, unity_send_signal("Master Ready");
&bytes_read, portMAX_DELAY)); unity_wait_for_signal("Slave Ready");
TEST_ASSERT_EQUAL(rx_buffer_size, bytes_read);
/* Check for empty packet */ /* Slave is ready, start the writing task */
if (rx_buffer[0] == 0) { // empty packet ESP_LOGI(TAG, "I2S TDM master receive & send start");
if (is_packet_valid == 0) { // omit leading empty packets esp_err_t read_ret = ESP_OK;
packet_cnt = 0; xTaskNotifyGive(subtask_handle);
} else { while (count < TEST_I2S_ARRAY_LENGTH && fail_cnt < TEST_I2S_MAX_FAIL_CNT && time < TEST_I2S_FRAME_TIMEOUT_SEC) {
ESP_LOGW(TAG, "empty packet %"PRIu32, packet_cnt); read_ret = i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size, &bytes_read, 1000);
} if (read_ret != ESP_OK) {
continue; break;
} }
is_packet_valid = 1; for (int i = 0; i < rx_buffer_size / sizeof(uint32_t); i++) {
/* Check received packet */ if (rx_buffer[i] == count) {
uint8_t is_good_packet = 1; count++;
uint32_t last_value = rx_buffer[0]; if (count >= TEST_I2S_ARRAY_LENGTH) {
for (uint32_t j = 1; j < rx_buffer_size / sizeof(uint32_t); j ++) { break;
if (rx_buffer[j] == last_value + 1) { // increased by 1 }
last_value = rx_buffer[j]; if (!is_start) {
} else { is_start = true;
is_good_packet = 0; }
ESP_LOGW(TAG, "corrupted packet %"PRIu32, packet_cnt); } else if (is_start) {
break; // corrupted packet ESP_LOGE(TAG, "Failed at index: %d real: %"PRIu32" expect: %"PRIu32"\n", i, rx_buffer[i], count);
is_start = false;
count = 1;
fail_cnt++;
} }
} }
if (is_good_packet) { time += (float)TEST_I2S_MAX_FAIL_CNT / (float)sample_rate;
good_packet_cnt ++;
}
} }
unity_send_signal("Master Finished");
ESP_LOGI(TAG, "Send signal to terminate subtask"); ESP_LOGI(TAG, "Send signal to terminate subtask");
xTaskNotifyGive(subtask_handle); // notify subtask to exit xTaskNotifyGive(subtask_handle); // notify subtask to exit
@@ -164,6 +170,8 @@ static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_w
ESP_LOGI(TAG, "Deleting subtask"); ESP_LOGI(TAG, "Deleting subtask");
unity_utils_task_delete(subtask_handle); // delete subtask unity_utils_task_delete(subtask_handle); // delete subtask
ESP_LOGI(TAG, "I2S TDM master send stop");
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_tx_handle));
ESP_LOGI(TAG, "I2S TDM master receive stop"); ESP_LOGI(TAG, "I2S TDM master receive stop");
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle));
@@ -172,15 +180,9 @@ static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_w
ESP_LOGI(TAG, "Deleting i2s tx and rx channels"); ESP_LOGI(TAG, "Deleting i2s tx and rx channels");
TEST_ESP_OK(i2s_del_channel(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_del_channel(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_del_channel(i2s_tdm_tx_handle)); TEST_ESP_OK(i2s_del_channel(i2s_tdm_tx_handle));
TEST_ASSERT_TRUE_MESSAGE(read_ret == ESP_OK, "Master read timeout ");
uint32_t bad_packet_cnt = TEST_I2S_PACKET_COUNT - good_packet_cnt; TEST_ASSERT_TRUE_MESSAGE(fail_cnt < TEST_I2S_MAX_FAIL_CNT, "Broken data received ");
ESP_LOGI(TAG, "Total Packet: %d Good Packet: %"PRIu32" Bad Packet %"PRIu32, TEST_ASSERT_TRUE_MESSAGE(time < TEST_I2S_FRAME_TIMEOUT_SEC, "Waiting for valid data timeout ");
TEST_I2S_PACKET_COUNT, good_packet_cnt, bad_packet_cnt);
/* if the bad packet count exceed the threshold, test failed */
if (bad_packet_cnt > TEST_I2S_BAD_PACKET_THRESHOLD) {
ESP_LOGE(TAG, "Bad Packet count exceed the threshold %d, test failed", TEST_I2S_BAD_PACKET_THRESHOLD);
TEST_FAIL();
}
} }
static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_width, i2s_tdm_slot_mask_t slot_mask) static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_width, i2s_tdm_slot_mask_t slot_mask)
@@ -223,28 +225,23 @@ static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_wi
uint32_t *rx_buffer = malloc(rx_buffer_size); uint32_t *rx_buffer = malloc(rx_buffer_size);
TEST_ASSERT(rx_buffer); TEST_ASSERT(rx_buffer);
ESP_LOGI(TAG, "I2S TDM slave receive & send start");
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle));
TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle)); TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle));
uint32_t packet_cnt = 0; unity_send_signal("Slave Ready");
size_t bytes_read = 0, bytes_written = 0; unity_wait_for_signal("Master Ready");
while (packet_cnt < TEST_I2S_PACKET_COUNT) {
TEST_ESP_OK(i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size,
&bytes_read, portMAX_DELAY));
TEST_ASSERT_EQUAL(rx_buffer_size, bytes_read);
TEST_ESP_OK(i2s_channel_write(i2s_tdm_tx_handle, rx_buffer, rx_buffer_size, ESP_LOGI(TAG, "I2S TDM slave receive & send start");
&bytes_written, portMAX_DELAY)); size_t bytes_read = 0, bytes_written = 0;
TEST_ASSERT_EQUAL(rx_buffer_size, bytes_written); /* Loop until reading or writing failed, which indicates the master has finished and deleted the I2S peripheral */
if (rx_buffer[0]) { // packet is not empty while (true) {
packet_cnt ++; if (i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size, &bytes_read, 500) != ESP_OK) {
break;
}
if (i2s_channel_write(i2s_tdm_tx_handle, rx_buffer, rx_buffer_size, &bytes_written, 500) != ESP_OK) {
break;
} }
} }
unity_wait_for_signal("Master Finished");
/* Send empty buffers to flush DMA ringbuffer until timeout */
memset(rx_buffer, 0, rx_buffer_size);
while (i2s_channel_write(i2s_tdm_tx_handle, rx_buffer, rx_buffer_size,
&bytes_written, pdMS_TO_TICKS(200)) != ESP_ERR_TIMEOUT);
ESP_LOGI(TAG, "I2S TDM slave receive stop"); ESP_LOGI(TAG, "I2S TDM slave receive stop");
TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle));

View File

@@ -11,4 +11,4 @@ import pytest
2, 2,
], indirect=True) ], indirect=True)
def test_i2s_tdm_full_duplex(case_tester) -> None: # type: ignore def test_i2s_tdm_full_duplex(case_tester) -> None: # type: ignore
case_tester.run_all_cases() case_tester.run_all_cases(timeout=30)

View File

@@ -1,2 +1,3 @@
CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n CONFIG_ESP_TASK_WDT=n
CONFIG_I2S_ENABLE_DEBUG_LOG=y

View File

@@ -419,8 +419,9 @@ finish:
*/ */
static inline void i2s_ll_tx_start(i2s_dev_t *hw) static inline void i2s_ll_tx_start(i2s_dev_t *hw)
{ {
hw->tx_conf.tx_update = 0; // Have to update registers before start
hw->tx_conf.tx_update = 1; hw->tx_conf.tx_update = 1;
while (hw->tx_conf.tx_update);
hw->tx_conf.tx_start = 1; hw->tx_conf.tx_start = 1;
} }
@@ -431,8 +432,9 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
*/ */
static inline void i2s_ll_rx_start(i2s_dev_t *hw) static inline void i2s_ll_rx_start(i2s_dev_t *hw)
{ {
hw->rx_conf.rx_update = 0; // Have to update registers before start
hw->rx_conf.rx_update = 1; hw->rx_conf.rx_update = 1;
while (hw->rx_conf.rx_update);
hw->rx_conf.rx_start = 1; hw->rx_conf.rx_start = 1;
} }

View File

@@ -434,8 +434,9 @@ finish:
*/ */
static inline void i2s_ll_tx_start(i2s_dev_t *hw) static inline void i2s_ll_tx_start(i2s_dev_t *hw)
{ {
hw->tx_conf.tx_update = 0; // Have to update registers before start
hw->tx_conf.tx_update = 1; hw->tx_conf.tx_update = 1;
while (hw->tx_conf.tx_update);
hw->tx_conf.tx_start = 1; hw->tx_conf.tx_start = 1;
} }
@@ -446,8 +447,9 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
*/ */
static inline void i2s_ll_rx_start(i2s_dev_t *hw) static inline void i2s_ll_rx_start(i2s_dev_t *hw)
{ {
hw->rx_conf.rx_update = 0; // Have to update registers before start
hw->rx_conf.rx_update = 1; hw->rx_conf.rx_update = 1;
while (hw->rx_conf.rx_update);
hw->rx_conf.rx_start = 1; hw->rx_conf.rx_start = 1;
} }

View File

@@ -420,8 +420,9 @@ finish:
*/ */
static inline void i2s_ll_tx_start(i2s_dev_t *hw) static inline void i2s_ll_tx_start(i2s_dev_t *hw)
{ {
hw->tx_conf.tx_update = 0; // Have to update registers before start
hw->tx_conf.tx_update = 1; hw->tx_conf.tx_update = 1;
while (hw->tx_conf.tx_update);
hw->tx_conf.tx_start = 1; hw->tx_conf.tx_start = 1;
} }
@@ -432,8 +433,9 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
*/ */
static inline void i2s_ll_rx_start(i2s_dev_t *hw) static inline void i2s_ll_rx_start(i2s_dev_t *hw)
{ {
hw->rx_conf.rx_update = 0; // Have to update registers before start
hw->rx_conf.rx_update = 1; hw->rx_conf.rx_update = 1;
while (hw->rx_conf.rx_update);
hw->rx_conf.rx_start = 1; hw->rx_conf.rx_start = 1;
} }

View File

@@ -419,8 +419,9 @@ finish:
*/ */
static inline void i2s_ll_tx_start(i2s_dev_t *hw) static inline void i2s_ll_tx_start(i2s_dev_t *hw)
{ {
hw->tx_conf.tx_update = 0; // Have to update registers before start
hw->tx_conf.tx_update = 1; hw->tx_conf.tx_update = 1;
while (hw->tx_conf.tx_update);
hw->tx_conf.tx_start = 1; hw->tx_conf.tx_start = 1;
} }
@@ -431,8 +432,9 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
*/ */
static inline void i2s_ll_rx_start(i2s_dev_t *hw) static inline void i2s_ll_rx_start(i2s_dev_t *hw)
{ {
hw->rx_conf.rx_update = 0; // Have to update registers before start
hw->rx_conf.rx_update = 1; hw->rx_conf.rx_update = 1;
while (hw->rx_conf.rx_update);
hw->rx_conf.rx_start = 1; hw->rx_conf.rx_start = 1;
} }