Merge pull request #363 from ed-xmos/feature/sw_pll_sync_ua

Use sw_pll for sync targets on XS3
This commit is contained in:
Ross Owen
2024-01-26 16:57:16 +00:00
committed by GitHub
24 changed files with 1265 additions and 973 deletions

View File

@@ -14,7 +14,7 @@ HEAD
* CHANGED: QUAD_SPI_FLASH replaced by XUA_QUAD_SPI_FLASH (default: 1)
* CHANGED: UserBufferManagementInit() now takes a sample rate parameter
* CHANGED: xcore.ai targets use sigma-delta software PLL for clock recovery of
digital Rx streams by default.
digital Rx streams and synch USB audio by default.
* Changes to dependencies:

View File

@@ -12,6 +12,8 @@
#include "dfu_interface.h"
#endif
#include "xua_clocking.h"
/** The audio driver thread.
*
* This function drives I2S ports and handles samples to/from other digital
@@ -28,18 +30,24 @@
*
* \param p_lrclk Nullable port for I2S sample clock
*
* \param p_bclk Nullable port for I2S bit
* \param p_bclk Nullable port for I2S bit clock
*
* \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_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif
* \param i_SoftPll Interface to software PLL task
*
* \param c_spdif_tx Channel connected to S/PDIF transmitter core from lib_spdif
*
* \param c_dig Channel connected to the clockGen() thread for
* receiving/transmitting samples
*
* \param c_mclk_change Channel notifying clockgen of an mclk frequency change
* \param c_audio_rate_change Channel notifying ep_buffer of an mclk frequency change and sync for stable clock
*
* \param dfuInterface Interface supporting DFU methods
*
* \param c_pdm_in Channel for receiving decimated PDM samples
*/
void XUA_AudioHub(chanend ?c_aud,
clock ?clk_audio_mclk,
@@ -54,12 +62,14 @@ void XUA_AudioHub(chanend ?c_aud,
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__))
, chanend c_dig
, chanend c_mclk_change
#endif
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__))
, chanend c_audio_rate_change
#endif
#if (((XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)) || defined(__DOXYGEN__))
, server interface i_dfu ?dfuInterface
#endif
#if (XUA_NUM_PDM_MICS > 0)
#if (XUA_NUM_PDM_MICS > 0 || defined(__DOXYGEN__))
, chanend c_pdm_in
#endif
);
@@ -79,8 +89,29 @@ void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode,
#endif // __XC__
void UserBufferManagementInit(unsigned samFreq);
/**
* @brief User buffer management code
*
* This function is called at the sample rate of the USB Audio stack (e.g,. 48 kHz) and between the two parameter arrays
* contain a full multi-channel audio-frame. The first array carries all the data that has been received from the USB host
* and is to be presented to the audio interfaces. The second array carries all the data received from the interfaces and
* is to be presented to the USB host. The user can chose to intercept and overwrite the samples stored in these arrays.
*
* \param sampsFromUsbToAudio Samples received from USB host and to be presented to audio interfaces
*
* \param sampsFromAudioToUsb Samples received from the audio interfaces and to be presented to the USB host
*/
void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]);
/**
* @brief User buffer managment init code
*
* This function is called once, before the first call to UserBufferManagement(), and can be used to initialise any
* related user state
*
* \param sampFreq The initial sample frequency
*
*/
void UserBufferManagementInit(unsigned sampFreq);
#endif // _XUA_AUDIOHUB_H_

View File

@@ -1,7 +1,7 @@
// Copyright 2011-2022 XMOS LIMITED.
// Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef __XUA_BUFFER_H__
#define __XUA_BUFFER_H__
#ifndef _XUA_BUFFER_H_
#define _XUA_BUFFER_H_
#if __XC__
@@ -25,7 +25,9 @@
* \param c_aud_ctl Audio control channel connected to Endpoint0()
* \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself)
* \param c_aud Channel connected to XUA_AudioHub() core
* \param c_audio_rate_change Channel to notify and synchronise on audio rate change
* \param i_pll_ref Interface to task that toggles reference pin to CS2100
* \param c_swpll_update Channel connected to software PLL task. Expects master clock counts based on USB frames.
*/
void XUA_Buffer(
chanend c_aud_out,
@@ -51,8 +53,14 @@ void XUA_Buffer(
, chanend c_hid
#endif
, chanend c_aud
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOXYGEN__)
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
, chanend c_audio_rate_change
#if (!XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, client interface pll_ref_if i_pll_ref
#endif
#if (XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, chanend c_swpll_update
#endif
#endif
);
@@ -81,11 +89,18 @@ void XUA_Buffer_Ep(chanend c_aud_out,
#ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOXYGEN__)
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
, chanend c_audio_rate_change
#if (!XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, client interface pll_ref_if i_pll_ref
#endif
#if (XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, chanend c_swpll_update
#endif
#endif
);
/** Manage the data transfer between the USB audio buffer and the
* Audio I/O driver.
*

View File

@@ -6,6 +6,8 @@
#include <xs1.h>
#include "sw_pll_wrapper.h"
interface pll_ref_if
{
void toggle();
@@ -26,9 +28,10 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync);
* clock
* \param c_clk_int channel connected to the decouple() thread for clock
* interrupts
* \param c_audio_rate_change channel to notify of master clock change
* \param p_for_mclk_count_aud port used for counting mclk and providing a timestamp
* \param c_sw_pll channel used to communicate with software PLL task
*
* \param c_mclk_change channel to notify of master clock change
*/
void clockGen( streaming chanend ?c_spdif_rx,
chanend ?c_adat_rx,
@@ -36,7 +39,12 @@ void clockGen( streaming chanend ?c_spdif_rx,
chanend c_audio,
chanend c_clk_ctl,
chanend c_clk_int,
port ?p_for_mclk_count_aud,
chanend c_mclk_change);
chanend c_audio_rate_change
#if XUA_USE_SW_PLL
, port p_for_mclk_count_aud
, chanend c_sw_pll
#endif
);
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED.
// Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
* @brief Defines relating to device configuration and customisation of lib_xua
@@ -11,7 +11,9 @@
#include "xua_conf.h"
#endif
/* Default tile arrangement */
/*
* Tile arrangement defines
*/
/**
* @brief Location (tile) of audio I/O. Default: 0
@@ -55,12 +57,9 @@
#define PLL_REF_TILE AUDIO_IO_TILE
#endif
/**
* @brief Disable USB functionalty just leaving AudioHub
/*
* Channel based defines
*/
#ifndef XUA_USB_EN
#define XUA_USB_EN (1)
#endif
/**
* @brief Number of input channels (device to host). Default: NONE (Must be defined by app)
@@ -79,18 +78,57 @@
#endif
/**
* @brief Number of DSD output channels. Default: 0 (disabled)
* @brief Number of PDM microphones in the design.
*
* Default: 0
*/
#ifndef XUA_NUM_PDM_MICS
#define XUA_NUM_PDM_MICS (0)
#endif
/**
* @brief Number of DSD output channels.
*
* Default: 0 (disabled)
*/
#if defined(DSD_CHANS_DAC) && (DSD_CHANS_DAC != 0)
#if defined(NATIVE_DSD) && (NATIVE_DSD == 0)
#undef NATIVE_DSD
#else
#define NATIVE_DSD (1) /* Always enable Native DSD when DSD mode is enabled */
#define NATIVE_DSD 1 /* Always enable Native DSD when DSD mode is enabled */
#endif
#else
#define DSD_CHANS_DAC (0)
#define DSD_CHANS_DAC 0
#endif
/**
* @brief Number of I2S channesl to DAC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_DAC
#error I2S_CHANS_DAC not defined
#define I2S_CHANS_DAC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_DAC (I2S_CHANS_DAC / I2S_CHANS_PER_FRAME)
#endif
/**
* @brief Number of I2S channels from ADC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_ADC
#error I2S_CHANS_ADC not defined
#define I2S_CHANS_ADC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_ADC (I2S_CHANS_ADC / I2S_CHANS_PER_FRAME)
#endif
/*
* Defines relating to the interface to external audio hardware i.e. DAC/ADC
*/
#define XUA_PCM_FORMAT_I2S (0)
#define XUA_PCM_FORMAT_TDM (1)
/**
@@ -114,36 +152,23 @@
**/
#ifndef I2S_CHANS_PER_FRAME
#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
#define I2S_CHANS_PER_FRAME (8)
#define I2S_CHANS_PER_FRAME 8
#else
#define I2S_CHANS_PER_FRAME (2)
#define I2S_CHANS_PER_FRAME 2
#endif
#endif
/**
* @brief Number of IS2 channesl to DAC/CODEC. Must be a multiple of 2.
* @brief Number of bits per channel for I2S/TDM. Supported values: 16/32-bit.
*
* Default: NONE (Must be defined by app)
* Default: 32 bits
*/
#ifndef I2S_CHANS_DAC
#error I2S_CHANS_DAC not defined
#define I2S_CHANS_DAC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_DAC (I2S_CHANS_DAC / I2S_CHANS_PER_FRAME)
#ifndef XUA_I2S_N_BITS
#define XUA_I2S_N_BITS (32)
#endif
/**
* @brief Number of I2S channels from ADC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_ADC
#error I2S_CHANS_ADC not defined
#define I2S_CHANS_ADC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_ADC (I2S_CHANS_ADC / I2S_CHANS_PER_FRAME)
#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
/**
@@ -197,35 +222,32 @@
#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
/*
* Clocking related defines
*/
#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
* @brief Max supported sample frequency for device (Hz).
*
* Default: 192000Hz
*/
#ifndef MAX_FREQ
#define MAX_FREQ (192000)
#endif
/**
* @brief Min supported sample frequency for device (Hz). Default 44100
* @brief Min supported sample frequency for device (Hz).
*
* Default: 44100Hz
*/
#ifndef MIN_FREQ
#define MIN_FREQ (44100)
#endif
/**
* @brief Master clock defines for 44100 rates (in Hz). Default: NONE (Must be defined by app)
* @brief Master clock defines for 44100 rates (in Hz).
*
* Default: NONE (Must be defined by app)
*/
#ifndef MCLK_441
#error MCLK_441 not defined
@@ -233,13 +255,29 @@
#endif
/**
* @brief Master clock defines for 48000 rates (in Hz). Default: NONE (Must be defined by app)
* @brief Master clock defines for 48000 rates (in Hz).
*
* Default: NONE (Must be defined by app)
*/
#ifndef MCLK_48
#error MCLK_48 not defined
#define MCLK_48 (256 * 48000) /* Define anyway for doygen */
#endif
/**
* @brief Enable/disable the use of the secondary/application PLL for generating and recovering master-clocks.
* Only available on xcore.ai devices.
*
* Default: Enabled (for xcore.ai devices)
*/
#ifndef XUA_USE_SW_PLL
#if defined(__XS3A__)
#define XUA_USE_SW_PLL (1)
#else
#define XUA_USE_SW_PLL (0)
#endif
#endif
/**
* @brief Default device sample frequency. A safe default should be used.
*
@@ -249,10 +287,25 @@
#define DEFAULT_FREQ (MIN_FREQ)
#endif
/* Audio Class Defines */
#define DEFAULT_MCLK (((DEFAULT_FREQ % 7350) == 0) ? MCLK_441 : MCLK_48)
/**
* @brief USB Audio Class Version.
* @brief Defines whether XMOS device runs as master (i.e. drives LR and Bit clocks)
*
* 0: XMOS is I2S master. 1: CODEC is I2s master.
*
* Default: 0 (XMOS is master)
*/
#ifndef CODEC_MASTER
#define CODEC_MASTER (0)
#endif
/*
* Audio Class defines
*/
/**
* @brief USB Audio Class Version
*
* Default: 2 (Audio Class version 2.0)
*/
@@ -261,9 +314,9 @@
#endif
/**
* @brief Whether or not to fall back to Audio Class 1.0 in USB Full-speed.
* @brief Enable/disable fall back to Audio Class 1.0 in USB Full-speed.
*
* Default: 0 (Disabled i.e. do not fall back to UAC 1.0
* Default: Disabled
*/
#ifndef AUDIO_CLASS_FALLBACK
#define AUDIO_CLASS_FALLBACK (0)
@@ -278,7 +331,7 @@
#if (AUDIO_CLASS == 2)
/* Whether to run in Audio Class 2.0 mode in USB Full-speed */
#if !defined(FULL_SPEED_AUDIO_2) && (AUDIO_CLASS_FALLBACK == 0)
#define FULL_SPEED_AUDIO_2 (1) /* Default to falling back to UAC2 */
#define FULL_SPEED_AUDIO_2 1 /* Default to falling back to UAC2 */
#endif
#endif
@@ -295,16 +348,17 @@
#error AUDIO_CLASS set to 1 and FULL_SPEED_AUDIO_2 enabled!
#endif
/* Feature defines */
/*
* Feature defines
*/
/**
* @brief Number of PDM microphones in the design.
* @brief Disable USB functionalty just leaving AudioHub
*
* Default: None
* Default: Enabled
*/
#ifndef XUA_NUM_PDM_MICS
#define XUA_NUM_PDM_MICS (0)
#ifndef XUA_USB_EN
#define XUA_USB_EN (1)
#endif
/**
@@ -318,18 +372,14 @@
#endif
/**
* @brief Size of a frame of microphone data samples.
*
* Default: 1
* @brief Size of a frame of microphone data samples. Default: 1
*/
#ifndef XUA_MIC_FRAME_SIZE
#define XUA_MIC_FRAME_SIZE (1)
#endif
/**
* @brief Enable MIDI functionality including buffering, descriptors etc.
*
* Default: 0 (Disabled)
* @brief Enable MIDI functionality including buffering, descriptors etc. Default: DISABLED
*/
#ifndef MIDI
#define MIDI (0)
@@ -363,9 +413,7 @@
#endif
/**
* @brief Enables ADAT Tx.
*
* Default: 0 (Disabled)
* @brief Enables ADAT Tx. Default: 0 (Disabled)
*/
#ifndef XUA_ADAT_TX_EN
#define XUA_ADAT_TX_EN (0)
@@ -381,18 +429,14 @@
#endif
/**
* @brief Enables SPDIF Rx.
*
* Default: 0 (Disabled)
* @brief Enables SPDIF Rx. Default: 0 (Disabled)
*/
#ifndef XUA_SPDIF_RX_EN
#define XUA_SPDIF_RX_EN (0)
#endif
/**
* @brief Enables ADAT Rx.
*
* Default: 0 (Disabled)
* @brief Enables ADAT Rx. Default: 0 (Disabled)
*/
#ifndef XUA_ADAT_RX_EN
#define XUA_ADAT_RX_EN (0)
@@ -455,15 +499,6 @@
#undef XUA_DFU_EN
#endif
/**
* @brief Use a QSPI (rather than SPI) flash for DFU (and boot)
*
* Default: 1 (True i.e use QSPI flash)
*/
#if !defined(XUA_QUAD_SPI_FLASH)
#define XUA_QUAD_SPI_FLASH (1)
#endif
/**
* @brief Enable HID playback controls functionality.
*
@@ -488,12 +523,11 @@
* You must also supply your own function to deal with the HID endpoint(s)
* in this case.
*/
#if( 0 < HID_CONTROLS )
#if (HID_CONTROLS) || defined (__DOXYGEN__)
#define XUA_HID_ENABLED (1)
#define XUA_OR_STATIC_HID_ENABLED (1)
#endif
#if defined(__static_hid_report_h_exists__)
#define XUA_OR_STATIC_HID_ENABLED (1)
#endif
@@ -509,17 +543,6 @@
#define HID_OUT_REQUIRED (0)
#endif
/**
* @brief Defines whether XMOS device runs as master (i.e. drives LR and Bit clocks)
*
* 0: XMOS is I2S master. 1: CODEC is I2s master.
*
* Default: 0 (XMOS is master)
*/
#ifndef CODEC_MASTER
#define CODEC_MASTER (0)
#endif
/**
* @brief Serial Number String used by the device
*
@@ -1160,7 +1183,7 @@
#define VOLUME_RES_MIXER (0x100)
#endif
/* Handle out volume control in the mixer - enabled by default if mixer enabled */
/* Handle out volume control in the mixer - enabled by default */
#ifndef OUT_VOLUME_IN_MIXER
#if MIXER
#define OUT_VOLUME_IN_MIXER (1)
@@ -1276,6 +1299,8 @@ enum USBEndpointNumber_Out
#endif /* __ASSEMBLER__ */
#define AUDIO_STOP_FOR_DFU (0x12345678)
#define AUDIO_START_FROM_DFU (0x87654321)
#define AUDIO_REBOOT_FROM_DFU (0xa5a5a5a5)
/* Result of db_to_mult(MAX_VOLUME, 8, 29) */
#define MAX_VOLUME_MULT (0x20000000)

View File

@@ -38,6 +38,7 @@ Frequencies and Clocks
.. doxygendefine:: DEFAULT_FREQ
.. doxygendefine:: MCLK_441
.. doxygendefine:: MCLK_48
.. doxygendefine:: XUA_USE_SW_PLL
Audio Class
-----------

View File

@@ -1,74 +1,49 @@
Required User Function Definitions
==================================
|newpage|
The following functions need to be defined by an application using the XMOS USB Audio framework.
User Function Definitions
=========================
The following functions can be defined by an application using `lib_xua`.
.. note:: Default, empty, implementations of these functions are provided in `lib_xua`. These are marked
as weak symbols so the application can simply define its own version of them.
External Audio Hardware Configuration Functions
-----------------------------------------------
.. c:function:: void AudioHwInit(chanend ?c_codec)
The following functions can be optionally used by the design to configure external audio hardware.
As a minimum, in most applications, it is expected that a implementation of `AudioHwConfig()` will need
to be provided.
This function is called when the audio core starts after the
device boots up and should initialize the external audio harware e.g. clocking, DAC, ADC etc
:param c_codec: An optional chanend that was original passed into
:c:func:`audio` that can be used to communicate
with other cores.
.. doxygenfunction:: AudioHwInit
.. doxygenfunction:: AudioHwConfig
.. doxygenfunction:: AudioHwConfig_Mute
.. doxygenfunction:: AudioHwConfig_UnMute
.. c:function:: void AudioHwConfig(unsigned samFreq, unsigned mclk, chanend ?c_codec, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
This function is called when the audio core starts or changes
sample rate. It should configure the extenal audio hardware to run at the specified
sample rate given the supplied master clock frequency.
:param samFreq: The sample frequency in Hz that the hardware should be configured to (in Hz).
:param mclk: The master clock frequency that is required in Hz.
:param c_codec: An optional chanend that was original passed into
:c:func:`audio` that can be used to communicate
with other cores.
:param dsdMode: Signifies if the audio hardware should be configured for DSD operation
:param sampRes_DAC: The sample resolution of the DAC stream
:param sampRes_ADC: The sample resolution of the ADC stream
Audio Streaming Functions
-------------------------
Audio Stream Start/Stop Functions
---------------------------------
The following functions can be optionally used by the design. They can be useful for mute lines etc.
.. c:function:: void AudioStreamStart(void)
.. doxygenfunction:: UserAudioStreamStart
.. doxygenfunction:: UserAudioStreamStop
.. doxygenfunction:: UserAudioInputStreamStart
.. doxygenfunction:: UserAudioInputStreamStop
.. doxygenfunction:: UserAudioOutputStreamStart
.. doxygenfunction:: UserAudioOutputStreamStop
This function is called when the audio stream from device to host
starts.
.. c:function:: void AudioStreamStop(void)
This function is called when the audio stream from device to host stops.
Host Active
-----------
Host Active Functions
---------------------
The following function can be used to signal that the device is connected to a valid host.
This is called on a change in state.
.. c:function:: void AudioStreamStart(int active)
:param active: Indicates if the host is active or not. 1 for active else 0.
.. doxygenfunction:: UserHostActive
HID Controls
------------
The following function is called when the device wishes to read physical user input (buttons etc).
The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used.
.. c:function:: void UserReadHIDButtons(unsigned char hidData[])
:param hidData: The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used.
.. doxygenfunction:: UserHIDGetData

View File

@@ -39,8 +39,11 @@ Setting the synchronisation mode of the device is done using the define in :ref:
- USB synchronisation mode
- ``XUA_SYNCMODE_ASYNC``
When operating in synchronous mode an external Cirrus Logic CS2100 device is required for master clock
generation. The codebase expects to drive a synchronisation signal to this external device
When operating in synchronous mode a local master clock must be generated that is synchronised to the incoming
SoF rate from USB. Either an external Cirrus Logic CS2100 device is required for this purpose
or, on xcore.ai devices, the on-chip application PLL may be used via lib_sw_pll.
In the case of using the CS2100, the codebase expects to drive a synchronisation signal to this external device
as a reference.
The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set appropriately.
@@ -56,8 +59,11 @@ The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set ap
* - ``PLL_REF_TILE``
- Tile location of reference to CS2100 device
- ``AUDIO_IO_TILE``
* - ``XUA_USE_SW_PLL``
- Whether or not to use sw_pll to recover the clock (xcore.ai only)
- 1 for xcore.ai targets. May be overridden to 0 in ``xua_conf.h``
The codebase expects this reference signal port to be defined in the application XN file as ``PORT_PLL_REF``.
The codebase expects the CS2100 reference signal port to be defined in the application XN file as ``PORT_PLL_REF``.
This may be a port of any bit-width, however, connection to bit[0] is assumed::
<Port Location="XS1_PORT_1A" Name="PORT_PLL_REF"/>

View File

@@ -1,5 +1,5 @@
XMOSNEWSTYLE = 2
DOXYGEN_DIRS=../../api
DOXYGEN_DIRS=../../api ../../src/core/user/audiostream ../../src/core/user/hostactive ../../src/core/user/hid ../../src/core/user/audiohw
SOURCE_INCLUDE_DIRS=../../../lib_xua
SPHINX_MASTER_DOC=lib_xua

View File

@@ -14,6 +14,7 @@ set(LIB_INCLUDES api
src/core/support
src/core/user
src/core/user/audiostream
src/core/user/audiohw
src/core/user/hid
src/core/user/hostactive
src/hid
@@ -26,7 +27,7 @@ set(LIB_DEPENDENT_MODULES "lib_locks"
"lib_xassert"
"lib_xud"
"lib_adat"
"lib_sw_pll(develop)")
"lib_sw_pll")
set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm)

View File

@@ -54,6 +54,7 @@ INCLUDE_DIRS = $(EXPORT_INCLUDE_DIRS) \
src/core/support \
src/core/user \
src/core/user/audiostream \
src/core/user/audiohw \
src/core/user/hid \
src/core/user/hostactive \
src/hid \
@@ -70,6 +71,7 @@ SOURCE_DIRS = src/core \
src/core/ports \
src/core/support \
src/core/user/audiostream \
src/core/user/audiohw \
src/core/user/hostactive \
src/core/xuduser \
src/dfu \

View File

@@ -20,6 +20,7 @@
#include "xua.h"
#include "audiohw.h"
#include "audioports.h"
#include "mic_array_conf.h"
#if (XUA_SPDIF_TX_EN)
@@ -640,7 +641,9 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#endif
#if (XUA_ADAT_RX_EN || XUA_SPDIF_RX_EN)
, chanend c_dig_rx
, chanend c_mclk_change
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, chanend c_audio_rate_change
#endif
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, server interface i_dfu ?dfuInterface
@@ -800,15 +803,23 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
}
#endif
/* Configure Clocking/CODEC/DAC/ADC for SampleFreq/MClk */
AudioHwConfig(curFreq, mClk, dsdMode, curSamRes_DAC, curSamRes_ADC);
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Notify clockgen of new mCLk */
c_mclk_change <: mClk;
c_mclk_change <: curFreq;
/* Wait for ACK back from clockgen to signal clocks all good */
c_mclk_change :> int _;
/* User should mute audio hardware */
AudioHwConfig_Mute();
/* User code should configure audio harware for SampleFreq/MClk etc */
AudioHwConfig(curFreq, mClk, dsdMode, curSamRes_DAC, curSamRes_ADC);
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Notify clockgen of new mCLk */
c_audio_rate_change <: mClk;
c_audio_rate_change <: curFreq;
/* Wait for ACK back from clockgen or ep_buffer to signal clocks all good */
c_audio_rate_change :> int _;
#endif
/* User should unmute audio hardware */
AudioHwConfig_UnMute();
}
if(!firstRun)

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED.
// Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h"
#if XUA_USB_EN
@@ -105,8 +105,13 @@ void XUA_Buffer(
#endif
, chanend c_aud
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#if(XUA_USE_SW_PLL)
, chanend c_sw_pll
#else
, client interface pll_ref_if i_pll_ref
#endif
#endif
)
{
#ifdef CHAN_BUFF_CTRL
@@ -141,7 +146,12 @@ void XUA_Buffer(
, c_buff_ctrl
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#if(XUA_USE_SW_PLL)
, c_sw_pll
#else
, i_pll_ref
#endif
#endif
);
@@ -190,9 +200,14 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl
#endif
#if XUA_SYNCMODE == XUA_SYNCMODE_SYNC
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#if (XUA_USE_SW_PLL)
, chanend c_sw_pll
#else
, client interface pll_ref_if i_pll_ref
#endif
#endif
)
{
XUD_ep ep_aud_out = XUD_InitEp(c_aud_out);
@@ -247,7 +262,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#if (NUM_USB_CHAN_IN > 0)
unsigned bufferIn = 1;
#endif
unsigned sofCount = 0;
int sofCount = 0;
unsigned mod_from_last_time = 0;
#ifdef FB_TOLERANCE_TEST
@@ -294,7 +309,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
unsigned iap_ea_native_interface_alt_setting = 0;
unsigned iap_ea_native_control_to_send = 0;
unsigned iap_ea_native_incoming = 0;
#endif
#endif
@@ -357,6 +371,24 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#ifndef LOCAL_CLOCK_MARGIN
#define LOCAL_CLOCK_MARGIN (1000)
#endif
#if (XUA_USE_SW_PLL)
/* Setup the phase frequency detector */
const unsigned controller_rate_hz = 100;
const unsigned pfd_ppm_max = 2000; /* PPM range before we assume unlocked */
sw_pll_pfd_state_t sw_pll_pfd;
sw_pll_pfd_init(&sw_pll_pfd,
1, /* How often the PFD is invoked per call */
masterClockFreq / controller_rate_hz, /* pll ratio integer */
0, /* Assume precise timing of sampling */
pfd_ppm_max);
outuint(c_sw_pll, masterClockFreq);
outct(c_sw_pll, XS1_CT_END);
inuint(c_sw_pll); /* receive ACK */
inct(c_sw_pll);
#else /* XUA_USE_SW_PLL */
timer t_sofCheck;
unsigned timeLastEdge;
unsigned timeNextEdge;
@@ -365,6 +397,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
i_pll_ref.toggle();
#endif
#endif /* (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) */
while(1)
{
XUD_Result_t result;
@@ -427,7 +461,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* Reset FB */
/* Note, Endpoint 0 will hold off host for a sufficient period to allow our feedback
* to stabilise (i.e. sofCount == 128 to fire) */
sofCount = 1;
sofCount = 0;
clocks = 0;
clockcounter = 0;
mod_from_last_time = 0;
@@ -450,7 +484,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
masterClockFreq = MCLK_441;
}
}
#endif
#endif /* (MAX_FREQ != MIN_FREQ) */
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this
* core locked, it must stay responsive to packets (MIDI etc) and SOFs. So, set a flag and check for
* handshake elsewhere */
@@ -502,13 +536,13 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
}
#endif
/* Pass on sample freq change to decouple() via global flag (saves a chanend) */
/* Note: freqChange flags now used to communicate other commands also */
/* Note: freqChange_flag now used to communicate other commands also */
SET_SHARED_GLOBAL0(g_freqChange, cmd); /* Set command */
SET_SHARED_GLOBAL(g_freqChange_flag, cmd); /* Set Flag */
}
break;
}
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && (!XUA_USE_SW_PLL)
case t_sofCheck when timerafter(timeNextEdge) :> void:
i_pll_ref.toggle();
timeLastEdge = timeNextEdge;
@@ -523,28 +557,61 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* SOF notification from XUD_Manager() */
case inuint_byref(c_sof, u_tmp):
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
/* This really could (should) be done in decouple. However, for a quick demo this is okay
* Decouple expects a 16:16 number in fixed point stored in the global g_speed */
unsigned usbSpeed;
int framesPerSec;
GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed);
static int sofCount = 0;
#if (XUA_USE_SW_PLL)
/* Run PFD and sw_pll controller at 100Hz */
const int sofFreqDivider = (usbSpeed == XUD_SPEED_HS) ? (8000 / controller_rate_hz) : (1000 / controller_rate_hz);
#else /* (XUA_USE_SW_PLL) */
/* 1000 toggles per second for CS2100 reference -> 500 Hz */
const int toggleRateHz = 1000;
const int sofFreqDivider = (usbSpeed == XUD_SPEED_HS) ? (8000 / toggleRateHz) : (1000 / toggleRateHz);
#endif /* (XUA_USE_SW_PLL) */
framesPerSec = (usbSpeed == XUD_SPEED_HS) ? 8000 : 1000;
clocks = ((int64_t) sampleFreq << 16) / framesPerSec;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks));
sofCount += 1000;
if (sofCount == framesPerSec)
sofCount++;
if (sofCount == sofFreqDivider)
{
#if (XUA_USE_SW_PLL)
/* Grab port timer count, run through PFD and send to sw_pll */
unsigned short mclk_pt;
asm volatile("getts %0, res[%1]" : "=r" (mclk_pt) : "r" (p_off_mclk));
uint8_t first_loop = 0;
unsafe{
sw_pll_calc_error_from_port_timers(&sw_pll_pfd, &first_loop, mclk_pt, 0);
}
int error = 0;
if(!first_loop)
{
error = sw_pll_pfd.mclk_diff;
}
sw_pll_pfd.mclk_pt_last = mclk_pt;
/* Send error to sw_pll */
outuint(c_sw_pll, error);
outct(c_sw_pll, XS1_CT_END);
#else /* (XUA_USE_SW_PLL) */
/* Do toggle for CS2100 reference clock */
/* Port is accessed via interface to allow flexibilty with location */
i_pll_ref.toggle();
t_sofCheck :> timeLastEdge;
sofCount = 0;
timeNextEdge = timeLastEdge + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#endif /* (XUA_USE_SW_PLL) */
sofCount = 0;
}
/* This really could (should) be done in decouple. However, for a quick demo this is okay
* Decouple expects a 16:16 number in fixed point stored in the global g_speed */
const int framesPerSec = (usbSpeed == XUD_SPEED_HS) ? 8000 : 1000;
clocks = ((int64_t) sampleFreq << 16) / framesPerSec;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks));
#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
/* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to
@@ -646,7 +713,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
clockcounter = 0;
}
#else
/* Assuming 48kHz from a 24.576 master clock (0.0407uS period)
* MCLK ticks per SOF = 125uS / 0.0407 = 3072 MCLK ticks per SOF.
* expected Feedback is 48000/8000 = 6 samples. so 0x60000 in 16:16 format.
@@ -691,7 +757,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
clocks < (expected_fb + FB_TOLERANCE))
#endif
{
int usb_speed;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
@@ -897,7 +962,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#endif
#endif
#if XUA_HID_ENABLED
#if (XUA_HID_ENABLED)
/* HID Report Data */
case XUD_SetData_Select(c_hid, ep_hid, result):
hid_ready_flag = 0U;
@@ -963,6 +1028,33 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
break;
#endif /* ifdef MIDI */
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
case c_audio_rate_change :> u_tmp:
unsigned selected_mclk_rate = u_tmp;
c_audio_rate_change :> u_tmp; /* Sample rate is discarded as only care about mclk */
#if (XUA_USE_SW_PLL)
sw_pll_pfd_init(&sw_pll_pfd,
1, /* How often the PFD is invoked per call */
selected_mclk_rate / controller_rate_hz, /* pll muliplication ratio integer */
0, /* Assume precise timing of sampling */
pfd_ppm_max);
restart_sigma_delta(c_sw_pll, selected_mclk_rate);
/* Delay ACK until sw_pll says it is ready */
#else
c_audio_rate_change <: 0; /* ACK back to audio to release I2S immediately */
#endif /* XUA_USE_SW_PLL */
break;
#if (XUA_USE_SW_PLL)
/* This is fired when sw_pll has completed initialising a new mclk_rate */
case inuint_byref(c_sw_pll, u_tmp):
inct(c_sw_pll);
c_audio_rate_change <: 0; /* ACK back to audio to release */
break;
#endif /* (XUA_USE_SW_PLL) */
#endif /* (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) */
#ifdef IAP
/* Received word from iap thread - Check for ACK or Data */
case iap_get_ack_or_reset_or_data(c_iap, is_ack_iap, is_reset, datum_iap):

View File

@@ -7,7 +7,6 @@
#include "xua.h"
#include "xua_commands.h"
#include "xua_clocking.h"
#include "sw_pll_wrapper.h"
#if (XUA_SPDIF_RX_EN)
#include "spdif.h"
@@ -200,9 +199,7 @@ static inline int validSamples(Counter &counter, int clockIndex)
}
#endif
#if USE_SW_PLL
/* Pointer to sw_pll struct to allow it to be used in separate SDM thread */
extern sw_pll_state_t * unsafe sw_pll_ptr;
#if XUA_USE_SW_PLL
unsafe
{
unsigned * unsafe selected_mclk_rate_ptr = NULL;
@@ -225,8 +222,12 @@ void clockGen ( streaming chanend ?c_spdif_rx,
chanend c_dig_rx,
chanend c_clk_ctl,
chanend c_clk_int,
port ?p_for_mclk_count_aud,
chanend c_mclk_change)
chanend c_audio_rate_change
#if XUA_USE_SW_PLL
, port p_for_mclk_count_aud
, chanend c_sw_pll
#endif
)
{
timer t_local;
unsigned timeNextEdge, timeLastEdge, timeNextClockDetection;
@@ -245,13 +246,14 @@ void clockGen ( streaming chanend ?c_spdif_rx,
timer t_external;
unsigned selected_mclk_rate = MCLK_48; // Assume 24.576MHz initial clock
unsigned selected_sample_rate = 0;
#if XUA_USE_SW_PLL
unsigned mclks_per_sample = 0;
unsigned short mclk_time_stamp = 0;
/* Get MCLK count */
if(!isnull(p_for_mclk_count_aud))
{
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
}
#endif
#endif
#if (XUA_SPDIF_RX_EN)
@@ -343,28 +345,12 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Initial ref clock output and get timestamp */
i_pll_ref.init();
#if (USE_SW_PLL && (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN))
chan c_sigma_delta;
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
int reset_sw_pll_pfd = 1;
int require_ack_to_audio = 0;
/* Set selected_mclk_rate_ptr to point at local var selected_mclk_rate */
unsafe {
selected_mclk_rate_ptr = &selected_mclk_rate;
}
par
{
while(1)
{
unsafe
{
SigmaDeltaTask(c_sigma_delta, selected_mclk_rate_ptr);
}
}
#else
{
restart_sigma_delta(c_sw_pll, MCLK_48); /* default to 48kHz - this will be reset shortly when host selects rate */
#endif
while(1)
{
select
@@ -482,7 +468,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Generate local clock from timer */
case t_local when timerafter(timeNextEdge) :> void:
#if USE_SW_PLL
#if XUA_USE_SW_PLL
/* Do nothing - hold the most recent sw_pll setting */
#else
/* Setup next local clock edge */
@@ -533,13 +519,14 @@ void clockGen ( streaming chanend ?c_spdif_rx,
break;
#endif
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && USE_SW_PLL)
case inuint_byref(c_sigma_delta, tmp):
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
case inuint_byref(c_sw_pll, tmp):
inct(c_sw_pll);
/* Send ACK back to audiohub to allow I2S to start
This happens only on SDM restart and only once */
if(require_ack_to_audio)
{
c_mclk_change <: tmp;
c_audio_rate_change <: tmp;
require_ack_to_audio = 0;
}
break;
@@ -547,17 +534,17 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Receive notification of audio streaming settings change and store */
case c_mclk_change :> selected_mclk_rate:
c_mclk_change :> selected_sample_rate;
#if USE_SW_PLL
case c_audio_rate_change :> selected_mclk_rate:
c_audio_rate_change :> selected_sample_rate;
#if XUA_USE_SW_PLL
mclks_per_sample = selected_mclk_rate / selected_sample_rate;
restart_sigma_delta(c_sigma_delta);
restart_sigma_delta(c_sw_pll, selected_mclk_rate);
reset_sw_pll_pfd = 1;
/* We will shedule an ACK when sigma delta is up and running */
require_ack_to_audio = 1;
#else
/* Send ACK immediately as we are good to go if not using SW_PLL */
c_mclk_change <: 0;
c_audio_rate_change <: 0;
#endif
break;
#endif
@@ -566,11 +553,10 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Receive sample from S/PDIF RX thread (streaming chan) */
case c_spdif_rx :> spdifRxData:
#if XUA_USE_SW_PLL
/* Record time of sample */
if(!isnull(p_for_mclk_count_aud))
{
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
}
#endif
t_local :> spdifRxTime;
/* Check parity and ignore if bad */
@@ -640,10 +626,10 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Setup for next edge */
timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#if USE_SW_PLL
#if XUA_USE_SW_PLL
do_sw_pll_phase_frequency_detector_dig_rx( mclk_time_stamp,
mclks_per_sample,
c_sigma_delta,
c_sw_pll,
spdifCounters.receivedSamples,
reset_sw_pll_pfd);
#else
@@ -660,11 +646,11 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#if (XUA_ADAT_RX_EN)
/* receive sample from ADAT rx thread (streaming channel with CT_END) */
case inuint_byref(c_adat_rx, tmp):
#if XUA_USE_SW_PLL
/* record time of sample */
if(!isnull(p_for_mclk_count_aud))
{
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
}
#endif
t_local :> adatReceivedTime;
/* Sync is: 1 | (user_byte << 4) */
@@ -757,10 +743,10 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Setup for next edge */
timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#if USE_SW_PLL
#if XUA_USE_SW_PLL
do_sw_pll_phase_frequency_detector_dig_rx( mclk_time_stamp,
mclks_per_sample,
c_sigma_delta,
c_sw_pll,
adatCounters.receivedSamples,
reset_sw_pll_pfd);
#else
@@ -906,5 +892,4 @@ void clockGen ( streaming chanend ?c_spdif_rx,
} /* select */
} /* while(1) */
} /* clkgen task scope */
}

View File

@@ -4,17 +4,9 @@
#ifndef _SW_PLL_WRAPPPER_H_
#define _SW_PLL_WRAPPPER_H_
/* By default we use SW_PLL for Digital Rx configs running on xcore.ai */
/* Note: Not yet implemented for Synchronous mode */
#ifdef __XS3A__
#ifndef USE_SW_PLL
#define USE_SW_PLL 1
#endif /* USE_SW_PLL */
#else
#define USE_SW_PLL 0
#endif /* __XS3A__ */
#include "xua.h"
#if USE_SW_PLL
#if XUA_USE_SW_PLL
extern "C"
{
#include "sw_pll.h"
@@ -27,29 +19,29 @@ extern "C"
/** Task that receives an error term, passes it through a PI controller and periodically
* calclulates a sigma delta output value and sends it to the PLL fractional register.
*
* \param c_sigma_delta Channel connected to the clocking thread to pass raw error terms.
* \param selected_mclk_rate_ptr Pointer to the mclk rate variable declared in clockgen.
* \param c_sw_pll Channel connected to the clocking thread to pass raw error terms.
*/
void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ptr);
void sw_pll_task(chanend c_sw_pll);
/** Helper function that sends a special restart command. It causes the SDM task
* to quit and restart using the new mclk.
*
* \param c_sigma_delta Channel connected to the clocking thread to pass raw error terms.
* \param c_sw_pll Channel connected to the clocking thread to pass raw error terms.
* \param mclk_Rate The mclk frequency in Hz.
*/
void restart_sigma_delta(chanend c_sigma_delta);
void restart_sigma_delta(chanend c_sw_pll, unsigned mclk_rate);
/** Performs a frequency comparsion between the incoming digital Rx stream and the local mclk.
*
* \param mclk_time_stamp The captured mclk count (using port timer) at the time of sample Rx.
* \param mclks_per_sample The nominal number of mclks per audio sample.
* \param c_sigma_delta Channel connected to the sigma delta and controller thread.
* \param c_sw_pll Channel connected to the sigma delta and controller thread.
* \param receivedSamples The number of received samples since tha last call to this function.
* \param reset_sw_pll_pfd Reference to a flag which will be used to signal reset of this function's state.
*/
void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
unsigned mclks_per_sample,
chanend c_sigma_delta,
chanend c_sw_pll,
int receivedSamples,
int &reset_sw_pll_pfd);
@@ -61,5 +53,5 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
* returns The SDM update interval in ticks and the initial DCO setting for nominal frequency */
{unsigned, unsigned} InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk);
#endif /* USE_SW_PLL */
#endif /* XUA_USE_SW_PLL */
#endif /* _SW_PLL_WRAPPPER_H_ */

View File

@@ -7,10 +7,10 @@
#include "sw_pll_wrapper.h"
#include "xua.h"
#if USE_SW_PLL
#if XUA_USE_SW_PLL
{unsigned, unsigned} InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk)
{unsigned, unsigned} init_sw_pll(sw_pll_state_t &sw_pll, unsigned mClk)
{
/* Autogenerated SDM App PLL setup by dco_model.py using 22.5792_1M profile */
/* Input freq: 24000000
@@ -74,7 +74,7 @@
void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
unsigned mclks_per_sample,
chanend c_sigma_delta,
chanend c_sw_pll,
int receivedSamples,
int &reset_sw_pll_pfd)
{
@@ -106,7 +106,8 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
}
/* send PFD output to the sigma delta thread */
outuint(c_sigma_delta, (int) f_error);
outuint(c_sw_pll, (int) f_error);
outct(c_sw_pll, XS1_CT_END);
last_mclk_time_stamp = mclk_time_stamp;
control_loop_counter = 0;
@@ -114,30 +115,34 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
}
}
void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ptr){
void sw_pll_task(chanend c_sw_pll){
/* Zero is an invalid number and the SDM will not write the frac reg until
the first control value has been received. This avoids issues with
channel lockup if two tasks (eg. init and SDM) try to write at the same time. */
while(1)
{
unsigned selected_mclk_rate = inuint(c_sw_pll);
inct(c_sw_pll);
int f_error = 0;
int dco_setting = 0; /* gets set at InitSWPLL */
unsigned sdm_interval = 0; /* gets set at InitSWPLL */
int dco_setting = 0; /* gets set at init_sw_pll */
unsigned sdm_interval = 0; /* gets set at init_sw_pll */
sw_pll_state_t sw_pll;
unsafe
{
/* initialse the SDM and gather SDM initial settings */
{sdm_interval, dco_setting} = InitSWPLL(sw_pll, (unsigned)*selected_mclk_rate_ptr);
}
{sdm_interval, dco_setting} = init_sw_pll(sw_pll, selected_mclk_rate);
tileref_t this_tile = get_local_tile_id();
timer tmr;
int32_t time_trigger;
tmr :> time_trigger;
time_trigger += sdm_interval; /* ensure first loop has correct delay */
int running = 1;
outuint(c_sigma_delta, 0); /* Signal back via clockgen to audio to start I2S */
outuint(c_sw_pll, 0); /* Signal back via clockgen to audio to start I2S */
outct(c_sw_pll, XS1_CT_END);
unsigned rx_word = 0;
while(running)
@@ -145,7 +150,8 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_
/* Poll for new SDM control value */
select
{
case inuint_byref(c_sigma_delta, rx_word):
case inuint_byref(c_sw_pll, rx_word):
inct(c_sw_pll);
if(rx_word == DISABLE_SDM)
{
f_error = 0;
@@ -180,13 +186,17 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_
unsafe {
sw_pll_do_sigma_delta(&sw_pll.sdm_state, this_tile, dco_setting);
}
} /* if running */
} /* while running */
} /* while(1) */
}
void restart_sigma_delta(chanend c_sigma_delta)
void restart_sigma_delta(chanend c_sw_pll, unsigned selected_mclk_rate)
{
outuint(c_sigma_delta, DISABLE_SDM); /* Resets SDM */
outuint(c_sw_pll, DISABLE_SDM); /* Resets SDM */
outct(c_sw_pll, XS1_CT_END);
outuint(c_sw_pll, selected_mclk_rate);
outct(c_sw_pll, XS1_CT_END);
}
#endif /* USE_SW_PLL */
#endif /* XUA_USE_SW_PLL */

View File

@@ -314,7 +314,13 @@ void usb_audio_io(chanend ?c_aud_in,
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, client interface pll_ref_if i_pll_ref
, port ?p_for_mclk_count_aud
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#endif
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
, port p_for_mclk_count_aud
, chanend c_sw_pll
#endif
)
{
@@ -324,15 +330,13 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
chan c_dig_rx;
chan c_mclk_change; /* Notification of new mclk freq to clockgen */
chan c_audio_rate_change; /* Notification of new mclk freq to clockgen and synch */
#if XUA_USE_SW_PLL
/* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/
if(!isnull(p_for_mclk_count_aud))
{
unsigned x = 0;
asm("ldw %0, dp[clk_audio_mclk]":"=r"(x));
asm("setclk res[%0], %1"::"r"(p_for_mclk_count_aud), "r"(x));
}
#endif /* XUA_USE_SW_PLL */
#endif /* (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) */
#if (XUA_NUM_PDM_MICS > 0) && (PDM_TILE == AUDIO_IO_TILE)
@@ -378,7 +382,9 @@ void usb_audio_io(chanend ?c_aud_in,
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, c_dig_rx
, c_mclk_change
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, c_audio_rate_change
#endif
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, dfuInterface
@@ -405,8 +411,12 @@ void usb_audio_io(chanend ?c_aud_in,
c_dig_rx,
c_clk_ctl,
c_clk_int,
p_for_mclk_count_aud,
c_mclk_change);
c_audio_rate_change
#if XUA_USE_SW_PLL
, p_for_mclk_count_aud
, c_sw_pll
#endif
);
}
#endif
@@ -483,9 +493,16 @@ int main()
#endif
#endif
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) )
interface pll_ref_if i_pll_ref;
#endif
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
chan c_sw_pll;
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
chan c_audio_rate_change; /* Notification of new mclk freq to ep_buffer */
#endif
chan c_sof;
chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */
chan c_xud_in[ENDPOINT_COUNT_IN];
@@ -507,7 +524,7 @@ int main()
{
USER_MAIN_CORES
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN))
on tile[PLL_REF_TILE]: PllRefPinTask(i_pll_ref, p_pll_ref);
#endif
on tile[XUD_TILE]:
@@ -578,7 +595,12 @@ int main()
#endif
, c_mix_out
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#if (!XUA_USE_SW_PLL)
, i_pll_ref
#else
, c_sw_pll
#endif
#endif
);
//:
@@ -593,6 +615,10 @@ int main()
#endif /* XUA_USB_EN */
}
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
on tile[AUDIO_IO_TILE]: sw_pll_task(c_sw_pll);
#endif
on tile[AUDIO_IO_TILE]:
{
@@ -609,14 +635,16 @@ int main()
, dfuInterface
#endif
#if (XUA_NUM_PDM_MICS > 0)
#if (PDM_TILE == AUDIO_IO_TILE)
, c_ds_output
#endif
, c_pdm_pcm
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, i_pll_ref
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#endif
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
, p_for_mclk_count_audio
, c_sw_pll
#endif
);
}

View File

@@ -0,0 +1,29 @@
// Copyright 2023-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Default implementations of AudioHwInit(), AudioHwConfig(), AudioHwConfig_Mute() and AudioHwConfig_UnMute() */
void AudioHwInit() __attribute__ ((weak));
void AudioHwInit()
{
return;
}
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) __attribute__ ((weak));
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
return;
}
void AudioHwConfig_Mute() __attribute__ ((weak));
void AudioHwConfig_Mute()
{
return;
}
void AudioHwConfig_UnMute() __attribute__ ((weak));
void AudioHwConfig_UnMute()
{
return;
}

View File

@@ -0,0 +1,54 @@
// Copyright 2023-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _AUDIO_HW_H_
#define _AUDIO_HW_H_
/* The functions below should be implemented for the external audio hardware arrangement of a specific design.
* Note, default (empty) implementations of these are provided in audiohub_user.c
*/
/**
* @brief User audio hardware initialisation code
*
* This function is called when the device starts up and should contain user code to perform any required audio hardware initialisation
*/
void AudioHwInit(void);
/**
* @brief User audio hardware configuration code
*
* This function is called when on sample rate change and should contain user code to configure audio hardware
* (clocking, CODECs etc) for a specific mClk/Sample frequency
*
* \param samFreq The new sample frequency (in Hz)
*
* \param mClk The new master clock frequency (in Hz)
*
* \param dsdMode DSD mode, DSD_MODE_NATIVE, DSD_MODE_DOP or DSD_MODE_OFF
*
* \param sampRes_DAC Playback sample resolution (in bits)
*
* \param sampRes_ADC Record sample resolution (in bits)
*/
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC);
/**
* @brief User code mute audio hardware
*
* This function is called before AudioHwConfig() and should contain user code to mute audio hardware before a
* sample rate change in order to reduced audible pops/clicks
*
* Note, if using the application PLL of a xcore.ai device this function will be called before the master-clock is
* changed
*/
void AudioHwConfig_Mute(void);
/**
* @brief User code to un-mute audio hardware
*
* This function is called after AudioHwConfig() and should contain user code to un-mute audio hardware after a
* sample rate change
*/
void AudioHwConfig_UnMute(void);
#endif

View File

@@ -1,30 +1,51 @@
// Copyright 2011-2021 XMOS LIMITED.
// Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _AUDIOSTREAM_H_
#define _AUDIOSTREAM_H_
/* Functions that handle functions that must occur on stream start/stop e.g. DAC mute/un-mute
*
* THESE NEED IMPLEMENTING FOR A SPECIFIC DESIGN
*
* */
/* Functions that handle functionality that occur on stream start/stop e.g. DAC mute/un-mute.
* They should be implemented for the external audio hardware arrangement of a specific design.
*/
/* Any actions required for stream start e.g. DAC un-mute - run every stream start */
/**
* @brief User stream start code
*
* User code to perform any actions required at every stream start - either input or output
*/
void UserAudioStreamStart(void);
/* Any actions required on stream stop e.g. DAC mute - run every steam stop */
/**
* @brief User stream stop code
*
* User code to perform any actions required on every stream stop - either input or output*/
void UserAudioStreamStop(void);
/* Any actions required on input stream start */
/**
* @brief User input stream stop code
*
* User code to perform any actions required on input stream start i.e. device to host
*/
void UserAudioInputStreamStart(void);
/* Any actions required on input stream stop */
/**
* @brief User input stream stop code
*
* User code to perform any actions required on input stream stop i.e. device to host
*/
void UserAudioInputStreamStop(void);
/* Any actions required on output stream start */
/**
* @brief User output stream start code
*
* User code to perform any actions required on output stream start i.e. host to device
*/
void UserAudioOutputStreamStart(void);
/* Any actions required on output stream stop */
/**
* @brief User output stream stop code
*
* User code to perfrom any actions required on output stream stop i.e. host to device
*/
void UserAudioOutputStreamStop(void);
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2013-2023 XMOS LIMITED.
// Copyright 2013-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
@@ -10,8 +10,8 @@
* Document section numbers refer to the HID Device Class Definition, version 1.11.
*/
#ifndef __USER_HID_H__
#define __USER_HID_H__
#ifndef _USER_HID_H_
#define _USER_HID_H_
#include <stddef.h>
@@ -34,15 +34,9 @@ typedef struct hidEvent_t {
#define HID_MAX_DATA_BYTES ( 4 )
#define HID_EVENT_INVALID_ID ( 0x100 )
#if XUA_HID_ENABLED
/**
* \brief Get the data for the next HID Report
*
* \note This function returns the HID data as a list of unsigned char because the
* \c XUD_SetReady_In() accepts data for transmission to the USB Host using
* this type.
*
* \param[in] id The HID Report ID (see 5.6, 6.2.2.7, 8.1 and 8.2)
* Set to zero if the application provides only one HID Report
* which does not include a Report ID
@@ -61,5 +55,4 @@ size_t UserHIDGetData( const unsigned id, unsigned char hidData[ HID_MAX_DATA_BY
*/
void UserHIDInit( void );
#endif /* ( 0 < HID_CONTROLS ) */
#endif /* __USER_HID_H__ */
#endif /* _USER_HID_H_ */

View File

@@ -1,4 +1,12 @@
// Copyright 2013-2021 XMOS LIMITED.
// Copyright 2013-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
* @brief User host active code
*
* This function can be used to perform user defined actions based on host present/not-present events.
* This function is called on a change in state.
*
* \param active Indicates if the host is active or not. 1 for active, else 0
*/
void UserHostActive(int active);

View File

@@ -3,10 +3,12 @@ TEST_FLAGS ?=
XCC_FLAGS_HS = -O3 -g -DXUD_CORE_CLOCK=600 -save-temps -DUSB_TILE=tile[0] -DLOCAL_CLOCK_INCREMENT=10000 -DLOCAL_CLOCK_MARGIN=100 \
-DBUS_SPEED=2 \
-DXUA_USE_SW_PLL=0 \
$(TEST_FLAGS)
XCC_FLAGS_FS = -O3 -g -DXUD_CORE_CLOCK=600 -save-temps -DUSB_TILE=tile[0] -DLOCAL_CLOCK_INCREMENT=10000 -DLOCAL_CLOCK_MARGIN=100 \
-DBUS_SPEED=1 \
-DXUA_USE_SW_PLL=0 \
$(TEST_FLAGS)
TARGET = test_xs3_600.xn

View File

@@ -1,4 +1,4 @@
// Copyright 2022 XMOS LIMITED.
// Copyright 2022-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Simple test to ensure reference clock to CS2100 device continues when SOF clock not available
@@ -173,6 +173,7 @@ int main()
chan c_in[EP_COUNT_IN];
chan c_sof;
chan c_aud_ctl;
chan c_audio_rate_change;
interface pll_ref_if i_pll_ref;
@@ -185,7 +186,7 @@ int main()
XUA_Buffer_Ep(c_out[1], /* USB Audio Out*/
c_in[1], /* USB Audio In */
c_sof, c_aud_ctl, p_off_mclk, i_pll_ref
c_sof, c_aud_ctl, p_off_mclk, c_audio_rate_change, i_pll_ref
);
}
@@ -193,4 +194,6 @@ int main()
checker();
}
return 0;
}