zephyr/tests/drivers/audio/dmic_api/src/main.c
Sebastian Głąb 687f4ce244 tests: drivers: audio: dmic_api: Enable coverage calculation
When test is run with enabled coverage, additional RAM is required
to store coverage data.
Digital Microphone test is reserving most of the available RAM
to store audio samples. Thus, test fails to build with coverage
enabled due to RAM overflow.

Decrease size of the audio buffer when coverage is enabled.

Signed-off-by: Sebastian Głąb <sebastian.glab@nordicsemi.no>
2025-04-29 13:01:07 +02:00

305 lines
9.4 KiB
C

/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Based on DMIC driver sample, which is:
* Copyright (c) 2021 Nordic Semiconductor ASA
*/
#include <zephyr/kernel.h>
#include <zephyr/audio/dmic.h>
#include <zephyr/ztest.h>
static const struct device *dmic_dev = DEVICE_DT_GET(DT_ALIAS(dmic_dev));
#if DT_HAS_COMPAT_STATUS_OKAY(nxp_dmic)
#define PDM_CHANNELS 4 /* Two L/R pairs of channels */
#define SAMPLE_BIT_WIDTH 16
#define BYTES_PER_SAMPLE sizeof(int16_t)
#define SLAB_ALIGN 4
#define MAX_SAMPLE_RATE 48000
#elif DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_pdm)
#define PDM_CHANNELS 2
#define SAMPLE_BIT_WIDTH 16
#define BYTES_PER_SAMPLE sizeof(int16_t)
#define SLAB_ALIGN 4
#define MAX_SAMPLE_RATE 48000
#else
#error "Unsupported DMIC device"
#endif
/* Milliseconds to wait for a block to be read. */
#define READ_TIMEOUT 1000
/* Size of a block for 100 ms of audio data. */
#if defined(CONFIG_COVERAGE)
/* Use smaller buffer in coverage mode. */
#define BLOCK_SIZE(_sample_rate, _number_of_channels) \
(BYTES_PER_SAMPLE * (_sample_rate / 100) * _number_of_channels)
#else
#define BLOCK_SIZE(_sample_rate, _number_of_channels) \
(BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels)
#endif
/* Driver will allocate blocks from this slab to receive audio data into them.
* Application, after getting a given block from the driver and processing its
* data, needs to free that block.
*/
#define MAX_BLOCK_SIZE BLOCK_SIZE(MAX_SAMPLE_RATE, PDM_CHANNELS)
#define BLOCK_COUNT 8
K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, SLAB_ALIGN);
static struct pcm_stream_cfg pcm_stream = {
.pcm_width = SAMPLE_BIT_WIDTH,
.mem_slab = &mem_slab,
};
static struct dmic_cfg dmic_cfg = {
.io = {
/* These fields can be used to limit the PDM clock
* configurations that the driver is allowed to use
* to those supported by the microphone.
*/
.min_pdm_clk_freq = 1000000,
.max_pdm_clk_freq = 3500000,
.min_pdm_clk_dc = 40,
.max_pdm_clk_dc = 60,
},
.streams = &pcm_stream,
.channel = {
.req_num_streams = 1,
},
};
/* Verify that dmic_trigger fails when DMIC is not configured
* this test must run first, before DMIC has been configured
*/
ZTEST(dmic, test_0_start_fail)
{
int ret;
zassert_true(device_is_ready(dmic_dev), "DMIC device is not ready");
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
zassert_not_equal(ret, 0, "DMIC trigger should fail when DMIC is not configured");
}
static int do_pdm_transfer(const struct device *dmic,
struct dmic_cfg *cfg)
{
int ret;
void *buffer;
uint32_t size;
TC_PRINT("PCM output rate: %u, channels: %u\n",
cfg->streams[0].pcm_rate, cfg->channel.req_num_chan);
ret = dmic_configure(dmic, cfg);
if (ret < 0) {
TC_PRINT("DMIC configuration failed: %d\n", ret);
return ret;
}
/* Check that the driver is properly populating the "act*" fields */
zassert_equal(cfg->channel.act_num_chan,
cfg->channel.req_num_chan,
"DMIC configure should populate act_num_chan field");
zassert_equal(cfg->channel.act_chan_map_lo,
cfg->channel.req_chan_map_lo,
"DMIC configure should populate act_chan_map_lo field");
zassert_equal(cfg->channel.act_chan_map_hi,
cfg->channel.req_chan_map_hi,
"DMIC configure should populate act_chan_map_hi field");
ret = dmic_trigger(dmic, DMIC_TRIGGER_START);
if (ret < 0) {
TC_PRINT("DMIC start trigger failed: %d\n", ret);
return ret;
}
/* We read more than the total BLOCK_COUNT to insure the DMIC
* driver correctly reallocates memory slabs as it exhausts existing
* ones.
*/
for (int i = 0; i < (2 * BLOCK_COUNT); i++) {
ret = dmic_read(dmic, 0, &buffer, &size, READ_TIMEOUT);
if (ret < 0) {
TC_PRINT("DMIC read failed: %d\n", ret);
return ret;
}
TC_PRINT("%d - got buffer %p of %u bytes\n", i, buffer, size);
k_mem_slab_free(&mem_slab, buffer);
}
ret = dmic_trigger(dmic, DMIC_TRIGGER_STOP);
if (ret < 0) {
TC_PRINT("DMIC stop trigger failed: %d\n", ret);
return ret;
}
return 0;
}
/* Verify that the DMIC can transfer from a single channel */
ZTEST(dmic, test_single_channel)
{
dmic_cfg.channel.req_num_chan = 1;
dmic_cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
dmic_cfg.streams[0].block_size =
BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate,
dmic_cfg.channel.req_num_chan);
zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0,
"Single channel transfer failed");
}
/* Verify that the DMIC can transfer from a L/R channel pair */
ZTEST(dmic, test_stereo_channel)
{
dmic_cfg.channel.req_num_chan = 2;
dmic_cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) |
dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT);
dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
dmic_cfg.streams[0].block_size =
BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate,
dmic_cfg.channel.req_num_chan);
zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0,
"L/R channel transfer failed");
dmic_cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) |
dmic_build_channel_map(1, 0, PDM_CHAN_LEFT);
zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0,
"R/L channel transfer failed");
}
/* Test DMIC with maximum number of channels */
ZTEST(dmic, test_max_channel)
{
enum pdm_lr lr;
uint8_t pdm_hw_chan;
dmic_cfg.channel.req_num_chan = PDM_CHANNELS;
dmic_cfg.channel.req_chan_map_lo = 0;
dmic_cfg.channel.req_chan_map_hi = 0;
for (uint8_t i = 0; i < PDM_CHANNELS; i++) {
lr = ((i % 2) == 0) ? PDM_CHAN_LEFT : PDM_CHAN_RIGHT;
pdm_hw_chan = i >> 1;
if (i < 4) {
dmic_cfg.channel.req_chan_map_lo |=
dmic_build_channel_map(i, pdm_hw_chan, lr);
} else {
dmic_cfg.channel.req_chan_map_hi |=
dmic_build_channel_map(i, pdm_hw_chan, lr);
}
}
dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
dmic_cfg.streams[0].block_size =
BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate,
dmic_cfg.channel.req_num_chan);
zassert_equal(do_pdm_transfer(dmic_dev, &dmic_cfg), 0,
"Maximum channel transfer failed");
}
/* Test pausing and restarting a channel */
ZTEST(dmic, test_pause_restart)
{
int ret, i;
void *buffer;
uint32_t size;
dmic_cfg.channel.req_num_chan = 1;
dmic_cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
dmic_cfg.streams[0].block_size =
BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate,
dmic_cfg.channel.req_num_chan);
ret = dmic_configure(dmic_dev, &dmic_cfg);
zassert_equal(ret, 0, "DMIC configure failed");
/* Start the DMIC, and pause it immediately */
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
zassert_equal(ret, 0, "DMIC start failed");
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_PAUSE);
zassert_equal(ret, 0, "DMIC pause failed");
/* There may be some buffers in the DMIC queue, but a read
* should eventually time out while it is paused
*/
for (i = 0; i < (2 * BLOCK_COUNT); i++) {
ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
if (ret < 0) {
break;
}
k_mem_slab_free(&mem_slab, buffer);
}
zassert_not_equal(ret, 0, "DMIC is paused, reads should timeout");
TC_PRINT("Queue drained after %d reads\n", i);
/* Unpause the DMIC */
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_RELEASE);
zassert_equal(ret, 0, "DMIC release failed");
/* Reads should not timeout now */
for (i = 0; i < (2 * BLOCK_COUNT); i++) {
ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
if (ret < 0) {
break;
}
k_mem_slab_free(&mem_slab, buffer);
}
zassert_equal(ret, 0, "DMIC is active, reads should succeed");
TC_PRINT("%d reads completed\n", (2 * BLOCK_COUNT));
/* Stop the DMIC, and repeat the same tests */
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
zassert_equal(ret, 0, "DMIC stop failed");
/* Versus a pause, DMIC reads should immediately stop once DMIC times
* out
*/
ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
zassert_not_equal(ret, 0, "DMIC read should timeout when DMIC is stopped");
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
zassert_equal(ret, 0, "DMIC restart failed");
/* Reads should not timeout now */
for (i = 0; i < (2 * BLOCK_COUNT); i++) {
ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
if (ret < 0) {
break;
}
k_mem_slab_free(&mem_slab, buffer);
}
zassert_equal(ret, 0, "DMIC is active, reads should succeed");
TC_PRINT("%d reads completed\n", (2 * BLOCK_COUNT));
/* Test is over. Stop the DMIC */
ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
zassert_equal(ret, 0, "DMIC stop failed");
}
/* Verify that channel map without adjacent L/R pairs fails */
ZTEST(dmic, test_bad_pair)
{
int ret;
dmic_cfg.channel.req_num_chan = 2;
dmic_cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) |
dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT);
dmic_cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
dmic_cfg.streams[0].block_size =
BLOCK_SIZE(dmic_cfg.streams[0].pcm_rate,
dmic_cfg.channel.req_num_chan);
ret = dmic_configure(dmic_dev, &dmic_cfg);
zassert_not_equal(ret, 0, "DMIC configure should fail with "
"two of same channel in map");
dmic_cfg.channel.req_num_chan = 2;
dmic_cfg.channel.req_chan_map_lo =
dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT) |
dmic_build_channel_map(1, 1, PDM_CHAN_LEFT);
ret = dmic_configure(dmic_dev, &dmic_cfg);
zassert_not_equal(ret, 0, "DMIC configure should fail with "
"non adjacent channels in map");
}
ZTEST_SUITE(dmic, NULL, NULL, NULL, NULL, NULL);