tests: drivers: i2s_speed: Allow testing on nRF52840 DK and nRF5340 DK

This is a follow-up to commit 954dfa755b.

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 <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2021-08-09 10:50:14 +02:00 committed by Carles Cufí
commit 0f949caee1
8 changed files with 333 additions and 52 deletions

View file

@ -12,3 +12,25 @@ config I2S_TEST_SEPARATE_DEVICES
bool "Use two separate I2S ports for loopback" bool "Use two separate I2S ports for loopback"
help help
Use separate I2S ports for transmit and receive. 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.

View file

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

View file

@ -0,0 +1,11 @@
&uart1 {
status = "disabled";
};
&i2s0 {
status = "okay";
sck-pin = <36>;
lrck-pin = <35>;
sdout-pin = <34>;
sdin-pin = <33>;
};

View file

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

View file

@ -0,0 +1,11 @@
&uart1 {
status = "disabled";
};
&i2s0 {
status = "okay";
sck-pin = <37>;
lrck-pin = <36>;
sdout-pin = <33>;
sdin-pin = <32>;
};

View file

@ -13,12 +13,19 @@ void test_i2s_rx_transfer_configure(void);
void test_i2s_transfer_short(void); void test_i2s_transfer_short(void);
void test_i2s_transfer_long(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) void test_main(void)
{ {
ztest_test_suite(i2s_speed_test, ztest_test_suite(i2s_speed_test,
ztest_unit_test(test_i2s_tx_transfer_configure), ztest_unit_test(test_i2s_tx_transfer_configure),
ztest_unit_test(test_i2s_rx_transfer_configure), ztest_unit_test(test_i2s_rx_transfer_configure),
ztest_unit_test(test_i2s_transfer_short), 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); ztest_run_test_suite(i2s_speed_test);
} }

View file

@ -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(rx_0_mem_slab, BLOCK_SIZE, NUM_BLOCKS + 2, 32);
K_MEM_SLAB_DEFINE(tx_0_mem_slab, BLOCK_SIZE, NUM_BLOCKS, 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) static void fill_buf(int16_t *tx_block, int att)
{ {
for (int i = 0; i < SAMPLE_NO; i++) { 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) 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) { if (rx_block[2 * i] != data_l[i] >> att) {
TC_PRINT("Error: att %d: data_l mismatch at position " TC_PRINT("Error: att %d: data_l mismatch at position "
"%d, expected %d, actual %d\n", "%d, expected %d, actual %d\n",
@ -84,58 +110,80 @@ static int verify_buf(int16_t *rx_block, int att)
#define TIMEOUT 2000 #define TIMEOUT 2000
#define FRAME_CLK_FREQ 44000 #define FRAME_CLK_FREQ 44000
/** Configure I2S TX transfer. */ static int configure_stream(const struct device *dev_i2s, enum i2s_dir dir)
void test_i2s_tx_transfer_configure(void)
{ {
const struct device *dev_i2s;
struct i2s_config i2s_cfg;
int ret; int ret;
struct i2s_config i2s_cfg;
dev_i2s = device_get_binding(I2S_DEV_NAME_TX);
zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_TX " not found");
/* Configure */
i2s_cfg.word_size = 16U; i2s_cfg.word_size = 16U;
i2s_cfg.channels = 2U; i2s_cfg.channels = 2U;
i2s_cfg.format = I2S_FMT_DATA_FORMAT_I2S; 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.frame_clk_freq = FRAME_CLK_FREQ;
i2s_cfg.block_size = BLOCK_SIZE; i2s_cfg.block_size = BLOCK_SIZE;
i2s_cfg.mem_slab = &tx_0_mem_slab;
i2s_cfg.timeout = TIMEOUT; i2s_cfg.timeout = TIMEOUT;
i2s_cfg.options = I2S_OPT_LOOPBACK;
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); ret = i2s_configure(dev_i2s, I2S_DIR_TX, &i2s_cfg);
zassert_equal(ret, 0, "Failed to configure I2S TX stream"); 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. */ /** Configure I2S RX transfer. */
void test_i2s_rx_transfer_configure(void) void test_i2s_rx_transfer_configure(void)
{ {
const struct device *dev_i2s;
struct i2s_config i2s_cfg;
int ret; int ret;
dev_i2s = device_get_binding(I2S_DEV_NAME_RX); dev_i2s_rx = device_get_binding(I2S_DEV_NAME_RX);
zassert_not_null(dev_i2s, "device " I2S_DEV_NAME_RX " not found"); zassert_not_null(dev_i2s_rx, "device " I2S_DEV_NAME_RX " not found");
/* Configure */ ret = configure_stream(dev_i2s_rx, I2S_DIR_RX);
zassert_equal(ret, TC_PASS, NULL);
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");
} }
/** @brief Short I2S transfer. /** @brief Short I2S transfer.
@ -148,19 +196,17 @@ void test_i2s_rx_transfer_configure(void)
*/ */
void test_i2s_transfer_short(void) void test_i2s_transfer_short(void)
{ {
const struct device *dev_i2s_rx; if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) {
const struct device *dev_i2s_tx; TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n");
ztest_test_skip();
return;
}
void *rx_block[3]; void *rx_block[3];
void *tx_block; void *tx_block;
size_t rx_size; size_t rx_size;
int ret; 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 */ /* Prefill TX queue */
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block, K_FOREVER); 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) void test_i2s_transfer_long(void)
{ {
const struct device *dev_i2s_rx; if (IS_ENABLED(CONFIG_I2S_TEST_USE_I2S_DIR_BOTH)) {
const struct device *dev_i2s_tx; TC_PRINT("RX/TX transfer requires use of I2S_DIR_BOTH.\n");
ztest_test_skip();
return;
}
void *rx_block[NUM_BLOCKS]; void *rx_block[NUM_BLOCKS];
void *tx_block[NUM_BLOCKS]; void *tx_block[NUM_BLOCKS];
size_t rx_size; size_t rx_size;
@ -238,12 +288,6 @@ void test_i2s_transfer_long(void)
int num_verified; int num_verified;
int ret; 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 */ /* Prepare TX data blocks */
for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) { for (tx_idx = 0; tx_idx < NUM_BLOCKS; tx_idx++) {
ret = k_mem_slab_alloc(&tx_0_mem_slab, &tx_block[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"); 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");
}

View file

@ -2,3 +2,11 @@ tests:
drivers.i2s.speed: drivers.i2s.speed:
depends_on: i2s depends_on: i2s
tags: drivers 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