[Sound-open-firmware] [PATCH v4 1/5] Makefile and configure changes to add testbench
This patch adds the changes to configure.ac and Makefile to add the testbench directories
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- Makefile.am | 2 +- configure.ac | 5 +++++ tune/Makefile.am | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tune/Makefile.am
diff --git a/Makefile.am b/Makefile.am index ea01286..4f359ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS = rimage rmbox topology +SUBDIRS = rimage rmbox topology tune diff --git a/configure.ac b/configure.ac index 87c7519..d5e4f48 100644 --- a/configure.ac +++ b/configure.ac @@ -17,6 +17,11 @@ AC_OUTPUT([ rimage/Makefile rmbox/Makefile topology/Makefile + tune/Makefile + tune/src/Makefile + tune/mux/Makefile + tune/passthrough/Makefile + tune/eq/Makefile ])
echo " diff --git a/tune/Makefile.am b/tune/Makefile.am new file mode 100644 index 0000000..b2dc2d9 --- /dev/null +++ b/tune/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src eq mux passthrough
This patch does the following:
Fixes the bug in fileread component's nread count when EOF is reached.
Updates the copy method in the components to return the number of frames read/written
Updates the buffer produce and consume calls with the number of bytes read/written
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- tune/c/include/common_test.h | 185 +++++++++++++++++++++++++++++ tune/c/src/fileread.c | 266 ++++++++++++++++++++++++++++++++++++++++++ tune/c/src/filewrite.c | 271 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 722 insertions(+) create mode 100644 tune/c/include/common_test.h create mode 100644 tune/c/src/fileread.c create mode 100644 tune/c/src/filewrite.c
diff --git a/tune/c/include/common_test.h b/tune/c/include/common_test.h new file mode 100644 index 0000000..73d6fe5 --- /dev/null +++ b/tune/c/include/common_test.h @@ -0,0 +1,185 @@ +#ifndef _COMMON_TEST_H +#define _COMMON_TEST_H + +/* + * 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + */ + +#include <stdint.h> +#include <stddef.h> +#include <time.h> +#include <stdio.h> + +#include <reef/ipc.h> +#include <reef/audio/pipeline.h> +#include <reef/audio/format.h> + +/* + * Static Buffer Convenience Constructors. + */ +#define SPIPE_BUFFER(bid, bsize) \ + {.comp.id = bid, .size = bsize} +#define SPIPE_COMP_CONNECT(source, sink) \ + {.source_id = source, .sink_id = sink} + +/* + * Static Component Convenience Constructors. + */ +#define SCONFIG {.frame_fmt = SOF_IPC_FRAME_S32_LE, .periods_sink = 2,\ + .periods_source = 2, .preload_count = 1} +#define SPIPE_COMP(cid, ctype, csize) \ + {.id = cid, .type = ctype, .hdr.size = sizeof(struct csize)} +#define SPIPE_HOST(scomp, hno_irq, hdmac, hchan, hconfig) \ + {.comp = scomp, .no_irq = hno_irq, .dmac_id = hdmac,\ + .dmac_chan = hchan, .dmac_config = hconfig,\ + .config = SCONFIG} +#define SPIPE_DAI(scomp, ddai_type, ddai_idx, ddmac, dchan, dconfig) \ + {.comp = scomp, .type = ddai_type, .index = ddai_idx, \ + .dmac_id = ddmac, .dmac_chan = dchan, .dmac_config = dconfig, \ + .config = SCONFIG} +#define SPIPE_VOL(scomp, vmin, vmax) \ + {.comp = scomp, .min_value = vmin, .max_value = vmax,\ + .config = SCONFIG} +#define SPIPE_MIX(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_SRC(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_EQ_FIR(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_EQ_IIR(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_TONE(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_FILEREAD(scomp, fn_in) {.comp = scomp, .config = SCONFIG, .fn = fn_in} +#define SPIPE_FILEWRITE(scomp, fn_out) {.comp = scomp, .config = SCONFIG, .fn = fn_out} + + +/* + * Static Pipeline Convenience Constructor + */ +#define SPIPE_PIPE(pid, pcore, pcid, psid, pdeadline, ppriority, pframes) \ + {.pipeline_id = pid, .core = pcore, .deadline = pdeadline, \ + .priority = ppriority, .comp_id = pcid, .sched_id = psid, \ + .frames_per_sched = pframes} +#define SPIPE_PIPE_CONNECT(psource, bsource, bid, psink, bsink) \ + {.pipeline_source_id = psource, .comp_source_id = bsource, \ + .buffer_id = bid, .pipeline_sink_id = psink, .comp_sink_id = bsink} + +/* + * Static pipeline container and constructor + */ + +#define SCOMP(ccomps) \ + {.comps = (struct sof_ipc_comp *)ccomps, .num_comps \ + = ARRAY_SIZE(ccomps)} + + +#define SPIPE(ncomp, sbuffer, sconnect) \ + {.scomps = ncomp, .num_scomps = ARRAY_SIZE(ncomp), \ + .buffer = sbuffer, .num_buffers = ARRAY_SIZE(sbuffer), \ + .connect = sconnect, .num_connections = ARRAY_SIZE(sconnect)} + +#define FILEREAD_FN_MAXLENGTH 256 +#define FILEWRITE_FN_MAXLENGTH 256 + +struct scomps { + struct sof_ipc_comp *comps; + uint32_t num_comps; +}; + +struct spipe { + struct scomps *scomps; + uint32_t num_scomps; + struct sof_ipc_buffer *buffer; + uint32_t num_buffers; + struct sof_ipc_pipe_comp_connect *connect; + uint32_t num_connections; +}; + +struct fileread_state { + char fn[FILEREAD_FN_MAXLENGTH]; + FILE *fh; + int reached_eof; + int n; +}; + +struct fileread_comp_data { + uint32_t period_bytes; + uint32_t channels; + uint32_t frame_bytes; + uint32_t rate; + struct fileread_state frs; + int (*fileread_func)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames); + +}; + +struct filewrite_state { + char fn[FILEWRITE_FN_MAXLENGTH]; + FILE *fh; + int write_fail; + int n; +}; + +struct filewrite_comp_data { + uint32_t period_bytes; + uint32_t channels; + uint32_t frame_bytes; + uint32_t rate; + struct filewrite_state fws; + int (*filewrite_func)(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames); + +}; + +/* Test bench component IPC */ + +struct sof_ipc_comp_fileread { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + char *fn; +} __attribute__((packed)); + +struct sof_ipc_comp_filewrite { + struct sof_ipc_comp comp; + struct sof_ipc_comp_config config; + char *fn; +} __attribute__((packed)); + +int scheduler_init(struct reef *reef); +void sys_comp_fileread_init(void); +void sys_comp_filewrite_init(void); + +int test_bench_pipeline_setup(struct reef *reef, + struct sof_ipc_pipe_new pipeline[], struct spipe spipe[]); + +int test_bench_pipeline_params_unmute_start(struct ipc *ipc, + int32_t fs, int nch, int deadline, int sched_id, int pipe_id); + +void test_bench_enable_trace(void); +void test_bench_disable_trace(void); + +#endif diff --git a/tune/c/src/fileread.c b/tune/c/src/fileread.c new file mode 100644 index 0000000..e771e20 --- /dev/null +++ b/tune/c/src/fileread.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2016, 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <reef/reef.h> +#include <reef/lock.h> +#include <reef/list.h> +#include <reef/stream.h> +#include <reef/work.h> +#include <reef/clock.h> +#include <reef/audio/component.h> +#include <reef/audio/format.h> +#include <reef/audio/pipeline.h> +#include "common_test.h" + +/* fileread component private data */ + +static int fileread_s32_default(struct comp_dev *dev, struct comp_buffer *sink, + struct comp_buffer *source, uint32_t frames) +{ + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int32_t *dest = (int32_t *) sink->w_ptr; + int i, n, n_wrap_dest, ret; + int nch = dev->params.channels; + int nread = 0; + + n = frames * nch; + while (n > 0) { + n_wrap_dest = (int32_t *) sink->end_addr - dest; + if (n < n_wrap_dest) { + /* No circular wrap need */ + while (n > 0) { + n -= nch; + i = 0; + for (i = 0; i < nch; i++) { + ret = fscanf(cd->frs.fh, "%d", dest); + if (ret == EOF) + goto read_eof; + nread++; + dest++; + } + } + } else { + /* Process until wrap */ + while (n_wrap_dest > 0) { + n -= nch; + n_wrap_dest -= nch; + for (i = 0; i < nch; i++) { + ret = fscanf(cd->frs.fh, "%d", dest); + nread++; + dest++; + if (ret == EOF) + goto read_eof; + } + } + /* No need to check if past end_addr, + * it is so just subtract buffer size. + */ + dest = (int32_t *) ((size_t) dest - sink->alloc_size); + } + } + + cd->frs.n += nread; + return nread; + +read_eof: + cd->frs.n += nread; + cd->frs.reached_eof = 1; + sink->w_ptr = dest; +} + +static struct comp_dev *fileread_new(struct sof_ipc_comp *comp) +{ + struct comp_dev *dev; + struct sof_ipc_comp_fileread *fileread; + struct sof_ipc_comp_fileread *ipc_fileread + = (struct sof_ipc_comp_fileread *) comp; + struct fileread_comp_data *cd; + + dev = malloc(COMP_SIZE(struct sof_ipc_comp_fileread)); + if (dev == NULL) + return NULL; + + fileread = (struct sof_ipc_comp_fileread *) &dev->comp; + memcpy(fileread, ipc_fileread, sizeof(struct sof_ipc_comp_fileread)); + + cd = malloc(sizeof(*cd)); + if (cd == NULL) { + free(dev); + return NULL; + } + + comp_set_drvdata(dev, cd); + cd->fileread_func = fileread_s32_default; + + /* Get filename from IPC and open file */ + strncpy(cd->frs.fn, ipc_fileread->fn, FILEREAD_FN_MAXLENGTH); + cd->frs.fh = fopen(cd->frs.fn, "r"); + if (cd->frs.fh == NULL) { + fprintf(stderr, "Error: File %s open for read failed.\n", + cd->frs.fn); + free(cd); + free(dev); + return NULL; + } + cd->frs.reached_eof = 0; + cd->frs.n = 0; + + return dev; +} + +static void fileread_free(struct comp_dev *dev) +{ + struct fileread_data *td = comp_get_drvdata(dev); + struct fileread_comp_data *cd = comp_get_drvdata(dev); + + fclose(cd->frs.fh); + free(td); + free(dev); +} + +/* set component audio stream parameters */ +static int fileread_params(struct comp_dev *dev) +{ + struct fileread_comp_data *cd = comp_get_drvdata(dev); + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + + /* Need to compute this in non-host endpoint */ + dev->frame_bytes = + dev->params.sample_container_bytes * dev->params.channels; + + /* calculate period size based on config */ + cd->period_bytes = dev->frames * dev->frame_bytes; + + /* File to sink supports only S32_LE PCM format */ + if (config->frame_fmt != SOF_IPC_FRAME_S32_LE) + return -EINVAL; + + return 0; +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int fileread_cmd(struct comp_dev *dev, int cmd, void *data) +{ + switch (cmd) { + case COMP_CMD_START: + dev->state = COMP_STATE_RUNNING; + break; + case COMP_CMD_STOP: + if (dev->state == COMP_STATE_RUNNING || + dev->state == COMP_STATE_DRAINING || + dev->state == COMP_STATE_PAUSED) { + comp_buffer_reset(dev); + dev->state = COMP_STATE_SETUP; + } + break; + case COMP_CMD_PAUSE: + /* only support pausing for running */ + if (dev->state == COMP_STATE_RUNNING) + dev->state = COMP_STATE_PAUSED; + + break; + case COMP_CMD_RELEASE: + dev->state = COMP_STATE_RUNNING; + break; + default: + break; + } + + return 0; +} + +/* copy and process stream data from source to sink buffers + * returns the number of bytes copied + */ +static int fileread_copy(struct comp_dev *dev) +{ + struct comp_buffer *sink; + struct comp_buffer *source = NULL; + struct fileread_comp_data *cd = comp_get_drvdata(dev); + int ret = 0; + + /* fileread component sink buffer */ + sink = list_first_item(&dev->bsink_list, struct comp_buffer, + source_list); + + /* Test that sink has enough free frames */ + if (sink->free >= cd->period_bytes && !cd->frs.reached_eof) { + /* Read PCM samples from file */ + ret = cd->fileread_func(dev, sink, source, dev->frames); + if (ret > 0) + comp_update_buffer_produce(sink, ret * + dev->params.sample_container_bytes); + } + + return ret; +} + +static int fileread_prepare(struct comp_dev *dev) +{ + dev->state = COMP_STATE_PREPARE; + + return 0; +} + +static int fileread_preload(struct comp_dev *dev) +{ + return fileread_copy(dev); +} + +static int fileread_reset(struct comp_dev *dev) +{ + dev->state = COMP_STATE_INIT; + + return 0; +} + +struct comp_driver comp_fileread = { + .type = SOF_COMP_FILEREAD, + .ops = + { + .new = fileread_new, + .free = fileread_free, + .params = fileread_params, + .cmd = fileread_cmd, + .copy = fileread_copy, + .prepare = fileread_prepare, + .reset = fileread_reset, + .preload = fileread_preload, + }, +}; + +void sys_comp_fileread_init(void) +{ + comp_register(&comp_fileread); +} diff --git a/tune/c/src/filewrite.c b/tune/c/src/filewrite.c new file mode 100644 index 0000000..8beee24 --- /dev/null +++ b/tune/c/src/filewrite.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2016, 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> +#include <reef/reef.h> +#include <reef/lock.h> +#include <reef/list.h> +#include <reef/stream.h> +#include <reef/work.h> +#include <reef/clock.h> +#include <reef/audio/component.h> +#include <reef/audio/format.h> +#include <reef/audio/pipeline.h> +#include "common_test.h" + +/* filewrite component private data */ + +static int filewrite_s32_default(struct comp_dev *dev, + struct comp_buffer *sink, struct comp_buffer *source, uint32_t frames) +{ + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int32_t *src = (int32_t *) source->r_ptr; + int i, n, n_wrap_src, ret; + int nch = dev->params.channels; + int nwrite = 0; + + if (cd->fws.write_fail) + return; + + n = frames * nch; + + while (n > 0) { + n_wrap_src = (int32_t *) source->end_addr - src; + if (n < n_wrap_src) { + /* No circular wrap need */ + while (n > 0) { + n -= nch; + i = 0; + for (i = 0; i < nch; i++) { + ret = fprintf(cd->fws.fh, "%d\n", *src); + nwrite++; + src++; + if (ret < 0) { + cd->fws.write_fail = 1; + goto quit; + } + } + } + } else { + /* Process until wrap */ + while (n_wrap_src > 0) { + n -= nch; + n_wrap_src -= nch; + for (i = 0; i < nch; i++) { + ret = fprintf(cd->fws.fh, "%d\n", *src); + nwrite++; + src++; + if (ret < 0) { + cd->fws.write_fail = 1; + goto quit; + } + } + } + /* No need to check if past end_addr, + * it is so just subtract buffer size. + */ + src = (int32_t *) ((size_t) src - source->alloc_size); + } + } + +quit: + cd->fws.n += nwrite; + return nwrite; +} + +static struct comp_dev *filewrite_new(struct sof_ipc_comp *comp) +{ + struct comp_dev *dev; + struct sof_ipc_comp_filewrite *filewrite; + struct sof_ipc_comp_filewrite *ipc_filewrite + = (struct sof_ipc_comp_filewrite *) comp; + struct filewrite_comp_data *cd; + + dev = malloc(COMP_SIZE(struct sof_ipc_comp_filewrite)); + if (dev == NULL) + return NULL; + + filewrite = (struct sof_ipc_comp_filewrite *) &dev->comp; + memcpy(filewrite, ipc_filewrite, sizeof(struct sof_ipc_comp_filewrite)); + + cd = malloc(sizeof(*cd)); + if (cd == NULL) { + free(dev); + return NULL; + } + + comp_set_drvdata(dev, cd); + cd->filewrite_func = filewrite_s32_default; + + strncpy(cd->fws.fn, ipc_filewrite->fn, FILEWRITE_FN_MAXLENGTH); + cd->fws.fh = fopen(cd->fws.fn, "w"); + if (cd->fws.fh == NULL) { + fprintf(stderr, "Error: File %s open for write failed.\n", + cd->fws.fn); + free(cd); + free(dev); + return NULL; + } + + cd->fws.write_fail = 0; + cd->fws.n = 0; + return dev; +} + +static void filewrite_free(struct comp_dev *dev) +{ + struct filewrite_data *td = comp_get_drvdata(dev); + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + + fclose(cd->fws.fh); + free(td); + free(dev); +} + +/* set component audio stream parameters */ +static int filewrite_params(struct comp_dev *dev) +{ + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + struct sof_ipc_comp_config *config = COMP_GET_CONFIG(dev); + + /* Need to compute this in non-dai endpoint */ + dev->frame_bytes = + dev->params.sample_container_bytes * dev->params.channels; + + /* calculate period size based on config */ + cd->period_bytes = dev->frames * dev->frame_bytes; + + /* File to sink supports only S32_LE PCM format */ + if (config->frame_fmt != SOF_IPC_FRAME_S32_LE) + return -EINVAL; + + return 0; +} + +/* used to pass standard and bespoke commands (with data) to component */ +static int filewrite_cmd(struct comp_dev *dev, int cmd, void *data) +{ + switch (cmd) { + case COMP_CMD_START: + dev->state = COMP_STATE_RUNNING; + break; + case COMP_CMD_STOP: + if (dev->state == COMP_STATE_RUNNING || + dev->state == COMP_STATE_DRAINING || + dev->state == COMP_STATE_PAUSED) { + comp_buffer_reset(dev); + dev->state = COMP_STATE_SETUP; + } + break; + case COMP_CMD_PAUSE: + /* only support pausing for running */ + if (dev->state == COMP_STATE_RUNNING) + dev->state = COMP_STATE_PAUSED; + + break; + case COMP_CMD_RELEASE: + dev->state = COMP_STATE_RUNNING; + break; + default: + break; + } + + return 0; +} + +/* copy and process stream data from source to sink buffers + * returns the number of bytes copied + */ +static int filewrite_copy(struct comp_dev *dev) +{ + struct comp_buffer *sink = NULL; + struct comp_buffer *source; + struct filewrite_comp_data *cd = comp_get_drvdata(dev); + int ret = 0; + + /* Get filewrite component source buffer */ + source = list_first_item(&dev->bsource_list, struct comp_buffer, + sink_list); + + /* Test that source has enough free frames */ + if (source->avail >= cd->period_bytes) { + /* Write PCM samples into file */ + ret = cd->filewrite_func(dev, sink, source, dev->frames); + comp_update_buffer_consume(source, ret * + dev->params.sample_container_bytes); + } + + if (cd->fws.write_fail) + return -EINVAL; + + return ret; +} + +static int filewrite_prepare(struct comp_dev *dev) +{ + dev->state = COMP_STATE_PREPARE; + + return 0; +} + +static int filewrite_preload(struct comp_dev *dev) +{ + return filewrite_copy(dev); +} + +static int filewrite_reset(struct comp_dev *dev) +{ + dev->state = COMP_STATE_INIT; + + return 0; +} + +struct comp_driver comp_filewrite = { + .type = SOF_COMP_FILEWRITE, + .ops = + { + .new = filewrite_new, + .free = filewrite_free, + .params = filewrite_params, + .cmd = filewrite_cmd, + .copy = filewrite_copy, + .prepare = filewrite_prepare, + .reset = filewrite_reset, + .preload = filewrite_preload, + }, +}; + +void sys_comp_filewrite_init(void) +{ + comp_register(&comp_filewrite); +}
This patch updates the following in the common_test methods
testbench_pipeline_setup() updated with pointer to pipeline desc and connection description
testbench_pipeline_params() method added to set stream params
testbench_params_unmute_start() updated with the above method
formatting changes to print relevant error messages
MUX component specific convenience constructors
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- tune/c/include/common_test.h | 6 +- tune/c/src/common_test.c | 419 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 tune/c/src/common_test.c
diff --git a/tune/c/include/common_test.h b/tune/c/include/common_test.h index 73d6fe5..01eadec 100644 --- a/tune/c/include/common_test.h +++ b/tune/c/include/common_test.h @@ -39,6 +39,7 @@ #include <stdio.h>
#include <reef/ipc.h> +#include <reef/reef.h> #include <reef/audio/pipeline.h> #include <reef/audio/format.h>
@@ -69,6 +70,7 @@ {.comp = scomp, .min_value = vmin, .max_value = vmax,\ .config = SCONFIG} #define SPIPE_MIX(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_MUX(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_SRC(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_EQ_FIR(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_EQ_IIR(scomp) {.comp = scomp, .config = SCONFIG} @@ -76,7 +78,6 @@ #define SPIPE_FILEREAD(scomp, fn_in) {.comp = scomp, .config = SCONFIG, .fn = fn_in} #define SPIPE_FILEWRITE(scomp, fn_out) {.comp = scomp, .config = SCONFIG, .fn = fn_out}
- /* * Static Pipeline Convenience Constructor */ @@ -96,7 +97,6 @@ {.comps = (struct sof_ipc_comp *)ccomps, .num_comps \ = ARRAY_SIZE(ccomps)}
- #define SPIPE(ncomp, sbuffer, sconnect) \ {.scomps = ncomp, .num_scomps = ARRAY_SIZE(ncomp), \ .buffer = sbuffer, .num_buffers = ARRAY_SIZE(sbuffer), \ @@ -178,6 +178,8 @@ int test_bench_pipeline_setup(struct reef *reef,
int test_bench_pipeline_params_unmute_start(struct ipc *ipc, int32_t fs, int nch, int deadline, int sched_id, int pipe_id); +int test_bench_pipeline_params(struct ipc *ipc, int32_t fs, + int nch, int host_comp_id, int deadline, uint32_t pipe_id);
void test_bench_enable_trace(void); void test_bench_disable_trace(void); diff --git a/tune/c/src/common_test.c b/tune/c/src/common_test.c new file mode 100644 index 0000000..a245286 --- /dev/null +++ b/tune/c/src/common_test.c @@ -0,0 +1,419 @@ +/* Simple test bench versions of SOF functions */ + +/* + * 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: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + * Liam Girdwood liam.r.girdwood@linux.intel.com + * Keyon Jie yang.jie@linux.intel.com + */ + +#include <stdint.h> +#include <stddef.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <reef/task.h> +#include <reef/alloc.h> +#include <reef/ipc.h> +#include <reef/wait.h> +#include <reef/intel-ipc.h> +#include <reef/audio/pipeline.h> +#include "common_test.h" + +/* Use simplified host test bench versions for features */ +#define TB_HOST_ALLOC_FREE_MEMCPY 1 +#define TB_TRACE 1 +#define TB_SCHEDULE 1 +#define TB_IPC 1 +#define TB_TIMER 1 + +#if TB_IPC +struct ipc *_ipc; + +int ipc_stream_send_xrun(struct comp_dev *dev, + struct sof_ipc_stream_posn *posn) +{ + return 0; +} +#endif + +#if TB_SCHEDULE + +struct schedule_data { + spinlock_t lock; + struct list_item list; /* list of tasks in priority queue */ + uint32_t clock; +}; +static struct schedule_data *sch; +#endif + +#if TB_TRACE +static int test_bench_trace = 1; +#endif + +#if TB_TRACE + +char replace_blank(char x) +{ + char y = x; + + if ((y != '-') && (y != '+') && (y != '_')) { + if (y < 'A') + y = ' '; + + if (y > 'z') + y = ' '; + } + return y; +} + +void _trace_event(uint32_t event) +{ + char a, b, c; + + if (test_bench_trace > 0) { + a = replace_blank((char) (event & 0xff)); + b = replace_blank((char) ((event >> 8) & 0xff)); + c = replace_blank((char) ((event >> 16) & 0xff)); + printf("Trace %10d %c%c%c\n", event, c, b, a); + } +} + +void test_bench_enable_trace(void) +{ + test_bench_trace = 1; + printf("Trace print enabled\n"); +} + +void test_bench_disable_trace(void) +{ + test_bench_trace = 0; + printf("Trace print disabled\n"); +} +#endif + +#if TB_HOST_ALLOC_FREE_MEMCPY + +void *rmalloc(int zone, int module, size_t bytes) +{ + return malloc(bytes); +} + +void *rzalloc(int zone, int module, size_t bytes) +{ + return malloc(bytes); +} + +void rfree(void *ptr) +{ + free(ptr); +} + +void *rballoc(int zone, int module, size_t bytes) +{ + return malloc(bytes); +} + +void rbfree(void *ptr) +{ + free(ptr); +} + +void *xthal_memcpy(void *dest, const void *src, size_t size) +{ + return memcpy(dest, src, size); +} + +#endif + +#if TB_SCHEDULE + +void schedule_task_complete(struct task *task) +{ + + list_item_del(&task->list); + task->state = TASK_STATE_COMPLETED; +} + +void schedule_task(struct task *task, uint32_t deadline, uint16_t priority, + void *data) +{ + task->deadline = deadline; + task->priority = priority; + task->sdata = data; + list_item_prepend(&task->list, &sch->list); + task->state = TASK_STATE_QUEUED; + + if (task->func) + task->func(task->data); + + schedule_task_complete(task); +} + +void schedule(void) +{ +} + +int scheduler_init(struct reef *reef) +{ + trace_pipe("ScI"); + + sch = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(*sch)); + list_init(&sch->list); + spinlock_init(&sch->lock); + + return 0; +} + +#endif + +#if TB_IPC + +int platform_ipc_init(struct ipc *ipc) +{ + struct intel_ipc_data *iipc; + //uint32_t imrd; + int i; + + _ipc = ipc; + + /* init ipc data */ + iipc = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(struct intel_ipc_data)); + ipc_set_drvdata(_ipc, iipc); + _ipc->dsp_msg = NULL; + list_init(&ipc->empty_list); + list_init(&ipc->msg_list); + spinlock_init(&ipc->lock); + + for (i = 0; i < MSG_QUEUE_SIZE; i++) + list_item_prepend(&ipc->message[i].list, &ipc->empty_list); + + /* allocate page table buffer */ + iipc->page_table = rballoc(RZONE_SYS, RFLAGS_NONE, + HOST_PAGE_SIZE); + if (iipc->page_table) + bzero(iipc->page_table, HOST_PAGE_SIZE); + + /* PM */ + iipc->pm_prepare_D3 = 0; + + return 0; +} + +int ipc_stream_send_xrun(struct comp_dev *cdev, + struct sof_ipc_stream_posn *posn) +{ + return 0; +} + +#endif + +#if TB_TIMER + +/* get timestamp for DAI stream DMA position */ +void platform_dai_timestamp(struct comp_dev *dai, + struct sof_ipc_stream_posn *posn) +{ +} + +/* get timestamp for host stream DMA position */ +void platform_host_timestamp(struct comp_dev *host, + struct sof_ipc_stream_posn *posn) +{ +} + +#endif + +int test_bench_pipeline_setup(struct reef *reef, + struct sof_ipc_pipe_new *pipeline, struct spipe *spipe) +{ + struct scomps *sc; + int i, j; + struct sof_ipc_comp *c; + struct ipc *ipc; + + /* Init IPC */ + if (ipc_init(reef) < 0) { + fprintf(stderr, "error: IPC init\n"); + return -EINVAL; + } + ipc = reef->ipc; + + /* Init scheduler */ + if (scheduler_init(reef) < 0) { + fprintf(stderr, "error: scheduler init\n"); + return -EINVAL; + } + + /* Init pipeline system */ + if (pipeline_init() < 0) { + fprintf(stderr, "error: pipeline init\n"); + return -EINVAL; + } + + + /* Register components */ + sc = spipe->scomps; + for (i = 0; i < spipe->num_scomps; i++) { + c = sc[i].comps; + for (j = 0; j < sc[i].num_comps; j++) { + if (ipc_comp_new(ipc, c) < 0) { + fprintf(stderr, "error: comp register\n"); + return -EINVAL; + } + c = (void *) c + c->hdr.size; + } + } + + /* Pipeline new must be after component registration since the + * scheduling component must exist. + */ + if (ipc_pipeline_new(ipc, pipeline) < 0) { + fprintf(stderr, "error: pipeline new\n"); + return -EINVAL; + } + + /* Register buffers */ + for (i = 0; i < spipe->num_buffers; i++) { + if (ipc_buffer_new(ipc, &spipe->buffer[i]) < 0) { + fprintf(stderr, "error: buffer new\n"); + return -EINVAL; + } + } + + /* Connect components */ + for (i = 0; i < spipe->num_connections; i++) { + if (ipc_comp_connect(ipc, &spipe->connect[i]) < 0) { + fprintf(stderr, "error: comp connect\n"); + return -EINVAL; + } + } + + /* Pipeline complete */ + ipc_pipeline_complete(ipc, pipeline->comp_id); + + return 0; +} + +int test_bench_pipeline_params_unmute_start(struct ipc *ipc, + int32_t fs, int nch, int deadline, int sched_id, int pipe_id) +{ + int ret; + struct ipc_comp_dev *pcm_dev; + struct pipeline *p; + struct comp_dev *cd; + + ret = test_bench_pipeline_params(ipc, fs, nch, sched_id, deadline, + pipe_id); + if (ret < 0) { + printf("error: pipeline params\n"); + return -EINVAL; + } + + /* Get IPC component device for pipeline and f */ + pcm_dev = ipc_get_comp(ipc, sched_id); + if (pcm_dev == NULL) { + printf("error: ipc get comp\n"); + return -EINVAL; + } + + /* Point to pipeline */ + cd = pcm_dev->cd; + p = pcm_dev->cd->pipeline; + + /* Unmute and start the pipeline */ + pipeline_cmd(p, cd, COMP_CMD_UNMUTE, NULL); + pipeline_cmd(p, cd, COMP_CMD_START, NULL); + + return 0; +} + +int test_bench_pipeline_params(struct ipc *ipc, int32_t fs, + int nch, int host_comp_id, int deadline, uint32_t pipe_id) +{ + int fs_period, ret = 0; + struct ipc_comp_dev *pcm_dev; + struct pipeline *p; + struct comp_dev *cd; + struct sof_ipc_pcm_params params; + struct sof_ipc_ctrl_data ctrl_cmd; + + /* Compute period from sample rates */ + fs_period = (int) (0.9999 + fs * deadline / 1e6); + + params.comp_id = pipe_id; + params.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + params.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + params.params.direction = SOF_IPC_STREAM_PLAYBACK; + params.params.rate = fs; + params.params.channels = nch; + switch (params.params.frame_fmt) { + case(SOF_IPC_FRAME_S16_LE): + params.params.sample_container_bytes = 2; + params.params.sample_valid_bytes = 2; + params.params.host_period_bytes = fs_period * nch * + params.params.sample_container_bytes; + break; + case(SOF_IPC_FRAME_S32_LE): + params.params.sample_container_bytes = 4; + params.params.sample_valid_bytes = 4; + params.params.host_period_bytes = fs_period * nch * + params.params.sample_container_bytes; + break; + default: + printf("error: invalid frame format\n"); + return -EINVAL; + } + + /* Get IPC component device for pipeline*/ + pcm_dev = ipc_get_comp(ipc, host_comp_id); + if (pcm_dev == NULL) { + printf("error: ipc get comp\n"); + return -EINVAL; + } + + /* Point to pipeline */ + cd = pcm_dev->cd; + p = pcm_dev->cd->pipeline; + + /* Pipeline params */ + if (p == NULL) { + printf("error: pipeline NULL\n"); + return -EINVAL; + } + + ret = pipeline_params(p, cd, ¶ms); + if (ret < 0) { + printf("error: pipeline_params\n"); + } + + return ret; +}
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; +}
This patch adds the testbench code for the passthrough component. The testbench simulates a simple pipeline with a passthrough comp connected to 1 source buffer and 1 sink buffer and the contents of input file are copied to the output file.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- tune/c/include/common_test.h | 1 + tune/passthrough/Makefile.am | 27 ++++ tune/passthrough/passthrough_test.c | 259 ++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 tune/passthrough/Makefile.am create mode 100644 tune/passthrough/passthrough_test.c
diff --git a/tune/c/include/common_test.h b/tune/c/include/common_test.h index 01eadec..3cd6bfd 100644 --- a/tune/c/include/common_test.h +++ b/tune/c/include/common_test.h @@ -71,6 +71,7 @@ .config = SCONFIG} #define SPIPE_MIX(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_MUX(scomp) {.comp = scomp, .config = SCONFIG} +#define SPIPE_PASS(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_SRC(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_EQ_FIR(scomp) {.comp = scomp, .config = SCONFIG} #define SPIPE_EQ_IIR(scomp) {.comp = scomp, .config = SCONFIG} diff --git a/tune/passthrough/Makefile.am b/tune/passthrough/Makefile.am new file mode 100644 index 0000000..1ca1b60 --- /dev/null +++ b/tune/passthrough/Makefile.am @@ -0,0 +1,27 @@ +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 = passthrough_test + +passthrough_test_SOURCES = passthrough_test.c \ + ../../../sof.git/src/audio/passthrough.c \ + ../../../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/passthrough/passthrough_test.c b/tune/passthrough/passthrough_test.c new file mode 100644 index 0000000..a6f9099 --- /dev/null +++ b/tune/passthrough/passthrough_test.c @@ -0,0 +1,259 @@ +/* + * 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) + +/* main firmware context */ +static struct reef reef; + +/* + * Test Pipeline (ID=0) + * + * Fileread(0) ---> B(3)---> Passthrough(1) ---> B(4) ---> Filewrite(2) + * + */ + +#define FR_ID 0 +#define PASSTHROUGH_ID 1 /* scheduling component */ +#define FW_ID1 2 +#define FW_ID2 3 +#define PIPE_ID 0 +#define PIPE_COMP_ID 7 +#define SCHED_ID PASSTHROUGH_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), + "in.txt"), /* ID = 0 */ +}; + +static struct sof_ipc_comp_filewrite filewrite_p0[] = { + SPIPE_FILEWRITE(SPIPE_COMP(2, SOF_COMP_FILEWRITE, + sof_ipc_comp_filewrite), "out_1.txt"), /* ID = 2 */ +}; + +static struct sof_ipc_comp_passthrough passthrough_p0[] = { + SPIPE_PASS(SPIPE_COMP(1, SOF_COMP_PASSTHROUGH, + sof_ipc_comp_passthrough)), /* ID = 1 */ +}; + +static struct scomps scomps_p0[] = { + SCOMP(fileread_p0), + SCOMP(passthrough_p0), + SCOMP(filewrite_p0), +}; + +/* + * Buffers used in static pipeline 2. + */ +static struct sof_ipc_buffer buffers_p0[] = { + SPIPE_BUFFER(3, INT_PERIOD_SIZE * 2), /* B(3) */ + SPIPE_BUFFER(4, INT_PERIOD_SIZE * 2), /* B(4) */ + +}; + +/* pipeline 0 component/buffer connections */ +static struct sof_ipc_pipe_comp_connect connect_p0[] = { + SPIPE_COMP_CONNECT(0, 3), /* p(0): Fileread(0) -> B(3) */ + SPIPE_COMP_CONNECT(3, 1), /* p(0): B(3) -> Passthrough(1) */ + SPIPE_COMP_CONNECT(1, 4), /* p(0): Passthrough(1) -> B(4) */ + SPIPE_COMP_CONNECT(4, 2), /* p(0): B(4)-> Filewrite(2) */ +}; + +/* 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 */ + 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) +{ + /* copy period frames from input file to output files */ + while (frcd->frs.reached_eof == 0) + pipeline_schedule_copy(p, pcm_dev->cd); + + return 0; +} + +void usage(char *executable) +{ + fprintf(stderr, "Usage: %s <input.txt> <output.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; + clock_t tic, toc; + double c_realtime, t_exec; + + /* handle command line arguments*/ + if (argc > 2) { + switch (argc) { + case 3: + fileread_p0[0].fn = argv[1]; + filewrite_p0[0].fn = argv[2]; + 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_passthrough_init(); + + for (i = 0; i < ARRAY_SIZE(pipeline); i++) { + /* 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, 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; + + 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; +}
participants (1)
-
Ranjani Sridharan