diff --git a/scripts/rebuild-testbench.sh b/scripts/rebuild-testbench.sh index b566dc6dcd91..d7da4bda8420 100755 --- a/scripts/rebuild-testbench.sh +++ b/scripts/rebuild-testbench.sh @@ -5,27 +5,42 @@ # fail on any errors set -e +# Defaults +BUILD_TYPE=native +BUILD_DIR_NAME=build_testbench +BUILD_TARGET=install + print_usage() { cat <] + -p Build testbench binary for xt-run for selected platform, e.g. -p tgl -f Build testbench with compiler provided by fuzzer (default path: $HOME/sof/work/AFL/afl-gcc) EOFUSAGE } +# die is copy from xtensa-build-all.sh +die() +{ + >&2 printf '%s ERROR: ' "$0" + # We want die() to be usable exactly like printf + # shellcheck disable=SC2059 + >&2 printf "$@" + exit 1 +} + rebuild_testbench() { cd "$BUILD_TESTBENCH_DIR" - rm -rf build_testbench - - mkdir build_testbench - cd build_testbench + rm -rf "$BUILD_DIR_NAME" + mkdir "$BUILD_DIR_NAME" + cd "$BUILD_DIR_NAME" cmake -DCMAKE_INSTALL_PREFIX=install .. - cmake --build . -- -j"$(nproc)" install + cmake --build . -- -j"$(nproc)" $BUILD_TARGET } export_CC_with_afl() @@ -34,6 +49,67 @@ export_CC_with_afl() export CC=${SOF_AFL} } +setup_xtensa_tools_build() +{ + BUILD_TYPE=xt + BUILD_TARGET= + BUILD_DIR_NAME=build_xt_testbench + + # check needed environment variables + test -n "${XTENSA_TOOLS_ROOT}" || die "XTENSA_TOOLS_ROOT need to be set.\n" + + # Get compiler version for platform + source "$SCRIPT_DIR/set_xtensa_params.sh" "$BUILD_PLATFORM" + + test -n "${XTENSA_TOOLS_VERSION}" || + die "Illegal platform $BUILD_PLATFORM, no XTENSA_TOOLS_VERSION found.\n" + test -n "${XTENSA_CORE}" || + die "Illegal platform $BUILD_PLATFORM, no XTENSA_CORE found.\n" + + compiler="xt-xcc" + install_bin=install/tools/$XTENSA_TOOLS_VERSION/XtensaTools/bin + tools_bin=$XTENSA_TOOLS_ROOT/$install_bin + testbench_sections="-Wl,--sections-placement $BUILD_TESTBENCH_DIR/testbench_xcc_sections.txt" + export CC=$tools_bin/$compiler + export LD=$tools_bin/xt-ld + export OBJDUMP=$tools_bin/xt-objdump + export LDFLAGS="-mlsp=sim -Wl,-LE $testbench_sections" + export XTENSA_CORE +} + +export_xtensa_setup() +{ + export_dir=$BUILD_TESTBENCH_DIR/$BUILD_DIR_NAME + export_script=$export_dir/xtrun_env.sh + xtbench=$export_dir/testbench + xtbench_run="XTENSA_CORE=$XTENSA_CORE \$XTENSA_TOOLS_ROOT/$install_bin/xt-run $xtbench" + cat < "$export_script" +export XTENSA_TOOLS_ROOT=$XTENSA_TOOLS_ROOT +export XTENSA_CORE=$XTENSA_CORE +XTENSA_PATH=$tools_bin +EOFSETUP +} + +testbench_usage() +{ + case "$BUILD_TYPE" in + xt) + export_xtensa_setup + cat < "$FN_TRACE" + # shellcheck disable=SC2086 + $VALGRIND_CMD $CMD 2> "$FN_TRACE" fi } parse_args "$@" -# Paths -HOST_ROOT=../../testbench/build_testbench -HOST_EXE=$HOST_ROOT/install/bin/testbench -HOST_LIB=$HOST_ROOT/sof_ep/install/lib -TPLG_LIB=$HOST_ROOT/sof_parser/install/lib +# Path to topologies TPLG_DIR=../../build_tools/test/topology +# Testbench path and executable +if [[ -z $XTRUN ]]; then + TESTBENCH=../../testbench/build_testbench/install/bin/testbench +else + BUILD_DIR=../../testbench/build_xt_testbench + TESTBENCH="$BUILD_DIR"/testbench + source "$BUILD_DIR"/xtrun_env.sh + XTRUN_CMD=$XTENSA_PATH/$XTRUN + if $VALGRIND; then + >&2 printf "WARNING: Ignoring VALGRIND with xt-run\n" + VALGRIND=false + fi +fi + +if $VALGRIND; then + VALGRIND_CMD="valgrind --leak-check=yes --error-exitcode=1" +else + VALGRIND_CMD= +fi + +HOST_EXE="$XTRUN_CMD $TESTBENCH" + # Use topology from component test topologies INFMT=s${BITS_IN}le OUTFMT=s${BITS_OUT}le TPLGFN=test-${DIRECTION}-ssp5-mclk-0-I2S-${COMP}-${INFMT}-${OUTFMT}-48k-24576k-codec.tplg TPLG=${TPLG_DIR}/${TPLGFN} +[ -f "$TPLG" ] || { + echo + echo "Error: topology $TPLG does not exist." + echo "Please run scripts/build-tools.sh -t" + exit 1 +} # If binary test vectors if [ "${FN_IN: -4}" == ".raw" ]; then @@ -146,17 +172,9 @@ OPTS="$DEBUG -r $FS_IN -R $FS_OUT -c $CHANNELS_IN -n $CHANNELS_OUT $BINFMT -t $T DATA="-i $FN_IN -o $FN_OUT" ARG="$OPTS $EXTRA_OPTS $DATA" CMD="$HOST_EXE $ARG" -if "$VALGRIND"; then - VALGRIND_CMD="valgrind --leak-check=yes --error-exitcode=1" -else - VALGRIND_CMD= -fi - -export LD_LIBRARY_PATH=$HOST_LIB:$TPLG_LIB # Run test bench echo "Command: $HOST_EXE" echo "Argument: $ARG" -echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" run_testbench diff --git a/tools/test/audio/process_test.m b/tools/test/audio/process_test.m index aaefe76a80a8..9693f8b03663 100644 --- a/tools/test/audio/process_test.m +++ b/tools/test/audio/process_test.m @@ -1,12 +1,24 @@ % process_test - test objective audio quality parameters % -% process_test(comp, bits_in_list, bits_out_list, fs) +% process_test(comp, bits_in_list, bits_out_list, fs, fulltest, xtrun) +% +% Inputs +% comp - component to test +% bits_in_list - input word lengths +% bits_out_list - output workd lengths +% fs - sample rate +% fulltest - 0 perform only chirp test, 1 perform all +% xtrun - set to 'xt-run' or 'xt-run --turbo' to test with xt-testbench +% +% E.g. +% process_test('eq-iir', 32, 32, 48000, 0, 'xt-run --turbo'); +% process_test('eq-iir', 32, 32); % SPDX-License-Identifier: BSD-3-Clause % Copyright(c) 2017-2022 Intel Corporation. All rights reserved. % Author: Seppo Ingalsuo -function [n_fail, n_pass, n_na] = process_test(comp, bits_in_list, bits_out_list, fs, fulltest) +function [n_fail, n_pass, n_na] = process_test(comp, bits_in_list, bits_out_list, fs, fulltest, xtrun) %% Defaults for call parameters if nargin < 1 comp = 'EQIIR'; @@ -28,6 +40,10 @@ fulltest = 1; end + if nargin < 6 + xtrun = ''; + end + %% Paths t.blobpath = '../../topology/topology1/m4'; plots = 'plots'; @@ -44,6 +60,7 @@ t.bits_in = bits_in_list; % Input word length from func arguments t.bits_out = bits_out_list; % Output word length from func arguments t.full_test = fulltest; % 0 is quick check only, 1 is full test + t.xtrun = xtrun; %% Show graphics or not. With visible plot windows Octave may freeze if too % many windows are kept open. As workaround setting close windows to @@ -335,6 +352,7 @@ test.ch = t.ch; test.fs = t.fs; test.plot_visible = t.plot_visible; + test.xtrun = t.xtrun; % Misc test.quick = 0; diff --git a/tools/test/audio/test_utils/test_run.m b/tools/test/audio/test_utils/test_run.m index 85ac73586308..d4e78ce9127a 100644 --- a/tools/test/audio/test_utils/test_run.m +++ b/tools/test/audio/test_utils/test_run.m @@ -86,6 +86,12 @@ fprintf(fh, 'FN_TRACE=\"/dev/null"\n'); end +if isfield(test, 'xtrun') + fprintf(fh, 'XTRUN=\"%s\"\n', test.xtrun); +else + fprintf(fh, 'XTRUN=\n'); +end + % Override defaults in comp_run.sh fprintf(fh, 'VALGRIND=false\n'); fclose(fh); diff --git a/tools/testbench/common_test.c b/tools/testbench/common_test.c index 18606439faff..765c13468386 100644 --- a/tools/testbench/common_test.c +++ b/tools/testbench/common_test.c @@ -29,6 +29,10 @@ #include "testbench/trace.h" #include +#if defined __XCC__ +#include +#endif + /* testbench helper functions for pipeline setup and trigger */ int tb_setup(struct sof *sof, struct testbench_prm *tp) @@ -281,3 +285,22 @@ void tb_enable_trace(bool enable) else debug_print("trace print disabled\n"); } + +void tb_gettime(struct timespec *td) +{ +#if !defined __XCC__ + clock_gettime(CLOCK_MONOTONIC, td); +#else + td->tv_nsec = 0; + td->tv_sec = 0; +#endif +} + +void tb_getcycles(uint64_t *cycles) +{ +#if defined __XCC__ + *cycles = XT_RSR_CCOUNT(); +#else + *cycles = 0; +#endif +} diff --git a/tools/testbench/file.c b/tools/testbench/file.c index 55adcbbcca60..87964a479dfd 100644 --- a/tools/testbench/file.c +++ b/tools/testbench/file.c @@ -617,6 +617,7 @@ static struct comp_dev *file_new(const struct comp_driver *drv, cd->fs.write_failed = false; cd->fs.n = 0; cd->fs.copy_count = 0; + cd->fs.cycles_count = 0; dev->state = COMP_STATE_READY; return dev; @@ -803,11 +804,13 @@ static int file_copy(struct comp_dev *dev) struct comp_buffer *buffer; struct dai_data *dd = comp_get_drvdata(dev); struct file_comp_data *cd = comp_get_drvdata(dd->dai); + uint64_t cycles0, cycles1; int snk_frames; int src_frames; int bytes = cd->sample_container_bytes; int ret = 0; + tb_getcycles(&cycles0); switch (cd->fs.mode) { case FILE_READ: /* file component sink buffer */ @@ -859,6 +862,9 @@ static int file_copy(struct comp_dev *dev) cd->fs.reached_eof); schedule_task_cancel(dev->pipeline->pipe_task); } + + tb_getcycles(&cycles1); + cd->fs.cycles_count += cycles1 - cycles0; return ret; } diff --git a/tools/testbench/include/testbench/common_test.h b/tools/testbench/include/testbench/common_test.h index a707e5b050b3..dd43c29ab0eb 100644 --- a/tools/testbench/include/testbench/common_test.h +++ b/tools/testbench/include/testbench/common_test.h @@ -36,6 +36,7 @@ struct tplg_context; * into per pipeline data and per topology data structures. */ struct testbench_prm { + long long total_cycles; char *tplg_file; /* topology file to use */ char *input_file[MAX_INPUT_FILE_NUM]; /* input file names */ char *output_file[MAX_OUTPUT_FILE_NUM]; /* output file names */ @@ -104,4 +105,8 @@ int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p); void debug_print(char *message); +void tb_gettime(struct timespec *td); + +void tb_getcycles(uint64_t *cycles); + #endif diff --git a/tools/testbench/include/testbench/file.h b/tools/testbench/include/testbench/file.h index 14d5e80dfacc..d18f97972985 100644 --- a/tools/testbench/include/testbench/file.h +++ b/tools/testbench/include/testbench/file.h @@ -11,6 +11,8 @@ #ifndef _FILE_H #define _FILE_H +#include + /**< Convert with right shift a bytes count to samples count */ #define FILE_BYTES_TO_S16_SAMPLES(s) ((s) >> 1) #define FILE_BYTES_TO_S32_SAMPLES(s) ((s) >> 2) @@ -29,14 +31,15 @@ enum file_format { /* file component state */ struct file_state { - char *fn; + uint64_t cycles_count; FILE *rfh, *wfh; /* read/write file handle */ - bool reached_eof; - bool write_failed; + char *fn; + int copy_count; int n; enum file_mode mode; enum file_format f_format; - int copy_count; + bool reached_eof; + bool write_failed; }; /* file comp data */ diff --git a/tools/testbench/testbench.c b/tools/testbench/testbench.c index 8aa1446549ea..f4302765408f 100644 --- a/tools/testbench/testbench.c +++ b/tools/testbench/testbench.c @@ -254,7 +254,7 @@ static void test_pipeline_get_file_stats(int pipeline_id) time = cd->pipeline->pipe_task->start; if (fcd->fs.copy_count == 0) fcd->fs.copy_count = 1; - printf("file %s: id %d: type %d: samples %d copies %d total time %zu uS avg time %zu uS\n", + printf("file %s: id %d: type %d: samples %d copies %d total time %lu uS avg time %lu uS\n", fcd->fs.fn, cd->ipc_config.id, cd->drv->type, fcd->fs.n, fcd->fs.copy_count, time, time / fcd->fs.copy_count); break; @@ -356,7 +356,7 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp) default: fprintf(stderr, "unknown option %c\n", option); ret = -EINVAL; - __attribute__ ((fallthrough)); + /* fallthrough */ case 'h': print_usage(argv[0]); exit(EXIT_SUCCESS); @@ -487,10 +487,16 @@ static int test_pipeline_start(struct testbench_prm *tp) static bool test_pipeline_check_state(struct testbench_prm *tp, int state) { struct pipeline *p; + uint64_t cycles0, cycles1; int i; + tb_getcycles(&cycles0); + schedule_ll_run_tasks(); + tb_getcycles(&cycles1); + tp->total_cycles += cycles1 - cycles0; + /* Run pipeline until EOF from fileread */ for (i = 0; i < tp->pipeline_num; i++) { p = get_pipeline_by_id(tp->pipelines[i]); @@ -522,7 +528,7 @@ static int test_pipeline_load(struct testbench_prm *tp, struct tplg_context *ctx } static void test_pipeline_stats(struct testbench_prm *tp, - struct tplg_context *ctx, uint64_t delta) + struct tplg_context *ctx, long long delta_t) { int count = 1; struct ipc_comp_dev *icd; @@ -530,7 +536,9 @@ static void test_pipeline_stats(struct testbench_prm *tp, struct dai_data *dd; struct pipeline *p; struct file_comp_data *frcd, *fwcd; - int n_in, n_out; + long long file_cycles, pipeline_cycles; + float pipeline_mcps; + int n_in, n_out, frames_out; int i; /* Get pointer to filewrite */ @@ -566,6 +574,7 @@ static void test_pipeline_stats(struct testbench_prm *tp, n_in = frcd->fs.n; n_out = fwcd->fs.n; + file_cycles = frcd->fs.cycles_count + fwcd->fs.cycles_count; /* print test summary */ printf("==========================================================\n"); @@ -586,10 +595,25 @@ static void test_pipeline_stats(struct testbench_prm *tp, printf("Output[%d] written to file: \"%s\"\n", i, tp->output_file[i]); } + frames_out = n_out / tp->channels_out; printf("Input sample (frame) count: %d (%d)\n", n_in, n_in / tp->channels_in); - printf("Output sample (frame) count: %d (%d)\n", n_out, n_out / tp->channels_out); - printf("Total execution time: %zu us, %.2f x realtime\n\n", - delta, (double)((double)n_out / tp->channels_out / tp->fs_out) * 1000000 / delta); + printf("Output sample (frame) count: %d (%d)\n", n_out, frames_out); + if (tp->total_cycles) { + pipeline_cycles = tp->total_cycles - file_cycles; + pipeline_mcps = (float)pipeline_cycles * tp->fs_out / frames_out / 1e6; + printf("Total execution cycles: %lld\n", tp->total_cycles); + printf("File component cycles: %lld\n", file_cycles); + printf("Pipeline cycles: %lld\n", pipeline_cycles); + printf("Pipeline MCPS: %6.2f\n", pipeline_mcps); + if (!tp->quiet) + printf("Warning: Use -q to avoid printing to increase MCPS.\n"); + } + + if (delta_t) + printf("Total execution time: %lld us, %.2f x realtime\n", + delta_t, (float)frames_out / tp->fs_out * 1000000 / delta_t); + + printf("\n"); } /* @@ -602,10 +626,10 @@ static int pipline_test(struct testbench_prm *tp) struct tplg_context ctx; struct timespec ts; struct timespec td0, td1; + long long delta_t; int err; int nsleep_time; int nsleep_limit; - uint64_t delta; /* build, run and teardown pipelines */ while (dp_count < tp->dynamic_pipeline_iterations) { @@ -637,7 +661,8 @@ static int pipline_test(struct testbench_prm *tp) dp_count, err); break; } - clock_gettime(CLOCK_MONOTONIC, &td0); + + tb_gettime(&td0); /* sleep to let the pipeline work - we exit at timeout OR * if copy iterations OR max_samples is reached (whatever first) @@ -652,8 +677,12 @@ static int pipline_test(struct testbench_prm *tp) tp->pipeline_duration_ms; while (nsleep_time < nsleep_limit) { +#if defined __XCC__ + err = 0; +#else /* wait for next tick */ err = nanosleep(&ts, &ts); +#endif if (err == 0) { nsleep_time += tp->tick_period_us; /* sleep fully completed */ if (test_pipeline_check_state(tp, SOF_TASK_STATE_CANCEL)) { @@ -670,7 +699,8 @@ static int pipline_test(struct testbench_prm *tp) } } - clock_gettime(CLOCK_MONOTONIC, &td1); + tb_gettime(&td1); + err = test_pipeline_stop(tp); if (err < 0) { fprintf(stderr, "error: pipeline stop %d failed %d\n", @@ -678,9 +708,9 @@ static int pipline_test(struct testbench_prm *tp) break; } - delta = (td1.tv_sec - td0.tv_sec) * 1000000; - delta += (td1.tv_nsec - td0.tv_nsec) / 1000; - test_pipeline_stats(tp, &ctx, delta); + delta_t = (td1.tv_sec - td0.tv_sec) * 1000000; + delta_t += (td1.tv_nsec - td0.tv_nsec) / 1000; + test_pipeline_stats(tp, &ctx, delta_t); err = test_pipeline_reset(tp); if (err < 0) { @@ -705,6 +735,7 @@ int main(int argc, char **argv) /* initialize input and output sample rates, files, etc. */ debug = 0; + tp.total_cycles = 0; tp.fs_in = 0; tp.fs_out = 0; tp.bits_in = 0; diff --git a/tools/testbench/testbench_xcc_sections.txt b/tools/testbench/testbench_xcc_sections.txt new file mode 100644 index 000000000000..ab0d7668f8fd --- /dev/null +++ b/tools/testbench/testbench_xcc_sections.txt @@ -0,0 +1,18 @@ +# +# IRAM placement +# +# put: .iram0.text .literal +# put: .iram0.text .text + +#put: .iram0.text .static_uuids +#put: .iram0.text .trace_ctx +#put: .text .static_uuids +#put: .text .trace_ctx +put: .bss .static_uuids +put: .bss .trace_ctx + +# sections order + +# .text +# .static_uuids +# .trace_ctx diff --git a/tools/testbench/topology.c b/tools/testbench/topology.c index b47cbf873c6b..d943dc1a8793 100644 --- a/tools/testbench/topology.c +++ b/tools/testbench/topology.c @@ -7,7 +7,6 @@ /* Topology loader to set up components and pipeline */ -#include #include #include #include @@ -161,7 +160,7 @@ static int tb_register_pga(struct testbench_prm *tp, struct tplg_context *ctx) static int tb_register_pipeline(struct testbench_prm *tp, struct tplg_context *ctx) { struct sof *sof = ctx->sof; - struct sof_ipc_pipe_new pipeline = {0}; + struct sof_ipc_pipe_new pipeline = {{0}}; int ret; ret = tplg_new_pipeline(ctx, &pipeline, sizeof(pipeline), NULL); diff --git a/tools/tplg_parser/include/tplg_parser/topology.h b/tools/tplg_parser/include/tplg_parser/topology.h index 0899c2320d01..e4e7a3a37a02 100644 --- a/tools/tplg_parser/include/tplg_parser/topology.h +++ b/tools/tplg_parser/include/tplg_parser/topology.h @@ -85,7 +85,7 @@ struct tplg_context { ({struct snd_soc_tplg_hdr *ptr; \ ptr = (struct snd_soc_tplg_hdr *)(ctx->tplg_base + ctx->tplg_offset); \ if (ptr->size != sizeof(*ptr)) { \ - printf("%s %d hdr size mismatch 0x%x:0x%lx at offset %ld\n", \ + printf("%s %d hdr size mismatch 0x%x:0x%zx at offset %ld\n", \ __func__, __LINE__, ptr->size, sizeof(*ptr), \ ctx->tplg_offset); assert(0); \ } \