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"
|
||||
#endif
|
||||
|
||||
#include "xua_clocking.h"
|
||||
|
||||
/** The audio driver thread.
|
||||
*
|
||||
* 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 i_SoftPll Interface to software PLL task
|
||||
*
|
||||
* \param c_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif
|
||||
*
|
||||
* \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 out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
|
||||
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__)
|
||||
, chanend c_spdif_tx
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
* \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 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 +52,13 @@ void XUA_Buffer(
|
||||
, chanend c_hid
|
||||
#endif
|
||||
, 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
|
||||
#endif
|
||||
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||
, chanend c_swpll_update
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
|
||||
@@ -81,10 +87,16 @@ void XUA_Buffer_Ep(chanend c_aud_out,
|
||||
#ifdef CHAN_BUFF_CTRL
|
||||
, chanend c_buff_ctrl
|
||||
#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
|
||||
#endif
|
||||
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
|
||||
, chanend c_swpll_update
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/** Manage the data transfer between the USB audio buffer and the
|
||||
* 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);
|
||||
|
||||
#if (XUA_USE_APP_PLL)
|
||||
struct PllSettings
|
||||
struct SoftPllState
|
||||
{
|
||||
// Count we expect on MCLK port timer at SW PLL check point.
|
||||
// Note, we expect wrapping so this is essentiually a modulus
|
||||
unsigned adder;
|
||||
unsigned fracIdx;
|
||||
int firstUpdate;
|
||||
unsigned expectedClkMod;
|
||||
unsigned initialSetting;
|
||||
unsigned setting;
|
||||
unsigned firstUpdate;
|
||||
|
||||
unsigned ds_in;
|
||||
int ds_fb;
|
||||
int ds_x1;
|
||||
int ds_x2;
|
||||
};
|
||||
|
||||
void AppPllEnable(tileref tile, int mclkFreq_hz);
|
||||
void AppPllGetSettings(int clkFreq_hz, struct PllSettings &pllSettings);
|
||||
void AppPllUpdate(tileref tile, unsigned short mclk_pt, struct PllSettings &pllSettings);
|
||||
void AppPllGetSettings(int clkFreq_hz, struct SoftPllState &pllState);
|
||||
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
|
||||
|
||||
|
||||
@@ -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 out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
|
||||
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)
|
||||
, chanend c_spdif_out
|
||||
#endif
|
||||
@@ -663,6 +666,12 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
||||
unsigned divide;
|
||||
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 */
|
||||
/* Note, marked unsafe since other cores may be using this mclk port */
|
||||
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
|
||||
|
||||
#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 */
|
||||
AudioHwInit();
|
||||
@@ -810,7 +815,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
||||
AudioHwConfig_Mute();
|
||||
|
||||
#if (XUA_USE_APP_PLL)
|
||||
AppPllEnable(tile[AUDIO_IO_TILE], mClk);
|
||||
i_softPll.init(mClk);
|
||||
#endif
|
||||
|
||||
/* User code should configure audio harware for SampleFreq/MClk etc */
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "xud.h"
|
||||
#include "testct_byref.h"
|
||||
|
||||
on tile[0] : out port p_test = XS1_PORT_1A;
|
||||
int tog = 0;
|
||||
|
||||
#if XUA_HID_ENABLED
|
||||
#include "xua_hid_report.h"
|
||||
#include "user_hid.h"
|
||||
@@ -104,8 +107,12 @@ void XUA_Buffer(
|
||||
, chanend c_hid
|
||||
#endif
|
||||
, 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
|
||||
#endif
|
||||
#endif
|
||||
)
|
||||
{
|
||||
@@ -140,8 +147,12 @@ void XUA_Buffer(
|
||||
#ifdef CHAN_BUFF_CTRL
|
||||
, c_buff_ctrl
|
||||
#endif
|
||||
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL)
|
||||
, i_pll_ref
|
||||
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
|
||||
#if(XUA_USE_APP_PLL)
|
||||
, c_swpll_update
|
||||
#else
|
||||
, i_pll_ref
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
|
||||
@@ -190,8 +201,12 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
||||
#ifdef CHAN_BUFF_CTRL
|
||||
, chanend c_buff_ctrl
|
||||
#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
|
||||
#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_control_to_send = 0;
|
||||
unsigned iap_ea_native_incoming = 0;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -359,10 +373,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
||||
#define LOCAL_CLOCK_MARGIN (1000)
|
||||
#endif
|
||||
|
||||
#if (XUA_USE_APP_PLL)
|
||||
struct PllSettings pllSettings;
|
||||
AppPllGetSettings(DEFAULT_MCLK, pllSettings);
|
||||
#else
|
||||
#if (!XUA_USE_APP_PLL)
|
||||
timer t_sofCheck;
|
||||
unsigned timeLastEdge;
|
||||
unsigned timeNextEdge;
|
||||
@@ -458,10 +469,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
||||
{
|
||||
masterClockFreq = MCLK_441;
|
||||
}
|
||||
|
||||
#if (XUA_USE_APP_PLL && (XUA_SYNCMODE == XUA_SYNCMODE_SYNC))
|
||||
AppPllGetSettings(masterClockFreq, pllSettings);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/* 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
|
||||
/* 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 */
|
||||
}
|
||||
@@ -567,9 +574,10 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
||||
pllUpdate = 0;
|
||||
unsigned short mclk_pt;
|
||||
asm volatile("getts %0, res[%1]" : "=r" (mclk_pt) : "r" (p_off_mclk));
|
||||
|
||||
// TODO on a change of SR we don't want to run this before audiohub has set the PLL to the new freq
|
||||
AppPllUpdate(tile[XUD_TILE], mclk_pt, pllSettings);
|
||||
outuint(c_swpll_update, mclk_pt);
|
||||
outct(c_swpll_update, XS1_CT_END);
|
||||
p_test <: tog;
|
||||
tog = !tog;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -19,19 +19,35 @@
|
||||
* 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.
|
||||
//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
|
||||
#define APP_PLL_CTL_SYNC_22M (0x0880FE01)
|
||||
#define APP_PLL_DIV_SYNC_22M (0x80000010)
|
||||
#define APP_PLL_F_INDEX_SYNC_22M (193)
|
||||
// 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.
|
||||
|
||||
//This can do +256ppm.
|
||||
//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
|
||||
#define APP_PLL_CTL_SYNC_24M (0x09012501)
|
||||
#define APP_PLL_DIV_SYNC_24M (0x8000000B)
|
||||
#define APP_PLL_F_INDEX_SYNC_24M (220)
|
||||
// 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.
|
||||
|
||||
// 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
|
||||
@@ -99,30 +115,7 @@ static void set_app_pll_init(tileref tile, int app_pll_ctrl)
|
||||
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)
|
||||
{
|
||||
@@ -136,17 +129,13 @@ void AppPllEnable(tileref tile, int clkFreq_hz)
|
||||
case 44100 * 512:
|
||||
app_pll_ctrl = APP_PLL_CTL_SYNC_22M;
|
||||
app_pll_div = APP_PLL_DIV_SYNC_22M;
|
||||
//app_pll_frac_i = APP_PLL_F_INDEX_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
|
||||
app_pll_frac = APP_PLL_FRAC_SYNC_22M;
|
||||
break;
|
||||
|
||||
case 48000 * 512:
|
||||
app_pll_ctrl = APP_PLL_CTL_SYNC_24M;
|
||||
app_pll_div = APP_PLL_DIV_SYNC_24M;
|
||||
//app_pll_frac_i = APP_PLL_F_INDEX_SYNC_24M;
|
||||
app_pll_frac = frac_values_80[APP_PLL_F_INDEX_SYNC_24M];
|
||||
//sw_pll_adder = 49152;
|
||||
app_pll_frac = APP_PLL_FRAC_SYNC_24M;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -203,85 +192,208 @@ void AppPllEnable(tileref tile, int clkFreq_hz)
|
||||
// Initialise the AppPLL and get it running.
|
||||
set_app_pll_init(tile, app_pll_ctrl);
|
||||
|
||||
// Write the fractional-n register
|
||||
// Set the top bit to enable the frac-n block.
|
||||
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, (0x80000000 | app_pll_frac));
|
||||
// Write the fractional-n register, note, the top bit is set to enable the frac-n block.
|
||||
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, 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
|
||||
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 AppPllUpdate(tileref tile, unsigned short mclk_pt, struct PllSettings &pllSettings)
|
||||
void SoftPllInit(int clkFreq_hz, struct SoftPllState &pllState)
|
||||
{
|
||||
static int mclk_pt_last;
|
||||
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)
|
||||
switch(clkFreq_hz)
|
||||
{
|
||||
mclk_pt_last = mclk_pt; // load last mclk measurement with sensible data
|
||||
pllSettings.firstUpdate = 0;
|
||||
case 44100 * 512:
|
||||
pllState.expectedClkMod = 29184;
|
||||
pllState.initialSetting = 0x6CF42;
|
||||
break;
|
||||
|
||||
case 48000 * 512:
|
||||
pllState.expectedClkMod = 49152;
|
||||
pllState.initialSetting = 0x81062;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
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;
|
||||
|
||||
// expectedClkMod is the value of the port counter that we expect given the desired MCLK in the 10ms time period we are running at.
|
||||
expectedPt = mclk_pt_last + expectedClksMod;
|
||||
|
||||
// Handle wrapping
|
||||
if (porttimeafter(mclk_pt, expectedPt))
|
||||
{
|
||||
diff = -(short)(expectedPt - mclk_pt);
|
||||
}
|
||||
else
|
||||
{
|
||||
int diff;
|
||||
diff = (short)(mclk_pt - expectedPt);
|
||||
}
|
||||
|
||||
// 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.
|
||||
expected_pt = mclk_pt_last + sw_pll_adder;
|
||||
// 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.
|
||||
|
||||
// Handle wrapping
|
||||
if (porttimeafter(mclk_pt, expected_pt))
|
||||
// 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.
|
||||
abs_error = -diff;
|
||||
int_error = int_error + abs_error; // Integral error.
|
||||
|
||||
error_p = (Kp * abs_error);
|
||||
error_i = (Ki * int_error);
|
||||
|
||||
newsetting = (error_p + error_i);
|
||||
|
||||
// Only output new frequency tune value if different to the previous setting
|
||||
if (newsetting != pllState.setting)
|
||||
{
|
||||
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 (set < 0)
|
||||
set = 0;
|
||||
else if (set > 0xFFFFF)
|
||||
set = 0xFFFFF;
|
||||
}
|
||||
|
||||
pllState.setting = newsetting;
|
||||
|
||||
// Return the setting to the NCO thread. -1 means no update
|
||||
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
|
||||
{
|
||||
diff = -(short)(expected_pt - mclk_pt);
|
||||
/* 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
|
||||
{
|
||||
diff = (short)(mclk_pt - expected_pt);
|
||||
ds_out = 1;
|
||||
pllState.ds_fb = 1048575; //pow(2, 20)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
error = diff; // Absolute error for last measurement cycle.
|
||||
cum_error = cum_error + error; // Integral error.
|
||||
time += 150; // We write reg every 1500ns.
|
||||
tmr when timerafter(time) :> void;
|
||||
|
||||
error_p = (Kp * error);
|
||||
error_i = (Ki * cum_error);
|
||||
|
||||
int newsetting = (error_p + error_i);
|
||||
|
||||
// Only write new PLL settings if they're different to the old settings
|
||||
if (newsetting != oldsetting)
|
||||
// ds_out = 1 => fraction of 1/1 = N+1. ds_out = 0 => fraction of 0/1 = N.
|
||||
if(running)
|
||||
{
|
||||
int frac_index = (app_pll_frac_i - newsetting) >> 2; // Tmp shift down to stop freq moving around much
|
||||
if (frac_index < 0)
|
||||
{
|
||||
frac_index = 0;
|
||||
}
|
||||
else if (frac_index > 326)
|
||||
{
|
||||
frac_index = 326;
|
||||
}
|
||||
// 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, (0x80000000 | frac_values_80[frac_index]));
|
||||
write_node_config_reg_no_ack(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, (ds_out << 31));
|
||||
}
|
||||
|
||||
oldsetting = newsetting;
|
||||
mclk_pt_last = mclk_pt;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -314,6 +314,9 @@ void usb_audio_io(chanend ?c_aud_in,
|
||||
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||
, client interface pll_ref_if i_pll_ref
|
||||
#endif
|
||||
#if (XUA_USE_APP_PLL)
|
||||
, client interface SoftPll_if i_softPll
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if (MIXER)
|
||||
@@ -364,6 +367,9 @@ void usb_audio_io(chanend ?c_aud_in,
|
||||
#define AUDIO_CHANNEL c_aud_in
|
||||
#endif
|
||||
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)
|
||||
, c_spdif_tx
|
||||
#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)
|
||||
interface pll_ref_if i_pll_ref;
|
||||
#endif
|
||||
|
||||
#if (XUA_USE_APP_PLL)
|
||||
interface SoftPll_if i_softPll;
|
||||
chan c_swpll_update;
|
||||
#endif
|
||||
chan c_sof;
|
||||
chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */
|
||||
chan c_xud_in[ENDPOINT_COUNT_IN];
|
||||
@@ -522,6 +533,10 @@ int main()
|
||||
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 */
|
||||
{
|
||||
unsigned x;
|
||||
@@ -563,8 +578,12 @@ int main()
|
||||
, c_xud_in[ENDPOINT_NUMBER_IN_HID]
|
||||
#endif
|
||||
, 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
|
||||
#else
|
||||
, c_swpll_update
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
//:
|
||||
@@ -579,6 +598,7 @@ int main()
|
||||
#endif /* XUA_USB_EN */
|
||||
}
|
||||
|
||||
on tile[AUDIO_IO_TILE]: XUA_SoftPll(tile[0], i_softPll, c_swpll_update);
|
||||
on tile[AUDIO_IO_TILE]:
|
||||
{
|
||||
/* Audio I/O task, includes mixing etc */
|
||||
@@ -601,6 +621,9 @@ int main()
|
||||
#endif
|
||||
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||
, i_pll_ref
|
||||
#endif
|
||||
#if (XUA_USE_APP_PLL)
|
||||
, i_softPll
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user