From 0f949caee12441dd4996017210d2d05971b3cf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Mon, 9 Aug 2021 10:50:14 +0200 Subject: [PATCH] tests: drivers: i2s_speed: Allow testing on nRF52840 DK and nRF5340 DK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a follow-up to commit 954dfa755b7a18b7b15c8233a879167cfd83b81b. Apply adjustments made in the i2s_api test to the i2s_speed one so that it can also be executed successfully on the nRF DK boards. Signed-off-by: Andrzej Głąbek --- tests/drivers/i2s/i2s_speed/Kconfig | 22 ++ .../i2s_speed/boards/nrf52840dk_nrf52840.conf | 4 + .../boards/nrf52840dk_nrf52840.overlay | 11 + .../boards/nrf5340dk_nrf5340_cpuapp.conf | 4 + .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 11 + tests/drivers/i2s/i2s_speed/src/main.c | 9 +- .../i2s/i2s_speed/src/test_i2s_speed.c | 316 +++++++++++++++--- tests/drivers/i2s/i2s_speed/testcase.yaml | 8 + 8 files changed, 333 insertions(+), 52 deletions(-) create mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.conf create mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.overlay create mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.conf create mode 100644 tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.overlay diff --git a/tests/drivers/i2s/i2s_speed/Kconfig b/tests/drivers/i2s/i2s_speed/Kconfig index 1af3d1b6de1..04e0892c1cf 100644 --- a/tests/drivers/i2s/i2s_speed/Kconfig +++ b/tests/drivers/i2s/i2s_speed/Kconfig @@ -12,3 +12,25 @@ config I2S_TEST_SEPARATE_DEVICES bool "Use two separate I2S ports for loopback" help Use separate I2S ports for transmit and receive. + +config I2S_TEST_USE_I2S_DIR_BOTH + bool "Use I2S_DIR_BOTH value to perform RX/TX transfers" + help + Use the I2S_DIR_BOTH enumeration value to trigger commands in test + cases involving both reception and transmission. Use of this option + is essential for devices that cannot independently start and stop + the RX and TX streams. + +config I2S_TEST_USE_GPIO_LOOPBACK + bool "Use GPIO loopback" + help + Use wiring between the data-out and data-in pins for looping back + data. This option is intended to be used for devices that do not + provide the internal loopback functionality. + +config I2S_TEST_ALLOWED_DATA_OFFSET + int "Allowed offset in received data" + default 0 + help + Maximum allowed offset between sent and received samples. Non-zero + value of this option may be needed when GPIO loopback is used. diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.conf b/tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 00000000000..4daa33ce949 --- /dev/null +++ b/tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,4 @@ +CONFIG_I2S_TEST_USE_I2S_DIR_BOTH=y +CONFIG_I2S_TEST_USE_GPIO_LOOPBACK=y +CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET=2 +CONFIG_I2S_LOG_LEVEL_INF=y diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.overlay b/tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 00000000000..43a5ce45dd0 --- /dev/null +++ b/tests/drivers/i2s/i2s_speed/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,11 @@ +&uart1 { + status = "disabled"; +}; + +&i2s0 { + status = "okay"; + sck-pin = <36>; + lrck-pin = <35>; + sdout-pin = <34>; + sdin-pin = <33>; +}; diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.conf b/tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 00000000000..4daa33ce949 --- /dev/null +++ b/tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,4 @@ +CONFIG_I2S_TEST_USE_I2S_DIR_BOTH=y +CONFIG_I2S_TEST_USE_GPIO_LOOPBACK=y +CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET=2 +CONFIG_I2S_LOG_LEVEL_INF=y diff --git a/tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.overlay b/tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 00000000000..ceeba511761 --- /dev/null +++ b/tests/drivers/i2s/i2s_speed/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,11 @@ +&uart1 { + status = "disabled"; +}; + +&i2s0 { + status = "okay"; + sck-pin = <37>; + lrck-pin = <36>; + sdout-pin = <33>; + sdin-pin = <32>; +}; diff --git a/tests/drivers/i2s/i2s_speed/src/main.c b/tests/drivers/i2s/i2s_speed/src/main.c index 02d763f2a1f..1905f9e93a9 100644 --- a/tests/drivers/i2s/i2s_speed/src/main.c +++ b/tests/drivers/i2s/i2s_speed/src/main.c @@ -13,12 +13,19 @@ void test_i2s_rx_transfer_configure(void); void test_i2s_transfer_short(void); void test_i2s_transfer_long(void); +void test_i2s_dir_both_transfer_configure(void); +void test_i2s_dir_both_transfer_short(void); +void test_i2s_dir_both_transfer_long(void); + void test_main(void) { ztest_test_suite(i2s_speed_test, ztest_unit_test(test_i2s_tx_transfer_configure), ztest_unit_test(test_i2s_rx_transfer_configure), ztest_unit_test(test_i2s_transfer_short), - ztest_unit_test(test_i2s_transfer_long)); + ztest_unit_test(test_i2s_transfer_long), + ztest_unit_test(test_i2s_dir_both_transfer_configure), + ztest_unit_test(test_i2s_dir_both_transfer_short), + ztest_unit_test(test_i2s_dir_both_transfer_long)); ztest_run_test_suite(i2s_speed_test); } diff --git a/tests/drivers/i2s/i2s_speed/src/test_i2s_speed.c b/tests/drivers/i2s/i2s_speed/src/test_i2s_speed.c index cde50a5cf6b..7e463d08fb5 100644 --- a/tests/drivers/i2s/i2s_speed/src/test_i2s_speed.c +++ b/tests/drivers/i2s/i2s_speed/src/test_i2s_speed.c @@ -53,6 +53,11 @@ static int16_t data_r[SAMPLE_NO] = { K_MEM_SLAB_DEFINE(rx_0_mem_slab, BLOCK_SIZE, NUM_BLOCKS + 2, 32); K_MEM_SLAB_DEFINE(tx_0_mem_slab, BLOCK_SIZE, NUM_BLOCKS, 32); +static const struct device *dev_i2s_rx; +static const struct device *dev_i2s_tx; +static const struct device *dev_i2s_rxtx; +static bool dir_both_supported; + static void fill_buf(int16_t *tx_block, int att) { for (int i = 0; i < SAMPLE_NO; i++) { @@ -63,7 +68,28 @@ static void fill_buf(int16_t *tx_block, int att) static int verify_buf(int16_t *rx_block, int att) { - for (int i = 0; i < SAMPLE_NO; i++) { + int sample_no = SAMPLE_NO; + +#if (CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET > 0) + static ZTEST_DMEM int offset = -1; + + if (offset < 0) { + do { + ++offset; + if (offset > CONFIG_I2S_TEST_ALLOWED_DATA_OFFSET) { + TC_PRINT("Allowed data offset exceeded\n"); + return -TC_FAIL; + } + } while (rx_block[2 * offset] != data_l[0] >> att); + + TC_PRINT("Using data offset: %d\n", offset); + } + + rx_block += 2 * offset; + sample_no -= offset; +#endif + + for (int i = 0; i < sample_no; i++) { if (rx_block[2 * i] != data_l[i] >> att) { TC_PRINT("Error: att %d: data_l mismatch at position " "%d, expected %d, actual %d\n", @@ -84,58 +110,80 @@ static int verify_buf(int16_t *rx_block, int att) #define TIMEOUT 2000 #define FRAME_CLK_FREQ 44000 -/** Configure I2S TX transfer. */ -void test_i2s_tx_transfer_configure(void) +static int configure_stream(const struct device *dev_i2s, enum i2s_dir dir) { - const struct device *dev_i2s; - struct i2s_config i2s_cfg; int ret; - - dev_i2s = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_TX " not found"); - - /* Configure */ + struct i2s_config i2s_cfg; i2s_cfg.word_size = 16U; i2s_cfg.channels = 2U; i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Transmit port as Master */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER; i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &tx_0_mem_slab; i2s_cfg.timeout = TIMEOUT; - i2s_cfg.options = I2S_OPT_LOOPBACK; - ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S TX stream"); + if (dir == I2S_DIR_TX) { + /* Configure the Transmit port as Master */ + i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER + | I2S_OPT_BIT_CLK_MASTER; + } else if (dir == I2S_DIR_RX) { + /* Configure the Receive port as Slave */ + i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE + | I2S_OPT_BIT_CLK_SLAVE; + } else { /* dir == I2S_DIR_BOTH */ + i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER + | I2S_OPT_BIT_CLK_MASTER; + } + + if (!IS_ENABLED(CONFIG_I2S_TEST_USE_GPIO_LOOPBACK)) { + i2s_cfg.options |= I2S_OPT_LOOPBACK; + } + + if (dir == I2S_DIR_TX || dir == I2S_DIR_BOTH) { + i2s_cfg.mem_slab = &tx_0_mem_slab; + ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg); + if (ret < 0) { + TC_PRINT("Failed to configure I2S TX stream (%d)\n", + ret); + return -TC_FAIL; + } + } + + if (dir == I2S_DIR_RX || dir == I2S_DIR_BOTH) { + i2s_cfg.mem_slab = &rx_0_mem_slab; + ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg); + if (ret < 0) { + TC_PRINT("Failed to configure I2S RX stream (%d)\n", + ret); + return -TC_FAIL; + } + } + + return TC_PASS; +} + +/** Configure I2S TX transfer. */ +void test_i2s_tx_transfer_configure(void) +{ + int ret; + + dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); + zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); + + ret = configure_stream(dev_i2s_tx, I2S_DIR_TX); + zassert_equal(ret, TC_PASS, NULL); } /** Configure I2S RX transfer. */ void test_i2s_rx_transfer_configure(void) { - const struct device *dev_i2s; - struct i2s_config i2s_cfg; int ret; - dev_i2s = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); + dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); + zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - /* Configure */ - - i2s_cfg.word_size = 16U; - i2s_cfg.channels = 2U; - i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; - /* Configure the Receive port as Slave */ - i2s_cfg.options = I2S_OPT_FRAME_CLK_SLAVE | I2S_OPT_BIT_CLK_SLAVE; - i2s_cfg.frame_clk_freq = FRAME_CLK_FREQ; - i2s_cfg.block_size = BLOCK_SIZE; - i2s_cfg.mem_slab = &rx_0_mem_slab; - i2s_cfg.timeout = TIMEOUT; - i2s_cfg.options = I2S_OPT_LOOPBACK; - - ret = i2s_configure(dev_i2s, I2S_DIR_RX, &i2s_cfg); - zassert_equal(ret, 0, "Failed to configure I2S RX stream"); + ret = configure_stream(dev_i2s_rx, I2S_DIR_RX); + zassert_equal(ret, TC_PASS, NULL); } /** @brief Short I2S transfer. @@ -148,19 +196,17 @@ void test_i2s_rx_transfer_configure(void) */ void test_i2s_transfer_short(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + void *rx_block[3]; void *tx_block; size_t rx_size; int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prefill TX queue */ for (int i = 0; i < 3; i++) { ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block, K_FOREVER); @@ -228,8 +274,12 @@ void test_i2s_transfer_short(void) */ void test_i2s_transfer_long(void) { - const struct device *dev_i2s_rx; - const struct device *dev_i2s_tx; + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n"); + ztest_test_skip(); + return; + } + void *rx_block[NUM_BLOCKS]; void *tx_block[NUM_BLOCKS]; size_t rx_size; @@ -238,12 +288,6 @@ void test_i2s_transfer_long(void) int num_verified; int ret; - dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX); - zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found"); - - dev_i2s_tx = device_get_binding(I2S_DEV_NAME_TX); - zassert_not_null(dev_i2s_tx, "device " I2S_DEV_NAME_TX " not found"); - /* Prepare TX data blocks */ for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) { ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block[tx_idx], @@ -310,3 +354,173 @@ void test_i2s_transfer_long(void) } zassert_equal(num_verified, NUM_BLOCKS, "Invalid RX blocks received"); } + +void test_i2s_dir_both_transfer_configure(void) +{ + int ret; + + dev_i2s_rxtx = device_get_binding(I2S_DEV_NAME_RX); + zassert_not_null(dev_i2s_rxtx, "device " I2S_DEV_NAME_RX " not found"); + + ret = configure_stream(dev_i2s_rxtx, I2S_DIR_BOTH); + zassert_equal(ret, TC_PASS, NULL); + + /* Check if the tested driver supports the I2S_DIR_BOTH value. + * Use the DROP trigger for this, as in the current state of the driver + * (READY, both TX and RX queues empty) it is actually a no-op. + */ + ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_DROP); + dir_both_supported = (ret == 0); + + if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) { + zassert_true(dir_both_supported, + "I2S_DIR_BOTH value is supposed to be supported."); + } +} + +/** @brief Short I2S transfer using I2S_DIR_BOTH. + * + * - START trigger starts both the transmission and reception. + * - Sending / receiving a short sequence of data returns success. + * - DRAIN trigger empties the transmit queue and stops both streams. + */ +void test_i2s_dir_both_transfer_short(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + void *rx_block[3]; + void *tx_block; + size_t rx_size; + int ret; + + /* Prefill TX queue */ + for (int i = 0; i < 3; i++) { + ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block, K_FOREVER); + zassert_equal(ret, 0, NULL); + fill_buf((uint16_t *)tx_block, i); + + ret = i2s_write(dev_i2s_rxtx, tx_block, BLOCK_SIZE); + zassert_equal(ret, 0, NULL); + + TC_PRINT("%d->OK\n", i); + } + + ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + /* All data written, drain TX queue and stop both streams. */ + ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + ret = i2s_read(dev_i2s_rxtx, &rx_block[0], &rx_size); + zassert_equal(ret, 0, NULL); + zassert_equal(rx_size, BLOCK_SIZE, NULL); + + ret = i2s_read(dev_i2s_rxtx, &rx_block[1], &rx_size); + zassert_equal(ret, 0, NULL); + zassert_equal(rx_size, BLOCK_SIZE, NULL); + + ret = i2s_read(dev_i2s_rxtx, &rx_block[2], &rx_size); + zassert_equal(ret, 0, NULL); + zassert_equal(rx_size, BLOCK_SIZE, NULL); + + /* Verify received data */ + ret = verify_buf((uint16_t *)rx_block[0], 0); + zassert_equal(ret, 0, NULL); + k_mem_slab_free(&rx_0_mem_slab, &rx_block[0]); + TC_PRINT("%d<-OK\n", 1); + + ret = verify_buf((uint16_t *)rx_block[1], 1); + zassert_equal(ret, 0, NULL); + k_mem_slab_free(&rx_0_mem_slab, &rx_block[1]); + TC_PRINT("%d<-OK\n", 2); + + ret = verify_buf((uint16_t *)rx_block[2], 2); + zassert_equal(ret, 0, NULL); + k_mem_slab_free(&rx_0_mem_slab, &rx_block[2]); + TC_PRINT("%d<-OK\n", 3); +} + +/** @brief Long I2S transfer using I2S_DIR_BOTH. + * + * - START trigger starts both the transmission and reception. + * - Sending / receiving a long sequence of data returns success. + * - DRAIN trigger empties the transmit queue and stops both streams. + */ +void test_i2s_dir_both_transfer_long(void) +{ + if (!dir_both_supported) { + TC_PRINT("I2S_DIR_BOTH value is not supported.\n"); + ztest_test_skip(); + return; + } + + void *rx_block[NUM_BLOCKS]; + void *tx_block[NUM_BLOCKS]; + size_t rx_size; + int tx_idx; + int rx_idx = 0; + int num_verified; + int ret; + + /* Prepare TX data blocks */ + for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) { + ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block[tx_idx], + K_FOREVER); + zassert_equal(ret, 0, NULL); + fill_buf((uint16_t *)tx_block[tx_idx], tx_idx % 3); + } + + tx_idx = 0; + + /* Prefill TX queue */ + ret = i2s_write(dev_i2s_rxtx, tx_block[tx_idx++], BLOCK_SIZE); + zassert_equal(ret, 0, NULL); + + ret = i2s_write(dev_i2s_rxtx, tx_block[tx_idx++], BLOCK_SIZE); + zassert_equal(ret, 0, NULL); + + ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_START); + zassert_equal(ret, 0, "RX/TX START trigger failed\n"); + + for (; tx_idx < NUM_BLOCKS; ) { + ret = i2s_write(dev_i2s_rxtx, tx_block[tx_idx++], BLOCK_SIZE); + zassert_equal(ret, 0, NULL); + + ret = i2s_read(dev_i2s_rxtx, &rx_block[rx_idx++], &rx_size); + zassert_equal(ret, 0, NULL); + zassert_equal(rx_size, BLOCK_SIZE, NULL); + } + + /* All data written, drain TX queue and stop both streams. */ + ret = i2s_trigger(dev_i2s_rxtx, I2S_DIR_BOTH, I2S_TRIGGER_DRAIN); + zassert_equal(ret, 0, "RX/TX DRAIN trigger failed"); + + ret = i2s_read(dev_i2s_rxtx, &rx_block[rx_idx++], &rx_size); + zassert_equal(ret, 0, NULL); + zassert_equal(rx_size, BLOCK_SIZE, NULL); + + ret = i2s_read(dev_i2s_rxtx, &rx_block[rx_idx++], &rx_size); + zassert_equal(ret, 0, NULL); + zassert_equal(rx_size, BLOCK_SIZE, NULL); + + TC_PRINT("%d TX blocks sent\n", tx_idx); + TC_PRINT("%d RX blocks received\n", rx_idx); + + /* Verify received data */ + num_verified = 0; + for (rx_idx = 0; rx_idx < NUM_BLOCKS; rx_idx++) { + ret = verify_buf((uint16_t *)rx_block[rx_idx], rx_idx % 3); + if (ret != 0) { + TC_PRINT("%d RX block invalid\n", rx_idx); + } else { + num_verified++; + } + k_mem_slab_free(&rx_0_mem_slab, &rx_block[rx_idx]); + } + zassert_equal(num_verified, NUM_BLOCKS, "Invalid RX blocks received"); +} diff --git a/tests/drivers/i2s/i2s_speed/testcase.yaml b/tests/drivers/i2s/i2s_speed/testcase.yaml index fa73cf6e42a..9d63b7fabdd 100644 --- a/tests/drivers/i2s/i2s_speed/testcase.yaml +++ b/tests/drivers/i2s/i2s_speed/testcase.yaml @@ -2,3 +2,11 @@ tests: drivers.i2s.speed: depends_on: i2s tags: drivers i2s + filter: not CONFIG_I2S_TEST_USE_GPIO_LOOPBACK + drivers.i2s.speed.gpio_loopback: + depends_on: i2s + tags: drivers i2s + filter: CONFIG_I2S_TEST_USE_GPIO_LOOPBACK + harness: ztest + harness_config: + fixture: gpio_loopback