From 1ef5129fdeef0c71fa747c6877d09a838e328c47 Mon Sep 17 00:00:00 2001 From: Ross Owen Date: Thu, 8 Jun 2023 15:31:12 +0100 Subject: [PATCH] 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 --- CHANGELOG.rst | 3 +- lib_xua/api/xua_conf_default.h | 19 ++- lib_xua/doc/rst/api_defines.rst | 9 ++ lib_xua/doc/rst/opt_i2s.rst | 19 ++- lib_xua/doc/rst/opt_location.rst | 2 +- lib_xua/doc/rst/overview.rst | 2 +- lib_xua/doc/rst/using_adv_i2s.rst | 4 +- lib_xua/module_build_info | 2 +- .../src/core/audiohub/audiohub_initport.xc | 46 ++++-- lib_xua/src/core/audiohub/xua_audiohub.xc | 120 ++++++++++----- lib_xua/src/core/ports/audioports.xc | 2 +- lib_xua/src/core/warnings.xc | 4 +- tests/test_i2s_loopback.py | 25 +-- tests/test_i2s_loopback/Makefile | 144 ++++-------------- tests/test_i2s_loopback/main.xc | 11 +- tests/test_i2s_loopback/simulation.xc | 5 +- 16 files changed, 221 insertions(+), 196 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d95b28c8..65af1b55 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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 ----- diff --git a/lib_xua/api/xua_conf_default.h b/lib_xua/api/xua_conf_default.h index 8b6242cd..ee3bc85f 100644 --- a/lib_xua/api/xua_conf_default.h +++ b/lib_xua/api/xua_conf_default.h @@ -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 */ diff --git a/lib_xua/doc/rst/api_defines.rst b/lib_xua/doc/rst/api_defines.rst index 76904020..a67b08fd 100644 --- a/lib_xua/doc/rst/api_defines.rst +++ b/lib_xua/doc/rst/api_defines.rst @@ -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 ^^^^ diff --git a/lib_xua/doc/rst/opt_i2s.rst b/lib_xua/doc/rst/opt_i2s.rst index 681626d9..005030b0 100644 --- a/lib_xua/doc/rst/opt_i2s.rst +++ b/lib_xua/doc/rst/opt_i2s.rst @@ -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:: -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. + diff --git a/lib_xua/doc/rst/opt_location.rst b/lib_xua/doc/rst/opt_location.rst index 45a5d1ee..0f3b210b 100644 --- a/lib_xua/doc/rst/opt_location.rst +++ b/lib_xua/doc/rst/opt_location.rst @@ -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 diff --git a/lib_xua/doc/rst/overview.rst b/lib_xua/doc/rst/overview.rst index 8be7bae7..c52db86a 100644 --- a/lib_xua/doc/rst/overview.rst +++ b/lib_xua/doc/rst/overview.rst @@ -25,7 +25,7 @@ Overview | +---------------------------------------------------------------------------------------------+ | | `USB Midi Device Class 1.0 `_ | +---------------------------------+---------------------------------------------------------------------------------------------+ - | Audio | I2S/TDM | + | Audio | I2S/TDM (16/32-bit) | | +---------------------------------------------------------------------------------------------+ | | S/PDIF | | +---------------------------------------------------------------------------------------------+ diff --git a/lib_xua/doc/rst/using_adv_i2s.rst b/lib_xua/doc/rst/using_adv_i2s.rst index 12d5029c..89dd025c 100644 --- a/lib_xua/doc/rst/using_adv_i2s.rst +++ b/lib_xua/doc/rst/using_adv_i2s.rst @@ -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 */ diff --git a/lib_xua/module_build_info b/lib_xua/module_build_info index b95b65f6..7c50052b 100644 --- a/lib_xua/module_build_info +++ b/lib_xua/module_build_info @@ -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) diff --git a/lib_xua/src/core/audiohub/audiohub_initport.xc b/lib_xua/src/core/audiohub/audiohub_initport.xc index cb3e7d2b..61ab6324 100644 --- a/lib_xua/src/core/audiohub/audiohub_initport.xc +++ b/lib_xua/src/core/audiohub/audiohub_initport.xc @@ -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 diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index 76229899..e50cee24 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -15,6 +15,8 @@ #include #include #include +#include + #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) diff --git a/lib_xua/src/core/ports/audioports.xc b/lib_xua/src/core/ports/audioports.xc index 10fd328f..fac58cbc 100644 --- a/lib_xua/src/core/ports/audioports.xc +++ b/lib_xua/src/core/ports/audioports.xc @@ -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; diff --git a/lib_xua/src/core/warnings.xc b/lib_xua/src/core/warnings.xc index 16ffc49e..1a8fadd5 100644 --- a/lib_xua/src/core/warnings.xc +++ b/lib_xua/src/core/warnings.xc @@ -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" diff --git a/tests/test_i2s_loopback.py b/tests/test_i2s_loopback.py index c824a355..fdf67bae 100644 --- a/tests/test_i2s_loopback.py +++ b/tests/test_i2s_loopback.py @@ -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 diff --git a/tests/test_i2s_loopback/Makefile b/tests/test_i2s_loopback/Makefile index 32615bb1..87a4b96c 100644 --- a/tests/test_i2s_loopback/Makefile +++ b/tests/test_i2s_loopback/Makefile @@ -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 diff --git a/tests/test_i2s_loopback/main.xc b/tests/test_i2s_loopback/main.xc index a98bc9ec..de36129f 100644 --- a/tests/test_i2s_loopback/main.xc +++ b/tests/test_i2s_loopback/main.xc @@ -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 #include @@ -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) >> 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); diff --git a/tests/test_i2s_loopback/simulation.xc b/tests/test_i2s_loopback/simulation.xc index 44513a04..9aafcf00 100644 --- a/tests/test_i2s_loopback/simulation.xc +++ b/tests/test_i2s_loopback/simulation.xc @@ -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