From 208491fe51a6548daa5d09252346dbd8052e5901 Mon Sep 17 00:00:00 2001 From: Ross Owen Date: Wed, 8 Mar 2023 10:53:33 +0000 Subject: [PATCH] Added mixer control unit tests (#316) * Added test_mixer_routing_output_ctrl * Added test_mixer_routing_input_ctrl * Some minor mixer test and code improvements * Update lib_xud dep version requirement --- .gitignore | 1 + lib_xua/module_build_info | 2 +- lib_xua/src/core/audiohub/xua_audiohub.xc | 5 +- lib_xua/src/core/clocking/clockgen.xc | 17 +- lib_xua/src/core/endpoint0/xua_endpoint0.c | 97 ++++---- lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc | 11 +- tests/test_mixer_routing_input/src/main.xc | 6 +- tests/test_mixer_routing_input_ctrl.py | 45 ++++ tests/test_mixer_routing_input_ctrl/Makefile | 19 ++ .../test_mixer_routing_input_ctrl/src/main.xc | 200 ++++++++++++++++ .../src/test_xs3_600.xn | 24 ++ .../src/xua_conf.h | 45 ++++ tests/test_mixer_routing_output/src/main.xc | 6 +- .../src/mixer_test_shared.h | 2 + tests/test_mixer_routing_output_ctrl.py | 45 ++++ tests/test_mixer_routing_output_ctrl/Makefile | 19 ++ .../src/main.xc | 221 ++++++++++++++++++ .../src/test_xs3_600.xn | 24 ++ .../src/xua_conf.h | 45 ++++ 19 files changed, 754 insertions(+), 80 deletions(-) create mode 100644 tests/test_mixer_routing_input_ctrl.py create mode 100644 tests/test_mixer_routing_input_ctrl/Makefile create mode 100644 tests/test_mixer_routing_input_ctrl/src/main.xc create mode 100644 tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn create mode 100644 tests/test_mixer_routing_input_ctrl/src/xua_conf.h create mode 100644 tests/test_mixer_routing_output_ctrl.py create mode 100644 tests/test_mixer_routing_output_ctrl/Makefile create mode 100644 tests/test_mixer_routing_output_ctrl/src/main.xc create mode 100644 tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn create mode 100644 tests/test_mixer_routing_output_ctrl/src/xua_conf.h diff --git a/.gitignore b/.gitignore index 3cc9f3c8..663e8294 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ _build* .DS_Store *.*~ *.swp +*.swn *~ *.swo diff --git a/lib_xua/module_build_info b/lib_xua/module_build_info index 58370231..7d23739b 100644 --- a/lib_xua/module_build_info +++ b/lib_xua/module_build_info @@ -13,7 +13,7 @@ DEPENDENT_MODULES = lib_locks(>=2.1.0) \ lib_mic_array(>=4.5.0) \ lib_spdif(>=4.2.1) \ lib_xassert(>=4.1.0) \ - lib_xud(>=2.2.1) \ + lib_xud(>=2.2.2) \ lib_adat(>=1.0.0) MODULE_XCC_FLAGS = $(XCC_FLAGS) \ diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index 4853822c..c297c544 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -381,9 +381,8 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out outuint(c_dig_rx, 0); #endif #if (XUA_SPDIF_TX_EN) && (NUM_USB_CHAN_OUT > 0) - outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward sample to S/PDIF Tx thread */ - unsigned sample = samplesOut[SPDIF_TX_INDEX + 1]; - outuint(c_spd_out, sample); /* Forward sample to S/PDIF Tx thread */ + outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward samples to S/PDIF Tx thread */ + outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX + 1]); #endif #if (XUA_NUM_PDM_MICS > 0) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 1c0a3334..ec5f1c77 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -1,4 +1,4 @@ -// Copyright 2011-2022 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include @@ -13,14 +13,14 @@ #include "spdif.h" #endif -#define LOCAL_CLOCK_INCREMENT 166667 -#define LOCAL_CLOCK_MARGIN 1666 +#define LOCAL_CLOCK_INCREMENT (166667) +#define LOCAL_CLOCK_MARGIN (1666) -#define MAX_SAMPLES 64 /* Must be power of 2 */ +#define MAX_SAMPLES (64) /* Must be power of 2 */ #define MAX_SPDIF_SAMPLES (2 * MAX_SAMPLES) /* Must be power of 2 */ #define MAX_ADAT_SAMPLES (8 * MAX_SAMPLES) /* Must be power of 2 */ -#define SPDIF_FRAME_ERRORS_THRESH 40 +#define SPDIF_FRAME_ERRORS_THRESH (40) unsigned g_digData[10]; @@ -241,12 +241,7 @@ extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; int VendorAudCoreReqs(unsigned cmd, chanend c); #pragma unsafe arrays -//#if (AUDIO_IO_TILE == PLL_REF_TILE) -#if 0 -void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int) -#else void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int) -#endif { timer t_local; unsigned timeNextEdge, timeLastEdge, timeNextClockDetection; @@ -723,7 +718,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) - /* Mixer requests data */ + /* AudioHub requests data */ case inuint_byref(c_dig_rx, tmp): #if (XUA_SPDIF_RX_EN) if(spdifUnderflow) diff --git a/lib_xua/src/core/endpoint0/xua_endpoint0.c b/lib_xua/src/core/endpoint0/xua_endpoint0.c index 98d88e51..888291f7 100755 --- a/lib_xua/src/core/endpoint0/xua_endpoint0.c +++ b/lib_xua/src/core/endpoint0/xua_endpoint0.c @@ -266,6 +266,49 @@ void XUA_Endpoint0_setVendorId(unsigned short vid) { #endif // AUDIO_CLASS == 1} } +#if (MIXER) +void InitLocalMixerState() +{ + for (int i = 0; i < MIX_INPUTS * MAX_MIX_COUNT; i++) + { + mixer1Weights[i] = 0x8001; //-inf + } + + /* Configure default connections */ + // TODO this should be a loop using defines. + mixer1Weights[0] = 0; + mixer1Weights[9] = 0; + mixer1Weights[18] = 0; + mixer1Weights[27] = 0; + mixer1Weights[36] = 0; + mixer1Weights[45] = 0; + mixer1Weights[54] = 0; + mixer1Weights[63] = 0; + +#if NUM_USB_CHAN_OUT > 0 + /* Setup up audio output channel mapping */ + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + channelMapAud[i] = i; + } +#endif + +#if NUM_USB_CHAN_IN > 0 + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + channelMapUsb[i] = i + NUM_USB_CHAN_OUT; + } +#endif + + /* Init mixer inputs */ + for(int j = 0; j < MAX_MIX_COUNT; j++) + for(int i = 0; i < MIX_INPUTS; i++) + { + mixSel[j][i] = i; + } +} +#endif + void concatenateAndCopyStrings(char* string1, char* string2, char* string_buffer) { debug_printf("concatenateAndCopyStrings() for \"%s\" and \"%s\"\n", string1, string2); @@ -412,62 +455,11 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(c XUA_Endpoint0_setStrTable(); -#if 0 - /* Dont need to init globals.. */ - /* Init tables for volumes (+ 1 for master) */ - for(int i = 0; i < NUM_USB_CHAN_OUT + 1; i++) - { - volsOut[i] = 0; - mutesOut[i] = 0; - } - - for(int i = 0; i < NUM_USB_CHAN_IN + 1; i++) - { - volsIn[i] = 0; - mutesIn[i] = 0; - } -#endif VendorRequests_Init(VENDOR_REQUESTS_PARAMS); #if (MIXER) /* Set up mixer default state */ - for (int i = 0; i < MIX_INPUTS * MAX_MIX_COUNT; i++) - { - mixer1Weights[i] = 0x8001; //-inf - } - - /* Configure default connections */ - // TODO this should be a loop using defines. - mixer1Weights[0] = 0; - mixer1Weights[9] = 0; - mixer1Weights[18] = 0; - mixer1Weights[27] = 0; - mixer1Weights[36] = 0; - mixer1Weights[45] = 0; - mixer1Weights[54] = 0; - mixer1Weights[63] = 0; - -#if NUM_USB_CHAN_OUT > 0 - /* Setup up audio output channel mapping */ - for(int i = 0; i < NUM_USB_CHAN_OUT; i++) - { - channelMapAud[i] = i; - } -#endif - -#if NUM_USB_CHAN_IN > 0 - for(int i = 0; i < NUM_USB_CHAN_IN; i++) - { - channelMapUsb[i] = i + NUM_USB_CHAN_OUT; - } -#endif - - /* Init mixer inputs */ - for(int j = 0; j < MAX_MIX_COUNT; j++) - for(int i = 0; i < MIX_INPUTS; i++) - { - mixSel[j][i] = i; - } + InitLocalMixerState(); #endif #ifdef VENDOR_AUDIO_REQS @@ -520,7 +512,6 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(c cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_FREQ + 3*i + 2] = (get_usb_to_device_rate() & 0xff0000)>> 16; } - cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff; //max packet size cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff00) >> 8; //max packet size #endif // NUM_USB_CHAN_OUT diff --git a/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc b/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc index c065daf3..3027b5b7 100644 --- a/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc +++ b/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc @@ -129,7 +129,7 @@ static unsigned longMul(unsigned a, unsigned b, int prec) } /* Update master volume i.e. i.e update weights for all channels */ -static void updateMasterVol( int unitID, chanend ?c_mix_ctl) +static void updateMasterVol(int unitID, chanend ?c_mix_ctl) { int x; #if (OUT_VOLUME_IN_MIXER == 0) @@ -138,7 +138,7 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl) #if (IN_VOLUME_IN_MIXER == 0) xc_ptr p_multIn = array_to_xc_ptr(multIn); #endif - switch( unitID) + switch(unitID) { case FU_USBOUT: { @@ -307,7 +307,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c /* Inspect request, NOTE: these are class specific requests */ switch( sp.bRequest ) { - /* CUR Request*/ case CUR: { @@ -668,7 +667,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c { return result; } - + if (dst < NUM_USB_CHAN_OUT) { channelMapAud[dst] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; @@ -790,7 +789,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c if((cs > 0) && (cs < (MAX_MIX_COUNT+1))) { (buffer, unsigned char[])[0] = mixSel[cs-1][cn]; - return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, 1 ); + return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, 1); } } } @@ -1137,7 +1136,7 @@ int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp /* Allow time for the change - feedback to stabilise */ FeedbackStabilityDelay(); - } + } } return XUD_SetBuffer(ep0_in, (buffer, unsigned char[]), 0); } diff --git a/tests/test_mixer_routing_input/src/main.xc b/tests/test_mixer_routing_input/src/main.xc index 7aaaab02..3ddd26b2 100644 --- a/tests/test_mixer_routing_input/src/main.xc +++ b/tests/test_mixer_routing_input/src/main.xc @@ -197,9 +197,9 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl) unsigned input = random_get_random_number(rg) % MIX_INPUTS; /* Note, we don't currently support a mix input dervived from another mix - * This is not trivial to test since the current mixer implementation only allows for one - * config update per "trigger" - */ + * This is not trivial to test since the current mixer implementation only allows for one + * config update per "trigger" + */ unsigned src = random_get_random_number(rg) % NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT; debug_printf("Iteration: %d\n", testIter); diff --git a/tests/test_mixer_routing_input_ctrl.py b/tests/test_mixer_routing_input_ctrl.py new file mode 100644 index 00000000..d867c42e --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl.py @@ -0,0 +1,45 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +def do_test(options, capfd, test_file, test_seed): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{testname}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + max_cycles = 15000000 + + simargs = [ + "--max-cycles", + str(max_cycles), + ] + + build_options = [] + build_options += ["TEST_SEED=" + str(test_seed)] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + build_options=build_options, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +def test_mixer_routing_output(options, capfd, test_file, test_seed): + + result = do_test(options, capfd, test_file, test_seed) + + assert result diff --git a/tests/test_mixer_routing_input_ctrl/Makefile b/tests/test_mixer_routing_input_ctrl/Makefile new file mode 100644 index 00000000..a66c3717 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/Makefile @@ -0,0 +1,19 @@ + +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1 +else +TEST_DEBUG_FLAGS = +endif + +TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) -DXUD_WEAK_API=1 + +XCC_FLAGS = -O3 $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua lib_logging lib_random + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_mixer_routing_input_ctrl/src/main.xc b/tests/test_mixer_routing_input_ctrl/src/main.xc new file mode 100644 index 00000000..e5072ab8 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/src/main.xc @@ -0,0 +1,200 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* This tests checks the parsing of control requests to endpoint 0 cause the correct changes in mixer output routing */ + +#include +#include +#include "platform.h" +#include "xua.h" +#include "debug_print.h" +#include "assert.h" +#include "xud.h" +#include "usbaudio20.h" +#include "random.h" + +#ifndef TEST_ITERATIONS +#define TEST_ITERATIONS (100) +#endif + +#include "./../test_mixer_routing_output/src/mixer_test_shared.h" + +/* Mixer input mapping - from xua_endpoint0.c */ +extern unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS]; + +/* From xua_ep0_uacreqs.xc */ +int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, chanend ?c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); + +/* From xua_endpoint0.c */ +void InitLocalMixerState(); + +int g_src = 0; + +/* Override func in lib_xud/src/user/client/XUD_EpFunctions.c for testing purposes */ +XUD_Result_t XUD_GetBuffer(XUD_ep ep_out, unsigned char buffer[], REFERENCE_PARAM(unsigned, length)) +{ + buffer[0] = g_src; + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_DoSetRequestStatus(XUD_ep ep_in) +{ + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_SetBuffer_EpMax(XUD_ep ep_in, unsigned char buffer[], unsigned datalength, unsigned epMax) +{ + assert(g_src == buffer[0]); + assert(datalength == 1); + return XUD_RES_OKAY; +} + +unsafe +{ + extern int volatile * const unsafe samples_to_device_map; + extern int volatile * const unsafe samples_to_host_map; +} + +void Fake_Endpoint0(chanend c_mix_ctl) +{ + XUD_ep ep0_out; /* Never initialised but not used */ + XUD_ep ep0_in; /* Never initialised but not used */ + USB_SetupPacket_t sp; + + random_generator_t rg = random_create_generator_from_seed(TEST_SEED); + + InitLocalMixerState(); + + sp.bmRequestType.Type = USB_BM_REQTYPE_TYPE_CLASS; // Note, parsing of this won't be tested since we call AudioClassRequests directly + sp.bmRequestType.Recipient = USB_BM_REQTYPE_RECIP_INTER; // Note, parsing of this won't be tested since we call AudioClassRequests directly + + for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++) + { + int unitId = ID_XU_MIXSEL; + unsigned mix = (random_get_random_number(rg) % (MAX_MIX_COUNT + 1)); // Mixs indexed from 1 + unsigned input = random_get_random_number(rg) % MIX_INPUTS; + + /* Note, we don't currently support a mix input dervived from another mix + * This is not trivial to test since the current mixer implementation only allows for one + * config update per "trigger" + */ + unsigned src = random_get_random_number(rg) % (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT); + + debug_printf("Mapping mix %d input %d", mix, input); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + + /* Create Control request data for routing change */ + int cs = mix; + int cn = input; + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_H2D; + sp.bRequest = CUR; + sp.wValue = cn | (cs << 8); + sp.wIndex = (unitId << 8); + sp.wLength = 1; + + g_src = src; /* This will get picked up by out implementation of XUD_GetBuffer */ + + /* Call the function used by Endpoint0 to parse the control data and update the mixer output routing */ + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + + /* Note, there is a race risk here. This could be resolved by adding a handshake to UpdateMixerOutputRouting() etc */ + + /* Now check the mixer setting have been modified as expected. To do this we inspect "internal" + * mixer and endpoint 0 state. + * + * Going forward we might wish to enhance the mixer API such that it can be tested as a black box. + * This would require the addition of "GET" API over it's ctrl channel + */ + + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_D2H; + + if(mix == 0) + { + /* If mix is 0 then we need to check that all mixers have been updated */ + for(int i = 0; i < MAX_MIX_COUNT; i++) + { + assert(g_src == mixSel[i][cn]); + + /* Need to read back from each mixer individually */ + sp.wValue = cn | ((i + 1)<< 8); + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + } + } + else + { + assert(g_src == mixSel[cs-1][cn]); + + /* Test read back. Note, the checking is in our overridden implementation of XUD_SetBuffer_EpMax*/ + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + } + + } + + printstrln("PASS"); + exit(0); +} + +void Fake_XUA_AudioHub_CtrlTest(chanend c_mix_aud) +{ + int readBuffNo = 0; + unsigned underflowWord = 0; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord); + } +} + +void Fake_XUA_Buffer_Decouple_CtrlTest(chanend c_dec_mix) +{ + unsigned samplesIn[NUM_USB_CHAN_IN]; + unsigned underflowSample; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + select + { + case inuint_byref(c_dec_mix, underflowSample): + + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + outuint(c_dec_mix, 0); + } + + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + samplesIn[i] = inuint(c_dec_mix); + } + + break; + } + } +} + +int main() +{ + chan c_dec_mix; + chan c_mix_aud; + chan c_mix_ctl; + + par + { + /* We need "fake" versions of the AudioHub and Decouple to keep the mixer running and taking updates via + * it's control channel */ + Fake_XUA_Buffer_Decouple_CtrlTest(c_dec_mix); + Fake_XUA_AudioHub_CtrlTest(c_mix_aud); + + /* Mixer from lib_xua */ + mixer(c_dec_mix, c_mix_aud, c_mix_ctl); + + Fake_Endpoint0(c_mix_ctl); + } + + /* TODO to hit this we need to fully close down i.e. kill mixer */ + return 0; +} + diff --git a/tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn b/tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_mixer_routing_input_ctrl/src/xua_conf.h b/tests/test_mixer_routing_input_ctrl/src/xua_conf.h new file mode 100644 index 00000000..c27f54a6 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/src/xua_conf.h @@ -0,0 +1,45 @@ +// Copyright 2016-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (10) +#define NUM_USB_CHAN_IN (10) +#define I2S_CHANS_DAC (10) +#define I2S_CHANS_ADC (10) + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIXER (1) +#define MAX_MIX_COUNT (8) + +#define UAC_FORCE_FEEDBACK_EP (0) +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/test_mixer_routing_output/src/main.xc b/tests/test_mixer_routing_output/src/main.xc index 43a8ffb2..dfcef930 100644 --- a/tests/test_mixer_routing_output/src/main.xc +++ b/tests/test_mixer_routing_output/src/main.xc @@ -100,7 +100,7 @@ void UpdateModel(uint32_t modelOut[CHANNEL_MAP_AUD_SIZE], uint32_t modelMixerOut void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl) { uint32_t modelOut[CHANNEL_MAP_AUD_SIZE]; - uint32_t modelIn[NUM_USB_CHAN_IN]; + uint32_t modelIn[CHANNEL_MAP_USB_SIZE]; uint32_t modelMixerOut[MAX_MIX_COUNT]; uint32_t testCmd[] = {SET_SAMPLES_TO_HOST_MAP, SET_SAMPLES_TO_DEVICE_MAP}; @@ -146,8 +146,8 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl) { /* Make a random update to the routing - route a random source to a random destination */ unsigned map = testCmd[random_get_random_number(rg) % (sizeof(testCmd)/sizeof(testCmd[0]))]; - unsigned dst = random_get_random_number(rg) % CHANNEL_MAP_AUD_SIZE; - unsigned src = random_get_random_number(rg) % NUM_USB_CHAN_OUT; + unsigned dst = random_get_random_number(rg) % CHANNEL_MAP_AUD_SIZE; // TODO this should be CHANNEL_MAP_USB_SIZE for SET_SAMPLES_TO_HOST_MAP + unsigned src = random_get_random_number(rg) % (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); switch(map) { diff --git a/tests/test_mixer_routing_output/src/mixer_test_shared.h b/tests/test_mixer_routing_output/src/mixer_test_shared.h index b586059f..eb7e1ddd 100644 --- a/tests/test_mixer_routing_output/src/mixer_test_shared.h +++ b/tests/test_mixer_routing_output/src/mixer_test_shared.h @@ -11,6 +11,8 @@ */ #define CHANNEL_MAP_AUD_SIZE NUM_USB_CHAN_OUT +#define CHANNEL_MAP_USB_SIZE NUM_USB_CHAN_IN + /* Number of channel sources, the channel ordering is as follows * i.e. * [0:NUM_USB_CHAN_OUT-1] : Channels from USB Host diff --git a/tests/test_mixer_routing_output_ctrl.py b/tests/test_mixer_routing_output_ctrl.py new file mode 100644 index 00000000..d867c42e --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl.py @@ -0,0 +1,45 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +def do_test(options, capfd, test_file, test_seed): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{testname}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + max_cycles = 15000000 + + simargs = [ + "--max-cycles", + str(max_cycles), + ] + + build_options = [] + build_options += ["TEST_SEED=" + str(test_seed)] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + build_options=build_options, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +def test_mixer_routing_output(options, capfd, test_file, test_seed): + + result = do_test(options, capfd, test_file, test_seed) + + assert result diff --git a/tests/test_mixer_routing_output_ctrl/Makefile b/tests/test_mixer_routing_output_ctrl/Makefile new file mode 100644 index 00000000..a66c3717 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/Makefile @@ -0,0 +1,19 @@ + +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1 +else +TEST_DEBUG_FLAGS = +endif + +TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) -DXUD_WEAK_API=1 + +XCC_FLAGS = -O3 $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua lib_logging lib_random + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_mixer_routing_output_ctrl/src/main.xc b/tests/test_mixer_routing_output_ctrl/src/main.xc new file mode 100644 index 00000000..f6f887ea --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/src/main.xc @@ -0,0 +1,221 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* This tests checks the parsing of control requests to endpoint 0 cause the correct changes in mixer output routing */ + +#include +#include +#include "platform.h" +#include "xua.h" +#include "debug_print.h" +#include "assert.h" +#include "xud.h" +#include "usbaudio20.h" +#include "random.h" + +#ifndef TEST_ITERATIONS +#define TEST_ITERATIONS (100) +#endif + +#include "./../test_mixer_routing_output/src/mixer_test_shared.h" + +/* Device channel mapping */ +extern unsigned char channelMapAud[NUM_USB_CHAN_OUT]; +extern unsigned char channelMapUsb[NUM_USB_CHAN_IN]; + +/* From xua_ep0_uacreqs.xc */ +int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, chanend ?c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); + +/* From xua_endpoint0.c */ +void InitLocalMixerState(); + +int g_src = 0; + +/* Override func in lib_xud/src/user/client/XUD_EpFunctions.c for testing purposes */ +XUD_Result_t XUD_GetBuffer(XUD_ep ep_out, unsigned char buffer[], REFERENCE_PARAM(unsigned, length)) +{ + buffer[0] = g_src; + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_DoSetRequestStatus(XUD_ep ep_in) +{ + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_SetBuffer_EpMax(XUD_ep ep_in, unsigned char buffer[], unsigned datalength, unsigned epMax) +{ + assert(g_src == buffer[0]); + assert(datalength == 1); + return XUD_RES_OKAY; +} + +unsafe +{ + extern int volatile * const unsafe samples_to_device_map; + extern int volatile * const unsafe samples_to_host_map; +} + +void Fake_Endpoint0(chanend c_mix_ctl) +{ + XUD_ep ep0_out; /* Never initialised but not used */ + XUD_ep ep0_in; /* Never initialised but not used */ + unsigned unitIds[] = {ID_XU_OUT, ID_XU_IN}; + USB_SetupPacket_t sp; + + random_generator_t rg = random_create_generator_from_seed(TEST_SEED); + + InitLocalMixerState(); + + sp.bmRequestType.Type = USB_BM_REQTYPE_TYPE_CLASS; // Note, parsing of this won't be tested since we call AudioClassRequests directly + sp.bmRequestType.Recipient = USB_BM_REQTYPE_RECIP_INTER; // Note, parsing of this won't be tested since we call AudioClassRequests directly + + for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++) + { + int unitId = unitIds[random_get_random_number(rg) % (sizeof(unitIds)/sizeof(unitIds[0]))]; + unsigned dst = random_get_random_number(rg); + + /* Note, we don't currently support a mix input derived from another mix + * This is not trivial to test since the current mixer implementation only allows for one + * config update per "trigger" + */ + int src = random_get_random_number(rg) % NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT; + + switch(unitId) + { + case ID_XU_OUT: + dst %= CHANNEL_MAP_AUD_SIZE; + debug_printf("Mapping output to AudioIF: %d", dst); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + break; + + case ID_XU_IN: + dst %= CHANNEL_MAP_USB_SIZE; + debug_printf("Mapping output to Host : %d", dst); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + break; + + default: + printstr("ERROR: Bad cmd in stim(): "); + printintln(unitId); + break; + } + + /* Create Control request data for routing change */ + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_H2D; + sp.bRequest = CUR; + sp.wValue = dst & 0xff; + sp.wIndex = (unitId << 8); + sp.wLength = 1; + + g_src = src; /* This will get picked up by out implementation of XUD_GetBuffer */ + + /* Call the function used by Endpoint0() to parse the control data and update the mixer output routing */ + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + + /* Note, there is a race risk here. This could be resolved by adding a handshake to UpdateMixerOutputRouting() etc */ + + /* Now check the mixer setting have been modified as expected. To do this we inspect "internal" + * mixer and endpoint 0 state. + * + * Going forward we might wish to enhance the mixer API such that it can be tested as a black box. + * This would require the addition of "GET" API over it's ctrl channel + */ + switch(unitId) + { + case ID_XU_OUT: + assert(g_src == channelMapAud[dst]); + unsafe + { + assert(g_src == samples_to_device_map[dst]); + } + break; + + case ID_XU_IN: + assert(g_src == channelMapUsb[dst]); + unsafe + { + assert(g_src == samples_to_host_map[dst]); + } + break; + + default: + assert(0); + break; + } + + /* Test read back. Note, the checking is our overridden implementation of XUD_SetBuffer_EpMax*/ + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_D2H; + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + } + + printstrln("PASS"); + exit(0); +} + +void Fake_XUA_AudioHub_CtrlTest(chanend c_mix_aud) +{ + int readBuffNo = 0; + unsigned underflowWord = 0; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord); + } +} + +void Fake_XUA_Buffer_Decouple_CtrlTest(chanend c_dec_mix) +{ + unsigned samplesIn[NUM_USB_CHAN_IN]; + unsigned underflowSample; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + select + { + case inuint_byref(c_dec_mix, underflowSample): + + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + outuint(c_dec_mix, 0); + } + + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + samplesIn[i] = inuint(c_dec_mix); + } + + break; + } + } +} + +int main() +{ + chan c_dec_mix; + chan c_mix_aud; + chan c_mix_ctl; + + par + { + /* We need "fake" versions of the AudioHub and Decouple to keep the mixer running and taking updates via + * it's control channel */ + Fake_XUA_Buffer_Decouple_CtrlTest(c_dec_mix); + Fake_XUA_AudioHub_CtrlTest(c_mix_aud); + + /* Mixer from lib_xua */ + mixer(c_dec_mix, c_mix_aud, c_mix_ctl); + + Fake_Endpoint0(c_mix_ctl); + } + + /* TODO to hit this we need to fully close down i.e. kill mixer */ + return 0; +} + diff --git a/tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn b/tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_mixer_routing_output_ctrl/src/xua_conf.h b/tests/test_mixer_routing_output_ctrl/src/xua_conf.h new file mode 100644 index 00000000..c27f54a6 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/src/xua_conf.h @@ -0,0 +1,45 @@ +// Copyright 2016-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (10) +#define NUM_USB_CHAN_IN (10) +#define I2S_CHANS_DAC (10) +#define I2S_CHANS_ADC (10) + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIXER (1) +#define MAX_MIX_COUNT (8) + +#define UAC_FORCE_FEEDBACK_EP (0) +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif