diff --git a/lib_xua/api/xua_audiohub.h b/lib_xua/api/xua_audiohub.h index c6463c9b..44b8de6d 100644 --- a/lib_xua/api/xua_audiohub.h +++ b/lib_xua/api/xua_audiohub.h @@ -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 diff --git a/lib_xua/api/xua_buffer.h b/lib_xua/api/xua_buffer.h index e3f7280a..173440b8 100644 --- a/lib_xua/api/xua_buffer.h +++ b/lib_xua/api/xua_buffer.h @@ -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. diff --git a/lib_xua/api/xua_clocking.h b/lib_xua/api/xua_clocking.h index cda21d27..6795bef9 100644 --- a/lib_xua/api/xua_clocking.h +++ b/lib_xua/api/xua_clocking.h @@ -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 diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index e5243cd2..433347e2 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -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 */ diff --git a/lib_xua/src/core/buffer/ep/ep_buffer.xc b/lib_xua/src/core/buffer/ep/ep_buffer.xc index c53a7db5..6c72dd7a 100644 --- a/lib_xua/src/core/buffer/ep/ep_buffer.xc +++ b/lib_xua/src/core/buffer/ep/ep_buffer.xc @@ -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 diff --git a/lib_xua/src/core/clocking/apppll.xc b/lib_xua/src/core/clocking/apppll.xc index 43914994..66120dfe 100644 --- a/lib_xua/src/core/clocking/apppll.xc +++ b/lib_xua/src/core/clocking/apppll.xc @@ -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 diff --git a/lib_xua/src/core/main.xc b/lib_xua/src/core/main.xc index 711afbce..b4dbbd38 100755 --- a/lib_xua/src/core/main.xc +++ b/lib_xua/src/core/main.xc @@ -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 ); }