- Added mixer related defines to reduce use of magic numbers

- Increases debug output when DEBUG flag set
- Removed some dead code
- Increased alignment of asm mixer functions
- Removed some usages of “xc_ptr” type in favour of native pointers in XC
- Added some asserts to mixer
- Added test_mixer_routing_input
- Moved test_mixer_routing_output to use shared code
This commit is contained in:
Ross Owen
2023-02-01 11:54:48 +00:00
parent 0ce91bec90
commit 73955c1a4c
13 changed files with 835 additions and 386 deletions

View File

@@ -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_input(options, capfd, test_file, test_seed):
result = do_test(options, capfd, test_file, test_seed)
assert result

View File

@@ -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)
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

View File

@@ -0,0 +1,243 @@
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Tests that routing of mixer inputs behaves as expected
*
* The device supports MAX_MIX_COUNT mixers each with MIX_INPUTS inputs.
*
* This test also assumes/checks that the default routing into each of the MIX_INPUTS inputs into
* each of the M mixer units is as follows:
*
* MIXER[0]:
* USB_FROM_HOST[0] -> MIXER[0].INPUT[0]
* USB_FROM_HOST[1] -> MIXER[0].INPUT[1]
* ...
USB_TO_HOST[0] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT]
USB_TO_HOST[1] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT+1]
...
* MIXER[MAX_MIX_COUNT-1]:
* USB_FROM_HOST[0] -> MIXER[MAX_MIX_COUNT-1].INPUT[0]
* USB_FROM_HOST[1] -> MIXER[MAX_MIX_COUNT-1].INPUT[1]
* ...
*
*/
#include <stdint.h>
#include <stddef.h>
#include "platform.h"
#include "xua.h"
#include "debug_print.h"
#include "assert.h"
#include "random.h"
#ifndef TEST_ITERATIONS
#define TEST_ITERATIONS (300)
#endif
#include "./../test_mixer_routing_output/src/mixer_test_shared.h"
struct ModelMixer
{
uint32_t deviceMap[NUM_USB_CHAN_OUT];
uint32_t hostMap[NUM_USB_CHAN_IN];
uint32_t mixMap_input[MAX_MIX_COUNT];
uint32_t mixMap_src[MAX_MIX_COUNT];
uint32_t mixOutput[MAX_MIX_COUNT];
};
void InitModel(struct ModelMixer &modelMixer)
{
for(size_t i = 0; i < NUM_USB_CHAN_OUT; i++)
{
modelMixer.deviceMap[i] = i;
}
for(size_t i = 0; i < NUM_USB_CHAN_IN; i++)
{
modelMixer.hostMap[i] = NUM_USB_CHAN_OUT+i;
}
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
// This test only allows for one "active" input to each mixer
modelMixer.mixMap_src[i] = i;
modelMixer.mixMap_input[i] = i;
uint32_t sample = i;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
modelMixer.mixOutput[i] = sample;
}
}
void GenExpectedSamples(struct ModelMixer &modelMixer,
uint32_t modelOut[NUM_USB_CHAN_OUT],
uint32_t modelIn[NUM_USB_CHAN_IN])
{
/* First generate model mix outputs - run MIX tiles to allow mix inputs derived from mix outputs to propagate */
for(int j = 0; j < MAX_MIX_COUNT; j++)
{
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
int src = modelMixer.mixMap_src[i];
modelMixer.mixOutput[i] = CreateSample(modelMixer.mixOutput, src);
}
}
for(size_t i = 0; i<NUM_USB_CHAN_OUT; i++)
{
int src = modelMixer.deviceMap[i];
modelOut[i] = CreateSample(modelMixer.mixOutput, src);
}
for(size_t i = 0; i<NUM_USB_CHAN_IN; i++)
{
int src = modelMixer.hostMap[i];
modelIn[i] = CreateSample(modelMixer.mixOutput, src);
}
}
void MapMixerInput(int mix, int input, int src, struct ModelMixer &modelMixer, chanend c_mix_ctl,
chanend c_stim_ah, chanend c_stim_de, uint32_t modelIn[], uint32_t modelOut[])
{
debug_printf("Mapping mix %d input %d", mix, input);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
/* This test only allows for one input to travel "untouched" to the mix output - since this test doesn't model the actual mixing.
* Because of this we must also mod the mixer weights, not just the mixer input map.
* If we simply just apply an update to the mixer input mapping it would not produce an observable difference on the mixer output
*/
/* Set previously "activated" input weight to 0 */
debug_printf("Setting mix %d, weight %d to 0\n", mix, modelMixer.mixMap_input[mix]);
SendTrigger(c_stim_ah, 1);
UpdateMixerWeight(c_mix_ctl, mix, modelMixer.mixMap_input[mix], 0);
/* Set new "activated" input wright to max (i.e. x1) */
debug_printf("Setting mix %d, weight %d to %x\n", mix, input, XUA_MIXER_MAX_MULT);
SendTrigger(c_stim_ah, 1);
UpdateMixerWeight(c_mix_ctl, mix, input, XUA_MIXER_MAX_MULT);
/* Update mixer input in model */
modelMixer.mixMap_src[mix] = src;
modelMixer.mixMap_input[mix] = input;
/* Run twice to allow mix inputs derived from mix outputs to propagate */
GenExpectedSamples(modelMixer, modelOut, modelIn);
/* Finally update the acutal mixer input map */
SendTrigger(c_stim_ah, 1);
UpdateMixMap(c_mix_ctl, mix, input, src);
SendTrigger(c_stim_ah, 1);
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
}
/* This task configures the routing and maintains a model of the expected routing output
* it provides this to the Fake AudioHub and Fake Decouple tasks such that they can self check
*/
void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
{
uint32_t modelOut[NUM_USB_CHAN_OUT];
uint32_t modelIn[NUM_USB_CHAN_IN];
random_generator_t rg = random_create_generator_from_seed(TEST_SEED);
struct ModelMixer modelMixer;
InitModel(modelMixer);
GenExpectedSamples(modelMixer, modelOut, modelIn);
/* There is single sample delay between the two mixer cores, so trigger twice to flush though a block
* of zero samples */
SendTrigger(c_stim_ah, 2);
/* Send expected samples to AH and DE and run checks */
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
/* Firstly route mixer outputs to the audio interfaces (we could have chosen host)
* such that we can observe and check the outputs from the mixer
*/
for(size_t i = 0; i < MAX_MIX_COUNT; i++)
{
int map = SET_SAMPLES_TO_DEVICE_MAP;
assert(i < NUM_USB_CHAN_OUT);
int dst = i;
int src = NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN+i; // mix0, mix1..
debug_printf("Mapping output to AudioIF: %d ", dst);
PrintDestString(map, dst);
debug_printf(" from %d", src);
PrintSourceString(src);
debug_printf("\n");
SendTrigger(c_stim_ah, 1);
/* Update the mixer */
UpdateMixerOutputRouting(c_mix_ctl, map, dst, src);
/* Update the model */
modelMixer.deviceMap[dst] = src;
}
/* Send expected samples to fake AudioHub and Decouple for checking */
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++)
{
/* Make a random update to the routing - route a random source to a random mix input */
unsigned mix = random_get_random_number(rg) % MAX_MIX_COUNT;
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("Iteration: %d\n", testIter);
MapMixerInput(mix, input, src, modelMixer, c_mix_ctl, c_stim_ah, c_stim_de, modelIn, modelOut);
}
/* Send kill messages to Fake AudioHub & Fake Decouple */
outct(c_stim_ah, XS1_CT_END);
inct(c_stim_ah);
outct(c_stim_de, XS1_CT_END);
inct(c_stim_de);
printstrln("PASS");
exit(0);
}
int main()
{
chan c_dec_mix;
chan c_mix_aud;
chan c_mix_ctl;
chan c_stim_ah;
chan c_stim_de;
par
{
Fake_XUA_Buffer_Decouple(c_dec_mix, c_stim_de);
Fake_XUA_AudioHub(c_mix_aud, c_stim_ah);
/* Mixer from lib_xua */
mixer(c_dec_mix, c_mix_aud, c_mix_ctl);
stim(c_stim_ah, c_stim_de, c_mix_ctl);
}
/* TODO to hit this we need to fully close down i.e. kill mixer */
return 0;
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Network xmlns="http://www.xmos.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.xmos.com http://www.xmos.com">
<Declarations>
<Declaration>tileref tile[2]</Declaration>
</Declarations>
<Packages>
<Package id="0" Type="XS3-UnA-1024-FB265">
<Nodes>
<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" Oscillator="24MHz" SystemFrequency="600MHz" ReferenceFrequency="100MHz">
<Tile Number="0" Reference="tile[0]"/>
<Tile Number="1" Reference="tile[1]"/>
</Node>
</Nodes>
</Package>
</Packages>
<JTAGChain>
<JTAGDevice NodeId="0"/>
</JTAGChain>
</Network>

View File

@@ -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

View File

@@ -1,7 +1,15 @@
TEST_FLAGS ?= -DTEST_SEED=$(TEST_SEED)
DEBUG ?= 0
XCC_FLAGS = -O3 -g -DDEBUG_PRINT_ENABLE_main=0 $(TEST_FLAGS)
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)
XCC_FLAGS = -O3 $(TEST_FLAGS)
TARGET = test_xs3_600.xn

View File

@@ -43,7 +43,6 @@
#include <stddef.h>
#include "platform.h"
#include "xua.h"
#define DEBUG_UNIT main
#include "debug_print.h"
#include "assert.h"
#include "random.h"
@@ -52,115 +51,7 @@
#define TEST_ITERATIONS (100)
#endif
#ifndef TEST_SEED
#error TEST_SEED must be defined!
#endif
void exit(int);
// Test sample format:
// byte[0]: Sample counter
// byte[1]: Channel
// byte[3]: Source (HOST:1/AUD IF:0)
#define SRC_HOST (2)
#define SRC_AUDIF (1)
#define SRC_OFF (0) // Important that this is 0 since mixer will generate 0 samples for 'off'
#define GET_COUNT(x) (x & 0xff)
#define GET_CHANNEL(x) ((x >> 8) & 0xff)
#define GET_SOURCE(x) ((x >> 16) & 0xff)
#define SET_COUNT(x, y) y = y & 0xff; x = x | y;
#define SET_CHANNEL(x, y) y = y & 0xff; x = x | (y<<8);
#define SET_SOURCE(x, y) x = x | (y<<16);
/* A limitation of the design is that the number of routable output destinations cannot be larger than NUM_USB_CHAN_OUT.
* This is due to the transfer samples from Mixer to AudioHub tasks being in blocks of NUM_USB_CHAN_OUT.
* This is not normally an issue - since every physical output interface channel on the device is normally derived from a
* USB channel from the host, but it certainly is a restriction.
*/
#define CHANNEL_MAP_AUD_SIZE NUM_USB_CHAN_OUT
/* Number of channel sources, the channel ordering is as follows
* i.e.
* [0:NUM_USB_CHAN_OUT-1] : Channels from USB Host
* [NUM_USB_CHAN_OUT:NUM_USB_CHAN_IN-1] : Channels from Audio Interfaces
* [NUM_USB_CHAN_N:MAX_MIX_COUNT-1] : Channels from Mixers
* [MAX_MIX_COUNT]: "Off" (Essentially samples always 0)
*/
/* Note, One larger for an "off" channel for mixer sources" */
#define SOURCE_COUNT (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1)
#pragma select handler
static inline void testct_byref(chanend c, unsigned &isCt)
{
isCt = testct(c);
}
void PrintSourceString(unsigned source)
{
debug_printf(" ");
if(source < NUM_USB_CHAN_OUT)
{
debug_printf("(DEVICE IN - HOST%d)", source);
}
else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN))
{
debug_printf("(DEVICE IN - AudioIF %d)", source - NUM_USB_CHAN_OUT);
}
else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT))
{
debug_printf("(MIX %d)", source - NUM_USB_CHAN_OUT - NUM_USB_CHAN_IN);
}
else
debug_printf("(off)");
debug_printf(" ");
}
void PrintDestString(unsigned map, unsigned dest)
{
switch(map)
{
case SET_SAMPLES_TO_DEVICE_MAP:
debug_printf("(DEVICE OUT - AudioIF)");
break;
case SET_SAMPLES_TO_HOST_MAP:
debug_printf("(DEVICE OUT - HOST)");
break;
}
}
void PrintSample(unsigned sample)
{
debug_printf("SOURCE: ");
if(GET_SOURCE(sample) == SRC_HOST)
debug_printf("HOST ");
else if(GET_SOURCE(sample) == SRC_AUDIF)
debug_printf("AUDIF ");
else if(GET_SOURCE(sample) == SRC_OFF)
debug_printf("OFF ");
else
debug_printf("UNKNOWN ");
debug_printf("CHANNEL: %d", GET_CHANNEL(sample));
}
/* Required by lib_xua */
void AudioHwInit()
{
return;
}
/* Required by lib_xua */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
return;
}
/* From xua_ep0_uacreqs.xc */
void UpdateMixerOutputRouting(chanend c_mix_ctl, unsigned map, unsigned dst, unsigned src);
void UpdateMixMap(chanend c_mix_ctl, int mix, int input, int src);
void UpdateMixerWeight(chanend c_mix_ctl, int mix, int index, unsigned val);
#include "./mixer_test_shared.h"
void UpdateModel(uint32_t modelOut[CHANNEL_MAP_AUD_SIZE], uint32_t modelMixerOut[MAX_MIX_COUNT], uint32_t modelIn[NUM_USB_CHAN_IN],
int map, int dst, int src)
@@ -214,7 +105,6 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
uint32_t testCmd[] = {SET_SAMPLES_TO_HOST_MAP, SET_SAMPLES_TO_DEVICE_MAP};
random_generator_t rg = random_create_generator_from_seed(TEST_SEED);
//assert(NUM_USB_CHAN_OUT >= MIX_INPUTS);
/* By default the mixer should output samples from USB host unmodified
* See mixer.xc L780
@@ -247,17 +137,10 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
modelIn[i] = sample;
}
outuint(c_stim_ah, 0);
/* Check default routing */
/* Send expected to AudioHub */
for(int i = 0; i < CHANNEL_MAP_AUD_SIZE; i++)
{
outuint(c_stim_ah, modelOut[i]);
}
/* Wait for handshake back and move on to next test */
inuint(c_stim_ah);
SendTrigger(c_stim_ah, 2);
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++)
{
@@ -276,6 +159,7 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
debug_printf("\n");
/* Update the mixer */
SendTrigger(c_stim_ah, 1);
UpdateMixerOutputRouting(c_mix_ctl, map, dst, src);
break;
@@ -287,43 +171,23 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
debug_printf("\n");
/* Update the mixer */
SendTrigger(c_stim_ah, 1);
UpdateMixerOutputRouting(c_mix_ctl, map, dst, src);
break;
default:
printstrln("ERROR BAD CMD");
printstr("ERROR: Bad cmd in stim(): ");
printintln(map);
break;
}
/* Update the model */
UpdateModel(modelOut, modelMixerOut, modelIn, map, dst, src);
/* Send expected to AudioHub */
outuint(c_stim_ah, 0);
for(int i = 0; i < CHANNEL_MAP_AUD_SIZE; i++)
{
outuint(c_stim_ah, modelOut[i]);
}
SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn);
/* Wait for handshake back and move on to next test */
inuint(c_stim_ah);
/* Send expected to Decouple */
outuint(c_stim_de, 0);
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_stim_de, modelIn[i]);
}
/* Wait for handshake back and move on to next test */
inuint(c_stim_de);
}
timer t;
unsigned time;
t :> time;
t when timerafter(time+10000) :> void;
/* Send kill messages to Fake AudioHub & Fake Decouple */
outct(c_stim_ah, XS1_CT_END);
inct(c_stim_ah);
@@ -335,136 +199,6 @@ void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl)
exit(0);
}
void CheckBlock(unsigned samplesOut[], uint32_t expectedOut[], size_t len)
{
for(int j = 0; j < len; j++)
{
debug_printf("%d: Expected: ", j);
PrintSample(expectedOut[j]);
debug_printf("\n");
if(expectedOut[j] != samplesOut[j])
{
printstr("ERROR: Actual: ");
PrintSample(samplesOut[j]);
}
assert(expectedOut[j] == samplesOut[j]);
}
}
/* From xua_audiohub.xc */
extern unsigned samplesOut[NUM_USB_CHAN_OUT];
extern unsigned samplesIn[2][NUM_USB_CHAN_IN];
#include "xua_audiohub_st.h"
int Fake_XUA_AudioHub(chanend c_mix_aud, chanend c_stim)
{
int readBuffNo = 0;
unsigned underflowWord = 0;
uint32_t expectedOut[NUM_USB_CHAN_OUT];
unsigned ct = 0;
for(size_t i = 0; i < NUM_USB_CHAN_IN; i++)
{
/* Note, we only used readBufNo = 0 */
unsigned sample = 0;
SET_SOURCE(sample, SRC_AUDIF);
SET_CHANNEL(sample, i);
samplesIn[0][i] = sample;
}
while(!ct)
{
/* This will populate samplesOut and send out samplesIn[readBuffNo] */
unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord);
select
{
case testct_byref(c_stim, ct):
if(!ct)
{
inuint(c_stim); // TODO don't really need this
/* Get expected */
for(int j = 0; j < NUM_USB_CHAN_OUT; j++)
{
expectedOut[j] = inuint(c_stim);
}
CheckBlock(samplesOut, expectedOut, NUM_USB_CHAN_OUT);
/* Handshake back */
outuint(c_stim, 0);
}
break;
default:
break;
}
}
outct(c_stim, XS1_CT_END);
inct(c_stim);
return 0;
}
int Fake_XUA_Buffer_Decouple(chanend c_dec_mix, chanend c_stim)
{
unsigned tmp;
uint32_t expectedSamplesIn[NUM_USB_CHAN_IN];
unsigned samplesIn[NUM_USB_CHAN_IN];
unsigned ct;
unsigned underflowSample;
while(!ct)
{
select
{
case inuint_byref(c_dec_mix, underflowSample):
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
unsigned sample = 0;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
outuint(c_dec_mix, sample);
}
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
samplesIn[i] = inuint(c_dec_mix);
}
break;
case testct_byref(c_stim, ct):
if(!ct)
{
inuint(c_stim); // TODO don't really need this
/* Get expected */
for(int j = 0; j < NUM_USB_CHAN_IN; j++)
{
expectedSamplesIn[j] = inuint(c_stim);
}
CheckBlock(samplesIn, expectedSamplesIn, NUM_USB_CHAN_IN);
/* Handshake back */
outuint(c_stim, 0);
}
break;
default:
break;
}
}
outct(c_stim, XS1_CT_END);
inct(c_stim);
return 0;
}
int main()
{
chan c_dec_mix;
@@ -487,3 +221,4 @@ int main()
/* TODO to hit this we need to fully close down i.e. kill mixer */
return 0;
}

View File

@@ -0,0 +1,323 @@
// Copyright 2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef TEST_SEED
#error TEST_SEED must be defined!
#endif
/* A limitation of the design is that the number of routable output destinations cannot be larger than NUM_USB_CHAN_OUT.
* This is due to the transfer samples from Mixer to AudioHub tasks being in blocks of NUM_USB_CHAN_OUT.
* This is not normally an issue - since every physical output interface channel on the device is normally derived from a
* USB channel from the host, but it certainly is a restriction.
*/
#define CHANNEL_MAP_AUD_SIZE NUM_USB_CHAN_OUT
/* Number of channel sources, the channel ordering is as follows
* i.e.
* [0:NUM_USB_CHAN_OUT-1] : Channels from USB Host
* [NUM_USB_CHAN_OUT:NUM_USB_CHAN_IN-1] : Channels from Audio Interfaces
* [NUM_USB_CHAN_N:MAX_MIX_COUNT-1] : Channels from Mixers
* [MAX_MIX_COUNT]: "Off" (Essentially samples always 0)
*/
/* Note, One larger for an "off" channel for mixer sources" */
#define SOURCE_COUNT (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1)
#define SET_EXPECTED (9)
#define TRIGGER (7)
// Test sample format:
// byte[0]: Sample counter
// byte[1]: Channel
// byte[3]: Source (HOST:1/AUD IF:0)
#define SRC_HOST (2)
#define SRC_AUDIF (1)
#define SRC_OFF (0) // Important that this is 0 since mixer will generate 0 samples for 'off'
#define GET_COUNT(x) ((x>>8) & 0xff)
#define GET_CHANNEL(x) ((x >> 16) & 0xff)
#define GET_SOURCE(x) ((x >> 24) & 0xff)
#define SET_COUNT(x, y) y = y & 0xff; x = x | (y<<8);
#define SET_CHANNEL(x, y) y = y & 0xff; x = x | (y<<16);
#define SET_SOURCE(x, y) x = x | (y<<24);
void exit(int);
#pragma select handler
static inline void testct_byref(chanend c, unsigned &isCt)
{
isCt = testct(c);
}
void SendTrigger(chanend c_stim_ah, int count)
{
for(int i = 0; i < count; i++)
outuint(c_stim_ah, TRIGGER);
}
uint32_t CreateSample(uint32_t modelMixerOutput[], int src)
{
uint32_t sample = 0;
if(src == (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT))
{
SET_SOURCE(sample, SRC_OFF);
}
else if(src >= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN))
{
src -= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN);
sample = modelMixerOutput[src];
}
else if (src >= NUM_USB_CHAN_IN)
{
SET_SOURCE(sample, SRC_AUDIF);
src -= NUM_USB_CHAN_OUT;
SET_CHANNEL(sample, src);
}
else
{
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, src);
}
return sample;
}
void PrintSourceString(unsigned source)
{
debug_printf(" ");
if(source < NUM_USB_CHAN_OUT)
{
debug_printf("(DEVICE IN - HOST%d)", source);
}
else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN))
{
debug_printf("(DEVICE IN - AudioIF %d)", source - NUM_USB_CHAN_OUT);
}
else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT))
{
debug_printf("(MIX %d)", source - NUM_USB_CHAN_OUT - NUM_USB_CHAN_IN);
}
else
debug_printf("(off)");
debug_printf(" ");
}
void PrintDestString(unsigned map, unsigned dest)
{
switch(map)
{
case SET_SAMPLES_TO_DEVICE_MAP:
debug_printf("(DEVICE OUT - AudioIF)");
break;
case SET_SAMPLES_TO_HOST_MAP:
debug_printf("(DEVICE OUT - HOST)");
break;
}
}
void PrintSample(unsigned sample)
{
debug_printf("SOURCE: ");
if(GET_SOURCE(sample) == SRC_HOST)
debug_printf("HOST ");
else if(GET_SOURCE(sample) == SRC_AUDIF)
debug_printf("AUDIF ");
else if(GET_SOURCE(sample) == SRC_OFF)
debug_printf("OFF ");
else
debug_printf("UNKNOWN ");
debug_printf("CHANNEL: %d", GET_CHANNEL(sample));
}
/* Required by lib_xua */
void AudioHwInit()
{
return;
}
/* Required by lib_xua */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
return;
}
/* From xua_ep0_uacreqs.xc */
void UpdateMixerOutputRouting(chanend c_mix_ctl, unsigned map, unsigned dst, unsigned src);
void UpdateMixMap(chanend c_mix_ctl, int mix, int input, int src);
void UpdateMixerWeight(chanend c_mix_ctl, int mix, int index, unsigned val);
void CheckBlock(unsigned samplesOut[], uint32_t expectedOut[], size_t len)
{
int fail = 0;;
for(int j = 0; j < len; j++)
{
debug_printf("%d: Expected: ", j);
PrintSample(expectedOut[j]);
debug_printf("\n");
if(expectedOut[j] != samplesOut[j])
{
printstr("ERROR: Actual: ");
PrintSample(samplesOut[j]);
debug_printf(" (%x)", samplesOut[j]);
printstr("\n");
fail = 1;
}
//assert(expectedOut[j] == samplesOut[j]);
}
assert(!fail);
}
/* Sending expected also causes fake_audiohub and fake_decouple to run sample checks */
void SendExpected(chanend c_stim_ah, chanend c_stim_de, uint32_t modelOut[], uint32_t modelIn[])
{
/* Send expected to AudioHub */
outuint(c_stim_ah, SET_EXPECTED);
for(int i = 0; i < CHANNEL_MAP_AUD_SIZE; i++)
{
outuint(c_stim_ah, modelOut[i]);
}
/* Wait for handshake back and move on to next test */
inuint(c_stim_ah);
/* Send expected to Decouple */
outuint(c_stim_de, SET_EXPECTED);
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_stim_de, modelIn[i]);
}
/* Wait for handshake back and move on to next test */
inuint(c_stim_de);
}
/* From xua_audiohub.xc */
extern unsigned samplesOut[NUM_USB_CHAN_OUT];
extern unsigned samplesIn[2][NUM_USB_CHAN_IN];
#include "xua_audiohub_st.h"
int Fake_XUA_AudioHub(chanend c_mix_aud, chanend c_stim)
{
int readBuffNo = 0;
unsigned underflowWord = 0;
uint32_t expectedOut[NUM_USB_CHAN_OUT];
unsigned ct = 0;
unsigned cmd = 0;
for(size_t i = 0; i < NUM_USB_CHAN_IN; i++)
{
/* Note, we only used readBufNo = 0 */
unsigned sample = 0;
SET_SOURCE(sample, SRC_AUDIF);
SET_CHANNEL(sample, i);
samplesIn[0][i] = sample;
}
while(!ct)
{
select
{
case testct_byref(c_stim, ct):
if(!ct)
{
cmd = inuint(c_stim);
switch(cmd)
{
case SET_EXPECTED:
for(int j = 0; j < NUM_USB_CHAN_OUT; j++)
{
expectedOut[j] = inuint(c_stim);
}
debug_printf("AudioHub:\n");
CheckBlock(samplesOut, expectedOut, NUM_USB_CHAN_OUT);
/* Handshake back */
outuint(c_stim, 0);
break;
case TRIGGER:
/* This will populate samplesOut and send out samplesIn[readBuffNo] */
unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord);
break;
default:
printstr("ERROR: bad cmd in Fake_XUA_AudioHub: ");
printintln(cmd);
assert(0);
break;
}
}
break;
}
}
outct(c_stim, XS1_CT_END);
inct(c_stim);
return 0;
}
int Fake_XUA_Buffer_Decouple(chanend c_dec_mix, chanend c_stim)
{
uint32_t expectedSamplesIn[NUM_USB_CHAN_IN];
unsigned samplesIn[NUM_USB_CHAN_IN];
unsigned ct;
unsigned underflowSample;
while(!ct)
{
select
{
case inuint_byref(c_dec_mix, underflowSample):
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
unsigned sample = 0;
SET_SOURCE(sample, SRC_HOST);
SET_CHANNEL(sample, i);
outuint(c_dec_mix, sample);
}
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
samplesIn[i] = inuint(c_dec_mix);
}
break;
case testct_byref(c_stim, ct):
if(!ct)
{
inuint(c_stim); // TODO don't really need this
/* Get expected */
for(int j = 0; j < NUM_USB_CHAN_IN; j++)
{
expectedSamplesIn[j] = inuint(c_stim);
}
debug_printf("Decouple:\n");
CheckBlock(samplesIn, expectedSamplesIn, NUM_USB_CHAN_IN);
/* Handshake back */
outuint(c_stim, 0);
}
break;
}
}
outct(c_stim, XS1_CT_END);
inct(c_stim);
return 0;
}