Further Audiohub tidyups. I2S ports params. Documentation updates inline.

This commit is contained in:
xross
2018-03-09 16:11:18 +00:00
parent 961d890b2c
commit b66e9cdc60
11 changed files with 3313 additions and 3131 deletions

View File

@@ -1,6 +1,13 @@
lib_xua Change Log
==================
0.2.0
-----
* ADDED: Documentation
* CHANGE: I2S hardware resources no longer used globally and must be passed to XUA_AudioHub()
* CHANGE: NO_USB define renamed to XUA_USB_EN
0.1.2
-----

View File

@@ -1,5 +1,8 @@
// Copyright (c) 2017-2018, XMOS Ltd, All rights reserved
#ifndef _XUA_CONF_H_
#define _XUA_CONF_H_
#define NUM_USB_CHAN_OUT 2
#define NUM_USB_CHAN_IN 2
#define I2S_CHANS_DAC 2
@@ -22,3 +25,5 @@
#define AUDIO_CLASS_FALLBACK 0
#define BCD_DEVICE 0x1234
#define XUA_DFU_EN 0
#endif

View File

@@ -24,6 +24,14 @@
*
* \param p_mclk_in Master clock inport port (must be 1-bit)
*
* \param p_lrclk Nullable port for I2S sample clock
*
* \param p_bclk Nullable port for I2S bit
*
* \param p_i2s_dac Nullable array of ports for I2S data output lines
*
* \param p_i2s_adc Nullable array of ports for I2S data input lines
*
* \param c_dig channel connected to the clockGen() thread for
* receiving/transmitting samples
*/
@@ -32,7 +40,9 @@ void XUA_AudioHub(chanend ?c_aud,
clock ?clk_audio_bclk,
in port p_mclk_in,
buffered _XUA_CLK_DIR port:32 ?p_lrclk,
buffered _XUA_CLK_DIR port:32 ?p_bclk
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 (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
, chanend c_spdif_tx
#endif

File diff suppressed because one or more lines are too long

15
lib_xua/doc/rst/feat.rst Normal file
View File

@@ -0,0 +1,15 @@
Features & Options
------------------
The previous sections describes only the basic core set of ``lib_xua`` details on enabling additional features e.g. S/PDIF are discussed in this section.
If using the "codeless" programming model then the steps in this section are informational only.
I2S/TDM
~~~~~~~
S/PDIF Transmit
~~~~~~~~~~~~~~~

View File

@@ -28,7 +28,7 @@ Host System Requirements
Hardware Platforms <hw>
Software Overview <sw>
Using lib_xua <using>
Implementation Detail <sw_arch>
Features <feat>
Known Issues <issues>

View File

@@ -1,10 +1,9 @@
Using lib_xud
-------------
This sections describes the basic usage of `lib_xud`. It provides a guide on how to program the USB Audio Devices using `lib_xud` including instructions for building and running
programs and creating your own custom USB audio applications.
This sections describes the basic usage of `lib_xud`. It provides a guide on how to program the USB Audio Devices using `lib_xud`.
Reviewing application note AN00246 is highly recommended.
Reviewing application note AN00246 is highly recommended at this point.
Library structure
~~~~~~~~~~~~~~~~~
@@ -27,36 +26,41 @@ Note, the midi and dfu directories are potential candidates for separate libs in
Including in a project
~~~~~~~~~~~~~~~~~~~~~~
All `lib_xua` functions can be accessed via the ``xud.h`` header filer::
All `lib_xua` functions can be accessed via the ``xua.h`` header filer::
#include <xua.h>
It is also requited to to add ``lib_xua`` to the ``USED_MODULES`` field of your application Makefile.
It is also required to add ``lib_xua`` to the ``USED_MODULES`` field of your application Makefile::
USED_MODULES = .. lib_xua ...
Core hardware resources
~~~~~~~~~~~~~~~~~~~~~~~
Currently all hardware resources used by `lib_xua` are simply declared globally.
The user must declare and initialise relevant hardware resources (globally) and pass them to the relevant function of `lib_xua`.
As an absolute minimum the following resources are required
As an absolute minimum the following resources are required:
- A 1-bit port for audio master clock input
- A n-bit port for internal feedback calculation (typically a free, unused port is used e.g. `16B`)
- A clock-block, which will be clocked from the master clock input port
.. note::
Since these resources are accessed globally naming is of importance
Example declaration of these resources might look as follows::
in port p_mclk_in = PORT_MCLK_IN;
in port p_for_mclk_count = PORT_MCLK_COUNT; /* Extra port for counting master clock ticks */
clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */
.. note::
If the ``XUD_AudioHub()`` and ``XUD_Buffer()`` cores reside on separate tiles a separate master clock input port must be provided to each, for example::
The `PORT_MCLK_IN` and `PORT_MCLK_COUNT` defintions are derived from the projects XN file
The ``XUA_AudioHub()`` function requires an audio master clock input to clock the physical audio I/O. Less obvious is the reasoning for the ``XUA_Buffer()``
task having the same requirement - it is used for the USB feedback system and packet sizing.
Due to the above, if the ``XUD_AudioHub()`` and ``XUA_Buffer()`` cores must reside on separate tiles a separate master clock input port must be provided to each, for example::
/* Master clock for the audio IO tile */
in port p_mclk_in = PORT_MCLK_IN;
@@ -64,8 +68,10 @@ If the ``XUD_AudioHub()`` and ``XUD_Buffer()`` cores reside on separate tiles a
/* Resources for USB feedback */
in port p_mclk_in_usb = PORT_MCLK_IN_USB; /* Extra master clock input for the USB tile */
Whilst this satisfies the basic requirements for the operation of `lib_xua` projects typically also needs some additional audio I/O, I2S or SPDIF for example.
These should be passed into the various cores as required (see API section)
Whilst the hardware resources described in this section satisfy the basic requirements for the operation (or build) of `lib_xua` projects typically also needs some additional audio I/O,
I2S or SPDIF for example.
These should be passed into the various cores as required - see API and Features sections.
Running the core components
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -82,10 +88,10 @@ In their most basic form the core components can be run as follows::
XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud);
/* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */
XUA_AudioHub(c_aud);
XUA_AudioHub(c_aud, ...) ;
}
``XUA_Buffer()`` expects its ``p_for_mclk_count`` argument to be clocked from the audio master clock
``XUA_Buffer()`` expects its ``p_for_mclk_count`` argument to be clocked from the audio master clock before being passed it.
The following code satisfies this requirement::
{
@@ -98,7 +104,7 @@ The following code satisfies this requirement::
}
.. note:: By keeping this configuraiton outside of the ``XUA_Buffer()`` function allow the possiblity to share the ``p_mclk_in_usb`` port with additional components
.. note:: Keeping this configuration outside of ``XUA_Buffer()`` does not preclude the possibllty of sharing ``p_mclk_in_usb`` port with additional components
To produce a fully operating device a call to ``XUD_Main()`` (from ``lib_xud``) must also be made for USB connectivity::
@@ -121,14 +127,50 @@ Additionally the required communication channels must also be declared::
chan c_aud_ctl;
This section provides enough information to implement a skeleton program for a USB Audio device. When running the xCORE device will present itself as a USB Audio Class device on the bus.
Configuring XUA
~~~~~~~~~~~~~~~
Built in main()
~~~~~~~~~~~~~~~
Configuration of the various build time options of ``lib_xua`` is done via the optional header `xua_conf.h`. Such build time options include audio class version, sample rates, channel counts etc.
Please see the API section for full listings.
The build system will automatically include the `xua_conf.h` header file as appropriate - the user should continue to include `xua.h` as previously directed. A simple example is shown below::
#ifndef _XUA_CONF_H_
#define _XUA_CONF_H_
/* Output channel count */
#define XUA_NUM_USB_CHAN_OUT (2)
/* Product string */
#define XUA_PRODUCT_STR_A2 "My Product"
#endif
User functions
~~~~~~~~~~~~~~
To enable custom functionality, such as configuring external audio hardware, custom functionality on stream start/stop etc various user overridable functions are provided (see API section for full listings). The default implementations are empty.
Codeless programming model
~~~~~~~~~~~~~~~~~~~~~~~~~~
Whilst it is possible to code a USB Audio device using the building blocks provided by `lib_xud` it is realised that this might not be desirable for some classes of customers or product.
For instance, some users may not have a large software development experience and simply want to customise some basic settings such as strings. Others may want to fully customise the implementation - adding additional functionality such as adding DSD or possibly only using a subset of the functions provided - just ``XUA_AudioHub``, for example.
In addition, the large number of supported features can lead a large number of tasks, hardware resources, communication channels etc, requiring quite a lot of code to be authored for each product.
In order to cater for the former class of users, a "codeless" option is provided. Put simply, a file ``main.xc`` is provided which includes a pre-authored ``main()`` function along with all of the required hardware resource declarations. Code is generated based on the options provided in ``xua_conf.h``
Using this development model the user simply must include a ``xua_conf.h`` with their settings and optionally implementations of any 'user functions' as desired. This, along with an XN file for their hardware platform, is all that is required to build a fully featured and functioning product.
This model also provides the benefit of a known-good, full codebase as a basis for a product.
This behaviour described in this section is the default behaviour of `lib_xud`, to disable this please set ``EXCLUDE_USB_AUDIO_MAIN`` to 1 in the application makefile or ``xua_conf.h``.
Enabling Additional Features
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This sections describes only the basic feature set of ``lib_xua`` details on enabling additional features e.g. S/PDIF can be found later in this document.

View File

@@ -1,4 +1,18 @@
#if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0)
#if (DSD_CHANS_DAC != 0)
extern buffered out port:32 p_dsd_dac[DSD_CHANS_DAC];
extern buffered out port:32 p_dsd_clk;
#endif
/* I2S Data I/O*/
#if (I2S_CHANS_DAC != 0)
extern buffered out port:32 p_i2s_dac[I2S_WIRES_DAC];
#endif
#if (I2S_CHANS_ADC != 0)
extern buffered in port:32 p_i2s_adc[I2S_WIRES_ADC];
#endif
/* This function performs the DSD native loop and outputs a 32b DSD stream per loop */
static inline void DoDsdNative(unsigned samplesOut[], unsigned &dsdSample_l, unsigned &dsdSample_r, unsigned divide)
{
@@ -39,7 +53,6 @@ static inline void DoDsdDop(int &everyOther, unsigned samplesOut[], unsigned &ds
to restart in I2S mode. */
static inline int DoDsdDopCheck(unsigned &dsdMode, int &dsdCount, unsigned curSamFreq, unsigned samplesOut[], unsigned &dsdMarker)
{
#if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0)
/* Check for DSD - note we only move into DoP mode if valid DoP Freq */
/* Currently we only check on channel 0 - we get all 0's on channels without data */
if((dsdMode == DSD_MODE_OFF) && (curSamFreq > 96000))
@@ -53,13 +66,6 @@ static inline int DoDsdDopCheck(unsigned &dsdMode, int &dsdCount, unsigned curSa
dsdMode = DSD_MODE_DOP;
dsdCount = 0;
dsdMarker = DSD_MARKER_2;
#if (I2S_CHANS_ADC != 0) || (I2S_CHANS_DAC != 0)
// Set clocks low
p_lrclk <: 0;
p_bclk <: 0;
#endif
p_dsd_clk <: 0;
return 0;
}
}
@@ -77,19 +83,11 @@ static inline int DoDsdDopCheck(unsigned &dsdMode, int &dsdCount, unsigned curSa
if((DSD_MASK(samplesOut[0]) != DSD_MARKER_2) && (DSD_MASK(samplesOut[1]) != DSD_MARKER_2))
{
dsdMode = DSD_MODE_OFF;
// Set clocks low
#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0)
p_lrclk <: 0;
p_bclk <: 0;
#endif
p_dsd_clk <: 0;
return 0;
}
}
}
#endif
return 1;
}
#endif

View File

@@ -1,5 +1,16 @@
#include "xua.h"
#include "dsd_support.h"
#if (DSD_CHANS_DAC != 0)
extern buffered out port:32 p_dsd_dac[DSD_CHANS_DAC];
extern buffered out port:32 p_dsd_clk;
#endif
extern unsigned dsdMode;
#if !CODEC_MASTER
static inline void InitPorts_master(unsigned divide, buffered out port:32 p_lrclk, buffered out port:32 p_bclk)
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])
{
#if (DSD_CHANS_DAC > 0)
if(dsdMode == DSD_MODE_OFF)
@@ -62,7 +73,7 @@ static inline void InitPorts_master(unsigned divide, buffered out port:32 p_lrcl
#else
static inline void InitPorts_slave(unsigned divide, buffered in port:32 p_lrclk, buffered in port:32 p_bclk)
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])
{
#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0)
unsigned tmp;

View File

@@ -49,11 +49,6 @@ static unsigned samplesOut[MAX(NUM_USB_CHAN_OUT, I2S_CHANS_DAC)];
static unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)];
#if (DSD_CHANS_DAC != 0)
extern buffered out port:32 p_dsd_dac[DSD_CHANS_DAC];
extern buffered out port:32 p_dsd_clk;
#endif
#ifdef XTA_TIMING_AUDIO
#pragma xta command "add exclusion received_command"
#pragma xta command "analyse path i2s_output_l i2s_output_r"
@@ -67,18 +62,6 @@ extern buffered out port:32 p_dsd_clk;
#pragma xta command "set required - 2000 ns"
#endif
/* I2S Data I/O*/
#if (I2S_CHANS_DAC != 0)
extern buffered out port:32 p_i2s_dac[I2S_WIRES_DAC];
#endif
#if (I2S_CHANS_ADC != 0)
extern buffered in port:32 p_i2s_adc[I2S_WIRES_ADC];
#endif
unsigned dsdMode = DSD_MODE_OFF;
#if (XUA_SPDIF_TX_EN)
extern buffered out port:32 p_spdif_tx;
#endif
@@ -91,66 +74,28 @@ extern buffered out port:32 p_adat_tx;
extern clock clk_mst_spd;
#endif
#include "init_ports.h"
#if CODEC_MASTER
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 in port:32 ?p_i2s_adc[I2S_WIRES_ADC]);
unsigned dsdMode = DSD_MODE_OFF;
#if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0)
#include "audiohub_dsd.h"
#endif
#ifdef ADAT_TX
unsigned adatCounter = 0;
unsigned adatSamples[8];
#pragma unsafe arrays
static inline void TransferAdatTxSamples(chanend c_adat_out, const unsigned samplesFromHost[], int smux, int handshake)
{
/* Do some re-arranging for SMUX.. */
unsafe
{
unsigned * unsafe samplesFromHostAdat = &samplesFromHost[ADAT_TX_INDEX];
/* Note, when smux == 1 this loop just does a straight 1:1 copy */
//if(smux != 1)
{
int adatSampleIndex = adatCounter;
for(int i = 0; i < (8/smux); i++)
{
adatSamples[adatSampleIndex] = samplesFromHostAdat[i];
adatSampleIndex += smux;
}
}
}
adatCounter++;
if(adatCounter == smux)
{
#ifdef ADAT_TX_USE_SHARED_BUFF
unsafe
{
/* Wait for ADAT core to be done with buffer */
/* Note, we are "running ahead" of the ADAT core */
inuint(c_adat_out);
/* Send buffer pointer over to ADAT core */
volatile unsigned * unsafe samplePtr = &adatSamples;
outuint(c_adat_out, (unsigned) samplePtr);
}
#else
#pragma loop unroll
for (int i = 0; i < 8; i++)
{
outuint(c_adat_out, samplesFromHost[ADAT_TX_INDEX + i]);
}
#endif
adatCounter = 0;
}
}
#include "audiohub_adat.h"
#endif
#pragma unsafe arrays
static inline unsigned DoSampleTransfer(chanend c_out, const int readBuffNo, const unsigned underflowWord)
{
if(XUA_USB_EN)
{
outuint(c_out, underflowWord);
@@ -277,18 +222,16 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
#if (NUM_PDM_MICS > 0)
, chanend c_pdm_pcm
#endif
, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk
, 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]
)
{
/* Since DAC and ADC buffered ports off by one sample we buffer previous ADC frame */
unsigned readBuffNo = 0;
unsigned index;
#ifdef RAMP_CHECK
unsigned prev=0;
int started = 0;
#endif
#if (DSD_CHANS_DAC != 0)
unsigned dsdMarker = DSD_MARKER_2; /* This alternates between DSD_MARKER_1 and DSD_MARKER_2 */
int dsdCount = 0;
@@ -375,9 +318,9 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
unsigned syncError = 0;
#if CODEC_MASTER
InitPorts_slave(divide, p_lrclk, p_bclk);
InitPorts_slave(divide, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
#else
InitPorts_master(divide, p_lrclk, p_bclk);
InitPorts_master(divide, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
#endif
/* Note we always expect syncError to be 0 when we are master */
@@ -599,7 +542,15 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
#if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0)
if(DoDsdDopCheck(dsdMode, dsdCount, curSamFreq, samplesOut, dsdMarker) == 0)
{
#if (I2S_CHANS_ADC != 0) || (I2S_CHANS_DAC != 0)
// Set clocks low
p_lrclk <: 0;
p_bclk <: 0;
#endif
p_dsd_clk <: 0;
return 0;
}
#endif
#if I2S_MODE_TDM
@@ -683,7 +634,6 @@ static void dummy_deliver(chanend ?c_out, unsigned &command)
{
int ct;
while (1)
{
select
@@ -733,7 +683,9 @@ static void dummy_deliver(chanend ?c_out, unsigned &command)
void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
in port p_mclk_in,
buffered _XUA_CLK_DIR port:32 ?p_lrclk,
buffered _XUA_CLK_DIR port:32 ?p_bclk
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 (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
, chanend c_spdif_out
#endif
@@ -994,9 +946,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#if (NUM_PDM_MICS > 0)
, c_pdm_in
#endif
, p_lrclk,
p_bclk
);
, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc);
#if (XUA_USB_EN)
if(command == SET_SAMPLE_FREQ)
@@ -1028,16 +978,16 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
par
{
DFUHandler(dfuInterface, null);
dummy_deliver(c_mix_out, command);
dummy_deliver(c_aud, command);
}
#else
dummy_deliver(c_mix_out, command);
dummy_deliver(c_aud, command);
#endif
curSamFreq = inuint(c_mix_out);
curSamFreq = inuint(c_aud);
if (curSamFreq == AUDIO_START_FROM_DFU)
{
outct(c_mix_out, XS1_CT_END);
outct(c_aud, XS1_CT_END);
break;
}
}

View File

@@ -83,6 +83,8 @@ on tile[AUDIO_IO_TILE] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] =
#endif
#if I2S_WIRES_DAC > 0
};
#else
#define p_i2s_dac null
#endif
#if I2S_WIRES_ADC > 0
@@ -112,6 +114,8 @@ on tile[AUDIO_IO_TILE] : buffered in port:32 p_i2s_adc[I2S_WIRES_ADC] =
#endif
#if I2S_WIRES_ADC > 0
};
#else
#define p_i2s_adc null
#endif
@@ -468,7 +472,7 @@ void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc,
#else
#define AUDIO_CHANNEL c_aud_in
#endif
XUA_AudioHub(AUDIO_CHANNEL
XUA_AudioHub(AUDIO_CHANNEL, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc
#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
, c_spdif_tx
#endif