forked from PAWPAW-Mirror/lib_xua
- Improved audio performance in synchronous mode when using the App PLL
- Software PLL code now uses a core rather than running entirely in XUA_Buffer_EP()
This commit is contained in:
@@ -12,6 +12,8 @@
|
|||||||
#include "dfu_interface.h"
|
#include "dfu_interface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "xua_clocking.h"
|
||||||
|
|
||||||
/** The audio driver thread.
|
/** The audio driver thread.
|
||||||
*
|
*
|
||||||
* This function drives I2S ports and handles samples to/from other digital
|
* This function drives I2S ports and handles samples to/from other digital
|
||||||
@@ -34,6 +36,8 @@
|
|||||||
*
|
*
|
||||||
* \param p_i2s_adc Nullable array of ports for I2S data input lines
|
* \param p_i2s_adc Nullable array of ports for I2S data input lines
|
||||||
*
|
*
|
||||||
|
* \param i_SoftPll Interface to software PLL task
|
||||||
|
*
|
||||||
* \param c_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif
|
* \param c_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif
|
||||||
*
|
*
|
||||||
* \param c_dig Channel connected to the clockGen() thread for
|
* \param c_dig Channel connected to the clockGen() thread for
|
||||||
@@ -47,6 +51,9 @@ void XUA_AudioHub(chanend ?c_aud,
|
|||||||
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 out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
|
||||||
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]
|
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]
|
||||||
|
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||||
|
, client interface SoftPll_if i_SoftPll
|
||||||
|
#endif
|
||||||
#if (XUA_SPDIF_TX_EN) || defined(__DOXYGEN__)
|
#if (XUA_SPDIF_TX_EN) || defined(__DOXYGEN__)
|
||||||
, chanend c_spdif_tx
|
, chanend c_spdif_tx
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
* \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself)
|
* \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_aud Channel connected to XUA_AudioHub() core
|
||||||
* \param i_pll_ref Interface to task that toggles reference pin to CS2100
|
* \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(
|
void XUA_Buffer(
|
||||||
chanend c_aud_out,
|
chanend c_aud_out,
|
||||||
@@ -51,8 +52,13 @@ void XUA_Buffer(
|
|||||||
, chanend c_hid
|
, chanend c_hid
|
||||||
#endif
|
#endif
|
||||||
, chanend c_aud
|
, chanend c_aud
|
||||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
|
||||||
|
#if (!XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||||
, client interface pll_ref_if i_pll_ref
|
, client interface pll_ref_if i_pll_ref
|
||||||
|
#endif
|
||||||
|
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||||
|
, chanend c_swpll_update
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -81,11 +87,17 @@ void XUA_Buffer_Ep(chanend c_aud_out,
|
|||||||
#ifdef CHAN_BUFF_CTRL
|
#ifdef CHAN_BUFF_CTRL
|
||||||
, chanend c_buff_ctrl
|
, chanend c_buff_ctrl
|
||||||
#endif
|
#endif
|
||||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
|
||||||
|
#if (!XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||||
, client interface pll_ref_if i_pll_ref
|
, client interface pll_ref_if i_pll_ref
|
||||||
|
#endif
|
||||||
|
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||||
|
, chanend c_swpll_update
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/** Manage the data transfer between the USB audio buffer and the
|
/** Manage the data transfer between the USB audio buffer and the
|
||||||
* Audio I/O driver.
|
* Audio I/O driver.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -31,18 +31,35 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync);
|
|||||||
void clockGen(streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int);
|
void clockGen(streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int);
|
||||||
|
|
||||||
#if (XUA_USE_APP_PLL)
|
#if (XUA_USE_APP_PLL)
|
||||||
struct PllSettings
|
struct SoftPllState
|
||||||
{
|
{
|
||||||
// Count we expect on MCLK port timer at SW PLL check point.
|
// Count we expect on MCLK port timer at SW PLL check point.
|
||||||
// Note, we expect wrapping so this is essentiually a modulus
|
// Note, we expect wrapping so this is essentiually a modulus
|
||||||
unsigned adder;
|
unsigned expectedClkMod;
|
||||||
unsigned fracIdx;
|
unsigned initialSetting;
|
||||||
int firstUpdate;
|
unsigned setting;
|
||||||
|
unsigned firstUpdate;
|
||||||
|
|
||||||
|
unsigned ds_in;
|
||||||
|
int ds_fb;
|
||||||
|
int ds_x1;
|
||||||
|
int ds_x2;
|
||||||
};
|
};
|
||||||
|
|
||||||
void AppPllEnable(tileref tile, int mclkFreq_hz);
|
void AppPllEnable(tileref tile, int mclkFreq_hz);
|
||||||
void AppPllGetSettings(int clkFreq_hz, struct PllSettings &pllSettings);
|
void AppPllGetSettings(int clkFreq_hz, struct SoftPllState &pllState);
|
||||||
void AppPllUpdate(tileref tile, unsigned short mclk_pt, struct PllSettings &pllSettings);
|
void AppPllUpdate(tileref tile, unsigned short mclk_pt, struct SoftPllState &pllState);
|
||||||
|
|
||||||
|
interface SoftPll_if
|
||||||
|
{
|
||||||
|
void init(int mclk_hz);
|
||||||
|
};
|
||||||
|
|
||||||
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
|
||||||
|
[[distributable]]
|
||||||
|
#endif
|
||||||
|
void XUA_SoftPll(tileref tile, server interface SoftPll_if i_softPll, chanend c_update);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -636,6 +636,9 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
|||||||
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 out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
|
||||||
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]
|
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
, client interface SoftPll_if i_softPll
|
||||||
|
#endif
|
||||||
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
|
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
|
||||||
, chanend c_spdif_out
|
, chanend c_spdif_out
|
||||||
#endif
|
#endif
|
||||||
@@ -663,6 +666,12 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
|||||||
unsigned divide;
|
unsigned divide;
|
||||||
unsigned firstRun = 1;
|
unsigned firstRun = 1;
|
||||||
|
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
/* Use xCORE.ai Secondary PLL to generate master clock
|
||||||
|
* This could be "fixed" for async mode or adjusted if in sync mode */
|
||||||
|
i_softPll.init(DEFAULT_MCLK);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Clock master clock-block from master-clock port */
|
/* Clock master clock-block from master-clock port */
|
||||||
/* Note, marked unsafe since other cores may be using this mclk port */
|
/* Note, marked unsafe since other cores may be using this mclk port */
|
||||||
configure_clock_src(clk_audio_mclk, p_mclk_in);
|
configure_clock_src(clk_audio_mclk, p_mclk_in);
|
||||||
@@ -688,11 +697,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (XUA_USE_APP_PLL)
|
|
||||||
/* Use xCORE.ai Secondary PLL to generate master clock
|
|
||||||
* This could be "fixed" for async mode or adjusted if in sync mode */
|
|
||||||
AppPllEnable(tile[AUDIO_IO_TILE], DEFAULT_MCLK);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Perform required CODEC/ADC/DAC initialisation */
|
/* Perform required CODEC/ADC/DAC initialisation */
|
||||||
AudioHwInit();
|
AudioHwInit();
|
||||||
@@ -810,7 +815,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
|||||||
AudioHwConfig_Mute();
|
AudioHwConfig_Mute();
|
||||||
|
|
||||||
#if (XUA_USE_APP_PLL)
|
#if (XUA_USE_APP_PLL)
|
||||||
AppPllEnable(tile[AUDIO_IO_TILE], mClk);
|
i_softPll.init(mClk);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* User code should configure audio harware for SampleFreq/MClk etc */
|
/* User code should configure audio harware for SampleFreq/MClk etc */
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
#include "xud.h"
|
#include "xud.h"
|
||||||
#include "testct_byref.h"
|
#include "testct_byref.h"
|
||||||
|
|
||||||
|
on tile[0] : out port p_test = XS1_PORT_1A;
|
||||||
|
int tog = 0;
|
||||||
|
|
||||||
#if XUA_HID_ENABLED
|
#if XUA_HID_ENABLED
|
||||||
#include "xua_hid_report.h"
|
#include "xua_hid_report.h"
|
||||||
#include "user_hid.h"
|
#include "user_hid.h"
|
||||||
@@ -104,8 +107,12 @@ void XUA_Buffer(
|
|||||||
, chanend c_hid
|
, chanend c_hid
|
||||||
#endif
|
#endif
|
||||||
, chanend c_aud
|
, chanend c_aud
|
||||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL)
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
|
||||||
|
#if(XUA_USE_APP_PLL)
|
||||||
|
, chanend c_swpll_update
|
||||||
|
#else
|
||||||
, client interface pll_ref_if i_pll_ref
|
, client interface pll_ref_if i_pll_ref
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -140,8 +147,12 @@ void XUA_Buffer(
|
|||||||
#ifdef CHAN_BUFF_CTRL
|
#ifdef CHAN_BUFF_CTRL
|
||||||
, c_buff_ctrl
|
, c_buff_ctrl
|
||||||
#endif
|
#endif
|
||||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL)
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
|
||||||
|
#if(XUA_USE_APP_PLL)
|
||||||
|
, c_swpll_update
|
||||||
|
#else
|
||||||
, i_pll_ref
|
, i_pll_ref
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -190,8 +201,12 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
|||||||
#ifdef CHAN_BUFF_CTRL
|
#ifdef CHAN_BUFF_CTRL
|
||||||
, chanend c_buff_ctrl
|
, chanend c_buff_ctrl
|
||||||
#endif
|
#endif
|
||||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL)
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
, chanend c_swpll_update
|
||||||
|
#else
|
||||||
, client interface pll_ref_if i_pll_ref
|
, client interface pll_ref_if i_pll_ref
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -295,7 +310,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
|||||||
unsigned iap_ea_native_interface_alt_setting = 0;
|
unsigned iap_ea_native_interface_alt_setting = 0;
|
||||||
unsigned iap_ea_native_control_to_send = 0;
|
unsigned iap_ea_native_control_to_send = 0;
|
||||||
unsigned iap_ea_native_incoming = 0;
|
unsigned iap_ea_native_incoming = 0;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -359,10 +373,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
|||||||
#define LOCAL_CLOCK_MARGIN (1000)
|
#define LOCAL_CLOCK_MARGIN (1000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (XUA_USE_APP_PLL)
|
#if (!XUA_USE_APP_PLL)
|
||||||
struct PllSettings pllSettings;
|
|
||||||
AppPllGetSettings(DEFAULT_MCLK, pllSettings);
|
|
||||||
#else
|
|
||||||
timer t_sofCheck;
|
timer t_sofCheck;
|
||||||
unsigned timeLastEdge;
|
unsigned timeLastEdge;
|
||||||
unsigned timeNextEdge;
|
unsigned timeNextEdge;
|
||||||
@@ -458,10 +469,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
|||||||
{
|
{
|
||||||
masterClockFreq = MCLK_441;
|
masterClockFreq = MCLK_441;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (XUA_USE_APP_PLL && (XUA_SYNCMODE == XUA_SYNCMODE_SYNC))
|
|
||||||
AppPllGetSettings(masterClockFreq, pllSettings);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this
|
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this
|
||||||
@@ -515,7 +522,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Pass on sample freq change to decouple() via global flag (saves a chanend) */
|
/* 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_GLOBAL0(g_freqChange, cmd); /* Set command */
|
||||||
SET_SHARED_GLOBAL(g_freqChange_flag, cmd); /* Set Flag */
|
SET_SHARED_GLOBAL(g_freqChange_flag, cmd); /* Set Flag */
|
||||||
}
|
}
|
||||||
@@ -567,9 +574,10 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
|||||||
pllUpdate = 0;
|
pllUpdate = 0;
|
||||||
unsigned short mclk_pt;
|
unsigned short mclk_pt;
|
||||||
asm volatile("getts %0, res[%1]" : "=r" (mclk_pt) : "r" (p_off_mclk));
|
asm volatile("getts %0, res[%1]" : "=r" (mclk_pt) : "r" (p_off_mclk));
|
||||||
|
outuint(c_swpll_update, mclk_pt);
|
||||||
// TODO on a change of SR we don't want to run this before audiohub has set the PLL to the new freq
|
outct(c_swpll_update, XS1_CT_END);
|
||||||
AppPllUpdate(tile[XUD_TILE], mclk_pt, pllSettings);
|
p_test <: tog;
|
||||||
|
tog = !tog;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -19,19 +19,35 @@
|
|||||||
* App PLL settings used for syncing to external clocks
|
* App PLL settings used for syncing to external clocks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fractions_80_top.h"
|
// Found solution: IN 24.000MHz, OUT 24.575758MHz, VCO 3244.00MHz, RD 3, FD 405.500 (m = 1, n = 2), OD 3, FOD 11, ERR -9.864ppm
|
||||||
|
#define APP_PLL_CTL_SYNC_24M (0x09019402)
|
||||||
|
#define APP_PLL_DIV_SYNC_24M (0x8000000A)
|
||||||
|
#define APP_PLL_FRAC_SYNC_24M (0x80000001)
|
||||||
|
|
||||||
// This can do +350ppm.
|
// Fout = (Fin/3)*divider/(2*2*3*11) = (fin/396) * divider = (24/396) * divider. = 2/33 * divider.
|
||||||
//Found solution: IN 24.000MHz, OUT 22.579211MHz, VCO 3070.77MHz, RD 2, FD 255.898 (m = 79, n = 88), OD 2, FOD 17, ERR 0.497ppm
|
// Total freq tune range = ((406/405) - 1) * 1000000 ppm = 2469ppm.
|
||||||
#define APP_PLL_CTL_SYNC_22M (0x0880FE01)
|
// Step size: 2469/2^20 ~= 2.4ppb.
|
||||||
#define APP_PLL_DIV_SYNC_22M (0x80000010)
|
// Setting of 0 (0x000000) => Divide of 405. Output freq = (2/33) * 405 = 24.545MHz. This is -1244ppm from ideal 24.576MHz.
|
||||||
#define APP_PLL_F_INDEX_SYNC_22M (193)
|
// Setting of 1 (0xFFFFFF) => Divide of 406. Output freq = (2/33) * 406 = 24.606MHz. This is +1223ppm from ideal 24.576MHz.
|
||||||
|
|
||||||
//This can do +256ppm.
|
// To achieve frequency f, Fraction Setting = ((33/2)*f) - 405
|
||||||
//Found solution: IN 24.000MHz, OUT 24.576007MHz, VCO 3538.95MHz, RD 2, FD 294.912 (m = 83, n = 91), OD 3, FOD 12, ERR 0.298ppm
|
// So to achieve 24.576MHz, Fraction Setting = (16.5*24.576) - 405 = 0.504
|
||||||
#define APP_PLL_CTL_SYNC_24M (0x09012501)
|
// Numerical input = round((Fraction setting * 2^20) = 0.504 * 1048576 = 528482 = 0x81062.
|
||||||
#define APP_PLL_DIV_SYNC_24M (0x8000000B)
|
|
||||||
#define APP_PLL_F_INDEX_SYNC_24M (220)
|
// Found solution: IN 24.000MHz, OUT 22.579365MHz, VCO 3251.43MHz, RD 3, FD 406.429 (m = 3, n = 7), OD 6, FOD 6, ERR 7.311ppm
|
||||||
|
#define APP_PLL_CTL_SYNC_22M (0x0A819502)
|
||||||
|
#define APP_PLL_DIV_SYNC_22M (0x80000005)
|
||||||
|
#define APP_PLL_FRAC_SYNC_22M (0x80000206)
|
||||||
|
|
||||||
|
// Fout = (Fin/3)*divider/(2*2*3*11) = (fin/396) * divider = (24/396) * divider. = 2/33 * divider.
|
||||||
|
// Total freq tune range = ((406/405) - 1) * 1000000 ppm = 2469ppm.
|
||||||
|
// Step size: 2469/2^20 ~= 2.4ppb.
|
||||||
|
// Setting of 0 (0x000000) => Divide of 405. Output freq = (2/33) * 405 = 24.545MHz. This is -1244ppm from ideal 24.576MHz.
|
||||||
|
// Setting of 1 (0xFFFFFF) => Divide of 406. Output freq = (2/33) * 406 = 24.606MHz. This is +1223ppm from ideal 24.576MHz.
|
||||||
|
|
||||||
|
// To achieve frequency f, Fraction Setting = ((33/2)*f) - 405
|
||||||
|
// So to achieve 24.576MHz, Fraction Setting = (16.5*24.576) - 405 = 0.504
|
||||||
|
// Numerical input = round((Fraction setting * 2^20) = 0.504 * 1048576 = 528482 = 0x81062.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* App PLL settings used for low jitter fixed local clocks
|
* App PLL settings used for low jitter fixed local clocks
|
||||||
@@ -99,30 +115,7 @@ static void set_app_pll_init(tileref tile, int app_pll_ctrl)
|
|||||||
delay_microseconds(500);
|
delay_microseconds(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppPllGetSettings(int clkFreq_hz, struct PllSettings &pllSettings)
|
|
||||||
{
|
|
||||||
pllSettings.firstUpdate = 1;
|
|
||||||
|
|
||||||
switch(clkFreq_hz)
|
|
||||||
{
|
|
||||||
case 44100 * 512:
|
|
||||||
pllSettings.fracIdx = APP_PLL_F_INDEX_SYNC_22M;
|
|
||||||
pllSettings.adder = 29184;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 48000 * 512:
|
|
||||||
pllSettings.fracIdx = APP_PLL_F_INDEX_SYNC_24M;
|
|
||||||
pllSettings.adder = 49152;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//unsigned sw_pll_adder;
|
|
||||||
//unsigned app_pll_frac_i;
|
|
||||||
|
|
||||||
void AppPllEnable(tileref tile, int clkFreq_hz)
|
void AppPllEnable(tileref tile, int clkFreq_hz)
|
||||||
{
|
{
|
||||||
@@ -136,17 +129,13 @@ void AppPllEnable(tileref tile, int clkFreq_hz)
|
|||||||
case 44100 * 512:
|
case 44100 * 512:
|
||||||
app_pll_ctrl = APP_PLL_CTL_SYNC_22M;
|
app_pll_ctrl = APP_PLL_CTL_SYNC_22M;
|
||||||
app_pll_div = APP_PLL_DIV_SYNC_22M;
|
app_pll_div = APP_PLL_DIV_SYNC_22M;
|
||||||
//app_pll_frac_i = APP_PLL_F_INDEX_SYNC_22M;
|
app_pll_frac = APP_PLL_FRAC_SYNC_22M;
|
||||||
app_pll_frac = frac_values_80[APP_PLL_F_INDEX_SYNC_22M];
|
|
||||||
//sw_pll_adder = 29184; // Count we expect on MCLK port timer at SW PLL check point. Note, we expect wrapping so this is essentiually a modulus
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 48000 * 512:
|
case 48000 * 512:
|
||||||
app_pll_ctrl = APP_PLL_CTL_SYNC_24M;
|
app_pll_ctrl = APP_PLL_CTL_SYNC_24M;
|
||||||
app_pll_div = APP_PLL_DIV_SYNC_24M;
|
app_pll_div = APP_PLL_DIV_SYNC_24M;
|
||||||
//app_pll_frac_i = APP_PLL_F_INDEX_SYNC_24M;
|
app_pll_frac = APP_PLL_FRAC_SYNC_24M;
|
||||||
app_pll_frac = frac_values_80[APP_PLL_F_INDEX_SYNC_24M];
|
|
||||||
//sw_pll_adder = 49152;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -203,85 +192,208 @@ void AppPllEnable(tileref tile, int clkFreq_hz)
|
|||||||
// Initialise the AppPLL and get it running.
|
// Initialise the AppPLL and get it running.
|
||||||
set_app_pll_init(tile, app_pll_ctrl);
|
set_app_pll_init(tile, app_pll_ctrl);
|
||||||
|
|
||||||
// Write the fractional-n register
|
// Write the fractional-n register, note, the top bit is set to enable the frac-n block.
|
||||||
// Set the top bit to enable the frac-n block.
|
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, app_pll_frac);
|
||||||
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, (0x80000000 | app_pll_frac));
|
|
||||||
|
// And then write the clock divider register to enable the output
|
||||||
|
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, app_pll_div);
|
||||||
|
|
||||||
// Wait for PLL output frequency to stabilise due to fractional divider enable
|
// Wait for PLL output frequency to stabilise due to fractional divider enable
|
||||||
delay_microseconds(100);
|
delay_microseconds(100);
|
||||||
|
|
||||||
// Write the clock divider register to enable the output
|
|
||||||
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, app_pll_div);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoftPllInit(int clkFreq_hz, struct SoftPllState &pllState)
|
||||||
void AppPllUpdate(tileref tile, unsigned short mclk_pt, struct PllSettings &pllSettings)
|
|
||||||
{
|
{
|
||||||
static int mclk_pt_last;
|
switch(clkFreq_hz)
|
||||||
static int oldsetting = 0;
|
|
||||||
static int cum_error = 0;
|
|
||||||
|
|
||||||
unsigned sw_pll_adder = pllSettings.adder;
|
|
||||||
unsigned app_pll_frac_i = pllSettings.fracIdx;
|
|
||||||
|
|
||||||
const int Kp = 0;
|
|
||||||
const int Ki = 1;
|
|
||||||
|
|
||||||
int error, error_p, error_i;
|
|
||||||
unsigned short expected_pt;
|
|
||||||
|
|
||||||
if(pllSettings.firstUpdate)
|
|
||||||
{
|
{
|
||||||
mclk_pt_last = mclk_pt; // load last mclk measurement with sensible data
|
case 44100 * 512:
|
||||||
pllSettings.firstUpdate = 0;
|
pllState.expectedClkMod = 29184;
|
||||||
|
pllState.initialSetting = 0x6CF42;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 48000 * 512:
|
||||||
|
pllState.expectedClkMod = 49152;
|
||||||
|
pllState.initialSetting = 0x81062;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
pllState.firstUpdate = 1;
|
||||||
{
|
|
||||||
|
pllState.ds_in = pllState.initialSetting;
|
||||||
|
pllState.ds_fb = 0;
|
||||||
|
pllState.ds_x1 = 0;
|
||||||
|
pllState.ds_x2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SoftPllUpdate(tileref tile, unsigned short mclk_pt, unsigned short mclk_pt_last, struct SoftPllState &pllState)
|
||||||
|
{
|
||||||
|
static int int_error = 0;
|
||||||
|
|
||||||
|
unsigned expectedClksMod = pllState.expectedClkMod;
|
||||||
|
unsigned initialSetting = pllState.initialSetting;
|
||||||
|
|
||||||
|
// TODO These values need revisiting
|
||||||
|
const int Kp = 0;
|
||||||
|
const int Ki = 32;
|
||||||
|
|
||||||
|
int newsetting;
|
||||||
|
int abs_error, error_p, error_i;
|
||||||
|
unsigned short expectedPt;
|
||||||
|
|
||||||
|
int set = -1;
|
||||||
int diff;
|
int diff;
|
||||||
|
|
||||||
// sw_pll_adder is the value of the port counter that we expect given the desired MCLK in the 10ms time period we are running at.
|
// expectedClkMod is the value of the port counter that we expect given the desired MCLK in the 10ms time period we are running at.
|
||||||
expected_pt = mclk_pt_last + sw_pll_adder;
|
expectedPt = mclk_pt_last + expectedClksMod;
|
||||||
|
|
||||||
// Handle wrapping
|
// Handle wrapping
|
||||||
if (porttimeafter(mclk_pt, expected_pt))
|
if (porttimeafter(mclk_pt, expectedPt))
|
||||||
{
|
{
|
||||||
diff = -(short)(expected_pt - mclk_pt);
|
diff = -(short)(expectedPt - mclk_pt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
diff = (short)(mclk_pt - expected_pt);
|
diff = (short)(mclk_pt - expectedPt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Add a bounds checker on diff to make sure it's roughly where we expect.
|
// TODO Add a bounds checker on diff to make sure it's roughly where we expect.
|
||||||
// If it isn't we should ignore it as it's either a glitch or from clock start/stop.
|
// If it isn't we should ignore it as it's either a glitch or from clock start/stop.
|
||||||
|
|
||||||
error = diff; // Absolute error for last measurement cycle.
|
// Absolute error for last measurement cycle. If diff is positive, port timer was beyond where it should have been, so MCLK was too fast. So this needs to describe a negative error.
|
||||||
cum_error = cum_error + error; // Integral error.
|
abs_error = -diff;
|
||||||
|
int_error = int_error + abs_error; // Integral error.
|
||||||
|
|
||||||
error_p = (Kp * error);
|
error_p = (Kp * abs_error);
|
||||||
error_i = (Ki * cum_error);
|
error_i = (Ki * int_error);
|
||||||
|
|
||||||
int newsetting = (error_p + error_i);
|
newsetting = (error_p + error_i);
|
||||||
|
|
||||||
// Only write new PLL settings if they're different to the old settings
|
// Only output new frequency tune value if different to the previous setting
|
||||||
if (newsetting != oldsetting)
|
if (newsetting != pllState.setting)
|
||||||
{
|
{
|
||||||
int frac_index = (app_pll_frac_i - newsetting) >> 2; // Tmp shift down to stop freq moving around much
|
set = (initialSetting + newsetting); // init_set is the value to feed into the NCO to give the "expected" frequency (24.576 or 22.5792). Not required but will make lock faster.
|
||||||
if (frac_index < 0)
|
if (set < 0)
|
||||||
{
|
set = 0;
|
||||||
frac_index = 0;
|
else if (set > 0xFFFFF)
|
||||||
}
|
set = 0xFFFFF;
|
||||||
else if (frac_index > 326)
|
|
||||||
{
|
|
||||||
frac_index = 326;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write_node_config_reg_no_ack(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, (0x80000000 | frac_values_80[frac_index]));
|
pllState.setting = newsetting;
|
||||||
}
|
|
||||||
|
|
||||||
oldsetting = newsetting;
|
// Return the setting to the NCO thread. -1 means no update
|
||||||
mclk_pt_last = mclk_pt;
|
return set;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
|
||||||
|
[[distributable]]
|
||||||
|
#endif
|
||||||
|
void XUA_SoftPll(tileref tile, server interface SoftPll_if i_softPll, chanend c_update)
|
||||||
|
{
|
||||||
|
unsigned ds_out;
|
||||||
|
timer tmr;
|
||||||
|
int time;
|
||||||
|
unsigned mclk_pt;
|
||||||
|
unsigned short mclk_pt_last;
|
||||||
|
struct SoftPllState pllState;
|
||||||
|
int running = 0;
|
||||||
|
int firstUpdate = 1;
|
||||||
|
|
||||||
|
tmr :> time;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
select
|
||||||
|
{
|
||||||
|
/* Interface used for basic frequency setting such that it can be distributed
|
||||||
|
* when the update code is not required */
|
||||||
|
case i_softPll.init(int mclk_hz):
|
||||||
|
AppPllEnable(tile, mclk_hz);
|
||||||
|
SoftPllInit(mclk_hz, pllState);
|
||||||
|
running = 1;
|
||||||
|
firstUpdate = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Channel used for update such that other side is not blocked */
|
||||||
|
/* TODO add CT handshake before opening route */
|
||||||
|
case inuint_byref(c_update, mclk_pt):
|
||||||
|
inct(c_update);
|
||||||
|
|
||||||
|
if(firstUpdate)
|
||||||
|
{
|
||||||
|
firstUpdate = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int setting = SoftPllUpdate(tile, (unsigned short) mclk_pt, mclk_pt_last, pllState);
|
||||||
|
|
||||||
|
if(setting != -1)
|
||||||
|
{
|
||||||
|
pllState.ds_in = setting;
|
||||||
|
pllState.ds_in = pllState.ds_in & 0x000FFFFF; // Ensure input is limited to 20 bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mclk_pt_last = (unsigned short) mclk_pt;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default :
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second order, Single bit delta sigma - mod2
|
||||||
|
pllState.ds_x1 += pllState.ds_in - pllState.ds_fb;
|
||||||
|
pllState.ds_x2 += pllState.ds_x1 - pllState.ds_fb;
|
||||||
|
if (pllState.ds_x2 < 0)
|
||||||
|
{
|
||||||
|
ds_out = 0;
|
||||||
|
pllState.ds_fb = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ds_out = 1;
|
||||||
|
pllState.ds_fb = 1048575; //pow(2, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now write the register.
|
||||||
|
// We need to write the register at a specific period at a fast rate.
|
||||||
|
// This period needs to be (div ref clk period (ns) * how many times we repeat same value)
|
||||||
|
// In this case, div ref clk = 24/3 = 8MHz. So div ref clk period = 125ns.
|
||||||
|
// So minimum period we could use is 125ns.
|
||||||
|
// We use sw ref clk to time the register write. This uses 10ns clock ticks.
|
||||||
|
// So this period should be a multiple of 10ns too.
|
||||||
|
// So shortest period to meet these requirements is 250ns. This is still very fast though.
|
||||||
|
// 1000ns is a great choice if we can.
|
||||||
|
// 1500ns is a better choice.
|
||||||
|
// 2250ns is the next choice.
|
||||||
|
// The slower we write, our jitter goes up significantly.
|
||||||
|
// Measuring rms jitter 100Hz-40kHz and TIE (TIE across 80ms of clock output).
|
||||||
|
// 750ns - jitter = 51ps, tie = +-1.5ns.
|
||||||
|
// 1500ns - jitter = 110ps, tie = +-2.5ns.
|
||||||
|
// 2250ns - jitter = 162ps, tie = +-3.2ns. // Note to be measured for single bit DS.
|
||||||
|
|
||||||
|
time += 150; // We write reg every 1500ns.
|
||||||
|
tmr when timerafter(time) :> void;
|
||||||
|
|
||||||
|
// ds_out = 1 => fraction of 1/1 = N+1. ds_out = 0 => fraction of 0/1 = N.
|
||||||
|
if(running)
|
||||||
|
{
|
||||||
|
// Write the register. Because we are timing the reg writes accurately we do not need to use reg write with ack.
|
||||||
|
// This saves a lot of time. Additionally, apparently we can shorten the time for this reg write by only setting up
|
||||||
|
// the channel once and just doing a few instructions to do the write each time.
|
||||||
|
// We can hard code this in assembler.
|
||||||
|
|
||||||
|
write_node_config_reg_no_ack(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, (ds_out << 31));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -314,6 +314,9 @@ void usb_audio_io(chanend ?c_aud_in,
|
|||||||
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||||
, client interface pll_ref_if i_pll_ref
|
, client interface pll_ref_if i_pll_ref
|
||||||
#endif
|
#endif
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
, client interface SoftPll_if i_softPll
|
||||||
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
#if (MIXER)
|
#if (MIXER)
|
||||||
@@ -364,6 +367,9 @@ void usb_audio_io(chanend ?c_aud_in,
|
|||||||
#define AUDIO_CHANNEL c_aud_in
|
#define AUDIO_CHANNEL c_aud_in
|
||||||
#endif
|
#endif
|
||||||
XUA_AudioHub(AUDIO_CHANNEL, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc
|
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_USE_APP_PLL)
|
||||||
|
, i_softPll
|
||||||
|
#endif
|
||||||
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
|
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
|
||||||
, c_spdif_tx
|
, c_spdif_tx
|
||||||
#endif
|
#endif
|
||||||
@@ -470,6 +476,11 @@ int main()
|
|||||||
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||||
interface pll_ref_if i_pll_ref;
|
interface pll_ref_if i_pll_ref;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
interface SoftPll_if i_softPll;
|
||||||
|
chan c_swpll_update;
|
||||||
|
#endif
|
||||||
chan c_sof;
|
chan c_sof;
|
||||||
chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */
|
chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */
|
||||||
chan c_xud_in[ENDPOINT_COUNT_IN];
|
chan c_xud_in[ENDPOINT_COUNT_IN];
|
||||||
@@ -522,6 +533,10 @@ int main()
|
|||||||
c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg);
|
c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
//XUA_SoftPll(tile[0], i_softPll, c_swpll_update);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Core USB audio task, buffering, USB etc */
|
/* Core USB audio task, buffering, USB etc */
|
||||||
{
|
{
|
||||||
unsigned x;
|
unsigned x;
|
||||||
@@ -563,8 +578,12 @@ int main()
|
|||||||
, c_xud_in[ENDPOINT_NUMBER_IN_HID]
|
, c_xud_in[ENDPOINT_NUMBER_IN_HID]
|
||||||
#endif
|
#endif
|
||||||
, c_mix_out
|
, c_mix_out
|
||||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && (!XUA_USE_APP_PLL))
|
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
|
||||||
|
#if (!XUA_USE_APP_PLL)
|
||||||
, i_pll_ref
|
, i_pll_ref
|
||||||
|
#else
|
||||||
|
, c_swpll_update
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
//:
|
//:
|
||||||
@@ -579,6 +598,7 @@ int main()
|
|||||||
#endif /* XUA_USB_EN */
|
#endif /* XUA_USB_EN */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on tile[AUDIO_IO_TILE]: XUA_SoftPll(tile[0], i_softPll, c_swpll_update);
|
||||||
on tile[AUDIO_IO_TILE]:
|
on tile[AUDIO_IO_TILE]:
|
||||||
{
|
{
|
||||||
/* Audio I/O task, includes mixing etc */
|
/* Audio I/O task, includes mixing etc */
|
||||||
@@ -601,6 +621,9 @@ int main()
|
|||||||
#endif
|
#endif
|
||||||
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||||
, i_pll_ref
|
, i_pll_ref
|
||||||
|
#endif
|
||||||
|
#if (XUA_USE_APP_PLL)
|
||||||
|
, i_softPll
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user