Variable i2s bit width (#331)

- Add support for variable width I2S (via XUA_I2S_N_BITS)
- Add support for variable width TDM (again via XUD_I2S_N_BITS when XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM)
- Includes support for xcore as I2S/TDM master and slave
- Add testing of the the above to test_i2s_loopback
- Rationalised test config building in test_i2s_loopback
- Documentation updated
This commit is contained in:
Ross Owen
2023-06-08 15:31:12 +01:00
committed by GitHub
parent d3ad29e8a6
commit 1ef5129fde
16 changed files with 221 additions and 196 deletions

View File

@@ -4,9 +4,10 @@ lib_xua Change Log
UNRELEASED
----------
* ADDED: Configurable word-length for I2S/TDM via XUA_I2S_N_BITS
* FIXED: Memory corruption due to erroneous initialisation of mixer weights when not in use (#152)
* FIXED: UserHostActive() not being called as expected (#326)
* FIXED: Exception when entering DSD mode
* FIXED: Exception when entering DSD mode (#327)
3.4.0
-----

View File

@@ -93,7 +93,11 @@
#define XUA_PCM_FORMAT_I2S (0)
#define XUA_PCM_FORMAT_TDM (1)
/**
* @brief Format of PCM audio interface. Should be set to XUA_PCM_FORMAT_I2S or XUA_PCM_FORMAT_TDM
*
* Default: XUA_PCM_FORMAT_I2S
*/
#ifdef XUA_PCM_FORMAT
#if (XUA_PCM_FORMAT != XUA_PCM_FORMAT_I2S) && (XUA_PCM_FORMAT != XUA_PCM_FORMAT_TDM)
#error Bad value for XUA_PCM_FORMAT
@@ -193,6 +197,19 @@
#define I2S_DOWNSAMPLE_CHANS_IN I2S_CHANS_ADC
#endif
/**
* @brief Number of bits per channel for I2S/TDM. Supported values: 16/32-bit.
*
* Default: 32 bits
*/
#ifndef XUA_I2S_N_BITS
#define XUA_I2S_N_BITS (32)
#endif
#if (XUA_I2S_N_BITS != 16) && (XUA_I2S_N_BITS != 32)
#error Unsupported value for XUA_I2S_N_BITS (only values 16/32 supported)
#endif
/**
* @brief Max supported sample frequency for device (Hz). Default: 192000
*/

View File

@@ -50,6 +50,15 @@ Audio Class
Feature Configuration
---------------------
I2S/TDM
^^^^^^^
.. doxygendefine:: I2S_CHANS_DAC
.. doxygendefine:: I2S_CHANS_ADC
.. doxygendefine:: CODEC_MASTER
.. doxygendefine:: XUA_I2S_N_BITS
.. doxygendefine:: XUA_PCM_FORMAT
MIDI
^^^^

View File

@@ -23,11 +23,14 @@ The defines in :ref:`opt_i2s_defines` effect the I2S implementation.
- The desired number of input channels via I2S (0 for disabled)
- N/A (Must be defined)
* - ``XUA_PCM_FORMAT``
- Enabled either TDM or I2S mode
- Enables either TDM or I2S mode
- ``XUA_PCM_FORMAT_I2S``
* - ``CODEC_MASTER``
- Sets is xCORE is I2S master or slave
- Sets if xCORE is I2S master or slave
- ``0`` (xCORE is master)
* - ``XUA_I2S_N_BITS``
- I2S/TDM word length (16, 32-bit supported)
- ``32``
The I2S code expects that the ports required for I2S (master clock, LR-clock, bit-clock and data lines) are be defined in the application XN file in the relevant `Tile``.
For example::
@@ -42,8 +45,16 @@ For example::
<Port Location="XS1_PORT_1G" Name="PORT_I2S_ADC1"/>
</Tile>
All of the I2S related ports must be 1-bit ports.
All of the I2S/TDM related ports must be 1-bit ports.
.. note::
TDM mode allows 8 channels (rather than 2) to be supplied on each dataline.
TDM mode allows 8 channels (rather than 2) to be supplied on each data-line.
.. note::
Data output/input is in "I2S" format, rather than, say "left-justified" or "right-justified" formats.
I2S format specifies a single bit-clock delay after the LR-clock transition before sample-data is driven/received.
This also applies to TDM mode. TDM support in ADC/DAC hardware is quite varied, an "offset" value may need to be programmed into
the external device for compatible operation.

View File

@@ -21,7 +21,7 @@ full listing of these ``TILE`` defines.
- Description
- Default
* - ``AUDIO_IO_TILE``
- Tile on which I2S, ADAT Rx, S/PDIF Rx & mixer resides
- Tile on which I2S/TDM, ADAT Rx, S/PDIF Rx & mixer resides
- ``0``
* - ``XUD_TILE``
- Tile on which USB resides, including buffering for all USB interfaces/endppoints

View File

@@ -25,7 +25,7 @@ Overview
| +---------------------------------------------------------------------------------------------+
| | `USB Midi Device Class 1.0 <http://www.usb.org/developers/devclass_docs/midi10.pdf>`_ |
+---------------------------------+---------------------------------------------------------------------------------------------+
| Audio | I2S/TDM |
| Audio | I2S/TDM (16/32-bit) |
| +---------------------------------------------------------------------------------------------+
| | S/PDIF |
| +---------------------------------------------------------------------------------------------+

View File

@@ -5,7 +5,7 @@ I2S/TDM
I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core.
In order to enable I2S on must declare an array of ports for the data-lines (one for each direction)::
In order to enable I2S/TDM on must declare an array of ports for the data-lines (one for each direction)::
/* Port declarations. Note, the defines come from the XN file */
buffered out port:32 p_i2s_dac[] = {PORT_I2S_DAC0}; /* I2S Data-line(s) */
@@ -22,7 +22,7 @@ Ports for the sample and bit clocks are also required::
These ports must then be passed to the ``XUA_AudioHub()`` task appropriately.
I2S functionality also requires two clock-blocks, one for bit and sample clock e.g.::
I2S/TDM functionality also requires two clock-blocks, one for bit-clock and another for the master clock e.g.::
/* Clock-block declarations */
clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_4; /* Bit clock */

View File

@@ -11,7 +11,7 @@ endif
DEPENDENT_MODULES = lib_locks(>=2.1.0) \
lib_logging(>=3.1.1) \
lib_mic_array(>=4.5.0) \
lib_spdif(>=4.2.1) \
lib_spdif(>=5.0.0) \
lib_xassert(>=4.1.0) \
lib_xud(>=2.2.3) \
lib_adat(>=1.0.0)

View File

@@ -1,7 +1,6 @@
// Copyright 2018-2022 XMOS LIMITED.
// Copyright 2018-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h"
#include "dsd_support.h"
#if (DSD_CHANS_DAC != 0)
@@ -12,7 +11,7 @@ extern buffered out port:32 p_dsd_clk;
extern unsigned dsdMode;
#if !CODEC_MASTER
void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC])
void InitPorts_master(buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC])
{
#if (DSD_CHANS_DAC > 0)
if(dsdMode == DSD_MODE_OFF)
@@ -39,7 +38,12 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu
#endif
unsigned tmp;
p_lrclk <: 0 @ tmp;
if(XUA_I2S_N_BITS == 32)
p_lrclk <: 0 @ tmp;
else
tmp = partout_timestamped(p_lrclk, XUA_I2S_N_BITS, 0);
tmp += 100;
/* Since BCLK is free-running, setup outputs/inputs at a known point in the future */
@@ -47,19 +51,30 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
p_i2s_dac[i] @ tmp <: 0;
if(XUA_I2S_N_BITS == 32)
p_i2s_dac[i] @ tmp <: 0;
else
partout_timed(p_i2s_dac[i], XUA_I2S_N_BITS, 0, tmp);
}
#endif
unsigned lrClkVal = 0x7FFFFFFF;
if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
p_lrclk @ tmp <: 0x80000000;
{
lrClkVal = 0x80000000;
}
if(XUA_I2S_N_BITS == 32)
p_lrclk @ tmp <: lrClkVal;
else
p_lrclk @ tmp <: 0x7FFFFFFF;
partout_timed(p_lrclk, XUA_I2S_N_BITS, lrClkVal, tmp);
#if (I2S_CHANS_ADC != 0)
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
asm("setpt res[%0], %1"::"r"(p_i2s_adc[i]),"r"(tmp-1));
if(XUA_I2S_N_BITS != 32)
set_port_shift_count(p_i2s_adc[i], XUA_I2S_N_BITS);
}
#endif
#endif /* (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0) */
@@ -75,7 +90,7 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu
#endif
}
#else
void InitPorts_slave(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC])
void InitPorts_slave(buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC])
{
#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0)
unsigned tmp;
@@ -92,7 +107,7 @@ void InitPorts_slave(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buf
p_lrclk when pinseq(0) :> void @ tmp;
#endif
tmp += (I2S_CHANS_PER_FRAME * 32) - 32 + 1 ;
tmp += ((I2S_CHANS_PER_FRAME * XUA_I2S_N_BITS) - XUA_I2S_N_BITS + 1) ;
/* E.g. 2 * 32 - 32 + 1 = 33 for stereo */
/* E.g. 8 * 32 - 32 + 1 = 225 for 8 chan TDM */
@@ -100,7 +115,10 @@ void InitPorts_slave(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buf
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
p_i2s_dac[i] @ tmp <: 0;
if(XUA_I2S_N_BITS == 32)
p_i2s_dac[i] @ tmp <: 0;
else
partout_timed(p_i2s_dac[i], XUA_I2S_N_BITS, 0, tmp);
}
#endif
@@ -108,11 +126,15 @@ void InitPorts_slave(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buf
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
asm("setpt res[%0], %1"::"r"(p_i2s_adc[i]),"r"(tmp-1));
asm("setpt res[%0], %1"::"r"(p_i2s_adc[i]),"r"(tmp-1));
if(XUA_I2S_N_BITS != 32)
set_port_shift_count(p_i2s_adc[i], XUA_I2S_N_BITS);
}
#endif
asm("setpt res[%0], %1"::"r"(p_lrclk),"r"(tmp-1));
if(XUA_I2S_N_BITS != 32)
set_port_shift_count(p_lrclk, XUA_I2S_N_BITS);
#endif /* (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0) */
}
#endif

View File

@@ -15,6 +15,8 @@
#include <xclib.h>
#include <xs1_su.h>
#include <string.h>
#include <xassert.h>
#include "xua.h"
@@ -50,19 +52,6 @@ unsigned samplesOut[MAX(NUM_USB_CHAN_OUT, I2S_CHANS_DAC)];
unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)];
#ifdef XTA_TIMING_AUDIO
#pragma xta command "add exclusion received_command"
#pragma xta command "analyse path i2s_output_l i2s_output_r"
#pragma xta command "set required - 2000 ns"
#pragma xta command "add exclusion received_command"
#pragma xta command "add exclusion received_underflow"
#pragma xta command "add exclusion divide_1"
#pragma xta command "add exclusion deliver_return"
#pragma xta command "analyse path i2s_output_r i2s_output_l"
#pragma xta command "set required - 2000 ns"
#endif
#if (XUA_ADAT_TX_EN)
extern buffered out port:32 p_adat_tx;
#endif
@@ -76,7 +65,7 @@ void InitPorts_slave
#else
void InitPorts_master
#endif
(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
(buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]);
@@ -96,7 +85,17 @@ static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:3
#if CODEC_MASTER
unsigned syncError = 0;
unsigned lrval = 0;
p_lrclk :> lrval;
const unsigned lrval_mask = (0xffffffff << (32 - XUA_I2S_N_BITS));
if(XUA_I2S_N_BITS != 32)
{
asm volatile("in %0, res[%1]":"=r"(lrval):"r"(p_lrclk):"memory");
set_port_shift_count(p_lrclk, XUA_I2S_N_BITS);
}
else
{
p_lrclk :> lrval;
}
if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
{
@@ -114,30 +113,46 @@ static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:3
}
else
{
if(frameCount == 0)
syncError += (lrval != 0x80000000);
if(XUA_I2S_N_BITS == 32)
{
if(frameCount == 0)
syncError = (lrval != 0x80000000);
else
syncError = (lrval != 0x7FFFFFFF);
}
else
syncError += (lrval != 0x7FFFFFFF);
{
if(frameCount == 0)
syncError = ((lrval & lrval_mask) != 0x80000000);
else
syncError = ((lrval | (~lrval_mask)) != 0x7FFFFFFF);
}
}
return syncError;
#else
unsigned clkVal;
if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
{
if(frameCount == (I2S_CHANS_PER_FRAME-1))
p_lrclk <: 0x80000000;
clkVal = 0x80000000;
else
p_lrclk <: 0x00000000;
clkVal = 0x00000000;
}
else
{
if(frameCount == 0)
p_lrclk <: 0x80000000;
clkVal = 0x80000000;
else
p_lrclk <: 0x7fffffff;
clkVal = 0x7fffffff;
}
if(XUA_I2S_N_BITS == 32)
p_lrclk <: clkVal;
else
partout(p_lrclk, XUA_I2S_N_BITS, clkVal >> (32 - XUA_I2S_N_BITS));
return 0;
#endif
@@ -254,9 +269,9 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
if ((I2S_CHANS_DAC > 0 || I2S_CHANS_ADC > 0))
{
#if CODEC_MASTER
InitPorts_slave(divide, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
InitPorts_slave(p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
#else
InitPorts_master(divide, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
InitPorts_master(p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
#endif
}
@@ -290,9 +305,17 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
// p_i2s_adc[index++] :> sample;
// Manual IN instruction since compiler generates an extra setc per IN (bug #15256)
unsigned sample;
asm volatile("in %0, res[%1]" : "=r"(sample) : "r"(p_i2s_adc[index++]));
asm volatile("in %0, res[%1]" : "=r"(sample) : "r"(p_i2s_adc[index]));
sample = bitrev(sample);
int chanIndex = ((frameCount-2)&(I2S_CHANS_PER_FRAME-1))+i; // channels 0, 2, 4.. on each line.
if(XUA_I2S_N_BITS != 32)
{
set_port_shift_count(p_i2s_adc[index], XUA_I2S_N_BITS);
sample <<= (32 - XUA_I2S_N_BITS);
}
index++;
int chanIndex = ((frameCount-2) & (I2S_CHANS_PER_FRAME-1)) + i; // channels 0, 2, 4.. on each line.
#if (AUD_TO_USB_RATIO > 1)
if ((AUD_TO_USB_RATIO - 1) == audioToUsbRatioCounter)
@@ -344,7 +367,10 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
src_ff3v_fir_coefs[2-audioToUsbRatioCounter]);
}
#endif /* (AUD_TO_USB_RATIO > 1) */
p_i2s_dac[index++] <: bitrev(samplesOut[frameCount +i]);
if(XUA_I2S_N_BITS == 32)
p_i2s_dac[index++] <: bitrev(samplesOut[frameCount +i]);
else
partout(p_i2s_dac[index++], XUA_I2S_N_BITS, bitrev(samplesOut[frameCount +i]));
}
#endif // (I2S_CHANS_DAC != 0)
@@ -417,8 +443,15 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
{
/* Manual IN instruction since compiler generates an extra setc per IN (bug #15256) */
unsigned sample;
asm volatile("in %0, res[%1]" : "=r"(sample) : "r"(p_i2s_adc[index++]));
asm volatile("in %0, res[%1]" : "=r"(sample) : "r"(p_i2s_adc[index]));
sample = bitrev(sample);
if(XUA_I2S_N_BITS != 32)
{
set_port_shift_count(p_i2s_adc[index], XUA_I2S_N_BITS);
sample <<= (32 - XUA_I2S_N_BITS);
}
index++;
int chanIndex = ((frameCount-2)&(I2S_CHANS_PER_FRAME-1))+i; // channels 1, 3, 5.. on each line.
#if (AUD_TO_USB_RATIO > 1 && !I2S_DOWNSAMPLE_MONO_IN)
if ((AUD_TO_USB_RATIO - 1) == audioToUsbRatioCounter)
@@ -450,7 +483,6 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
#endif
index = 0;
#pragma xta endpoint "i2s_output_r"
#if (I2S_CHANS_DAC != 0)
/* Output "odd" channel to DAC (i.e. right) */
#pragma loop unroll
@@ -469,7 +501,10 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
src_ff3v_fir_coefs[2-audioToUsbRatioCounter]);
}
#endif /* (AUD_TO_USB_RATIO > 1) */
p_i2s_dac[index++] <: bitrev(samplesOut[frameCount + i]);
if(XUA_I2S_N_BITS == 32)
p_i2s_dac[index++] <: bitrev(samplesOut[frameCount + i]);
else
partout(p_i2s_dac[index++], XUA_I2S_N_BITS, bitrev(samplesOut[frameCount + i]));
}
#endif // (I2S_CHANS_DAC != 0)
@@ -523,7 +558,6 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
}
}
}
#pragma xta endpoint "deliver_return"
return 0;
}
@@ -681,13 +715,13 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
/* Calculate master clock to bit clock (or DSD clock) divide for current sample freq
* e.g. 11.289600 / (176400 * 64) = 1 */
{
#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
/* I2S has 32 bits per sample. *8 as 8 channels */
unsigned numBits = 256;
#else
/* I2S has 32 bits per sample. *2 as 2 channels */
unsigned numBits = 64;
#endif
unsigned numBits = XUA_I2S_N_BITS * 2;
if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
{
/* TDM has 8 channels */
numBits *= 4;
}
#if (DSD_CHANS_DAC > 0)
if(dsdMode == DSD_MODE_DOP)
@@ -703,7 +737,15 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#endif
divide = mClk / (curSamFreq * numBits);
/* TODO; we should catch and handle the case when divide is 0. Currently design will lock up */
//Do some checks
xassert((divide > 0) && "Error: divider is 0, BCLK rate unachievable");
unsigned remainder = mClk % ( curSamFreq * numBits);
xassert((!remainder) && "Error: MCLK not divisible into BCLK by an integer number");
unsigned divider_is_odd = divide & 0x1;
xassert((!divider_is_odd) && "Error: divider is odd, clockblock cannot produce desired BCLK");
}
#if (DSD_CHANS_DAC > 0)

View File

@@ -100,7 +100,7 @@ void ConfigAudioPorts(
/* Do some clocking shifting to get data in the valid window */
/* E.g. Only shift when running at 88.2+ kHz TDM slave */
int bClkDelay_fall = 0;
if(curSamFreq * I2S_CHANS_PER_FRAME * 32 >= 20000000)
if(curSamFreq * I2S_CHANS_PER_FRAME * XUA_I2S_N_BITS >= 20000000)
{
/* 18 * 2ns = 36ns. This results in a -4ns (36 - 40) shift at 96KHz and -8ns (36 - 44) at 88.4KHz */
bClkDelay_fall = 18;

View File

@@ -1,8 +1,8 @@
// Copyright 2013-2021 XMOS LIMITED.
// Copyright 2013-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
Warnings relating to configuration defines located in this XC source file rather than the devicedefines.h header file in order to avoid multiple warnings being issued when the devicedefines.h header file is included in multiple files.
Warnings relating to configuration defines located in this XC source file rather than the xua_conf.h header file in order to avoid multiple warnings being issued when the xua_conf.h header file is included in multiple files.
*/
#include "xua_conf_full.h"

View File

@@ -1,4 +1,4 @@
# Copyright 2018-2022 XMOS LIMITED.
# Copyright 2018-2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
@@ -11,16 +11,21 @@ import sys
def test_file(request):
return str(request.node.fspath)
def do_test(
pcm_format, i2s_role, channel_count, sample_rate, test_file, options, capfd
pcm_format, i2s_role, channel_count, sample_rate, word_length, test_file, options, capfd
):
build_options = []
output = []
testname, _ = os.path.splitext(os.path.basename(test_file))
desc = f"simulation_{pcm_format}_{i2s_role}_{channel_count}in_{channel_count}out_{sample_rate}"
build_options += [f"pcm_format={pcm_format}"]
build_options += [f"i2s_role={i2s_role}"]
build_options += [f"channel_count={channel_count}"]
build_options += [f"sample_rate={sample_rate}"]
build_options += [f"word_length={word_length}"]
desc = f"simulation_{pcm_format}_{i2s_role}_{channel_count}in_{channel_count}out_{sample_rate}_{word_length}bit"
binary = f"{testname}/bin/{desc}/{testname}_{desc}.xe"
tester = testers.ComparisonTester(open("pass.expect"))
@@ -52,6 +57,7 @@ def do_test(
result = Pyxsim.run_on_simulator(
binary,
build_options=build_options,
tester=tester,
simargs=simargs,
capfd=capfd,
@@ -65,25 +71,26 @@ def do_test(
@pytest.mark.parametrize("i2s_role", ["master", "slave"])
@pytest.mark.parametrize("pcm_format", ["i2s", "tdm"])
@pytest.mark.parametrize("channel_count", [2, 8, 16])
@pytest.mark.parametrize("sample_rate", ["48khz", "96khz", "192khz"])
@pytest.mark.parametrize("word_length", [16, 32]) # I2S world length in bits
@pytest.mark.parametrize("sample_rate", [48000, 96000, 192000])
def test_i2s_loopback(
i2s_role, pcm_format, channel_count, sample_rate, test_file, options, capfd
i2s_role, pcm_format, channel_count, sample_rate, word_length, test_file, options, capfd
):
if pcm_format == "i2s" and channel_count == 16:
pytest.skip("Invalid parameter combination")
if pcm_format == "i2s" and sample_rate not in ["48khz", "192khz"]:
if pcm_format == "i2s" and sample_rate not in [48000, 192000]:
pytest.skip("Invalid parameter combination")
if pcm_format == "tdm" and channel_count == 2:
pytest.skip("Invalid parameter combination")
if pcm_format == "tdm" and sample_rate == "192khz":
if pcm_format == "tdm" and sample_rate == 192000:
pytest.skip("Invalid parameter combination")
result = do_test(
pcm_format, i2s_role, channel_count, sample_rate, test_file, options, capfd
pcm_format, i2s_role, channel_count, sample_rate, word_length, test_file, options, capfd
)
assert result

View File

@@ -1,126 +1,44 @@
TARGET = xk-audio-216-mc.xn
USED_MODULES = lib_xua lib_i2c lib_logging
BUILD_FLAGS = -O0 -g -lflash -DXUD_CORE_CLOCK=600 -fxscope -save-temps -march=xs2a -DUSB_TILE=tile[1]
BUILD_FLAGS = -O3 -g -lflash -DXUD_CORE_CLOCK=600 -fxscope -save-temps -march=xs2a -DUSB_TILE=tile[1] \
-DXUA_ADAT_RX_EN=0 -DXUA_ADAT_TX_EN=0 -DXUA_SPDIF_RX_EN=0 -DXUA_SPDIF_TX_EN=0 -DMIDI=0 \
-DSIMULATION=1
BUILD_FLAGS_i2s_master_2in_2out_48khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -DI2S_CHANS_ADC=2 -DI2S_CHANS_DAC=2 \
-D DEFAULT_FREQ=48000
ifndef pcm_format
$(error pcm_format is not set)
endif
BUILD_FLAGS_i2s_slave_2in_2out_48khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -DI2S_CHANS_ADC=2 -DI2S_CHANS_DAC=2 \
-D DEFAULT_FREQ=48000 -DCODEC_MASTER=1
ifndef i2s_role
$(error i2s_role is not set)
endif
BUILD_FLAGS_i2s_master_2in_2out_192khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -D I2S_CHANS_ADC=2 -D I2S_CHANS_DAC=2 \
-D DEFAULT_FREQ=192000
ifndef channel_count
$(error channel_count is not set)
endif
BUILD_FLAGS_i2s_slave_2in_2out_192khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -DI2S_CHANS_ADC=2 -DI2S_CHANS_DAC=2 \
-D DEFAULT_FREQ=192000 -DCODEC_MASTER=1
ifndef sample_rate
$(error sample_rate is not set)
endif
BUILD_FLAGS_i2s_master_8in_8out_48khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=48000
ifndef word_length
$(error word_length is not set)
endif
BUILD_FLAGS_i2s_slave_8in_8out_48khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=48000 -DCODEC_MASTER=1
ifeq ($(pcm_format),tdm)
BUILD_FLAGS += -DXUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM
endif
ifeq ($(i2s_role),slave)
BUILD_FLAGS += -DCODEC_MASTER=1
endif
BUILD_FLAGS_i2s_master_8in_8out_192khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=192000 \
-O2 # optimisations to meet timing
BUILD_FLAGS_i2s_slave_8in_8out_192khz = $(BUILD_FLAGS) \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=192000 -DCODEC_MASTER=1 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_master_8in_8out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=48000 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_master_8in_8out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=96000 \
-O3 # optimisations to meet timing
BUILD_FLAGS_tdm_slave_8in_8out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=48000 -DCODEC_MASTER=1 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_slave_8in_8out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \
-D DEFAULT_FREQ=96000 -DCODEC_MASTER=1 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_master_16in_16out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \
-D DEFAULT_FREQ=48000 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_master_16in_16out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \
-D DEFAULT_FREQ=96000 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_slave_16in_16out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \
-D DEFAULT_FREQ=48000 -DCODEC_MASTER=1 \
-O2 # optimisations to meet timing
BUILD_FLAGS_tdm_slave_16in_16out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \
-D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \
-D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \
-D DEFAULT_FREQ=96000 -DCODEC_MASTER=1 \
-O2 # optimisations to meet timing
#XCC_FLAGS_hardware_i2s_master_2in_2out_48khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_2in_2out_48khz)
#XCC_FLAGS_hardware_i2s_master_2in_2out_192khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_2in_2out_192khz)
#XCC_FLAGS_hardware_i2s_master_8in_8out_48khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_8in_8out_48khz)
#XCC_FLAGS_hardware_i2s_master_8in_8out_192khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_8in_8out_192khz)
#XCC_FLAGS_hardware_tdm_master_8in_8out_48khz = -D HARDWARE $(BUILD_FLAGS_tdm_master_8in_8out_48khz)
XCC_FLAGS_simulation_i2s_master_2in_2out_48khz = -D SIMULATION $(BUILD_FLAGS_i2s_master_2in_2out_48khz)
XCC_FLAGS_simulation_i2s_slave_2in_2out_48khz = -D SIMULATION $(BUILD_FLAGS_i2s_slave_2in_2out_48khz)
XCC_FLAGS_simulation_i2s_master_2in_2out_192khz = -D SIMULATION $(BUILD_FLAGS_i2s_master_2in_2out_192khz)
XCC_FLAGS_simulation_i2s_slave_2in_2out_192khz = -D SIMULATION $(BUILD_FLAGS_i2s_slave_2in_2out_192khz)
XCC_FLAGS_simulation_i2s_master_8in_8out_48khz = -D SIMULATION $(BUILD_FLAGS_i2s_master_8in_8out_48khz)
XCC_FLAGS_simulation_i2s_slave_8in_8out_48khz = -D SIMULATION $(BUILD_FLAGS_i2s_slave_8in_8out_48khz)
XCC_FLAGS_simulation_i2s_master_8in_8out_192khz = -D SIMULATION $(BUILD_FLAGS_i2s_master_8in_8out_192khz)
XCC_FLAGS_simulation_i2s_slave_8in_8out_192khz = -D SIMULATION $(BUILD_FLAGS_i2s_slave_8in_8out_192khz)
XCC_FLAGS_simulation_tdm_master_8in_8out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_8in_8out_48khz)
XCC_FLAGS_simulation_tdm_master_8in_8out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_8in_8out_96khz)
XCC_FLAGS_simulation_tdm_slave_8in_8out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_8in_8out_48khz)
XCC_FLAGS_simulation_tdm_slave_8in_8out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_8in_8out_96khz)
XCC_FLAGS_simulation_tdm_master_16in_16out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_16in_16out_48khz)
XCC_FLAGS_simulation_tdm_master_16in_16out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_16in_16out_96khz)
XCC_FLAGS_simulation_tdm_slave_16in_16out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_16in_16out_48khz)
XCC_FLAGS_simulation_tdm_slave_16in_16out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_16in_16out_96khz)
XCC_FLAGS_simulation_${pcm_format}_${i2s_role}_$(channel_count)in_$(channel_count)out_$(sample_rate)_$(word_length)bit = $(BUILD_FLAGS) \
-DNUM_USB_CHAN_IN=${channel_count} \
-DNUM_USB_CHAN_OUT=${channel_count} \
-DI2S_CHANS_DAC=${channel_count} \
-DI2S_CHANS_ADC=${channel_count} \
-DDEFAULT_FREQ=${sample_rate} \
-DXUA_I2S_N_BITS=${word_length}
XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -1,4 +1,4 @@
// Copyright 2016-2022 XMOS LIMITED.
// Copyright 2016-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <platform.h>
#include <stdlib.h>
@@ -9,7 +9,6 @@
#define DEBUG_UNIT MAIN
#include "debug_print.h"
/* Port declarations. Note, the defines come from the xn file */
#if I2S_WIRES_DAC > 0
on tile[AUDIO_IO_TILE] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] =
@@ -92,9 +91,10 @@ clock clk_audio_mclk = on tile[AUDIO_IO_TILE]: XS1_CLKBLK_2; /*
#define TOTAL_TEST_FRAMES (5 * DEFAULT_FREQ)
#endif
#define SAMPLE(frame_count, channel_num) (((frame_count) << 8) | ((channel_num) & 0xFF))
#define SAMPLE_FRAME_NUM(test_word) ((test_word) >> 8)
#define SAMPLE_CHANNEL_NUM(test_word) ((test_word) & 0xFF)
#define SHIFT (16) /* Note, we shift samples up such that we can test down to 16bit I2S */
#define SAMPLE(frame_count, channel_num) ((((frame_count) << 8) | ((channel_num) & 0xFF))<<SHIFT)
#define SAMPLE_FRAME_NUM(test_word) ((test_word>>SHIFT) >> 8)
#define SAMPLE_CHANNEL_NUM(test_word) ((test_word>>SHIFT) & 0xFF)
void generator(chanend c_checker, chanend c_out)
{
@@ -105,7 +105,6 @@ void generator(chanend c_checker, chanend c_out)
frame_count = 0;
while (1) {
underflow_word = inuint(c_out);

View File

@@ -1,4 +1,4 @@
// Copyright 2016-2022 XMOS LIMITED.
// Copyright 2016-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifdef SIMULATION
@@ -41,7 +41,7 @@ extern out port p_lrclk_gen;
extern clock clk_audio_lrclk_gen;
void slave_mode_clk_setup(const unsigned samFreq, const unsigned chans_per_frame){
const unsigned data_bits = 32;
const unsigned data_bits = XUA_I2S_N_BITS;
const unsigned mclk_freq = 24576000;
const unsigned mclk_bclk_ratio = mclk_freq / (chans_per_frame * samFreq * data_bits);
@@ -61,5 +61,4 @@ void slave_mode_clk_setup(const unsigned samFreq, const unsigned chans_per_frame
master_mode_clk_setup();
}
#endif
#endif