From 21335983478a2c943194de67fb6d2b2c965c9e09 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 3 Jan 2024 15:56:15 +0000 Subject: [PATCH 01/33] Add sw_pll dep and prepare for par in clockgen --- lib_xua/module_build_info | 3 +- lib_xua/src/core/clocking/clockgen.xc | 897 +++++++++++++------------- 2 files changed, 457 insertions(+), 443 deletions(-) diff --git a/lib_xua/module_build_info b/lib_xua/module_build_info index c327e5c2..25f5c719 100644 --- a/lib_xua/module_build_info +++ b/lib_xua/module_build_info @@ -14,7 +14,8 @@ DEPENDENT_MODULES = lib_locks(>=2.1.0) \ lib_spdif(>=5.0.0) \ lib_xassert(>=4.1.0) \ lib_xud(>=2.2.3) \ - lib_adat(>=1.0.0) + lib_adat(>=1.0.0) \ + lib_sw_pll(>=2.0.1) MODULE_XCC_FLAGS = $(XCC_FLAGS) \ -O3 \ diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index d6ebe69a..ddbe9b80 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -8,6 +8,13 @@ #include "xua_commands.h" #include "xua_clocking.h" +#ifdef __XS3A__ +extern "C" +{ + #include "sw_pll.h" +} +#endif + #if (XUA_SPDIF_RX_EN) #include "spdif.h" #endif @@ -333,490 +340,496 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa /* Initial ref clock output and get timestamp */ i_pll_ref.init(); - while(1) { - select + while(1) { + select + { #ifdef LEVEL_METER_LEDS #warning Level metering enabled - case t_level when timerafter(levelTime) :> void: + case t_level when timerafter(levelTime) :> void: - levelTime += LEVEL_UPDATE_RATE; + levelTime += LEVEL_UPDATE_RATE; - /* Copy over level data and reset */ - for(int i = 0; i< NUM_USB_CHAN_IN; i++) - { - int tmp; - - /* Read level data */ - //g_inputLevelData[i] = samples_to_host_inputs[i]; - asm volatile("ldw %0, %1[%2]":"=r"(tmp):"r"((const int *)samples_to_host_inputs),"r"(i)); - g_inputLevelData[i] = tmp; - - /* Reset level data */ - //samples_to_host_inputs[i] = 0; - asm volatile("stw %0, %1[%2]"::"r"(0),"r"((const int *)samples_to_host_inputs),"r"(i)); - - /* Guard against host polling slower than timer and missing peaks */ - asm volatile("ldw %0, %1[%2]":"=r"(tmp):"r"((const int *)samples_to_host_inputs_buff),"r"(i)); - - if (g_inputLevelData[i] > tmp) - //if(g_inputLevelData[i] > samples_to_host_inputs_buff[i]) + /* Copy over level data and reset */ + for(int i = 0; i< NUM_USB_CHAN_IN; i++) { - //samples_to_host_inputs_buff[i] = g_inputLevelData[i]; - asm volatile("stw %0, %1[%2]"::"r"(tmp),"r"((const int *)samples_to_host_inputs),"r"(i)); - } - } + int tmp; - /* Call user LED refresh */ - VendorLedRefresh(g_inputLevelData); + /* Read level data */ + //g_inputLevelData[i] = samples_to_host_inputs[i]; + asm volatile("ldw %0, %1[%2]":"=r"(tmp):"r"((const int *)samples_to_host_inputs),"r"(i)); + g_inputLevelData[i] = tmp; - break; -#endif + /* Reset level data */ + //samples_to_host_inputs[i] = 0; + asm volatile("stw %0, %1[%2]"::"r"(0),"r"((const int *)samples_to_host_inputs),"r"(i)); - /* Updates to clock settings from endpoint 0 */ - case inuint_byref(c_clk_ctl, tmp): - switch(tmp) - { - case GET_SEL: - chkct(c_clk_ctl, XS1_CT_END); + /* Guard against host polling slower than timer and missing peaks */ + asm volatile("ldw %0, %1[%2]":"=r"(tmp):"r"((const int *)samples_to_host_inputs_buff),"r"(i)); - /* Send back current clock mode */ - outuint(c_clk_ctl, clkMode); - outct(c_clk_ctl, XS1_CT_END); - - break; - - case SET_SEL: - /* Update clock mode */ - clkMode = inuint(c_clk_ctl); - chkct(c_clk_ctl, XS1_CT_END); - -#ifdef CLOCK_VALIDITY_CALL - switch(clkMode) + if (g_inputLevelData[i] > tmp) + //if(g_inputLevelData[i] > samples_to_host_inputs_buff[i]) { - case CLOCK_INTERNAL: - VendorClockValidity(1); - break; -#if (XUA_ADAT_RX_EN) - case CLOCK_ADAT: - VendorClockValidity(clockValid[CLOCK_ADAT]); - break; -#endif -#if (XUA_SPDIF_RX_EN) - case CLOCK_SPDIF: - VendorClockValidity(clockValid[CLOCK_SPDIF]); - break; -#endif + //samples_to_host_inputs_buff[i] = g_inputLevelData[i]; + asm volatile("stw %0, %1[%2]"::"r"(tmp),"r"((const int *)samples_to_host_inputs),"r"(i)); } -#endif - break; - - case GET_VALID: - /* Clock Unit Index */ - tmp = inuint(c_clk_ctl); - chkct(c_clk_ctl, XS1_CT_END); - outuint(c_clk_ctl, clockValid[tmp]); - outct(c_clk_ctl, XS1_CT_END); - break; - - case GET_FREQ: - tmp = inuint(c_clk_ctl); - chkct(c_clk_ctl, XS1_CT_END); - outuint(c_clk_ctl, clockFreq[tmp]); - outct(c_clk_ctl, XS1_CT_END); - break; - - case SET_SMUX: - smux = inuint(c_clk_ctl); -#if (XUA_ADAT_RX_EN) - adatRd = 0; /* Reset adat FIFO */ - adatWr = 0; - adatSamps = 0; -#endif - chkct(c_clk_ctl, XS1_CT_END); - break; - - default: -#ifdef VENDOR_AUDCORE_REQS - if(VendorAudCoreReqs(tmp, c_clk_ctl)) -#endif - printstrln("ERR: Bad req in clockgen\n"); - break; - } - - break; - - /* Generate local clock from timer */ - case t_local when timerafter(timeNextEdge) :> void: - - /* Setup next local clock edge */ - i_pll_ref.toggle_timed(0); - - /* Record time of edge */ - timeLastEdge = timeNextEdge; - - /* Setup for next edge */ - timeNextClockDetection = timeNextEdge + (LOCAL_CLOCK_INCREMENT/2); - timeNextEdge += LOCAL_CLOCK_INCREMENT; - - /* If we are in an external clock mode and this fire, then clock invalid - * reset counters in case we are moved to digital clock - we want a well timed - * first edge */ -#if (XUA_SPDIF_RX_EN) - spdifCounters.receivedSamples = 0; -#endif -#if (XUA_ADAT_RX_EN) - adatCounters.receivedSamples = 0; -#endif - -#ifdef CLOCK_VALIDITY_CALL - if(clkMode == CLOCK_INTERNAL) - { - /* Internal clock always valid */ - VendorClockValidity(1); - } -#endif - break; - -#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) - case t_external when timerafter(timeNextClockDetection) :> void: - { - int valid; - timeNextClockDetection += (LOCAL_CLOCK_INCREMENT); -#if (XUA_SPDIF_RX_EN) - /* Returns 1 if valid clock found */ - valid = validSamples(spdifCounters, CLOCK_SPDIF); - setClockValidity(c_clk_int, CLOCK_SPDIF, valid, clkMode); -#endif -#if (XUA_ADAT_RX_EN) - /* Returns 1 if valid clock found */ - valid = validSamples(adatCounters, CLOCK_ADAT); - setClockValidity(c_clk_int, CLOCK_ADAT, valid, clkMode); -#endif - } - break; -#endif - -#if (XUA_SPDIF_RX_EN) - /* Receive sample from S/PDIF RX thread (streaming chan) */ - case c_spdif_rx :> spdifRxData: - - /* Record time of sample */ - t_local :> spdifRxTime; - - /* Check parity and ignore if bad */ - if(spdif_rx_check_parity(spdifRxData)) - continue; - - /* Get preamble */ - unsigned preamble = spdifRxData & SPDIF_RX_PREAMBLE_MASK; - - switch(preamble) - { - /* LEFT */ - case SPDIF_FRAME_X: - case SPDIF_FRAME_Z: - spdifLeft = SPDIF_RX_EXTRACT_SAMPLE(spdifRxData); - break; - - /* RIGHT */ - case SPDIF_FRAME_Y: - - /* Only store sample if not in overflow and stream is reasonably valid */ - if(!spdifOverflow && clockValid[CLOCK_SPDIF]) - { - /* Store left and right sample pair to buffer */ - spdifSamples[spdifWr] = spdifLeft; - spdifSamples[spdifWr+1] = SPDIF_RX_EXTRACT_SAMPLE(spdifRxData); - - spdifWr = (spdifWr + 2) & (MAX_SPDIF_SAMPLES - 1); - - spdifSamps += 2; - - /* Check for over flow */ - if(spdifSamps > MAX_SPDIF_SAMPLES-1) - { - spdifOverflow = 1; - } - - /* Check for coming out of under flow */ - if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1))) - { - spdifUnderflow = 0; - } - } - break; - - default: - /* Bad sample, skip */ - continue; - break; } - spdifCounters.samples += 1; + /* Call user LED refresh */ + VendorLedRefresh(g_inputLevelData); - if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF]) - { - spdifCounters.receivedSamples+=1; - - /* Inspect for if we need to produce an edge */ - if((spdifCounters.receivedSamples >= spdifCounters.samplesPerTick)) - { - /* Check edge is about right... S/PDIF may have changed freq... */ - if(timeafter(spdifRxTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN))) - { - /* Record edge time */ - timeLastEdge = spdifRxTime; - - /* Setup for next edge */ - timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; - - /* Toggle edge */ - i_pll_ref.toggle_timed(1); - - /* Reset counters */ - spdifCounters.receivedSamples = 0; - } - } - } - break; -#endif -#if (XUA_ADAT_RX_EN) - /* receive sample from ADAT rx thread (streaming channel with CT_END) */ - case inuint_byref(c_adat_rx, tmp): - /* record time of sample */ - t_local :> adatReceivedTime; - - /* Sync is: 1 | (user_byte << 4) */ - if(tmp&1) - { - /* user bits - start of frame */ - adatChannel = 0; - continue; - } - else - { - /* audio sample */ - adatSamplesEver++; - adatFrame[adatChannel] = tmp; - - adatChannel++; - if (adatChannel == 8) - { - /* only store left samples if not in overflow and stream is reasonably valid */ - if (!adatOverflow && clockValid[CLOCK_ADAT]) - { - /* Unpick the SMUX.. */ - if(smux == 2) - { - adatSamples[adatWr + 0] = adatFrame[0]; - adatSamples[adatWr + 1] = adatFrame[4]; - adatSamples[adatWr + 2] = adatFrame[1]; - adatSamples[adatWr + 3] = adatFrame[5]; - adatSamples[adatWr + 4] = adatFrame[2]; - adatSamples[adatWr + 5] = adatFrame[6]; - adatSamples[adatWr + 6] = adatFrame[3]; - adatSamples[adatWr + 7] = adatFrame[7]; - } - else if(smux) - { - - adatSamples[adatWr + 0] = adatFrame[0]; - adatSamples[adatWr + 1] = adatFrame[2]; - adatSamples[adatWr + 2] = adatFrame[4]; - adatSamples[adatWr + 3] = adatFrame[6]; - adatSamples[adatWr + 4] = adatFrame[1]; - adatSamples[adatWr + 5] = adatFrame[3]; - adatSamples[adatWr + 6] = adatFrame[5]; - adatSamples[adatWr + 7] = adatFrame[7]; - } - else - { - adatSamples[adatWr + 0] = adatFrame[0]; - adatSamples[adatWr + 1] = adatFrame[1]; - adatSamples[adatWr + 2] = adatFrame[2]; - adatSamples[adatWr + 3] = adatFrame[3]; - adatSamples[adatWr + 4] = adatFrame[4]; - adatSamples[adatWr + 5] = adatFrame[5]; - adatSamples[adatWr + 6] = adatFrame[6]; - adatSamples[adatWr + 7] = adatFrame[7]; - } - adatWr = (adatWr + 8) & (MAX_ADAT_SAMPLES - 1); - adatSamps += 8; - - /* check for overflow */ - if (adatSamps > MAX_ADAT_SAMPLES - 1) - { - adatOverflow = 1; - } - - /* check for coming out of underflow */ - if (adatUnderflow && (adatSamps >= (MAX_ADAT_SAMPLES >> 1))) - { - adatUnderflow = 0; - } - } - } - if(adatChannel == 4 || adatChannel == 8) - { - adatCounters.samples += 1; - - if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT]) - { - adatCounters.receivedSamples += 1; - - /* Inspect for if we need to produce an edge */ - if ((adatCounters.receivedSamples >= adatCounters.samplesPerTick)) - { - /* Check edge is about right... ADAT may have changed freq... */ - if (timeafter(adatReceivedTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN))) - { - /* Record edge time */ - timeLastEdge = adatReceivedTime; - - /* Setup for next edge */ - timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; - - /* Toggle edge */ - i_pll_ref.toggle_timed(1); - - /* Reset counters */ - adatCounters.receivedSamples = 0; - } - } - } - } - if (adatChannel == 8) - adatChannel = 0; - } break; #endif -#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) - /* AudioHub requests data */ - case inuint_byref(c_dig_rx, tmp): + /* Updates to clock settings from endpoint 0 */ + case inuint_byref(c_clk_ctl, tmp): + switch(tmp) + { + case GET_SEL: + chkct(c_clk_ctl, XS1_CT_END); + + /* Send back current clock mode */ + outuint(c_clk_ctl, clkMode); + outct(c_clk_ctl, XS1_CT_END); + + break; + + case SET_SEL: + /* Update clock mode */ + clkMode = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + +#ifdef CLOCK_VALIDITY_CALL + switch(clkMode) + { + case CLOCK_INTERNAL: + VendorClockValidity(1); + break; +#if (XUA_ADAT_RX_EN) + case CLOCK_ADAT: + VendorClockValidity(clockValid[CLOCK_ADAT]); + break; +#endif #if (XUA_SPDIF_RX_EN) - if(spdifUnderflow) - { - /* S/PDIF underflowing, send out zero samples */ - g_digData[0] = 0; - g_digData[1] = 0; + case CLOCK_SPDIF: + VendorClockValidity(clockValid[CLOCK_SPDIF]); + break; +#endif + } +#endif + break; + + case GET_VALID: + /* Clock Unit Index */ + tmp = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + outuint(c_clk_ctl, clockValid[tmp]); + outct(c_clk_ctl, XS1_CT_END); + break; + + case GET_FREQ: + tmp = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + outuint(c_clk_ctl, clockFreq[tmp]); + outct(c_clk_ctl, XS1_CT_END); + break; + + case SET_SMUX: + smux = inuint(c_clk_ctl); +#if (XUA_ADAT_RX_EN) + adatRd = 0; /* Reset adat FIFO */ + adatWr = 0; + adatSamps = 0; +#endif + chkct(c_clk_ctl, XS1_CT_END); + break; + + default: +#ifdef VENDOR_AUDCORE_REQS + if(VendorAudCoreReqs(tmp, c_clk_ctl)) +#endif + printstrln("ERR: Bad req in clockgen\n"); + break; } - else - { - /* Read out samples from S/PDIF buffer and send... */ - tmp = spdifSamples[spdifRd]; - tmp2 = spdifSamples[spdifRd + 1]; - spdifRd += 2; - spdifRd &= (MAX_SPDIF_SAMPLES - 1); + break; - g_digData[0] = tmp; - g_digData[1] = tmp2; + /* Generate local clock from timer */ + case t_local when timerafter(timeNextEdge) :> void: - spdifSamps -= 2; + /* Setup next local clock edge */ + i_pll_ref.toggle_timed(0); + printstr("d\n"); - /* spdifSamps could go to -1 */ - if(spdifSamps < 0) - { - /* We're out of S/PDIF samples, mark underflow condition */ - spdifUnderflow = 1; - spdifLeft = 0; - } - /* If we are in over flow condition and we have a sensible number of samples - * come out of overflow condition */ - if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1))) - { - spdifOverflow = 0; - } - } + /* Record time of edge */ + timeLastEdge = timeNextEdge; + + /* Setup for next edge */ + timeNextClockDetection = timeNextEdge + (LOCAL_CLOCK_INCREMENT/2); + timeNextEdge += LOCAL_CLOCK_INCREMENT; + + /* If we are in an external clock mode and this fire, then clock invalid + * reset counters in case we are moved to digital clock - we want a well timed + * first edge */ +#if (XUA_SPDIF_RX_EN) + spdifCounters.receivedSamples = 0; #endif #if (XUA_ADAT_RX_EN) - if (adatUnderflow) - { - /* ADAT underflowing, send out zero samples */ - g_digData[2] = 0; - g_digData[3] = 0; - g_digData[4] = 0; - g_digData[5] = 0; - g_digData[6] = 0; - g_digData[7] = 0; - g_digData[8] = 0; - g_digData[9] = 0; - } - else - { - /* read out samples from the ADAT buffer and send */ - /* always return 8 samples */ - /* SMUX II mode */ - if (smux == 2) - { - /* SMUX2 mode - 2 samples from fifo and 4 zero samples */ - g_digData[2] = adatSamples[adatRd + 0]; - g_digData[3] = adatSamples[adatRd + 1]; + adatCounters.receivedSamples = 0; +#endif +#ifdef CLOCK_VALIDITY_CALL + if(clkMode == CLOCK_INTERNAL) + { + /* Internal clock always valid */ + VendorClockValidity(1); + } +#endif + break; + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + case t_external when timerafter(timeNextClockDetection) :> void: + { + int valid; + timeNextClockDetection += (LOCAL_CLOCK_INCREMENT); +#if (XUA_SPDIF_RX_EN) + /* Returns 1 if valid clock found */ + valid = validSamples(spdifCounters, CLOCK_SPDIF); + setClockValidity(c_clk_int, CLOCK_SPDIF, valid, clkMode); +#endif +#if (XUA_ADAT_RX_EN) + /* Returns 1 if valid clock found */ + valid = validSamples(adatCounters, CLOCK_ADAT); + setClockValidity(c_clk_int, CLOCK_ADAT, valid, clkMode); +#endif + } + break; +#endif + +#if (XUA_SPDIF_RX_EN) + /* Receive sample from S/PDIF RX thread (streaming chan) */ + case c_spdif_rx :> spdifRxData: + + /* Record time of sample */ + t_local :> spdifRxTime; + + /* Check parity and ignore if bad */ + if(spdif_rx_check_parity(spdifRxData)) + continue; + + /* Get preamble */ + unsigned preamble = spdifRxData & SPDIF_RX_PREAMBLE_MASK; + + switch(preamble) + { + /* LEFT */ + case SPDIF_FRAME_X: + case SPDIF_FRAME_Z: + spdifLeft = SPDIF_RX_EXTRACT_SAMPLE(spdifRxData); + break; + + /* RIGHT */ + case SPDIF_FRAME_Y: + + /* Only store sample if not in overflow and stream is reasonably valid */ + if(!spdifOverflow && clockValid[CLOCK_SPDIF]) + { + /* Store left and right sample pair to buffer */ + spdifSamples[spdifWr] = spdifLeft; + spdifSamples[spdifWr+1] = SPDIF_RX_EXTRACT_SAMPLE(spdifRxData); + + spdifWr = (spdifWr + 2) & (MAX_SPDIF_SAMPLES - 1); + + spdifSamps += 2; + + /* Check for over flow */ + if(spdifSamps > MAX_SPDIF_SAMPLES-1) + { + spdifOverflow = 1; + } + + /* Check for coming out of under flow */ + if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1))) + { + spdifUnderflow = 0; + } + } + break; + + default: + /* Bad sample, skip */ + continue; + break; + } + + spdifCounters.samples += 1; + + if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF]) + { + spdifCounters.receivedSamples+=1; + + /* Inspect for if we need to produce an edge */ + if((spdifCounters.receivedSamples >= spdifCounters.samplesPerTick)) + { + /* Check edge is about right... S/PDIF may have changed freq... */ + if(timeafter(spdifRxTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN))) + { + /* Record edge time */ + timeLastEdge = spdifRxTime; + + /* Setup for next edge */ + timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; + + /* Toggle edge */ + i_pll_ref.toggle_timed(1); + printstr("s\n"); + + /* Reset counters */ + spdifCounters.receivedSamples = 0; + } + } + } + break; +#endif +#if (XUA_ADAT_RX_EN) + /* receive sample from ADAT rx thread (streaming channel with CT_END) */ + case inuint_byref(c_adat_rx, tmp): + /* record time of sample */ + t_local :> adatReceivedTime; + + /* Sync is: 1 | (user_byte << 4) */ + if(tmp&1) + { + /* user bits - start of frame */ + adatChannel = 0; + continue; + } + else + { + /* audio sample */ + adatSamplesEver++; + adatFrame[adatChannel] = tmp; + + adatChannel++; + if (adatChannel == 8) + { + /* only store left samples if not in overflow and stream is reasonably valid */ + if (!adatOverflow && clockValid[CLOCK_ADAT]) + { + /* Unpick the SMUX.. */ + if(smux == 2) + { + adatSamples[adatWr + 0] = adatFrame[0]; + adatSamples[adatWr + 1] = adatFrame[4]; + adatSamples[adatWr + 2] = adatFrame[1]; + adatSamples[adatWr + 3] = adatFrame[5]; + adatSamples[adatWr + 4] = adatFrame[2]; + adatSamples[adatWr + 5] = adatFrame[6]; + adatSamples[adatWr + 6] = adatFrame[3]; + adatSamples[adatWr + 7] = adatFrame[7]; + } + else if(smux) + { + + adatSamples[adatWr + 0] = adatFrame[0]; + adatSamples[adatWr + 1] = adatFrame[2]; + adatSamples[adatWr + 2] = adatFrame[4]; + adatSamples[adatWr + 3] = adatFrame[6]; + adatSamples[adatWr + 4] = adatFrame[1]; + adatSamples[adatWr + 5] = adatFrame[3]; + adatSamples[adatWr + 6] = adatFrame[5]; + adatSamples[adatWr + 7] = adatFrame[7]; + } + else + { + adatSamples[adatWr + 0] = adatFrame[0]; + adatSamples[adatWr + 1] = adatFrame[1]; + adatSamples[adatWr + 2] = adatFrame[2]; + adatSamples[adatWr + 3] = adatFrame[3]; + adatSamples[adatWr + 4] = adatFrame[4]; + adatSamples[adatWr + 5] = adatFrame[5]; + adatSamples[adatWr + 6] = adatFrame[6]; + adatSamples[adatWr + 7] = adatFrame[7]; + } + adatWr = (adatWr + 8) & (MAX_ADAT_SAMPLES - 1); + adatSamps += 8; + + /* check for overflow */ + if (adatSamps > MAX_ADAT_SAMPLES - 1) + { + adatOverflow = 1; + } + + /* check for coming out of underflow */ + if (adatUnderflow && (adatSamps >= (MAX_ADAT_SAMPLES >> 1))) + { + adatUnderflow = 0; + } + } + } + if(adatChannel == 4 || adatChannel == 8) + { + adatCounters.samples += 1; + + if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT]) + { + adatCounters.receivedSamples += 1; + + /* Inspect for if we need to produce an edge */ + if ((adatCounters.receivedSamples >= adatCounters.samplesPerTick)) + { + /* Check edge is about right... ADAT may have changed freq... */ + if (timeafter(adatReceivedTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN))) + { + /* Record edge time */ + timeLastEdge = adatReceivedTime; + + /* Setup for next edge */ + timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; + + /* Toggle edge */ + i_pll_ref.toggle_timed(1); + printstr("a\n"); + + /* Reset counters */ + adatCounters.receivedSamples = 0; + } + } + } + } + if (adatChannel == 8) + adatChannel = 0; + } + break; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + /* AudioHub requests data */ + case inuint_byref(c_dig_rx, tmp): +#if (XUA_SPDIF_RX_EN) + if(spdifUnderflow) + { + /* S/PDIF underflowing, send out zero samples */ + g_digData[0] = 0; + g_digData[1] = 0; + } + else + { + /* Read out samples from S/PDIF buffer and send... */ + tmp = spdifSamples[spdifRd]; + tmp2 = spdifSamples[spdifRd + 1]; + + spdifRd += 2; + spdifRd &= (MAX_SPDIF_SAMPLES - 1); + + g_digData[0] = tmp; + g_digData[1] = tmp2; + + spdifSamps -= 2; + + /* spdifSamps could go to -1 */ + if(spdifSamps < 0) + { + /* We're out of S/PDIF samples, mark underflow condition */ + spdifUnderflow = 1; + spdifLeft = 0; + } + + /* If we are in over flow condition and we have a sensible number of samples + * come out of overflow condition */ + if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1))) + { + spdifOverflow = 0; + } + } +#endif +#if (XUA_ADAT_RX_EN) + if (adatUnderflow) + { + /* ADAT underflowing, send out zero samples */ + g_digData[2] = 0; + g_digData[3] = 0; g_digData[4] = 0; g_digData[5] = 0; g_digData[6] = 0; g_digData[7] = 0; g_digData[8] = 0; g_digData[9] = 0; - adatRd = (adatRd + 2) & (MAX_ADAT_SAMPLES - 1); - adatSamps -= 2; - } - else if(smux) - { - /* SMUX mode - 4 samples from fifo and 4 zero samples */ - g_digData[2] = adatSamples[adatRd + 0]; - g_digData[3] = adatSamples[adatRd + 1]; - g_digData[4] = adatSamples[adatRd + 2]; - g_digData[5] = adatSamples[adatRd + 3]; - - g_digData[6] = 0; - g_digData[7] = 0; - g_digData[8] = 0; - g_digData[9] = 0; - adatRd = (adatRd + 4) & (MAX_ADAT_SAMPLES - 1); - adatSamps -= 4; } else { - /* no SMUX mode - 8 samples from fifo */ - g_digData[2] = adatSamples[adatRd + 0]; - g_digData[3] = adatSamples[adatRd + 1]; - g_digData[4] = adatSamples[adatRd + 2]; - g_digData[5] = adatSamples[adatRd + 3]; + /* read out samples from the ADAT buffer and send */ + /* always return 8 samples */ + /* SMUX II mode */ + if (smux == 2) + { + /* SMUX2 mode - 2 samples from fifo and 4 zero samples */ + g_digData[2] = adatSamples[adatRd + 0]; + g_digData[3] = adatSamples[adatRd + 1]; - g_digData[6] = adatSamples[adatRd + 4]; - g_digData[7] = adatSamples[adatRd + 5]; - g_digData[8] = adatSamples[adatRd + 6]; - g_digData[9] = adatSamples[adatRd + 7]; + g_digData[4] = 0; + g_digData[5] = 0; + g_digData[6] = 0; + g_digData[7] = 0; + g_digData[8] = 0; + g_digData[9] = 0; + adatRd = (adatRd + 2) & (MAX_ADAT_SAMPLES - 1); + adatSamps -= 2; + } + else if(smux) + { + /* SMUX mode - 4 samples from fifo and 4 zero samples */ + g_digData[2] = adatSamples[adatRd + 0]; + g_digData[3] = adatSamples[adatRd + 1]; + g_digData[4] = adatSamples[adatRd + 2]; + g_digData[5] = adatSamples[adatRd + 3]; - adatRd = (adatRd + 8) & (MAX_ADAT_SAMPLES - 1); - adatSamps -= 8; + g_digData[6] = 0; + g_digData[7] = 0; + g_digData[8] = 0; + g_digData[9] = 0; + adatRd = (adatRd + 4) & (MAX_ADAT_SAMPLES - 1); + adatSamps -= 4; + } + else + { + /* no SMUX mode - 8 samples from fifo */ + g_digData[2] = adatSamples[adatRd + 0]; + g_digData[3] = adatSamples[adatRd + 1]; + g_digData[4] = adatSamples[adatRd + 2]; + g_digData[5] = adatSamples[adatRd + 3]; + + g_digData[6] = adatSamples[adatRd + 4]; + g_digData[7] = adatSamples[adatRd + 5]; + g_digData[8] = adatSamples[adatRd + 6]; + g_digData[9] = adatSamples[adatRd + 7]; + + adatRd = (adatRd + 8) & (MAX_ADAT_SAMPLES - 1); + adatSamps -= 8; + } + + /* adatSamps could go to -1 */ + if (adatSamps < 0) + { + /* we're out of ADAT samples, mark underflow condition */ + adatUnderflow = 1; + } + + /* if we are in overflow condition and have a sensible number of samples + come out of overflow condition */ + if (adatOverflow && adatSamps < (MAX_ADAT_SAMPLES >> 1)) + { + adatOverflow = 0; + } } - - /* adatSamps could go to -1 */ - if (adatSamps < 0) - { - /* we're out of ADAT samples, mark underflow condition */ - adatUnderflow = 1; - } - - /* if we are in overflow condition and have a sensible number of samples - come out of overflow condition */ - if (adatOverflow && adatSamps < (MAX_ADAT_SAMPLES >> 1)) - { - adatOverflow = 0; - } - } #endif - outuint(c_dig_rx, 1); - break; + outuint(c_dig_rx, 1); + break; #endif - } - } + } /* select */ + } /* while(1) */ + } /* clkgen task scope */ } From d644775e4c96b625a2c1ac2fc8fecff56fc48302 Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 4 Jan 2024 12:16:26 +0000 Subject: [PATCH 02/33] Add in timestamp machinery --- lib_xua/api/xua_clocking.h | 11 ++- lib_xua/src/core/clocking/clockgen.xc | 123 ++++++++++++++++++++++++-- lib_xua/src/core/main.xc | 14 ++- 3 files changed, 139 insertions(+), 9 deletions(-) diff --git a/lib_xua/api/xua_clocking.h b/lib_xua/api/xua_clocking.h index c4028009..c41d130f 100644 --- a/lib_xua/api/xua_clocking.h +++ b/lib_xua/api/xua_clocking.h @@ -25,8 +25,15 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync); * \param c_clk_ctl channel connected to Endpoint0() for configuration of the * clock * \param c_clk_int channel connected to the decouple() thread for clock - interrupts + * interrupts + * \param p_for_mclk_count_aud port used for counting mclk and providing a timestamp */ -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, + port ?p_for_mclk_count_aud); #endif diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index ddbe9b80..a49b2356 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -8,7 +8,14 @@ #include "xua_commands.h" #include "xua_clocking.h" + #ifdef __XS3A__ +#define USE_SW_PLL 1 +#else +#define USE_SW_PLL 0 +#endif + +#if USE_SW_PLL extern "C" { #include "sw_pll.h" @@ -45,6 +52,7 @@ static int clockValid[NUM_CLOCKS]; /* Store current val static int clockInt[NUM_CLOCKS]; /* Interupt flag for clocks */ static int clockId[NUM_CLOCKS]; + [[distributable]] void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref) { @@ -86,6 +94,17 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref) } } +void do_sw_pll_control(sw_pll_state_t sw_pll, unsigned short mclk_time_stamp, chanend c_sigma_delta) +{ + static unsigned count = 0; + count++; + if(count == 30) + { + printuintln(mclk_time_stamp); + count = 0; + } +} + #if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) static int abs(int x) @@ -222,6 +241,72 @@ static inline int validSamples(Counter &counter, int clockIndex) } #endif +#if USE_SW_PLL +void SigmaDeltaTask(chanend c_sigma_delta){ + while(1) + { + c_sigma_delta :> int _; + } +} + +void InitSWPLL(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 + F: 134 + R: 0 + f: 8 + p: 18 + OD: 5 + ACD: 5 + */ + + #define APP_PLL_CTL_REG_22 0x0A808600 + #define APP_PLL_DIV_REG_22 0x80000005 + #define APP_PLL_FRAC_REG_22 0x80000812 + #define SW_PLL_SDM_CTRL_MID_22 498283 + #define SW_PLL_SDM_RATE_22 1000000 + + /* Autogenerated SDM App PLL setup by dco_model.py using 24.576_1M profile */ + /* Input freq: 24000000 + F: 146 + R: 0 + f: 4 + p: 10 + OD: 5 + ACD: 5 + */ + + #define APP_PLL_CTL_REG_24 0x0A809200 + #define APP_PLL_DIV_REG_24 0x80000005 + #define APP_PLL_FRAC_REG_24 0x8000040A + #define SW_PLL_SDM_CTRL_MID_24 478151 + #define SW_PLL_SDM_RATE_24 1000000 + + + const uint32_t app_pll_ctl_reg[2] = {APP_PLL_CTL_REG_22, APP_PLL_CTL_REG_24}; + const uint32_t app_pll_div_reg[2] = {APP_PLL_DIV_REG_22, APP_PLL_DIV_REG_24}; + const uint32_t app_pll_frac_reg[2] = {APP_PLL_FRAC_REG_22, APP_PLL_FRAC_REG_24}; + const uint32_t sw_pll_sdm_ctrl_mid[2] = {SW_PLL_SDM_CTRL_MID_22, SW_PLL_SDM_CTRL_MID_24}; + // const uint32_t sw_pll_sdm_rate[2] = {SW_PLL_SDM_RATE_22, SW_PLL_SDM_RATE_24}; + + const int clkIndex = mClk == MCLK_48 ? 1 : 0; + + sw_pll_sdm_init(&sw_pll, + SW_PLL_15Q16(0.0), + SW_PLL_15Q16(32.0), + SW_PLL_15Q16(0.25), + 0, /* LOOP COUNT Don't care for this API */ + 0, /* PLL_RATIO Don't care for this API */ + 0, /* No jitter compensation needed */ + app_pll_ctl_reg[clkIndex], + app_pll_div_reg[clkIndex], + app_pll_frac_reg[clkIndex], + sw_pll_sdm_ctrl_mid[clkIndex], + 3000 /* PPM_RANGE FOR PFD Don't care for this API*/ ); +} +#endif + #ifdef LEVEL_METER_LEDS void VendorLedRefresh(unsigned levelData[]); unsigned g_inputLevelData[NUM_USB_CHAN_IN]; @@ -232,7 +317,13 @@ extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; int VendorAudCoreReqs(unsigned cmd, chanend c); #pragma unsafe arrays -void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_dig_rx, 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_dig_rx, + chanend c_clk_ctl, + chanend c_clk_int, + port ?p_for_mclk_count_aud) { timer t_local; unsigned timeNextEdge, timeLastEdge, timeNextClockDetection; @@ -249,6 +340,9 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) timer t_external; + unsigned short mclk_time_stamp = 0; + /* Get MCLK count */ + asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); #endif #if (XUA_SPDIF_RX_EN) @@ -340,9 +434,21 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa /* Initial ref clock output and get timestamp */ i_pll_ref.init(); +#if USE_SW_PLL + chan c_sigma_delta; + sw_pll_state_t sw_pll; + InitSWPLL(sw_pll, MCLK_48); + + par { + SigmaDeltaTask(c_sigma_delta); +#else + { +#endif while(1) { + // TMP + c_sigma_delta <: 0; select { #ifdef LEVEL_METER_LEDS @@ -513,6 +619,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa case c_spdif_rx :> spdifRxData: /* Record time of sample */ + asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); t_local :> spdifRxTime; /* Check parity and ignore if bad */ @@ -582,10 +689,12 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa /* Setup for next edge */ timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; +#if USE_SW_PLL + do_sw_pll_control(sw_pll, mclk_time_stamp, c_sigma_delta); +#else /* Toggle edge */ i_pll_ref.toggle_timed(1); - printstr("s\n"); - +#endif /* Reset counters */ spdifCounters.receivedSamples = 0; } @@ -597,6 +706,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa /* receive sample from ADAT rx thread (streaming channel with CT_END) */ case inuint_byref(c_adat_rx, tmp): /* record time of sample */ + asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); t_local :> adatReceivedTime; /* Sync is: 1 | (user_byte << 4) */ @@ -689,10 +799,13 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa /* Setup for next edge */ timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; +#if USE_SW_PLL + do_sw_pll_control(sw_pll, mclk_time_stamp, c_sigma_delta); +#else /* Toggle edge */ i_pll_ref.toggle_timed(1); - printstr("a\n"); - +#endif + /* Reset counters */ adatCounters.receivedSamples = 0; } diff --git a/lib_xua/src/core/main.xc b/lib_xua/src/core/main.xc index efa8ec29..d46b6026 100755 --- a/lib_xua/src/core/main.xc +++ b/lib_xua/src/core/main.xc @@ -144,6 +144,7 @@ on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN; #if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) /* Reference to external clock multiplier */ on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; +on tile[AUDIO_IO_TILE] : port p_for_mclk_count_aud = PORT_MCLK_COUNT_2; #endif #ifdef MIDI @@ -309,6 +310,7 @@ 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 ) { @@ -318,6 +320,12 @@ void usb_audio_io(chanend ?c_aud_in, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) chan c_dig_rx; + + /* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/ + 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)); + #else #define c_dig_rx null #endif @@ -385,7 +393,7 @@ void usb_audio_io(chanend ?c_aud_in, * However, due to the use of an interface the pll reference signal port can be on another tile */ thread_speed(); - clockGen(c_spdif_rx, c_adat_rx, i_pll_ref, c_dig_rx, c_clk_ctl, c_clk_int); + clockGen(c_spdif_rx, c_adat_rx, i_pll_ref, c_dig_rx, c_clk_ctl, c_clk_int, p_for_mclk_count_aud); } #endif @@ -437,7 +445,7 @@ int main() #define c_adat_rx null #endif -#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) chan c_spdif_tx; #endif @@ -575,6 +583,7 @@ int main() on tile[AUDIO_IO_TILE]: { + /* Audio I/O task, includes mixing etc */ usb_audio_io(c_mix_out #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) @@ -595,6 +604,7 @@ int main() #endif #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) , i_pll_ref + , p_for_mclk_count_aud #endif ); } From 61f17f3fe913e75874fccd6f1b6064dc2646c1cf Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 4 Jan 2024 15:32:44 +0000 Subject: [PATCH 03/33] Add mclk change logic --- lib_xua/api/xua_audiohub.h | 3 + lib_xua/api/xua_clocking.h | 5 +- lib_xua/src/core/audiohub/xua_audiohub.xc | 5 ++ lib_xua/src/core/clocking/clockgen.xc | 79 ++++++++++++++++------- lib_xua/src/core/main.xc | 15 ++++- 5 files changed, 78 insertions(+), 29 deletions(-) diff --git a/lib_xua/api/xua_audiohub.h b/lib_xua/api/xua_audiohub.h index 90856ec4..6682676f 100644 --- a/lib_xua/api/xua_audiohub.h +++ b/lib_xua/api/xua_audiohub.h @@ -38,6 +38,8 @@ * * \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 */ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, @@ -52,6 +54,7 @@ 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) , server interface i_dfu ?dfuInterface diff --git a/lib_xua/api/xua_clocking.h b/lib_xua/api/xua_clocking.h index c41d130f..b3a74ab7 100644 --- a/lib_xua/api/xua_clocking.h +++ b/lib_xua/api/xua_clocking.h @@ -27,6 +27,8 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync); * \param c_clk_int channel connected to the decouple() thread for clock * interrupts * \param p_for_mclk_count_aud port used for counting mclk and providing a timestamp + * + * \param c_mclk_change channel to notify of master clock change */ void clockGen( streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, @@ -34,6 +36,7 @@ void clockGen( streaming chanend ?c_spdif_rx, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int, - port ?p_for_mclk_count_aud); + port p_for_mclk_count_aud, + chanend c_mclk_change); #endif diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index 1a9ae947..b8c5e472 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -640,6 +640,7 @@ 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 (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) , server interface i_dfu ?dfuInterface @@ -800,6 +801,10 @@ 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; +#endif } if(!firstRun) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index a49b2356..27bd85ba 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -94,17 +94,6 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref) } } -void do_sw_pll_control(sw_pll_state_t sw_pll, unsigned short mclk_time_stamp, chanend c_sigma_delta) -{ - static unsigned count = 0; - count++; - if(count == 30) - { - printuintln(mclk_time_stamp); - count = 0; - } -} - #if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) static int abs(int x) @@ -242,13 +231,6 @@ static inline int validSamples(Counter &counter, int clockIndex) #endif #if USE_SW_PLL -void SigmaDeltaTask(chanend c_sigma_delta){ - while(1) - { - c_sigma_delta :> int _; - } -} - void InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) { /* Autogenerated SDM App PLL setup by dco_model.py using 22.5792_1M profile */ @@ -303,8 +285,44 @@ void InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) app_pll_div_reg[clkIndex], app_pll_frac_reg[clkIndex], sw_pll_sdm_ctrl_mid[clkIndex], - 3000 /* PPM_RANGE FOR PFD Don't care for this API*/ ); + 3000 /* PPM_RANGE (FOR PFD) Don't care for this API*/ ); + + + printstr("Init sw_pll: "); printuintln(mClk); } + +void do_sw_pll_control(sw_pll_state_t sw_pll, unsigned short mclk_time_stamp, chanend c_sigma_delta) +{ + static unsigned count = 0; + count++; + if(count == 30) + { + printuintln(mclk_time_stamp); + count = 0; + } +} + +void SigmaDeltaTask(chanend c_sigma_delta){ + int dco_setting = 0; + + while(1) + { + c_sigma_delta :> dco_setting; + printstr("sigma-delta got: "); printintln(dco_setting); + if(dco_setting == 0) + { + c_sigma_delta <: 0; /* Send ACK */ + } + } +} + + +void disable_sigma_delta(chanend c_sigma_delta) +{ + c_sigma_delta <: 0; /* Stops SD */ + c_sigma_delta :> int _; /* Wait for ACK so we know reg write is complete */ +} + #endif #ifdef LEVEL_METER_LEDS @@ -323,7 +341,8 @@ 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) + port p_for_mclk_count_aud, + chanend c_mclk_change) { timer t_local; unsigned timeNextEdge, timeLastEdge, timeNextClockDetection; @@ -340,6 +359,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) timer t_external; + unsigned selected_mclk_rate = 0; unsigned short mclk_time_stamp = 0; /* Get MCLK count */ asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); @@ -447,8 +467,6 @@ void clockGen ( streaming chanend ?c_spdif_rx, #endif while(1) { - // TMP - c_sigma_delta <: 0; select { #ifdef LEVEL_METER_LEDS @@ -564,11 +582,12 @@ void clockGen ( streaming chanend ?c_spdif_rx, /* Generate local clock from timer */ case t_local when timerafter(timeNextEdge) :> void: +#if USE_SW_PLL + /* Do nothing - hold the most recent sw_pll setting */ +#else /* Setup next local clock edge */ i_pll_ref.toggle_timed(0); - printstr("d\n"); - - +#endif /* Record time of edge */ timeLastEdge = timeNextEdge; @@ -614,6 +633,16 @@ void clockGen ( streaming chanend ?c_spdif_rx, break; #endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + case c_mclk_change :> selected_mclk_rate: +#if USE_SW_PLL + printstr("c_mclk_change: "); printuintln(selected_mclk_rate); + disable_sigma_delta(c_sigma_delta); /* Blocks until SD is idle */ + InitSWPLL(sw_pll, selected_mclk_rate); +#endif + break; +#endif + #if (XUA_SPDIF_RX_EN) /* Receive sample from S/PDIF RX thread (streaming chan) */ case c_spdif_rx :> spdifRxData: diff --git a/lib_xua/src/core/main.xc b/lib_xua/src/core/main.xc index d46b6026..3a82d1d3 100755 --- a/lib_xua/src/core/main.xc +++ b/lib_xua/src/core/main.xc @@ -320,6 +320,8 @@ 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 */ + /* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/ unsigned x = 0; @@ -373,6 +375,7 @@ 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 (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) , dfuInterface @@ -393,12 +396,18 @@ void usb_audio_io(chanend ?c_aud_in, * However, due to the use of an interface the pll reference signal port can be on another tile */ thread_speed(); - clockGen(c_spdif_rx, c_adat_rx, i_pll_ref, c_dig_rx, c_clk_ctl, c_clk_int, p_for_mclk_count_aud); + clockGen( c_spdif_rx, + c_adat_rx, + i_pll_ref, + c_dig_rx, + c_clk_ctl, + c_clk_int, + p_for_mclk_count_aud, + c_mclk_change); } #endif - //: - } + } // par } #ifndef USER_MAIN_DECLARATIONS From 780a4075197cb1fb35a6de8fcbb7486afca314cc Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 08:34:24 +0000 Subject: [PATCH 04/33] Fix lockup in aud->clkgen notification --- lib_xua/src/core/audiohub/xua_audiohub.xc | 1 + lib_xua/src/core/clocking/clockgen.xc | 109 ++++++++++++++++++---- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index b8c5e472..c6d53cfc 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -804,6 +804,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* Notify clockgen of new mCLk */ c_mclk_change <: mClk; + c_mclk_change <: curFreq; #endif } diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 27bd85ba..a489c047 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -8,6 +8,7 @@ #include "xua_commands.h" #include "xua_clocking.h" +#include // TODO DEV ONLY - DELME #ifdef __XS3A__ #define USE_SW_PLL 1 @@ -231,7 +232,7 @@ static inline int validSamples(Counter &counter, int clockIndex) #endif #if USE_SW_PLL -void InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) +unsigned InitSWPLL(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 @@ -270,7 +271,7 @@ void InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) const uint32_t app_pll_div_reg[2] = {APP_PLL_DIV_REG_22, APP_PLL_DIV_REG_24}; const uint32_t app_pll_frac_reg[2] = {APP_PLL_FRAC_REG_22, APP_PLL_FRAC_REG_24}; const uint32_t sw_pll_sdm_ctrl_mid[2] = {SW_PLL_SDM_CTRL_MID_22, SW_PLL_SDM_CTRL_MID_24}; - // const uint32_t sw_pll_sdm_rate[2] = {SW_PLL_SDM_RATE_22, SW_PLL_SDM_RATE_24}; + const uint32_t sw_pll_sdm_rate[2] = {SW_PLL_SDM_RATE_22, SW_PLL_SDM_RATE_24}; const int clkIndex = mClk == MCLK_48 ? 1 : 0; @@ -289,38 +290,102 @@ void InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) printstr("Init sw_pll: "); printuintln(mClk); + + return (XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex]); } -void do_sw_pll_control(sw_pll_state_t sw_pll, unsigned short mclk_time_stamp, chanend c_sigma_delta) +void do_sw_pll_control( sw_pll_state_t sw_pll, + unsigned short mclk_time_stamp, + unsigned mclks_per_sample, + chanend c_sigma_delta, + int receivedSamples) { + static unsigned short last_mclk_time_stamp = 0; static unsigned count = 0; count++; + + const unsigned expected_mclk_inc = mclks_per_sample * receivedSamples; + const unsigned short expected_mclk_count = (last_mclk_time_stamp + expected_mclk_inc) & 0xffff; + + short f_error = (int)mclk_time_stamp - (int)expected_mclk_count; + + if(count == 30) { - printuintln(mclk_time_stamp); - count = 0; + //calc mclk inc + + outuint(c_sigma_delta, (unsigned) (1000000 + f_error)); + count = 0; } + + last_mclk_time_stamp = mclk_time_stamp; } -void SigmaDeltaTask(chanend c_sigma_delta){ +void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ + /* 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. */ + int dco_setting = 0; + sw_pll_sdm_state_t sdm_state; + sw_pll_init_sigma_delta(&sdm_state); + + tileref_t this_tile = get_local_tile_id(); + + timer tmr; + int32_t time_trigger; + tmr :> time_trigger; + int send_ack_once = 1; while(1) { - c_sigma_delta :> dco_setting; - printstr("sigma-delta got: "); printintln(dco_setting); - if(dco_setting == 0) + /* Poll for new SDM control value */ + unsigned tmp; + select { - c_sigma_delta <: 0; /* Send ACK */ + case inuint_byref(c_sigma_delta, tmp): + dco_setting = (int32_t)tmp; + printstr("sigma-delta got: "); printintln(dco_setting); + break; + + // Do nothing & fall-through + default: + break; } + + /* Wait until the timer value has been reached + This implements a timing barrier and keeps + the loop rate constant. */ + select + { + case tmr when timerafter(time_trigger) :> int _: + time_trigger += sdm_interval; + break; + } + + /* Do not write to the frac reg until we get out first + control value. This will avoid the writing of the + frac reg from two different threads which may cause + a channel deadlock. */ + if(dco_setting != 0) + { + sw_pll_do_sigma_delta(&sdm_state, this_tile, dco_setting); + send_ack_once = 1; + } + else if(send_ack_once) + { + /* Send ACK once to synchrnoise with clockgen signalling it's OK to reconfig */ + outuint(c_sigma_delta, 0); /* Send ACK to say reg writes have ceased */ + send_ack_once = 0; + } } } void disable_sigma_delta(chanend c_sigma_delta) { - c_sigma_delta <: 0; /* Stops SD */ - c_sigma_delta :> int _; /* Wait for ACK so we know reg write is complete */ + outuint(c_sigma_delta, 0); /* Stops SD */ + inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */ } #endif @@ -360,6 +425,8 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) timer t_external; unsigned selected_mclk_rate = 0; + unsigned selected_sample_rate = 0; + unsigned mclks_per_sample = 0; unsigned short mclk_time_stamp = 0; /* Get MCLK count */ asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); @@ -457,11 +524,11 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if USE_SW_PLL chan c_sigma_delta; sw_pll_state_t sw_pll; - InitSWPLL(sw_pll, MCLK_48); + unsigned sdm_interval = InitSWPLL(sw_pll, MCLK_48); par { - SigmaDeltaTask(c_sigma_delta); + SigmaDeltaTask(c_sigma_delta, sdm_interval); #else { #endif @@ -635,10 +702,14 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) case c_mclk_change :> selected_mclk_rate: + c_mclk_change :> selected_sample_rate; + mclks_per_sample = selected_mclk_rate / selected_sample_rate; #if USE_SW_PLL printstr("c_mclk_change: "); printuintln(selected_mclk_rate); - disable_sigma_delta(c_sigma_delta); /* Blocks until SD is idle */ + disable_sigma_delta(c_sigma_delta); /* Blocks until SDM is idle */ InitSWPLL(sw_pll, selected_mclk_rate); + outuint(c_sigma_delta, sw_pll.sdm_state.ctrl_mid_point); /* Send ctrl mid point to enable SDM */ + printstr("swpll int'd\n"); #endif break; #endif @@ -684,12 +755,14 @@ void clockGen ( streaming chanend ?c_spdif_rx, if(spdifSamps > MAX_SPDIF_SAMPLES-1) { spdifOverflow = 1; + printstr("spo+\n"); // DELME } /* Check for coming out of under flow */ if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1))) { spdifUnderflow = 0; + printstr("spu-\n"); // DELME } } break; @@ -719,7 +792,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_control(sw_pll, mclk_time_stamp, c_sigma_delta); + do_sw_pll_control(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, spdifCounters.receivedSamples); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); @@ -829,7 +902,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_control(sw_pll, mclk_time_stamp, c_sigma_delta); + do_sw_pll_control(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, adatCounters.receivedSamples); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); @@ -876,6 +949,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, { /* We're out of S/PDIF samples, mark underflow condition */ spdifUnderflow = 1; + printstr("spu-\n"); // DELME spdifLeft = 0; } @@ -884,6 +958,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1))) { spdifOverflow = 0; + printstr("spo-\n"); // DELME } } #endif From 702e8d14b98d293ad6501a27d086012d973f2941 Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 10:59:32 +0000 Subject: [PATCH 05/33] Initial loop closed --- lib_xua/src/core/clocking/clockgen.xc | 71 +++++++++++++++++---------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index a489c047..efedac00 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -232,6 +232,12 @@ 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 */ +unsafe +{ + sw_pll_state_t * unsafe sw_pll_ptr = NULL; +} + unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) { /* Autogenerated SDM App PLL setup by dco_model.py using 22.5792_1M profile */ @@ -294,30 +300,27 @@ unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) return (XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex]); } -void do_sw_pll_control( sw_pll_state_t sw_pll, +void do_sw_pll_phase_frequency_detector( sw_pll_state_t sw_pll, unsigned short mclk_time_stamp, unsigned mclks_per_sample, chanend c_sigma_delta, int receivedSamples) { + /* Keep a store of the last mclk time stamp so we can work out the increment */ static unsigned short last_mclk_time_stamp = 0; - static unsigned count = 0; - count++; - const unsigned expected_mclk_inc = mclks_per_sample * receivedSamples; - const unsigned short expected_mclk_count = (last_mclk_time_stamp + expected_mclk_inc) & 0xffff; + /* Calculate what the zero-error mclk count increment should be for this many samples */ + const unsigned expected_mclk_inc = mclks_per_sample * receivedSamples / 2; /* divide by 2 because this fn is called per edge */ - short f_error = (int)mclk_time_stamp - (int)expected_mclk_count; + /* Calculate actualy time-stamped mclk count increment is */ + const unsigned short actual_mclk_inc = mclk_time_stamp - last_mclk_time_stamp; + /* The difference is the raw error in terms of mclk counts */ + short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc; - if(count == 30) - { - //calc mclk inc - - outuint(c_sigma_delta, (unsigned) (1000000 + f_error)); - count = 0; - } - + /* send PFD output to the sigma delta thread */ + outuint(c_sigma_delta, (unsigned) (1000000 + f_error)); + last_mclk_time_stamp = mclk_time_stamp; } @@ -326,9 +329,15 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ 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. */ - int dco_setting = 0; - sw_pll_sdm_state_t sdm_state; - sw_pll_init_sigma_delta(&sdm_state); + /* To be extra safe, spin on sw_pll_ptr until it has been initialised */ + while(sw_pll_ptr == NULL); + + int f_error = 0; + int dco_setting = SW_PLL_SDM_CTRL_MID_24; // TODO FIX ME + unsafe + { + sw_pll_init_sigma_delta(&sw_pll_ptr->sdm_state); + } tileref_t this_tile = get_local_tile_id(); @@ -344,8 +353,13 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ select { case inuint_byref(c_sigma_delta, tmp): - dco_setting = (int32_t)tmp; - printstr("sigma-delta got: "); printintln(dco_setting); + f_error = (int32_t)tmp; + printstr("sigma-delta got: "); printintln(f_error); + unsafe + { + sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error + 1000000); + dco_setting = sw_pll_ptr->sdm_state.current_ctrl_val; + } break; // Do nothing & fall-through @@ -367,9 +381,9 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ control value. This will avoid the writing of the frac reg from two different threads which may cause a channel deadlock. */ - if(dco_setting != 0) - { - sw_pll_do_sigma_delta(&sdm_state, this_tile, dco_setting); + if(f_error != 0) + unsafe { + sw_pll_do_sigma_delta(&sw_pll_ptr->sdm_state, this_tile, dco_setting); send_ack_once = 1; } else if(send_ack_once) @@ -524,6 +538,13 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if USE_SW_PLL chan c_sigma_delta; sw_pll_state_t sw_pll; + + /* Initialise before we par off the SDM task */ + unsafe + { + sw_pll_ptr = &sw_pll; + } + unsigned sdm_interval = InitSWPLL(sw_pll, MCLK_48); par @@ -708,7 +729,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, printstr("c_mclk_change: "); printuintln(selected_mclk_rate); disable_sigma_delta(c_sigma_delta); /* Blocks until SDM is idle */ InitSWPLL(sw_pll, selected_mclk_rate); - outuint(c_sigma_delta, sw_pll.sdm_state.ctrl_mid_point); /* Send ctrl mid point to enable SDM */ + outuint(c_sigma_delta, sw_pll.sdm_state.ctrl_mid_point); /* Send ctrl mid point to re-enable SDM and set to nominal mclk */ printstr("swpll int'd\n"); #endif break; @@ -792,7 +813,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_control(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, spdifCounters.receivedSamples); + do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, spdifCounters.receivedSamples); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); @@ -902,7 +923,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_control(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, adatCounters.receivedSamples); + do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, adatCounters.receivedSamples); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); From b242c545743c5fc5bb0811623d12fb4c1544633c Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 11:18:29 +0000 Subject: [PATCH 06/33] Reduce control loop rate to 100Hz --- lib_xua/src/core/clocking/clockgen.xc | 40 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index efedac00..3377b5c4 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -8,8 +8,6 @@ #include "xua_commands.h" #include "xua_clocking.h" -#include // TODO DEV ONLY - DELME - #ifdef __XS3A__ #define USE_SW_PLL 1 #else @@ -306,22 +304,35 @@ void do_sw_pll_phase_frequency_detector( sw_pll_state_t sw_pll, chanend c_sigma_delta, int receivedSamples) { + const unsigned control_loop_rate_divider = 6; /* 300Hz * 2 edges / 6 -> 100Hz loop rate */ + static unsigned control_loop_counter = 0; + static unsigned total_received_samples = 0; + /* Keep a store of the last mclk time stamp so we can work out the increment */ static unsigned short last_mclk_time_stamp = 0; - /* Calculate what the zero-error mclk count increment should be for this many samples */ - const unsigned expected_mclk_inc = mclks_per_sample * receivedSamples / 2; /* divide by 2 because this fn is called per edge */ + control_loop_counter++; - /* Calculate actualy time-stamped mclk count increment is */ - const unsigned short actual_mclk_inc = mclk_time_stamp - last_mclk_time_stamp; + total_received_samples += receivedSamples; - /* The difference is the raw error in terms of mclk counts */ - short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc; + if(control_loop_counter == control_loop_rate_divider) + { + /* Calculate what the zero-error mclk count increment should be for this many samples */ + const unsigned expected_mclk_inc = mclks_per_sample * total_received_samples / 2; /* divide by 2 because this fn is called per edge */ - /* send PFD output to the sigma delta thread */ - outuint(c_sigma_delta, (unsigned) (1000000 + f_error)); - - last_mclk_time_stamp = mclk_time_stamp; + /* Calculate actualy time-stamped mclk count increment is */ + const unsigned short actual_mclk_inc = mclk_time_stamp - last_mclk_time_stamp; + + /* The difference is the raw error in terms of mclk counts */ + short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc; + + /* send PFD output to the sigma delta thread */ + outuint(c_sigma_delta, (unsigned) (1000000 + f_error)); + + last_mclk_time_stamp = mclk_time_stamp; + control_loop_counter = 0; + total_received_samples = 0; + } } void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ @@ -354,7 +365,6 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ { case inuint_byref(c_sigma_delta, tmp): f_error = (int32_t)tmp; - printstr("sigma-delta got: "); printintln(f_error); unsafe { sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error + 1000000); @@ -539,7 +549,9 @@ void clockGen ( streaming chanend ?c_spdif_rx, chan c_sigma_delta; sw_pll_state_t sw_pll; - /* Initialise before we par off the SDM task */ + /* Initialise before we par off the SDM thread/ + We share the sw_pll struct across threads and this + us safe because the threads access different memebers */ unsafe { sw_pll_ptr = &sw_pll; From 8e161707a51ffa924a39d27a3e84cd1d34277ecc Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 11:25:41 +0000 Subject: [PATCH 07/33] Manually apply https://github.com/xmos/lib_xua/pull/359/files --- lib_xua/src/core/clocking/clockgen.xc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 3377b5c4..90f5dea2 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -978,7 +978,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, spdifSamps -= 2; /* spdifSamps could go to -1 */ - if(spdifSamps < 0) + if(spdifSamps <= 0) { /* We're out of S/PDIF samples, mark underflow condition */ spdifUnderflow = 1; @@ -1061,7 +1061,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, } /* adatSamps could go to -1 */ - if (adatSamps < 0) + if (adatSamps <= 0) { /* we're out of ADAT samples, mark underflow condition */ adatUnderflow = 1; From 56d728f349151402e3c4e9697f774475187597d1 Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 14:39:47 +0000 Subject: [PATCH 08/33] Fix PLL lock time 2s -> ~150ms --- lib_xua/src/core/clocking/clockgen.xc | 47 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index d70124fe..d67dcae1 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -236,6 +236,8 @@ unsafe sw_pll_state_t * unsafe sw_pll_ptr = NULL; } +#define DISABLE_SDM 0x1000000 /* Control value to disable SDM. Outside of normal range.*/ + unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) { /* Autogenerated SDM App PLL setup by dco_model.py using 22.5792_1M profile */ @@ -292,6 +294,8 @@ unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) sw_pll_sdm_ctrl_mid[clkIndex], 3000 /* PPM_RANGE (FOR PFD) Don't care for this API*/ ); + /* Reset SDM too */ + sw_pll_init_sigma_delta(&sw_pll.sdm_state); printstr("Init sw_pll: "); printuintln(mClk); @@ -302,7 +306,8 @@ void do_sw_pll_phase_frequency_detector( sw_pll_state_t sw_pll, unsigned short mclk_time_stamp, unsigned mclks_per_sample, chanend c_sigma_delta, - int receivedSamples) + int receivedSamples, + int &reset_sw_pll_pfd) { const unsigned control_loop_rate_divider = 6; /* 300Hz * 2 edges / 6 -> 100Hz loop rate */ static unsigned control_loop_counter = 0; @@ -325,9 +330,16 @@ void do_sw_pll_phase_frequency_detector( sw_pll_state_t sw_pll, /* The difference is the raw error in terms of mclk counts */ short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc; + if(reset_sw_pll_pfd) + { + f_error = 0; /* Skip first measurement as it will likely be very out */ + reset_sw_pll_pfd = 0; + } + printintln(f_error); + /* send PFD output to the sigma delta thread */ - outuint(c_sigma_delta, (unsigned) (1000000 + f_error)); + outuint(c_sigma_delta, (int) f_error); last_mclk_time_stamp = mclk_time_stamp; control_loop_counter = 0; @@ -344,7 +356,7 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ while(sw_pll_ptr == NULL); int f_error = 0; - int dco_setting = SW_PLL_SDM_CTRL_MID_24; // TODO FIX ME + int dco_setting = SW_PLL_SDM_CTRL_MID_24; // TODO Assume 24.576MHz? unsafe { sw_pll_init_sigma_delta(&sw_pll_ptr->sdm_state); @@ -364,11 +376,19 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ select { case inuint_byref(c_sigma_delta, tmp): - f_error = (int32_t)tmp; - unsafe + if(tmp == DISABLE_SDM) { - sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error + 1000000); - dco_setting = sw_pll_ptr->sdm_state.current_ctrl_val; + f_error = 0; + send_ack_once = 1; + } + else + { + f_error = (int32_t)tmp; + unsafe + { + sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error); + dco_setting = sw_pll_ptr->sdm_state.current_ctrl_val; + } } break; @@ -391,7 +411,7 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ control value. This will avoid the writing of the frac reg from two different threads which may cause a channel deadlock. */ - if(f_error != 0) + if(tmp != DISABLE_SDM) unsafe { sw_pll_do_sigma_delta(&sw_pll_ptr->sdm_state, this_tile, dco_setting); send_ack_once = 1; @@ -408,7 +428,7 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ void disable_sigma_delta(chanend c_sigma_delta) { - outuint(c_sigma_delta, 0); /* Stops SD */ + outuint(c_sigma_delta, DISABLE_SDM); /* Stops SDM */ inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */ } @@ -548,6 +568,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if USE_SW_PLL chan c_sigma_delta; sw_pll_state_t sw_pll; + int reset_sw_pll_pfd = 1; /* Initialise before we par off the SDM thread/ We share the sw_pll struct across threads and this @@ -741,7 +762,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, printstr("c_mclk_change: "); printuintln(selected_mclk_rate); disable_sigma_delta(c_sigma_delta); /* Blocks until SDM is idle */ InitSWPLL(sw_pll, selected_mclk_rate); - outuint(c_sigma_delta, sw_pll.sdm_state.ctrl_mid_point); /* Send ctrl mid point to re-enable SDM and set to nominal mclk */ + reset_sw_pll_pfd = 1; printstr("swpll int'd\n"); #endif break; @@ -825,7 +846,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, spdifCounters.receivedSamples); + do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, spdifCounters.receivedSamples, reset_sw_pll_pfd); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); @@ -935,7 +956,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, adatCounters.receivedSamples); + do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, adatCounters.receivedSamples, reset_sw_pll_pfd); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); @@ -982,7 +1003,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, { /* We're out of S/PDIF samples, mark underflow condition */ spdifUnderflow = 1; - printstr("spu-\n"); // DELME + printstr("spu+\n"); // DELME spdifLeft = 0; } From 4962cebc9c12682974d7b58538dc2f00691e92da Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 14:40:14 +0000 Subject: [PATCH 09/33] Comments only --- lib_xua/src/core/clocking/clockgen.xc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index d67dcae1..1a11bd88 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -352,11 +352,11 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ 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. */ - /* To be extra safe, spin on sw_pll_ptr until it has been initialised */ + /* To be extra safe, spin on sw_pll_ptr until it has been initialised by clockgen */ while(sw_pll_ptr == NULL); int f_error = 0; - int dco_setting = SW_PLL_SDM_CTRL_MID_24; // TODO Assume 24.576MHz? + int dco_setting = SW_PLL_SDM_CTRL_MID_24; // Assume 24.576MHz as initial clock unsafe { sw_pll_init_sigma_delta(&sw_pll_ptr->sdm_state); @@ -578,7 +578,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, sw_pll_ptr = &sw_pll; } - unsigned sdm_interval = InitSWPLL(sw_pll, MCLK_48); + unsigned sdm_interval = InitSWPLL(sw_pll, MCLK_48); // Assume 24.576MHz initial clock par { From a3419fdba78cb20a8a2c0baf8de888c12b77b5a1 Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 5 Jan 2024 16:18:38 +0000 Subject: [PATCH 10/33] Update USE_SW_PLL define usage --- lib_xua/src/core/clocking/clockgen.xc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 1a11bd88..1597a3ad 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -8,11 +8,15 @@ #include "xua_commands.h" #include "xua_clocking.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 +#endif /* __XS3A__ */ #if USE_SW_PLL extern "C" From dc81964f22b4a7b84212a729600e53e13b5447cd Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 8 Jan 2024 10:42:02 +0000 Subject: [PATCH 11/33] Add custom branch of sw_pll to xcommon cmake --- lib_xua/lib_build_info.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib_xua/lib_build_info.cmake b/lib_xua/lib_build_info.cmake index ee736f20..e130b355 100644 --- a/lib_xua/lib_build_info.cmake +++ b/lib_xua/lib_build_info.cmake @@ -25,7 +25,8 @@ set(LIB_DEPENDENT_MODULES "lib_locks" "lib_spdif" "lib_xassert" "lib_xud" - "lib_adat") + "lib_adat" + "lib_sw_pll(feature/fix_roundup)") set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm) From ccaaf40ab3c9ca766a387df7bceb6714e69f628e Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 8 Jan 2024 15:45:58 +0000 Subject: [PATCH 12/33] Ensure guarding for XS2 builds and fix clockgen race condition --- lib_xua/api/xua_clocking.h | 2 +- lib_xua/src/core/clocking/clockgen.xc | 29 ++++++++++++++++++--------- lib_xua/src/core/main.xc | 25 +++++++++++++---------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/lib_xua/api/xua_clocking.h b/lib_xua/api/xua_clocking.h index b3a74ab7..c29024d4 100644 --- a/lib_xua/api/xua_clocking.h +++ b/lib_xua/api/xua_clocking.h @@ -36,7 +36,7 @@ void clockGen( streaming chanend ?c_spdif_rx, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int, - port p_for_mclk_count_aud, + port ?p_for_mclk_count_aud, chanend c_mclk_change); #endif diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 1597a3ad..9ccd0e3f 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -373,21 +373,21 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ tmr :> time_trigger; int send_ack_once = 1; + unsigned rx_word; while(1) { /* Poll for new SDM control value */ - unsigned tmp; select { - case inuint_byref(c_sigma_delta, tmp): - if(tmp == DISABLE_SDM) + case inuint_byref(c_sigma_delta, rx_word): + if(rx_word == DISABLE_SDM) { f_error = 0; send_ack_once = 1; } else { - f_error = (int32_t)tmp; + f_error = (int32_t)rx_word; unsafe { sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error); @@ -415,7 +415,7 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ control value. This will avoid the writing of the frac reg from two different threads which may cause a channel deadlock. */ - if(tmp != DISABLE_SDM) + if(rx_word != DISABLE_SDM) unsafe { sw_pll_do_sigma_delta(&sw_pll_ptr->sdm_state, this_tile, dco_setting); send_ack_once = 1; @@ -454,7 +454,7 @@ 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, + port ?p_for_mclk_count_aud, chanend c_mclk_change) { timer t_local; @@ -477,7 +477,10 @@ void clockGen ( streaming chanend ?c_spdif_rx, unsigned mclks_per_sample = 0; unsigned short mclk_time_stamp = 0; /* Get MCLK count */ - asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); + if(!isnull(p_for_mclk_count_aud)) + { + asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); + } #endif #if (XUA_SPDIF_RX_EN) @@ -569,7 +572,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, /* Initial ref clock output and get timestamp */ i_pll_ref.init(); -#if USE_SW_PLL +#if (USE_SW_PLL && (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)) chan c_sigma_delta; sw_pll_state_t sw_pll; int reset_sw_pll_pfd = 1; @@ -777,7 +780,10 @@ void clockGen ( streaming chanend ?c_spdif_rx, case c_spdif_rx :> spdifRxData: /* Record time of sample */ - asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); + if(!isnull(p_for_mclk_count_aud)) + { + asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); + } t_local :> spdifRxTime; /* Check parity and ignore if bad */ @@ -866,7 +872,10 @@ void clockGen ( streaming chanend ?c_spdif_rx, /* receive sample from ADAT rx thread (streaming channel with CT_END) */ case inuint_byref(c_adat_rx, tmp): /* record time of sample */ - asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); + if(!isnull(p_for_mclk_count_aud)) + { + asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); + } t_local :> adatReceivedTime; /* Sync is: 1 | (user_byte << 4) */ diff --git a/lib_xua/src/core/main.xc b/lib_xua/src/core/main.xc index 3a82d1d3..b0c53e69 100755 --- a/lib_xua/src/core/main.xc +++ b/lib_xua/src/core/main.xc @@ -144,7 +144,11 @@ on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN; #if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) /* Reference to external clock multiplier */ on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; -on tile[AUDIO_IO_TILE] : port p_for_mclk_count_aud = PORT_MCLK_COUNT_2; +#ifdef __XS3A__ +on tile[AUDIO_IO_TILE] : port p_for_mclk_count_audio = PORT_MCLK_COUNT_2; +#else /* __XS3A__ */ +#define p_for_mclk_count_audio null +#endif /* __XS3A__ */ #endif #ifdef MIDI @@ -310,7 +314,7 @@ 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 + , port ?p_for_mclk_count_aud #endif ) { @@ -322,15 +326,14 @@ void usb_audio_io(chanend ?c_aud_in, chan c_dig_rx; chan c_mclk_change; /* Notification of new mclk freq to clockgen */ - /* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/ - 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)); - -#else - #define c_dig_rx null -#endif + 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_SPDIF_RX_EN || XUA_ADAT_RX_EN) */ #if (XUA_NUM_PDM_MICS > 0) && (PDM_TILE == AUDIO_IO_TILE) /* Configure clocks ports - sharing mclk port with I2S */ @@ -613,7 +616,7 @@ int main() #endif #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) , i_pll_ref - , p_for_mclk_count_aud + , p_for_mclk_count_audio #endif ); } From 3003ce7241c6ada9935100f6db4abdb1e3a41d92 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 08:56:58 +0000 Subject: [PATCH 13/33] Refactor sw_pll code into own source file --- lib_xua/src/core/clocking/clockgen.xc | 231 +------------------- lib_xua/src/core/clocking/sw_pll_wrapper.h | 58 +++++ lib_xua/src/core/clocking/sw_pll_wrapper.xc | 215 ++++++++++++++++++ 3 files changed, 285 insertions(+), 219 deletions(-) create mode 100644 lib_xua/src/core/clocking/sw_pll_wrapper.h create mode 100644 lib_xua/src/core/clocking/sw_pll_wrapper.xc diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 9ccd0e3f..da5f2711 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -7,23 +7,7 @@ #include "xua.h" #include "xua_commands.h" #include "xua_clocking.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__ */ - -#if USE_SW_PLL -extern "C" -{ - #include "sw_pll.h" -} -#endif +#include "sw_pll_wrapper.h" #if (XUA_SPDIF_RX_EN) #include "spdif.h" @@ -235,206 +219,7 @@ static inline int validSamples(Counter &counter, int clockIndex) #if USE_SW_PLL /* Pointer to sw_pll struct to allow it to be used in separate SDM thread */ -unsafe -{ - sw_pll_state_t * unsafe sw_pll_ptr = NULL; -} - -#define DISABLE_SDM 0x1000000 /* Control value to disable SDM. Outside of normal range.*/ - -unsigned InitSWPLL(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 - F: 134 - R: 0 - f: 8 - p: 18 - OD: 5 - ACD: 5 - */ - - #define APP_PLL_CTL_REG_22 0x0A808600 - #define APP_PLL_DIV_REG_22 0x80000005 - #define APP_PLL_FRAC_REG_22 0x80000812 - #define SW_PLL_SDM_CTRL_MID_22 498283 - #define SW_PLL_SDM_RATE_22 1000000 - - /* Autogenerated SDM App PLL setup by dco_model.py using 24.576_1M profile */ - /* Input freq: 24000000 - F: 146 - R: 0 - f: 4 - p: 10 - OD: 5 - ACD: 5 - */ - - #define APP_PLL_CTL_REG_24 0x0A809200 - #define APP_PLL_DIV_REG_24 0x80000005 - #define APP_PLL_FRAC_REG_24 0x8000040A - #define SW_PLL_SDM_CTRL_MID_24 478151 - #define SW_PLL_SDM_RATE_24 1000000 - - - const uint32_t app_pll_ctl_reg[2] = {APP_PLL_CTL_REG_22, APP_PLL_CTL_REG_24}; - const uint32_t app_pll_div_reg[2] = {APP_PLL_DIV_REG_22, APP_PLL_DIV_REG_24}; - const uint32_t app_pll_frac_reg[2] = {APP_PLL_FRAC_REG_22, APP_PLL_FRAC_REG_24}; - const uint32_t sw_pll_sdm_ctrl_mid[2] = {SW_PLL_SDM_CTRL_MID_22, SW_PLL_SDM_CTRL_MID_24}; - const uint32_t sw_pll_sdm_rate[2] = {SW_PLL_SDM_RATE_22, SW_PLL_SDM_RATE_24}; - - const int clkIndex = mClk == MCLK_48 ? 1 : 0; - - sw_pll_sdm_init(&sw_pll, - SW_PLL_15Q16(0.0), - SW_PLL_15Q16(32.0), - SW_PLL_15Q16(0.25), - 0, /* LOOP COUNT Don't care for this API */ - 0, /* PLL_RATIO Don't care for this API */ - 0, /* No jitter compensation needed */ - app_pll_ctl_reg[clkIndex], - app_pll_div_reg[clkIndex], - app_pll_frac_reg[clkIndex], - sw_pll_sdm_ctrl_mid[clkIndex], - 3000 /* PPM_RANGE (FOR PFD) Don't care for this API*/ ); - - /* Reset SDM too */ - sw_pll_init_sigma_delta(&sw_pll.sdm_state); - - printstr("Init sw_pll: "); printuintln(mClk); - - return (XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex]); -} - -void do_sw_pll_phase_frequency_detector( sw_pll_state_t sw_pll, - unsigned short mclk_time_stamp, - unsigned mclks_per_sample, - chanend c_sigma_delta, - int receivedSamples, - int &reset_sw_pll_pfd) -{ - const unsigned control_loop_rate_divider = 6; /* 300Hz * 2 edges / 6 -> 100Hz loop rate */ - static unsigned control_loop_counter = 0; - static unsigned total_received_samples = 0; - - /* Keep a store of the last mclk time stamp so we can work out the increment */ - static unsigned short last_mclk_time_stamp = 0; - - control_loop_counter++; - - total_received_samples += receivedSamples; - - if(control_loop_counter == control_loop_rate_divider) - { - /* Calculate what the zero-error mclk count increment should be for this many samples */ - const unsigned expected_mclk_inc = mclks_per_sample * total_received_samples / 2; /* divide by 2 because this fn is called per edge */ - - /* Calculate actualy time-stamped mclk count increment is */ - const unsigned short actual_mclk_inc = mclk_time_stamp - last_mclk_time_stamp; - - /* The difference is the raw error in terms of mclk counts */ - short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc; - if(reset_sw_pll_pfd) - { - f_error = 0; /* Skip first measurement as it will likely be very out */ - reset_sw_pll_pfd = 0; - } - printintln(f_error); - - - /* send PFD output to the sigma delta thread */ - outuint(c_sigma_delta, (int) f_error); - - last_mclk_time_stamp = mclk_time_stamp; - control_loop_counter = 0; - total_received_samples = 0; - } -} - -void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ - /* 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. */ - - /* To be extra safe, spin on sw_pll_ptr until it has been initialised by clockgen */ - while(sw_pll_ptr == NULL); - - int f_error = 0; - int dco_setting = SW_PLL_SDM_CTRL_MID_24; // Assume 24.576MHz as initial clock - unsafe - { - sw_pll_init_sigma_delta(&sw_pll_ptr->sdm_state); - } - - tileref_t this_tile = get_local_tile_id(); - - timer tmr; - int32_t time_trigger; - tmr :> time_trigger; - int send_ack_once = 1; - - unsigned rx_word; - while(1) - { - /* Poll for new SDM control value */ - select - { - case inuint_byref(c_sigma_delta, rx_word): - if(rx_word == DISABLE_SDM) - { - f_error = 0; - send_ack_once = 1; - } - else - { - f_error = (int32_t)rx_word; - unsafe - { - sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error); - dco_setting = sw_pll_ptr->sdm_state.current_ctrl_val; - } - } - break; - - // Do nothing & fall-through - default: - break; - } - - /* Wait until the timer value has been reached - This implements a timing barrier and keeps - the loop rate constant. */ - select - { - case tmr when timerafter(time_trigger) :> int _: - time_trigger += sdm_interval; - break; - } - - /* Do not write to the frac reg until we get out first - control value. This will avoid the writing of the - frac reg from two different threads which may cause - a channel deadlock. */ - if(rx_word != DISABLE_SDM) - unsafe { - sw_pll_do_sigma_delta(&sw_pll_ptr->sdm_state, this_tile, dco_setting); - send_ack_once = 1; - } - else if(send_ack_once) - { - /* Send ACK once to synchrnoise with clockgen signalling it's OK to reconfig */ - outuint(c_sigma_delta, 0); /* Send ACK to say reg writes have ceased */ - send_ack_once = 0; - } - } -} - - -void disable_sigma_delta(chanend c_sigma_delta) -{ - outuint(c_sigma_delta, DISABLE_SDM); /* Stops SDM */ - inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */ -} +extern sw_pll_state_t * unsafe sw_pll_ptr; #endif @@ -856,7 +641,11 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, spdifCounters.receivedSamples, reset_sw_pll_pfd); + do_sw_pll_phase_frequency_detector_dig_rx( mclk_time_stamp, + mclks_per_sample, + c_sigma_delta, + spdifCounters.receivedSamples, + reset_sw_pll_pfd); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); @@ -969,7 +758,11 @@ void clockGen ( streaming chanend ?c_spdif_rx, timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; #if USE_SW_PLL - do_sw_pll_phase_frequency_detector(sw_pll, mclk_time_stamp, mclks_per_sample, c_sigma_delta, adatCounters.receivedSamples, reset_sw_pll_pfd); + do_sw_pll_phase_frequency_detector_dig_rx( mclk_time_stamp, + mclks_per_sample, + c_sigma_delta, + adatCounters.receivedSamples, + reset_sw_pll_pfd); #else /* Toggle edge */ i_pll_ref.toggle_timed(1); diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.h b/lib_xua/src/core/clocking/sw_pll_wrapper.h new file mode 100644 index 00000000..74e1d51d --- /dev/null +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.h @@ -0,0 +1,58 @@ +// Copyright 2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* 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__ */ + + +extern "C" +{ + #include "sw_pll.h" +} + +/* Special control value to disable SDM. Outside of normal range which is less than 16b.*/ +#define DISABLE_SDM 0x1000000 + + +/** 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 sdm_interval Unisgned value containing the sigma delta period in timer ticks. + */ +void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval); + +/** Helper function that sends a special disable command and waits for ACK. This is used + * to help prevemt simultaenous access to the PLL register from two different threads, + * + * \param c_sigma_delta Channel connected to the clocking thread to pass raw error terms. + */ +void disable_sigma_delta(chanend c_sigma_delta); + +/** 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 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, + int receivedSamples, + int &reset_sw_pll_pfd); + +/** Initilaises the software PLL both hardware and state. Sets the mclk frequency to a nominal point. + * + * \param sw_pll Reference to a software pll state struct to be initialised. + * \param mClk The current nominal mClk frequency. + */ +unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk); diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc new file mode 100644 index 00000000..3ac482a2 --- /dev/null +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -0,0 +1,215 @@ +// Copyright 2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include + +#include "sw_pll_wrapper.h" + +#if USE_SW_PLL +extern "C" +{ + #include "sw_pll.h" +} + +/* Pointer to sw_pll struct to allow it to be used in separate SDM thread */ +unsafe +{ + sw_pll_state_t * unsafe sw_pll_ptr = NULL; +} + + +unsigned InitSWPLL(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 + F: 134 + R: 0 + f: 8 + p: 18 + OD: 5 + ACD: 5 + */ + + #define APP_PLL_CTL_REG_22 0x0A808600 + #define APP_PLL_DIV_REG_22 0x80000005 + #define APP_PLL_FRAC_REG_22 0x80000812 + #define SW_PLL_SDM_CTRL_MID_22 498283 + #define SW_PLL_SDM_RATE_22 1000000 + + /* Autogenerated SDM App PLL setup by dco_model.py using 24.576_1M profile */ + /* Input freq: 24000000 + F: 146 + R: 0 + f: 4 + p: 10 + OD: 5 + ACD: 5 + */ + + #define APP_PLL_CTL_REG_24 0x0A809200 + #define APP_PLL_DIV_REG_24 0x80000005 + #define APP_PLL_FRAC_REG_24 0x8000040A + #define SW_PLL_SDM_CTRL_MID_24 478151 + #define SW_PLL_SDM_RATE_24 1000000 + + + const uint32_t app_pll_ctl_reg[2] = {APP_PLL_CTL_REG_22, APP_PLL_CTL_REG_24}; + const uint32_t app_pll_div_reg[2] = {APP_PLL_DIV_REG_22, APP_PLL_DIV_REG_24}; + const uint32_t app_pll_frac_reg[2] = {APP_PLL_FRAC_REG_22, APP_PLL_FRAC_REG_24}; + const uint32_t sw_pll_sdm_ctrl_mid[2] = {SW_PLL_SDM_CTRL_MID_22, SW_PLL_SDM_CTRL_MID_24}; + const uint32_t sw_pll_sdm_rate[2] = {SW_PLL_SDM_RATE_22, SW_PLL_SDM_RATE_24}; + + const int clkIndex = mClk == MCLK_48 ? 1 : 0; + + sw_pll_sdm_init(&sw_pll, + SW_PLL_15Q16(0.0), + SW_PLL_15Q16(32.0), + SW_PLL_15Q16(0.25), + 0, /* LOOP COUNT Don't care for this API */ + 0, /* PLL_RATIO Don't care for this API */ + 0, /* No jitter compensation needed */ + app_pll_ctl_reg[clkIndex], + app_pll_div_reg[clkIndex], + app_pll_frac_reg[clkIndex], + sw_pll_sdm_ctrl_mid[clkIndex], + 3000 /* PPM_RANGE (FOR PFD) Don't care for this API*/ ); + + /* Reset SDM too */ + sw_pll_init_sigma_delta(&sw_pll.sdm_state); + + printstr("Init sw_pll: "); printuintln(mClk); + + return (XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex]); +} + +void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, + unsigned mclks_per_sample, + chanend c_sigma_delta, + int receivedSamples, + int &reset_sw_pll_pfd) +{ + const unsigned control_loop_rate_divider = 6; /* 300Hz * 2 edges / 6 -> 100Hz loop rate */ + static unsigned control_loop_counter = 0; + static unsigned total_received_samples = 0; + + /* Keep a store of the last mclk time stamp so we can work out the increment */ + static unsigned short last_mclk_time_stamp = 0; + + control_loop_counter++; + + total_received_samples += receivedSamples; + + if(control_loop_counter == control_loop_rate_divider) + { + /* Calculate what the zero-error mclk count increment should be for this many samples */ + const unsigned expected_mclk_inc = mclks_per_sample * total_received_samples / 2; /* divide by 2 because this fn is called per edge */ + + /* Calculate actualy time-stamped mclk count increment is */ + const unsigned short actual_mclk_inc = mclk_time_stamp - last_mclk_time_stamp; + + /* The difference is the raw error in terms of mclk counts */ + short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc; + if(reset_sw_pll_pfd) + { + f_error = 0; /* Skip first measurement as it will likely be very out */ + reset_sw_pll_pfd = 0; + } + printintln(f_error); + + + /* send PFD output to the sigma delta thread */ + outuint(c_sigma_delta, (int) f_error); + + last_mclk_time_stamp = mclk_time_stamp; + control_loop_counter = 0; + total_received_samples = 0; + } +} + +void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ + /* 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. */ + + /* To be extra safe, spin on sw_pll_ptr until it has been initialised by clockgen */ + while(sw_pll_ptr == NULL); + + int f_error = 0; + int dco_setting = SW_PLL_SDM_CTRL_MID_24; // Assume 24.576MHz as initial clock + unsafe + { + sw_pll_init_sigma_delta(&sw_pll_ptr->sdm_state); + } + + tileref_t this_tile = get_local_tile_id(); + + timer tmr; + int32_t time_trigger; + tmr :> time_trigger; + int send_ack_once = 1; + + unsigned rx_word; + while(1) + { + /* Poll for new SDM control value */ + select + { + case inuint_byref(c_sigma_delta, rx_word): + if(rx_word == DISABLE_SDM) + { + f_error = 0; + send_ack_once = 1; + } + else + { + f_error = (int32_t)rx_word; + unsafe + { + sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error); + dco_setting = sw_pll_ptr->sdm_state.current_ctrl_val; + } + } + break; + + // Do nothing & fall-through + default: + break; + } + + /* Wait until the timer value has been reached + This implements a timing barrier and keeps + the loop rate constant. */ + select + { + case tmr when timerafter(time_trigger) :> int _: + time_trigger += sdm_interval; + break; + } + + /* Do not write to the frac reg until we get out first + control value. This will avoid the writing of the + frac reg from two different threads which may cause + a channel deadlock. */ + if(rx_word != DISABLE_SDM) + unsafe { + sw_pll_do_sigma_delta(&sw_pll_ptr->sdm_state, this_tile, dco_setting); + send_ack_once = 1; + } + else if(send_ack_once) + { + /* Send ACK once to synchrnoise with clockgen signalling it's OK to reconfig */ + outuint(c_sigma_delta, 0); /* Send ACK to say reg writes have ceased */ + send_ack_once = 0; + } + } +} + + +void disable_sigma_delta(chanend c_sigma_delta) +{ + outuint(c_sigma_delta, DISABLE_SDM); /* Stops SDM */ + inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */ +} + +#endif From 87a105d8f6c9aaf4888fabc4e627249f6036714b Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 09:10:14 +0000 Subject: [PATCH 14/33] Tidy defines --- lib_xua/src/core/clocking/sw_pll_wrapper.h | 8 +++++++- lib_xua/src/core/clocking/sw_pll_wrapper.xc | 7 ++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.h b/lib_xua/src/core/clocking/sw_pll_wrapper.h index 74e1d51d..7b4e0236 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.h +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.h @@ -1,6 +1,9 @@ // Copyright 2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. +#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__ @@ -11,7 +14,7 @@ #define USE_SW_PLL 0 #endif /* __XS3A__ */ - +#if USE_SW_PLL extern "C" { #include "sw_pll.h" @@ -56,3 +59,6 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, * \param mClk The current nominal mClk frequency. */ unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk); + +#endif /* USE_SW_PLL */ +#endif /* _SW_PLL_WRAPPPER_H_ */ \ No newline at end of file diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc index 3ac482a2..527016ac 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.xc +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -5,12 +5,9 @@ #include #include "sw_pll_wrapper.h" +#include "xua.h" #if USE_SW_PLL -extern "C" -{ - #include "sw_pll.h" -} /* Pointer to sw_pll struct to allow it to be used in separate SDM thread */ unsafe @@ -212,4 +209,4 @@ void disable_sigma_delta(chanend c_sigma_delta) inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */ } -#endif +#endif /* USE_SW_PLL */ From e6899afbb9ce167b33a97eff589cd526bd5a4b09 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 10:36:14 +0000 Subject: [PATCH 15/33] Move sw_pll on to 2.1.0 and develop now fixes merged --- lib_xua/lib_build_info.cmake | 2 +- lib_xua/module_build_info | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_xua/lib_build_info.cmake b/lib_xua/lib_build_info.cmake index e130b355..fe37f3b3 100644 --- a/lib_xua/lib_build_info.cmake +++ b/lib_xua/lib_build_info.cmake @@ -26,7 +26,7 @@ set(LIB_DEPENDENT_MODULES "lib_locks" "lib_xassert" "lib_xud" "lib_adat" - "lib_sw_pll(feature/fix_roundup)") + "lib_sw_pll(develop)") set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm) diff --git a/lib_xua/module_build_info b/lib_xua/module_build_info index 25f5c719..444c67ce 100644 --- a/lib_xua/module_build_info +++ b/lib_xua/module_build_info @@ -15,7 +15,7 @@ DEPENDENT_MODULES = lib_locks(>=2.1.0) \ lib_xassert(>=4.1.0) \ lib_xud(>=2.2.3) \ lib_adat(>=1.0.0) \ - lib_sw_pll(>=2.0.1) + lib_sw_pll(>=2.1.0) MODULE_XCC_FLAGS = $(XCC_FLAGS) \ -O3 \ From 23f1a8d48e671efbaf7ff25d29dd1ac9a5723e72 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 13:02:57 +0000 Subject: [PATCH 16/33] Fix race condition when changing SR when audio got misaligned --- lib_xua/src/core/audiohub/xua_audiohub.xc | 1 + lib_xua/src/core/clocking/clockgen.xc | 1 + 2 files changed, 2 insertions(+) diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index c6d53cfc..9388cbf2 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -805,6 +805,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, /* Notify clockgen of new mCLk */ c_mclk_change <: mClk; c_mclk_change <: curFreq; + c_mclk_change :> int _; /* Acknowledge when clocks all setup */ #endif } diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index da5f2711..14517439 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -556,6 +556,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, InitSWPLL(sw_pll, selected_mclk_rate); reset_sw_pll_pfd = 1; printstr("swpll int'd\n"); + c_mclk_change <: 0; /* Acknowledge to hold off starting audio until done */ #endif break; #endif From 6d8d66f8237cee54013d27cd6623b275039d7be5 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 13:23:16 +0000 Subject: [PATCH 17/33] Changelog entry --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3ffb3cf7..3fe8179c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,7 @@ HEAD * RESOLVED: Repeated old S/PDIF and ADAT samples when entering underflow state * 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 application PLL for clock recovery 3.5.1 ----- From 2fcc9ca2ac48e138b5f19913f68b9dc10a935782 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 13:29:53 +0000 Subject: [PATCH 18/33] Add sw_pll dep in changelog --- CHANGELOG.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3fe8179c..6e641d9d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,7 +13,12 @@ HEAD * RESOLVED: Repeated old S/PDIF and ADAT samples when entering underflow state * 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 application PLL for clock recovery + * CHANGED: xcore-ai targets use sigma-delta software PLL for clock recovery of + digital Rx streams by default. + + * Changes to dependencies: + + - lib_sw_pll: Added dependency 2.1.0 3.5.1 ----- From 91d23fb1d60ed906373fa6df275f7823935654a5 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 13:32:41 +0000 Subject: [PATCH 19/33] Remove debug prints --- lib_xua/src/core/clocking/clockgen.xc | 6 ------ lib_xua/src/core/clocking/sw_pll_wrapper.xc | 4 ---- 2 files changed, 10 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 14517439..d3323a18 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -551,11 +551,9 @@ void clockGen ( streaming chanend ?c_spdif_rx, c_mclk_change :> selected_sample_rate; mclks_per_sample = selected_mclk_rate / selected_sample_rate; #if USE_SW_PLL - printstr("c_mclk_change: "); printuintln(selected_mclk_rate); disable_sigma_delta(c_sigma_delta); /* Blocks until SDM is idle */ InitSWPLL(sw_pll, selected_mclk_rate); reset_sw_pll_pfd = 1; - printstr("swpll int'd\n"); c_mclk_change <: 0; /* Acknowledge to hold off starting audio until done */ #endif break; @@ -605,14 +603,12 @@ void clockGen ( streaming chanend ?c_spdif_rx, if(spdifSamps > MAX_SPDIF_SAMPLES-1) { spdifOverflow = 1; - printstr("spo+\n"); // DELME } /* Check for coming out of under flow */ if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1))) { spdifUnderflow = 0; - printstr("spu-\n"); // DELME } } break; @@ -810,7 +806,6 @@ void clockGen ( streaming chanend ?c_spdif_rx, { /* We're out of S/PDIF samples, mark underflow condition */ spdifUnderflow = 1; - printstr("spu+\n"); // DELME spdifLeft = 0; } @@ -819,7 +814,6 @@ void clockGen ( streaming chanend ?c_spdif_rx, if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1))) { spdifOverflow = 0; - printstr("spo-\n"); // DELME } } #endif diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc index 527016ac..9345e89e 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.xc +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -75,8 +75,6 @@ unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) /* Reset SDM too */ sw_pll_init_sigma_delta(&sw_pll.sdm_state); - printstr("Init sw_pll: "); printuintln(mClk); - return (XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex]); } @@ -112,8 +110,6 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, f_error = 0; /* Skip first measurement as it will likely be very out */ reset_sw_pll_pfd = 0; } - printintln(f_error); - /* send PFD output to the sigma delta thread */ outuint(c_sigma_delta, (int) f_error); From 3291a054937f7d3ca0f1e33d07b0c8d61b52f59a Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 14:49:48 +0000 Subject: [PATCH 20/33] Remove dead code + warning --- lib_xua/src/core/clocking/clockgen.xc | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index d3323a18..20608c9a 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -89,27 +89,10 @@ static int abs(int x) return x; } -static int channelContainsControlToken(chanend x) -{ - unsigned char tmpc; - - select - { - case inct_byref(x, tmpc): - return 1; - default: - return 0; - } -} - static void outInterrupt(chanend c_interruptControl, int value) { - /* Non-blocking check for control token */ - //if (channelContainsControlToken(c_interruptControl)) - { - outuint(c_interruptControl, value); - outct(c_interruptControl, XS1_CT_END); - } + outuint(c_interruptControl, value); + outct(c_interruptControl, XS1_CT_END); } #endif From c6a970d7c0e23ead0706aac0ccf76ba2b879c0b7 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 16:02:07 +0000 Subject: [PATCH 21/33] Fix guarding on clkgen ACK --- lib_xua/src/core/clocking/clockgen.xc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 20608c9a..d6e31e42 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -537,8 +537,8 @@ void clockGen ( streaming chanend ?c_spdif_rx, disable_sigma_delta(c_sigma_delta); /* Blocks until SDM is idle */ InitSWPLL(sw_pll, selected_mclk_rate); reset_sw_pll_pfd = 1; - c_mclk_change <: 0; /* Acknowledge to hold off starting audio until done */ #endif + c_mclk_change <: 0; /* Acknowledge to hold off starting audio until done */ break; #endif From 9abfa167cac35201f41b19215011aad4ae197bc2 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 17:28:55 +0000 Subject: [PATCH 22/33] Initial documentation covering sw_pll --- lib_xua/doc/rst/arch.rst | 4 +++- lib_xua/doc/rst/feat_spdif_rx.rst | 7 ++++--- lib_xua/doc/rst/hw.rst | 4 ++-- lib_xua/doc/rst/opt_spdif_rx.rst | 4 ++-- lib_xua/doc/rst/sw_clocking.rst | 19 ++++++++++--------- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/lib_xua/doc/rst/arch.rst b/lib_xua/doc/rst/arch.rst index 448fa55a..86e02f83 100644 --- a/lib_xua/doc/rst/arch.rst +++ b/lib_xua/doc/rst/arch.rst @@ -54,7 +54,9 @@ In addition :ref:`usb_audio_optional_components` shows optional components that * - Clockgen - Drives an external frequency generator (PLL) and manages changes between internal clocks and external clocks arising - from digital input. + from digital input. on xCORE-AI Clockgen may also work in + conjunction with lib_sw_pll to produce a local clock from + the XCORE which is locked to the incoming digital stream. * - MIDI - Outputs and inputs MIDI over a serial UART interface. diff --git a/lib_xua/doc/rst/feat_spdif_rx.rst b/lib_xua/doc/rst/feat_spdif_rx.rst index 0753371d..28099cd5 100644 --- a/lib_xua/doc/rst/feat_spdif_rx.rst +++ b/lib_xua/doc/rst/feat_spdif_rx.rst @@ -29,10 +29,11 @@ The S/PDIF receiver should be called on the appropriate tile:: With the steps above an S/PDIF stream can be captured by the xCORE. To be functionally useful the audio master clock must be able to synchronise to this external digital stream. Additionally, the host can be notified regarding changes in the validity of this stream, it's frequency etc. To synchronise to external -streams the codebase assumes the use of an external Cirrus Logic CS2100 device. +streams the codebase assumes the use of an external Cirrus Logic CS2100 device or lib_sw_pll on xCORE-AI designs. -The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device and also handles -recording of clock validity etc. See :ref:`usb_audio_sec_clock_recovery` for full details regarding ``ClockGen()``. +The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device or timing information +to lib_sw_pll and also handles recording of clock validity etc. +See :ref:`usb_audio_sec_clock_recovery` for full details regarding ``ClockGen()``. It also provides a small FIFO for S/PDIF samples before they are forwarded to the ``AudioHub`` core. As such it requires to be inserted in the communication path between the S/PDIF receiver and the diff --git a/lib_xua/doc/rst/hw.rst b/lib_xua/doc/rst/hw.rst index b9aab949..500695c9 100644 --- a/lib_xua/doc/rst/hw.rst +++ b/lib_xua/doc/rst/hw.rst @@ -52,11 +52,11 @@ Three methods of generating an audio master clock are provided on the board: * A Skyworks Si5351B PLL device. The Si5351 is an I2C configurable clock generator that is ideally suited for replacing crystals, crystal oscillators, VCXOs, phase-locked loops (PLLs), and fanout buffers. - * xCORE.ai devices are equipped with a secondary (or 'application') PLL which can be used to generate audio clocks + * xCORE.ai devices are equipped with a secondary (or 'application') PLL which can be used to generate fixed audio clocks or recover external clocks using lib_sw_pll. Selection between these methods is done via writing to bits 6 and 7 of PORT 8D on tile[0]. -Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xCORE-200, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`. +Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xCORE-AI, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`. .. _hw_316_ctrlport: diff --git a/lib_xua/doc/rst/opt_spdif_rx.rst b/lib_xua/doc/rst/opt_spdif_rx.rst index 1000ef20..5e5031aa 100644 --- a/lib_xua/doc/rst/opt_spdif_rx.rst +++ b/lib_xua/doc/rst/opt_spdif_rx.rst @@ -33,8 +33,8 @@ This must be a 1-bit port, for example:: -When S/PDIF receive is enabled the codebase expects to drive a synchronisation signal to an external -Cirrus Logic CS2100 device for master-clock generation. +When S/PDIF receive is enabled the codebase expects to either drive a synchronisation signal to an external +Cirrus Logic CS2100 device or use lib_swp_pll (xCORE-AI only) for master-clock generation. The programmer should ensure the define in :ref:`opt_spdif_rx_ref_defines` is set appropriately. diff --git a/lib_xua/doc/rst/sw_clocking.rst b/lib_xua/doc/rst/sw_clocking.rst index 65880cb5..0bc395a6 100755 --- a/lib_xua/doc/rst/sw_clocking.rst +++ b/lib_xua/doc/rst/sw_clocking.rst @@ -15,17 +15,18 @@ the xCORE. Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external clock source from a digital stream (e.g. S/PDIF or ADAT input). The codebase supports the Cirrus -Logic CS2100 device for this purpose. Other devices may be supported via code modification. +Logic CS2100 device or use of lib_sw_pll (xCORE-AI only) for this purpose. Other devices may be +supported via code modification. -.. note:: - - It is expected that in a future release the secondary PLL in xCORE.ai devices, coupled with - associated software changes, will be capable of replacing the CS2100 part for most designs. +The Clock Recovery core (Clock Gen) is responsible for either generating the reference frequency +to the CS2100 device or driving lib_sw_pll from time measurements based on the local master clock +and the time of received samples. Clock Gen (via CS2100 or lib_sw_pll) generates the master clock +used over the whole design. This core also serves as a smaller buffer between ADAT and S/PDIF +receiving cores and the Audio Hub core. -The Clock Recovery core (Clock Gen) is responsible for generating the reference frequency -to the CS2100 device. This, in turn, generates the master clock used over the whole design. -This core also serves as a smaller buffer between ADAT and S/PDIF receiving cores and the Audio Hub -core. +When using lib_sw_pll (xCORE-AI only) an further core is instantiated which performs the sigma-delta +modulation of the xCORE PLL to ensure the lowest jitter over the audio band. See lib_sw_pll +documentation for further details. When running in *Internal Clock* mode this core simply generates this clock using a local timer, based on the XMOS reference clock. From c412c81fe3db5e9ced2a5bc3b2ca02642fc86a85 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 9 Jan 2024 17:40:54 +0000 Subject: [PATCH 23/33] Additional docs update for pll --- lib_xua/doc/rst/sw_clocking.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib_xua/doc/rst/sw_clocking.rst b/lib_xua/doc/rst/sw_clocking.rst index 0bc395a6..5fcbbbc5 100755 --- a/lib_xua/doc/rst/sw_clocking.rst +++ b/lib_xua/doc/rst/sw_clocking.rst @@ -32,16 +32,18 @@ When running in *Internal Clock* mode this core simply generates this clock usin timer, based on the XMOS reference clock. When running in an external clock mode (i.e. S/PDIF Clock" or "ADAT Clock" mode) samples are -received from the S/PDIF and/or ADAT receive core. The external frequency is calculated through -counting samples in a given period. The reference clock to the CS2100 is then generated based on -the reception of these samples. +received from the S/PDIF and/or ADAT receive core. The external frequency is calculated through +counting samples in a given period. Either the reference clock to the CS2100 is then generated based on +the reception of these samples or the timing information is provided to lib_sw_pll to generate +the phase-locked clock on-chip (xCORE-AI only). If an external stream becomes invalid, the *Internal Clock* timer event will fire to ensure that valid master clock generation continues regardless of cable unplugs etc. Efforts are made to ensure the transition between these clocks are relatively seamless. Additionally efforts are also -made to try and keep the jitter on the reference clock as low as possibly, regardless of activity +made to try and keep the jitter on the reference clock as low as possible, regardless of activity level of the Clock Gen core. The is achieved though the use of port times to schedule pin toggling -rather than directly outputting to the port. +rather than directly outputting to the port in the case of using the CS2100. For lib_sw_pll cases the +last setting is kept for the sigma-delta modulator ensuring clock continuity. The Clock Gen core gets clock selection Get/Set commands from Endpoint 0 via the ``c_clk_ctl`` channel. This core also records the validity of external clocks, which is also queried From 334f36e5e11cd0db977f5bac110348ea79160ea4 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 10 Jan 2024 09:40:47 +0000 Subject: [PATCH 24/33] Doc typo fixed --- lib_xua/doc/rst/arch.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_xua/doc/rst/arch.rst b/lib_xua/doc/rst/arch.rst index 86e02f83..adc6960c 100644 --- a/lib_xua/doc/rst/arch.rst +++ b/lib_xua/doc/rst/arch.rst @@ -54,7 +54,7 @@ In addition :ref:`usb_audio_optional_components` shows optional components that * - Clockgen - Drives an external frequency generator (PLL) and manages changes between internal clocks and external clocks arising - from digital input. on xCORE-AI Clockgen may also work in + from digital input. On xCORE-AI Clockgen may also work in conjunction with lib_sw_pll to produce a local clock from the XCORE which is locked to the incoming digital stream. * - MIDI From a0610fc1e06370b87d8d4e0061b051727d235b7e Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 10 Jan 2024 09:53:45 +0000 Subject: [PATCH 25/33] Fix copyright dates --- lib_xua/api/xua_audiohub.h | 2 +- lib_xua/api/xua_clocking.h | 2 +- lib_xua/src/core/audiohub/xua_audiohub.xc | 2 +- lib_xua/src/core/main.xc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib_xua/api/xua_audiohub.h b/lib_xua/api/xua_audiohub.h index 6682676f..8fa29eef 100644 --- a/lib_xua/api/xua_audiohub.h +++ b/lib_xua/api/xua_audiohub.h @@ -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. #ifndef _XUA_AUDIOHUB_H_ #define _XUA_AUDIOHUB_H_ diff --git a/lib_xua/api/xua_clocking.h b/lib_xua/api/xua_clocking.h index c29024d4..8a9f8b83 100644 --- a/lib_xua/api/xua_clocking.h +++ b/lib_xua/api/xua_clocking.h @@ -1,4 +1,4 @@ -// 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 _CLOCKING_H_ diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index 9388cbf2..fdad29ca 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -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. /** * @file xua_audiohub.xc diff --git a/lib_xua/src/core/main.xc b/lib_xua/src/core/main.xc index b0c53e69..c4403acb 100755 --- a/lib_xua/src/core/main.xc +++ b/lib_xua/src/core/main.xc @@ -1,4 +1,4 @@ -// Copyright 2012-2023 XMOS LIMITED. +// Copyright 2012-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include "xua.h" /* Device specific defines */ From 13d9229f527c54dd321e89d8913b2f050bf3ecc4 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 10 Jan 2024 09:57:18 +0000 Subject: [PATCH 26/33] Add junit test logging to unity stage --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 138f9ffd..3479f03c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -49,7 +49,8 @@ pipeline { withVenv { runWaf('.', "configure clean build --target=xcore200") viewEnv() { - runPython("TARGET=XCORE200 pytest -s") + runPython("TARGET=XCORE200 pytest -s --junitxml=pytest_unity.xml") + junit "pytest_unity.xml" } } } From edbadca0cd7dbef1f6179e4942c745565a9563f2 Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 10 Jan 2024 16:44:45 +0000 Subject: [PATCH 27/33] Fix xcore.ai branding --- CHANGELOG.rst | 2 +- lib_xua/doc/rst/arch.rst | 2 +- lib_xua/doc/rst/feat_spdif_rx.rst | 2 +- lib_xua/doc/rst/hw.rst | 2 +- lib_xua/doc/rst/opt_spdif_rx.rst | 2 +- lib_xua/doc/rst/sw_clocking.rst | 6 +++--- lib_xua/src/core/clocking/sw_pll_wrapper.h | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6e641d9d..dc98f70c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,7 +13,7 @@ HEAD * RESOLVED: Repeated old S/PDIF and ADAT samples when entering underflow state * 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 + * CHANGED: xcore.ai targets use sigma-delta software PLL for clock recovery of digital Rx streams by default. * Changes to dependencies: diff --git a/lib_xua/doc/rst/arch.rst b/lib_xua/doc/rst/arch.rst index adc6960c..ec7f7219 100644 --- a/lib_xua/doc/rst/arch.rst +++ b/lib_xua/doc/rst/arch.rst @@ -54,7 +54,7 @@ In addition :ref:`usb_audio_optional_components` shows optional components that * - Clockgen - Drives an external frequency generator (PLL) and manages changes between internal clocks and external clocks arising - from digital input. On xCORE-AI Clockgen may also work in + from digital input. On xcore.ai Clockgen may also work in conjunction with lib_sw_pll to produce a local clock from the XCORE which is locked to the incoming digital stream. * - MIDI diff --git a/lib_xua/doc/rst/feat_spdif_rx.rst b/lib_xua/doc/rst/feat_spdif_rx.rst index 28099cd5..2329fc8c 100644 --- a/lib_xua/doc/rst/feat_spdif_rx.rst +++ b/lib_xua/doc/rst/feat_spdif_rx.rst @@ -29,7 +29,7 @@ The S/PDIF receiver should be called on the appropriate tile:: With the steps above an S/PDIF stream can be captured by the xCORE. To be functionally useful the audio master clock must be able to synchronise to this external digital stream. Additionally, the host can be notified regarding changes in the validity of this stream, it's frequency etc. To synchronise to external -streams the codebase assumes the use of an external Cirrus Logic CS2100 device or lib_sw_pll on xCORE-AI designs. +streams the codebase assumes the use of an external Cirrus Logic CS2100 device or lib_sw_pll on xcore.ai designs. The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device or timing information to lib_sw_pll and also handles recording of clock validity etc. diff --git a/lib_xua/doc/rst/hw.rst b/lib_xua/doc/rst/hw.rst index 500695c9..934c824e 100644 --- a/lib_xua/doc/rst/hw.rst +++ b/lib_xua/doc/rst/hw.rst @@ -56,7 +56,7 @@ Three methods of generating an audio master clock are provided on the board: Selection between these methods is done via writing to bits 6 and 7 of PORT 8D on tile[0]. -Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xCORE-AI, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`. +Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xcore.ai, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`. .. _hw_316_ctrlport: diff --git a/lib_xua/doc/rst/opt_spdif_rx.rst b/lib_xua/doc/rst/opt_spdif_rx.rst index 5e5031aa..d978e336 100644 --- a/lib_xua/doc/rst/opt_spdif_rx.rst +++ b/lib_xua/doc/rst/opt_spdif_rx.rst @@ -34,7 +34,7 @@ This must be a 1-bit port, for example:: When S/PDIF receive is enabled the codebase expects to either drive a synchronisation signal to an external -Cirrus Logic CS2100 device or use lib_swp_pll (xCORE-AI only) for master-clock generation. +Cirrus Logic CS2100 device or use lib_swp_pll (xcore.ai only) for master-clock generation. The programmer should ensure the define in :ref:`opt_spdif_rx_ref_defines` is set appropriately. diff --git a/lib_xua/doc/rst/sw_clocking.rst b/lib_xua/doc/rst/sw_clocking.rst index 5fcbbbc5..2bf6272f 100755 --- a/lib_xua/doc/rst/sw_clocking.rst +++ b/lib_xua/doc/rst/sw_clocking.rst @@ -15,7 +15,7 @@ the xCORE. Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external clock source from a digital stream (e.g. S/PDIF or ADAT input). The codebase supports the Cirrus -Logic CS2100 device or use of lib_sw_pll (xCORE-AI only) for this purpose. Other devices may be +Logic CS2100 device or use of lib_sw_pll (xcore.ai only) for this purpose. Other devices may be supported via code modification. The Clock Recovery core (Clock Gen) is responsible for either generating the reference frequency @@ -24,7 +24,7 @@ and the time of received samples. Clock Gen (via CS2100 or lib_sw_pll) generates used over the whole design. This core also serves as a smaller buffer between ADAT and S/PDIF receiving cores and the Audio Hub core. -When using lib_sw_pll (xCORE-AI only) an further core is instantiated which performs the sigma-delta +When using lib_sw_pll (xcore.ai only) an further core is instantiated which performs the sigma-delta modulation of the xCORE PLL to ensure the lowest jitter over the audio band. See lib_sw_pll documentation for further details. @@ -35,7 +35,7 @@ When running in an external clock mode (i.e. S/PDIF Clock" or "ADAT Clock" mode) received from the S/PDIF and/or ADAT receive core. The external frequency is calculated through counting samples in a given period. Either the reference clock to the CS2100 is then generated based on the reception of these samples or the timing information is provided to lib_sw_pll to generate -the phase-locked clock on-chip (xCORE-AI only). +the phase-locked clock on-chip (xcore.ai only). If an external stream becomes invalid, the *Internal Clock* timer event will fire to ensure that valid master clock generation continues regardless of cable unplugs etc. Efforts are made to diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.h b/lib_xua/src/core/clocking/sw_pll_wrapper.h index 7b4e0236..918c41c0 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.h +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.h @@ -4,7 +4,7 @@ #ifndef _SW_PLL_WRAPPPER_H_ #define _SW_PLL_WRAPPPER_H_ -/* By default we use SW_PLL for Digital Rx configs running on XCORE-AI */ +/* 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 From 529aea28dca0c947e2d62a49773bda380937e4b1 Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 12 Jan 2024 11:14:36 +0000 Subject: [PATCH 28/33] Remove apppll.h and replace with calls to lib_sw_pll --- examples/AN00246_xua_example/src/hwsupport.xc | 19 ++- .../src/hwsupport.xc | 20 +++- examples/shared/apppll.h | 109 ------------------ 3 files changed, 29 insertions(+), 119 deletions(-) delete mode 100644 examples/shared/apppll.h diff --git a/examples/AN00246_xua_example/src/hwsupport.xc b/examples/AN00246_xua_example/src/hwsupport.xc index 5f019752..e0e671a0 100644 --- a/examples/AN00246_xua_example/src/hwsupport.xc +++ b/examples/AN00246_xua_example/src/hwsupport.xc @@ -1,9 +1,11 @@ -// Copyright 2017-2022 XMOS LIMITED. +// Copyright 2017-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include #include "xua.h" -#include "../../shared/apppll.h" +extern "C"{ + #include "sw_pll.h" +} on tile[0]: out port p_ctrl = XS1_PORT_8D; @@ -38,19 +40,26 @@ void AudioHwInit() delay_milliseconds(100); /* Use xCORE Secondary PLL to generate *fixed* master clock */ - AppPllEnable_SampleRate(DEFAULT_FREQ); + if(DEFAULT_FREQ % 22050 == 0) + { + sw_pll_fixed_clock(MCLK_441); + } + else + { + sw_pll_fixed_clock(MCLK_48); + } delay_milliseconds(100); /* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc. * It holds DAC in reset until it gets clocks anyway. - * Note, this example doesn't use the ADC's + * Note, this example doesn't use the ADCs */ } /* Configures the external audio hardware for the required sample frequency */ void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) { - AppPllEnable_SampleRate(samFreq); + sw_pll_fixed_clock(mClk); } diff --git a/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc b/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc index 5f019752..1847872c 100644 --- a/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc +++ b/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc @@ -1,9 +1,12 @@ -// Copyright 2017-2022 XMOS LIMITED. +// Copyright 2017-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include #include "xua.h" -#include "../../shared/apppll.h" +#include "xassert.h" +extern "C"{ + #include "sw_pll.h" +} on tile[0]: out port p_ctrl = XS1_PORT_8D; @@ -38,19 +41,26 @@ void AudioHwInit() delay_milliseconds(100); /* Use xCORE Secondary PLL to generate *fixed* master clock */ - AppPllEnable_SampleRate(DEFAULT_FREQ); + if(DEFAULT_FREQ % 22050 == 0) + { + sw_pll_fixed_clock(MCLK_441); + } + else + { + sw_pll_fixed_clock(MCLK_48); + } delay_milliseconds(100); /* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc. * It holds DAC in reset until it gets clocks anyway. - * Note, this example doesn't use the ADC's + * Note, this example doesn't use the ADCs */ } /* Configures the external audio hardware for the required sample frequency */ void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) { - AppPllEnable_SampleRate(samFreq); + sw_pll_fixed_clock(mClk); } diff --git a/examples/shared/apppll.h b/examples/shared/apppll.h deleted file mode 100644 index 1312c0ab..00000000 --- a/examples/shared/apppll.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2022 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#include -#include "xassert.h" - -// App PLL setup -#define APP_PLL_CTL_BYPASS (0) // 0 = no bypass, 1 = bypass. -#define APP_PLL_CTL_INPUT_SEL (0) // 0 = XTAL, 1 = sysPLL -#define APP_PLL_CTL_ENABLE (1) // 0 = disabled, 1 = enabled. - -// 24MHz in, 24.576MHz out, integer mode -// Found exact solution: IN 24000000.0, OUT 24576000.0, VCO 2457600000.0, RD 5, FD 512, OD 10, FOD 10 -#define APP_PLL_CTL_OD_48 (4) // Output divider = (OD+1) -#define APP_PLL_CTL_F_48 (511) // FB divider = (F+1)/2 -#define APP_PLL_CTL_R_48 (4) // Ref divider = (R+1) - -#define APP_PLL_CTL_48 ((APP_PLL_CTL_BYPASS << 29) | (APP_PLL_CTL_INPUT_SEL << 28) | (APP_PLL_CTL_ENABLE << 27) |\ - (APP_PLL_CTL_OD_48 << 23) | (APP_PLL_CTL_F_48 << 8) | APP_PLL_CTL_R_48) - -// Fractional divide is M/N -#define APP_PLL_FRAC_EN_48 (0) // 0 = disabled -#define APP_PLL_FRAC_NPLUS1_CYCLES_48 (0) // M value is this reg value + 1. -#define APP_PLL_FRAC_TOTAL_CYCLES_48 (0) // N value is this reg value + 1. -#define APP_PLL_FRAC_48 ((APP_PLL_FRAC_EN_48 << 31) | (APP_PLL_FRAC_NPLUS1_CYCLES_48 << 8) | APP_PLL_FRAC_TOTAL_CYCLES_48) - -// 24MHz in, 22.5792MHz out (44.1kHz * 512), frac mode -// Found exact solution: IN 24000000.0, OUT 22579200.0, VCO 2257920000.0, RD 5, FD 470.400 (m = 2, n = 5), OD 5, FOD 10 -#define APP_PLL_CTL_OD_441 (4) // Output divider = (OD+1) -#define APP_PLL_CTL_F_441 (469) // FB divider = (F+1)/2 -#define APP_PLL_CTL_R_441 (4) // Ref divider = (R+1) - -#define APP_PLL_CTL_441 ((APP_PLL_CTL_BYPASS << 29) | (APP_PLL_CTL_INPUT_SEL << 28) | (APP_PLL_CTL_ENABLE << 27) |\ - (APP_PLL_CTL_OD_441 << 23) | (APP_PLL_CTL_F_441 << 8) | APP_PLL_CTL_R_441) - -#define APP_PLL_FRAC_EN_44 (1) // 1 = enabled -#define APP_PLL_FRAC_NPLUS1_CYCLES_44 (1) // M value is this reg value + 1. -#define APP_PLL_FRAC_TOTAL_CYCLES_44 (4) // N value is this reg value + 1.define APP_PLL_CTL_R_441 (4) // Ref divider = (R+1) -#define APP_PLL_FRAC_44 ((APP_PLL_FRAC_EN_44 << 31) | (APP_PLL_FRAC_NPLUS1_CYCLES_44 << 8) | APP_PLL_FRAC_TOTAL_CYCLES_44) - -#define APP_PLL_DIV_INPUT_SEL (1) // 0 = sysPLL, 1 = app_PLL -#define APP_PLL_DIV_DISABLE (0) // 1 = disabled (pin connected to X1D11), 0 = enabled divider output to pin. -#define APP_PLL_DIV_VALUE (4) // Divide by N+1 - remember there's a /2 also afterwards for 50/50 duty cycle. -#define APP_PLL_DIV ((APP_PLL_DIV_INPUT_SEL << 31) | (APP_PLL_DIV_DISABLE << 16) | APP_PLL_DIV_VALUE) - -/* TODO support more than two freqs..*/ -void AppPllEnable(int32_t clkFreq_hz) -{ - switch(clkFreq_hz) - { - case 44100*512: - - // Disable the PLL - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF)); - // Enable the PLL to invoke a reset on the appPLL. - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441); - // Must write the CTL register twice so that the F and R divider values are captured using a running clock. - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441); - // Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values. - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF)); - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441); - - // Set the fractional divider if used - write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_44); - - break; - - case 48000*512: - - // Disable the PLL - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF)); - // Enable the PLL to invoke a reset on the appPLL. - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48); - // Must write the CTL register twice so that the F and R divider values are captured using a running clock. - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48); - // Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values. - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF)); - write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48); - - // Set the fractional divider if used - write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_48); - - break; - - default: - assert(0); - break; - } - - // Wait for PLL output frequency to stabilise due to fractional divider enable - delay_microseconds(100); - - // Turn on the clock output - write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, APP_PLL_DIV); -} - -void AppPllEnable_SampleRate(int32_t sampleRate_hz) -{ - assert(sampleRate_hz >= 22050); - - if(sampleRate_hz % 22050 == 0) - { - AppPllEnable(44100*512); - } - else - { - AppPllEnable(48000*512); - } -} - From d81b51010260594c22db0de0fe0928aeac4a2e6b Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 12 Jan 2024 15:38:03 +0000 Subject: [PATCH 29/33] Move sw_pll init to SD task to remove backpressure on clockgen --- lib_xua/src/core/audiohub/xua_audiohub.xc | 1 - lib_xua/src/core/clocking/clockgen.xc | 47 ++++++++++---------- lib_xua/src/core/clocking/sw_pll_wrapper.h | 16 +++---- lib_xua/src/core/clocking/sw_pll_wrapper.xc | 49 +++++++-------------- 4 files changed, 48 insertions(+), 65 deletions(-) diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index fdad29ca..885fe521 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -805,7 +805,6 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, /* Notify clockgen of new mCLk */ c_mclk_change <: mClk; c_mclk_change <: curFreq; - c_mclk_change :> int _; /* Acknowledge when clocks all setup */ #endif } diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index d6e31e42..b22dd906 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -26,11 +26,11 @@ unsigned g_digData[10]; typedef struct { - int receivedSamples; - int samples; - int savedSamples; - int lastDiff; - unsigned identicaldiffs; + int receivedSamples; /* Uses by clockgen to count number of dig rx samples to ascertain clock specs */ + int samples; /* Raw sample count - rolling int and never reset */ + int savedSamples; /* Used by validSamples() to store state of last raw sample count */ + int lastDiff; /* Used by validSamples() to store state of last sample count diff */ + unsigned identicaldiffs; /* Used by validSamples() to store state of number of identical diffs */ int samplesPerTick; } Counter; @@ -107,6 +107,7 @@ static inline void setClockValidity(chanend c_interruptControl, int clkIndex, in { clockValid[clkIndex] = valid; outInterrupt(c_interruptControl, clockId[clkIndex]); + printstr("clockValid=");printintln(valid); #ifdef CLOCK_VALIDITY_CALL #if (XUA_ADAT_RX_EN) @@ -191,7 +192,7 @@ static inline int validSamples(Counter &counter, int clockIndex) } } } - else + else /* No valid frequency found - reset state */ { counter.identicaldiffs = 0; counter.lastDiff = diff; @@ -203,7 +204,10 @@ static inline int validSamples(Counter &counter, int clockIndex) #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; - +unsafe +{ + unsigned * unsafe selected_mclk_rate_ptr = NULL; +} #endif #ifdef LEVEL_METER_LEDS @@ -240,7 +244,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) timer t_external; - unsigned selected_mclk_rate = 0; + unsigned selected_mclk_rate = MCLK_48; // Assume 24.576MHz initial clock unsigned selected_sample_rate = 0; unsigned mclks_per_sample = 0; unsigned short mclk_time_stamp = 0; @@ -342,22 +346,20 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (USE_SW_PLL && (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)) chan c_sigma_delta; - sw_pll_state_t sw_pll; int reset_sw_pll_pfd = 1; - - /* Initialise before we par off the SDM thread/ - We share the sw_pll struct across threads and this - us safe because the threads access different memebers */ - unsafe - { - sw_pll_ptr = &sw_pll; + unsafe { + selected_mclk_rate_ptr = &selected_mclk_rate; } - unsigned sdm_interval = InitSWPLL(sw_pll, MCLK_48); // Assume 24.576MHz initial clock - par { - SigmaDeltaTask(c_sigma_delta, sdm_interval); + while(1) + { + unsafe + { + SigmaDeltaTask(c_sigma_delta, selected_mclk_rate_ptr); + } + } #else { #endif @@ -532,13 +534,12 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) case c_mclk_change :> selected_mclk_rate: c_mclk_change :> selected_sample_rate; - mclks_per_sample = selected_mclk_rate / selected_sample_rate; + printintln(selected_sample_rate); #if USE_SW_PLL - disable_sigma_delta(c_sigma_delta); /* Blocks until SDM is idle */ - InitSWPLL(sw_pll, selected_mclk_rate); + mclks_per_sample = selected_mclk_rate / selected_sample_rate; + restart_sigma_delta(c_sigma_delta); reset_sw_pll_pfd = 1; #endif - c_mclk_change <: 0; /* Acknowledge to hold off starting audio until done */ break; #endif diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.h b/lib_xua/src/core/clocking/sw_pll_wrapper.h index 918c41c0..ba57559d 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.h +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.h @@ -21,23 +21,23 @@ extern "C" } /* Special control value to disable SDM. Outside of normal range which is less than 16b.*/ -#define DISABLE_SDM 0x1000000 +#define DISABLE_SDM 0x10000000 /** 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 sdm_interval Unisgned value containing the sigma delta period in timer ticks. + * \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. */ -void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval); +void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ptr); -/** Helper function that sends a special disable command and waits for ACK. This is used - * to help prevemt simultaenous access to the PLL register from two different threads, - * +/** 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. */ -void disable_sigma_delta(chanend c_sigma_delta); +void restart_sigma_delta(chanend c_sigma_delta); /** Performs a frequency comparsion between the incoming digital Rx stream and the local mclk. * diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc index 9345e89e..664fa5c2 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.xc +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -9,12 +9,6 @@ #if USE_SW_PLL -/* Pointer to sw_pll struct to allow it to be used in separate SDM thread */ -unsafe -{ - sw_pll_state_t * unsafe sw_pll_ptr = NULL; -} - unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) { @@ -120,19 +114,20 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, } } -void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ +void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ptr){ /* 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. */ - /* To be extra safe, spin on sw_pll_ptr until it has been initialised by clockgen */ - while(sw_pll_ptr == NULL); - int f_error = 0; int dco_setting = SW_PLL_SDM_CTRL_MID_24; // Assume 24.576MHz as initial clock + unsigned sdm_interval = 0; + sw_pll_state_t sw_pll; + unsafe { - sw_pll_init_sigma_delta(&sw_pll_ptr->sdm_state); + printf("SigmaDeltaTask: %u\n", *selected_mclk_rate_ptr); + sdm_interval = InitSWPLL(sw_pll, (unsigned)*selected_mclk_rate_ptr); } tileref_t this_tile = get_local_tile_id(); @@ -140,10 +135,10 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ timer tmr; int32_t time_trigger; tmr :> time_trigger; - int send_ack_once = 1; + int running = 1; unsigned rx_word; - while(1) + while(running) { /* Poll for new SDM control value */ select @@ -151,16 +146,17 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ case inuint_byref(c_sigma_delta, rx_word): if(rx_word == DISABLE_SDM) { + printhexln(rx_word); f_error = 0; - send_ack_once = 1; + running = 0; } else { f_error = (int32_t)rx_word; unsafe { - sw_pll_sdm_do_control_from_error(sw_pll_ptr, -f_error); - dco_setting = sw_pll_ptr->sdm_state.current_ctrl_val; + sw_pll_sdm_do_control_from_error(&sw_pll, -f_error); + dco_setting = sw_pll.sdm_state.current_ctrl_val; } } break; @@ -180,29 +176,16 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned sdm_interval){ break; } - /* Do not write to the frac reg until we get out first - control value. This will avoid the writing of the - frac reg from two different threads which may cause - a channel deadlock. */ - if(rx_word != DISABLE_SDM) unsafe { - sw_pll_do_sigma_delta(&sw_pll_ptr->sdm_state, this_tile, dco_setting); - send_ack_once = 1; + sw_pll_do_sigma_delta(&sw_pll.sdm_state, this_tile, dco_setting); } - else if(send_ack_once) - { - /* Send ACK once to synchrnoise with clockgen signalling it's OK to reconfig */ - outuint(c_sigma_delta, 0); /* Send ACK to say reg writes have ceased */ - send_ack_once = 0; - } - } + } /* if running */ } -void disable_sigma_delta(chanend c_sigma_delta) +void restart_sigma_delta(chanend c_sigma_delta) { - outuint(c_sigma_delta, DISABLE_SDM); /* Stops SDM */ - inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */ + outuint(c_sigma_delta, DISABLE_SDM); /* Resets SDM */ } #endif /* USE_SW_PLL */ From e04ecf5fc9ff6b5ecc886fac2ddb36b2ddbf49b0 Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 12 Jan 2024 16:01:16 +0000 Subject: [PATCH 30/33] Initial audio holdoff until SD initialised --- lib_xua/src/core/audiohub/xua_audiohub.xc | 3 +++ lib_xua/src/core/clocking/clockgen.xc | 14 ++++++++++++++ lib_xua/src/core/clocking/sw_pll_wrapper.xc | 5 +++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index 885fe521..3089523a 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -805,6 +805,9 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, /* 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 _; #endif } diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index b22dd906..b3fd4c18 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -347,6 +347,8 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if (USE_SW_PLL && (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)) chan c_sigma_delta; int reset_sw_pll_pfd = 1; + int require_ack_to_audio = 0; + unsafe { selected_mclk_rate_ptr = &selected_mclk_rate; } @@ -531,6 +533,17 @@ 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): + printstr("ACK\n"); + if(require_ack_to_audio) + { + c_mclk_change <: tmp; + require_ack_to_audio = 0; + } + break; +#endif + #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) case c_mclk_change :> selected_mclk_rate: c_mclk_change :> selected_sample_rate; @@ -539,6 +552,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, mclks_per_sample = selected_mclk_rate / selected_sample_rate; restart_sigma_delta(c_sigma_delta); reset_sw_pll_pfd = 1; + require_ack_to_audio = 1; #endif break; #endif diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc index 664fa5c2..64fbfdec 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.xc +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -137,7 +137,9 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ tmr :> time_trigger; int running = 1; - unsigned rx_word; + outuint(c_sigma_delta, 0); /* Signal back via clockgen to audio to start I2S */ + + unsigned rx_word = 0; while(running) { /* Poll for new SDM control value */ @@ -146,7 +148,6 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ case inuint_byref(c_sigma_delta, rx_word): if(rx_word == DISABLE_SDM) { - printhexln(rx_word); f_error = 0; running = 0; } From ce987622d921057cfa5e8a08b4cf66d325e361ae Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 12 Jan 2024 16:24:44 +0000 Subject: [PATCH 31/33] Fix missing ACK to audio for xcore-200 --- lib_xua/src/core/clocking/clockgen.xc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index b3fd4c18..1b491e2d 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -552,7 +552,11 @@ void clockGen ( streaming chanend ?c_spdif_rx, mclks_per_sample = selected_mclk_rate / selected_sample_rate; restart_sigma_delta(c_sigma_delta); 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 */ + c_mclk_change <: 0; #endif break; #endif From 57debd055820bc9b17e36395396a42472bb16d0f Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 15 Jan 2024 10:05:53 +0000 Subject: [PATCH 32/33] Set DCO to midpoint of SDM restart --- lib_xua/src/core/clocking/clockgen.xc | 6 +++--- lib_xua/src/core/clocking/sw_pll_wrapper.h | 5 +++-- lib_xua/src/core/clocking/sw_pll_wrapper.xc | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index 1b491e2d..a8164a1e 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -535,7 +535,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && USE_SW_PLL) case inuint_byref(c_sigma_delta, tmp): - printstr("ACK\n"); + /* Send ACK back to audiohub to allow I2S to start */ if(require_ack_to_audio) { c_mclk_change <: tmp; @@ -545,9 +545,9 @@ void clockGen ( streaming chanend ?c_spdif_rx, #endif #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + /* Receive notification of audio streaming settings change */ case c_mclk_change :> selected_mclk_rate: c_mclk_change :> selected_sample_rate; - printintln(selected_sample_rate); #if USE_SW_PLL mclks_per_sample = selected_mclk_rate / selected_sample_rate; restart_sigma_delta(c_sigma_delta); @@ -555,7 +555,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, /* 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 */ + /* Send ACK immediately as we are good to go if not using SW_PLL */ c_mclk_change <: 0; #endif break; diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.h b/lib_xua/src/core/clocking/sw_pll_wrapper.h index ba57559d..bc96c1ea 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.h +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.h @@ -57,8 +57,9 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, * * \param sw_pll Reference to a software pll state struct to be initialised. * \param mClk The current nominal mClk frequency. - */ -unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk); + * + * returns The SDM update interval and the initial DCO setting for nominal frequency */ +{unsigned, unsigned} InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk); #endif /* USE_SW_PLL */ #endif /* _SW_PLL_WRAPPPER_H_ */ \ No newline at end of file diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc index 64fbfdec..95c67057 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.xc +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -10,7 +10,7 @@ #if USE_SW_PLL -unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) +{unsigned, unsigned} InitSWPLL(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 @@ -69,7 +69,7 @@ unsigned InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk) /* Reset SDM too */ sw_pll_init_sigma_delta(&sw_pll.sdm_state); - return (XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex]); + return {XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex], sw_pll_sdm_ctrl_mid[clkIndex]}; } void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, @@ -120,14 +120,14 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ channel lockup if two tasks (eg. init and SDM) try to write at the same time. */ int f_error = 0; - int dco_setting = SW_PLL_SDM_CTRL_MID_24; // Assume 24.576MHz as initial clock - unsigned sdm_interval = 0; + int dco_setting = 0; /* gets set at InitSWPLL */ + unsigned sdm_interval = 0; /* gets set at InitSWPLL */ sw_pll_state_t sw_pll; unsafe { printf("SigmaDeltaTask: %u\n", *selected_mclk_rate_ptr); - sdm_interval = InitSWPLL(sw_pll, (unsigned)*selected_mclk_rate_ptr); + {sdm_interval, dco_setting} = InitSWPLL(sw_pll, (unsigned)*selected_mclk_rate_ptr); } tileref_t this_tile = get_local_tile_id(); From a4e6fd0194aa4240b6dbedafcad7b204f1c3639f Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 15 Jan 2024 10:39:09 +0000 Subject: [PATCH 33/33] More tidying --- lib_xua/src/core/clocking/clockgen.xc | 7 ++++--- lib_xua/src/core/clocking/sw_pll_wrapper.h | 2 +- lib_xua/src/core/clocking/sw_pll_wrapper.xc | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index a8164a1e..08040504 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -107,7 +107,6 @@ static inline void setClockValidity(chanend c_interruptControl, int clkIndex, in { clockValid[clkIndex] = valid; outInterrupt(c_interruptControl, clockId[clkIndex]); - printstr("clockValid=");printintln(valid); #ifdef CLOCK_VALIDITY_CALL #if (XUA_ADAT_RX_EN) @@ -349,6 +348,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, 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; } @@ -535,7 +535,8 @@ void clockGen ( streaming chanend ?c_spdif_rx, #if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && USE_SW_PLL) case inuint_byref(c_sigma_delta, tmp): - /* Send ACK back to audiohub to allow I2S to start */ + /* 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; @@ -545,7 +546,7 @@ void clockGen ( streaming chanend ?c_spdif_rx, #endif #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) - /* Receive notification of audio streaming settings change */ + /* 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 diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.h b/lib_xua/src/core/clocking/sw_pll_wrapper.h index bc96c1ea..be808696 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.h +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.h @@ -58,7 +58,7 @@ void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp, * \param sw_pll Reference to a software pll state struct to be initialised. * \param mClk The current nominal mClk frequency. * - * returns The SDM update interval and the initial DCO setting for nominal frequency */ + * 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 */ diff --git a/lib_xua/src/core/clocking/sw_pll_wrapper.xc b/lib_xua/src/core/clocking/sw_pll_wrapper.xc index 95c67057..7cb3163d 100644 --- a/lib_xua/src/core/clocking/sw_pll_wrapper.xc +++ b/lib_xua/src/core/clocking/sw_pll_wrapper.xc @@ -126,7 +126,7 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ unsafe { - printf("SigmaDeltaTask: %u\n", *selected_mclk_rate_ptr); + /* initialse the SDM and gather SDM initial settings */ {sdm_interval, dco_setting} = InitSWPLL(sw_pll, (unsigned)*selected_mclk_rate_ptr); } @@ -162,7 +162,7 @@ void SigmaDeltaTask(chanend c_sigma_delta, unsigned * unsafe selected_mclk_rate_ } break; - // Do nothing & fall-through + /* Do nothing & fall-through. Above case polls only once per loop */ default: break; }