This patch adds the testbench code for the MUX component. The testbench simulates a pipeline with a MUX comp connected to 1 source buffer and 2 sink buffers. The sink buffers are switched after every 100 periods resulting in the samples being written out to 2 separate output files.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- tune/mux/Makefile.am | 28 +++++ tune/mux/mux_test.c | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 tune/mux/Makefile.am create mode 100644 tune/mux/mux_test.c
diff --git a/tune/mux/Makefile.am b/tune/mux/Makefile.am new file mode 100644 index 0000000..17fdfef --- /dev/null +++ b/tune/mux/Makefile.am @@ -0,0 +1,28 @@ +AUTOMAKE_OPTIONS = subdir-objects + +SOF = ../../../sof.git +SRCADIR = $(SOF)/src/audio +INCDIR1 = ../c/include +INCDIR2 = $(SOF)/src/include +INCDIR3 = $(SOF)/src/arch/xtensa/include +INCDIR4 = $(SOF)/src/platform/baytrail/include +INCLUDE = -I$(INCDIR1) -I$(INCDIR2) -I$(INCDIR3) -I$(INCDIR4) -I$(SRCADIR) +DEFINE = -DCONFIG_BAYTRAIL -D__XTENSA__ -DMODULE_TEST + +AM_CPPFLAGS = $(INCLUDE) $(DEFINE) +AM_CFLAGS = -m32 -g -Wall +LDADD = -lm + +bin_PROGRAMS = mux_test + +mux_test_SOURCES = mux_test.c \ + ../../../sof.git/src/audio/mux.c \ + ../../../sof.git/src/include/reef/audio/mux.h \ + ../../../sof.git/src/audio/component.c \ + ../../../sof.git/src/audio/pipeline.c \ + ../../../sof.git/src/audio/buffer.c \ + ../../../sof.git/src/ipc/ipc.c \ + ../c/src/fileread.c \ + ../c/src/filewrite.c \ + ../c/src/common_test.h \ + ../c/src/common_test.c diff --git a/tune/mux/mux_test.c b/tune/mux/mux_test.c new file mode 100644 index 0000000..133a31d --- /dev/null +++ b/tune/mux/mux_test.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#include "common_test.h" + +/* Internal buffer */ +#define TEST_BENCH_DEADLINE 5000 /* 5 ms */ +#define TEST_BENCH_NCH 2 /* Stereo */ +#define TEST_BENCH_RATE_MAX 192000 /* Hz */ +#define TEST_BENCH_FRAME_SIZE (4 * TEST_BENCH_NCH) /* 4bytes x NCH */ +#define TEST_BENCH_PERIOD_FRAMES (TEST_BENCH_RATE_MAX*TEST_BENCH_DEADLINE/1e6) +#define INT_PERIOD_SIZE (TEST_BENCH_PERIOD_FRAMES * TEST_BENCH_FRAME_SIZE) +#define MUX_SWITCH_PERIODS 100 + +/* main firmware context */ +static struct reef reef; + +/* + * Test Pipeline (ID=0) + * + * +---> B(5) ---> Filewrite(2) + * Fileread(0) ---> B(4)---> Mux(1) ---| + * +---> B(6) ---> Filewrite(3) + */ + +#define FR_ID 0 +#define MUX_ID 1 /* scheduling component */ +#define FW_ID1 2 +#define FW_ID2 3 +#define PIPE_ID 0 +#define PIPE_COMP_ID 7 +#define SCHED_ID MUX_ID + + +/* components used in static pipeline */ +static struct sof_ipc_comp_fileread fileread_p0[] = { + SPIPE_FILEREAD(SPIPE_COMP(0, SOF_COMP_FILEREAD, sof_ipc_comp_fileread), + "mux_in.txt"), /* ID = 0 */ +}; + +static struct sof_ipc_comp_filewrite filewrite_p0[] = { + SPIPE_FILEWRITE(SPIPE_COMP(2, SOF_COMP_FILEWRITE, + sof_ipc_comp_filewrite), "mux_out_1.txt"), /* ID = 2 */ + SPIPE_FILEWRITE(SPIPE_COMP(3, SOF_COMP_FILEWRITE, + sof_ipc_comp_filewrite), "mux_out_2.txt"), /* ID = 3 */ +}; + +static struct sof_ipc_comp_mux mux_p0[] = { + SPIPE_MUX(SPIPE_COMP(1, SOF_COMP_MUX, sof_ipc_comp_mux)), /* ID = 1 */ +}; + +static struct scomps scomps_p0[] = { + SCOMP(fileread_p0), + SCOMP(mux_p0), + SCOMP(filewrite_p0), +}; + +/* + * Buffers used in static pipeline 2. + */ +static struct sof_ipc_buffer buffers_p0[] = { + SPIPE_BUFFER(4, INT_PERIOD_SIZE * 2), /* B(4) */ + SPIPE_BUFFER(5, INT_PERIOD_SIZE * 2), /* B(5) */ + SPIPE_BUFFER(6, INT_PERIOD_SIZE * 2), /* B(5) */ +}; + +/* pipeline 0 component/buffer connections */ +static struct sof_ipc_pipe_comp_connect connect_p0[] = { + SPIPE_COMP_CONNECT(0, 4), /* p(0): Fileread(0) -> B(4) */ + SPIPE_COMP_CONNECT(4, 1), /* p(0): B(3) -> Mux(1) */ + SPIPE_COMP_CONNECT(1, 5), /* p(0): Mux(1) -> B(5) */ + SPIPE_COMP_CONNECT(1, 6), /* p(0): Mux(1) -> B(6) */ + SPIPE_COMP_CONNECT(5, 2), /* p(0): B(5) -> Filewrite(2) */ + SPIPE_COMP_CONNECT(6, 3), /* p(0): B(5) -> Filewrite(3) */ +}; + +/* the static pipelines */ +static struct spipe spipe[] = { + SPIPE(scomps_p0, buffers_p0, connect_p0), +}; + +/* pipelines */ +struct sof_ipc_pipe_new pipeline[] = { + SPIPE_PIPE(PIPE_ID, 0, PIPE_COMP_ID, SCHED_ID, TEST_BENCH_DEADLINE, + TASK_PRI_LOW, TEST_BENCH_PERIOD_FRAMES), +}; + +/* pipeline setup, params and start */ +static int pipeline_calls(int i) +{ + int ret = 0; + struct pipeline *p; + struct ipc_comp_dev *pcm_dev; + + /* construct pipeline */ + ret = test_bench_pipeline_setup(&reef, &pipeline[i], &spipe[i]); + if (ret < 0) { + printf("error: pipeline setup\n"); + ret = -EINVAL; + goto out; + } + + /* set up pipeline params */ + ret = test_bench_pipeline_params(reef.ipc, TEST_BENCH_RATE_MAX, + TEST_BENCH_NCH, FR_ID, TEST_BENCH_DEADLINE, + pipeline[i].pipeline_id); + if (ret < 0) { + printf("error: pipeline params\n"); + ret = -EINVAL; + goto out; + } + + /* prepare pipeline and initialize default MUX config */ + pcm_dev = ipc_get_comp(reef.ipc, FR_ID); + p = pcm_dev->cd->pipeline; + ret = pipeline_prepare(p, pcm_dev->cd); + if (ret < 0) { + printf("error: pipeline prepare\n"); + ret = -EINVAL; + goto out; + } + + /* start pipeline */ + ret = pipeline_cmd(p, pcm_dev->cd, COMP_CMD_START, NULL); + if (ret < 0) { + printf("error: start pipeline cmd\n"); + ret = -EINVAL; + } + +out: + return ret; +} + +/* copy frames from input file until EOF */ +static int schedule_pipeline(struct ipc_comp_dev *pcm_dev, + struct pipeline *p, struct fileread_comp_data *frcd) +{ + int ret = 0, period = 0; + struct sof_ipc_ctrl_data *data; + + data = rzalloc(RZONE_RUNTIME, RFLAGS_NONE, + COMP_SIZE(struct sof_ipc_ctrl_data)); + if (data == NULL) { + printf("error: mem alloc ipc ctrl data\n"); + exit(EXIT_FAILURE); + } + + /* set route ctrl cmd data */ + data->num_elems = 1; + data->comp_id = MUX_ID; + data->cmd = SOF_CTRL_CMD_ROUTE; + data->type = SOF_CTRL_TYPE_VALUE_COMP_SET; + data->compv[0].index = 1; + data->compv[0].uvalue = 0; + + /* copy period frames from input file to output files + * switching output buffers every MUX_SWITCH_PERIODS periods + */ + while (frcd->frs.reached_eof == 0) { + if ((period % MUX_SWITCH_PERIODS == 0) && (period > 0)) { + /* pause pipeline + * stopping the pipeline results in the loss of 1 or 2 + * period samples + */ + ret = pipeline_cmd(p, pcm_dev->cd, COMP_CMD_PAUSE, + NULL); + if (ret < 0) { + printf("error: pause pipeline cmd\n"); + exit(EXIT_FAILURE); + } + + /*switch output buffer index */ + if (data->compv[0].uvalue == 0) + data->compv[0].uvalue = 1; + else + data->compv[0].uvalue = 0; + + /* re-route MUX */ + ret = pipeline_cmd(p, pcm_dev->cd, COMP_CMD_SET_VALUE, + data); + if (ret < 0) { + printf("error: mux route cmd\n"); + exit(EXIT_FAILURE); + } + + /* re-start pipeline */ + ret = pipeline_cmd(p, pcm_dev->cd, COMP_CMD_START, + NULL); + if (ret < 0) { + printf("error: start pipeline cmd\n"); + exit(EXIT_FAILURE); + } + } + + pipeline_schedule_copy(p, pcm_dev->cd); + period++; + } + + rfree(data); + + return ret; +} + +void usage(char *executable) +{ + fprintf(stderr, "Usage: %s <input.txt> <output1.txt> <output2.txt>\n", + executable); +} + +int main(int argc, char **argv) +{ + struct ipc_comp_dev *pcm_dev; + struct pipeline *p; + struct comp_dev *cd; + int ret = 0, samples_in, samples_out, i = 0; + struct fileread_comp_data *frcd; + struct filewrite_comp_data *fwcd1, *fwcd2; + clock_t tic, toc; + double c_realtime, t_exec; + + /* handle command line arguments*/ + if (argc > 3) { + switch (argc) { + case 4: + fileread_p0[0].fn = argv[1]; + filewrite_p0[0].fn = argv[2]; + filewrite_p0[1].fn = argv[3]; + break; + default: + usage(argv[0]); + return EXIT_SUCCESS; + } + } else { + usage(argv[0]); + return EXIT_SUCCESS; + } + + /* init components */ + sys_comp_init(); + sys_comp_fileread_init(); + sys_comp_filewrite_init(); + sys_comp_mux_init(); + + + + for (i = 0; i < ARRAY_SIZE(pipeline); i++) { + pipeline[i].frames_per_sched = (int) + (0.9999 + TEST_BENCH_RATE_MAX * + TEST_BENCH_DEADLINE / 1e6); + /* perform pipeline set up and start*/ + ret = pipeline_calls(i); + if (ret < 0) + exit(EXIT_FAILURE); + + /* Reduce print output */ + test_bench_disable_trace(); + + /* fileread and filewrite component data */ + pcm_dev = ipc_get_comp(reef.ipc, FW_ID1); + fwcd1 = comp_get_drvdata(pcm_dev->cd); + pcm_dev = ipc_get_comp(reef.ipc, FW_ID2); + fwcd2 = comp_get_drvdata(pcm_dev->cd); + pcm_dev = ipc_get_comp(reef.ipc, FR_ID); + frcd = comp_get_drvdata(pcm_dev->cd); + p = pcm_dev->cd->pipeline; + cd = pcm_dev->cd; + + tic = clock(); + + /* + * Run copy until EOF from fileread + */ + ret = schedule_pipeline(pcm_dev, p, frcd); + if (ret < 0) + exit(EXIT_FAILURE); + + toc = clock(); + + /* samples read in and written out */ + samples_in = frcd->frs.n; + samples_out = fwcd1->fws.n + fwcd2->fws.n; + + test_bench_enable_trace(); + + /* reset and free pipeline */ + ret = pipeline_reset(p, cd); + if (ret < 0) { + printf("error: pipeline reset\n"); + exit(EXIT_FAILURE); + } + pipeline_free(p); + + /* report test results */ + t_exec = (double) (toc - tic) / CLOCKS_PER_SEC; + c_realtime = (double) samples_out / TEST_BENCH_NCH / + TEST_BENCH_RATE_MAX / t_exec; + printf("Read %d input samples,\n", samples_in); + printf("wrote %d output samples,\n", samples_out); + printf("test execution time was %.2f us, %.2fx realtime.\n", + 1e3 * t_exec, c_realtime); + } + + return EXIT_SUCCESS; +}