Fix lockup in aud->clkgen notification

This commit is contained in:
Ed
2024-01-05 08:34:24 +00:00
parent 61f17f3fe9
commit 780a407519
2 changed files with 93 additions and 17 deletions

View File

@@ -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) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Notify clockgen of new mCLk */ /* Notify clockgen of new mCLk */
c_mclk_change <: mClk; c_mclk_change <: mClk;
c_mclk_change <: curFreq;
#endif #endif
} }

View File

@@ -8,6 +8,7 @@
#include "xua_commands.h" #include "xua_commands.h"
#include "xua_clocking.h" #include "xua_clocking.h"
#include <stdio.h> // TODO DEV ONLY - DELME
#ifdef __XS3A__ #ifdef __XS3A__
#define USE_SW_PLL 1 #define USE_SW_PLL 1
@@ -231,7 +232,7 @@ static inline int validSamples(Counter &counter, int clockIndex)
#endif #endif
#if USE_SW_PLL #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 */ /* Autogenerated SDM App PLL setup by dco_model.py using 22.5792_1M profile */
/* Input freq: 24000000 /* 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_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 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_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; 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); 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; static unsigned count = 0;
count++; 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) if(count == 30)
{ {
printuintln(mclk_time_stamp); //calc mclk inc
count = 0;
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; 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) while(1)
{ {
c_sigma_delta :> dco_setting; /* Poll for new SDM control value */
printstr("sigma-delta got: "); printintln(dco_setting); unsigned tmp;
if(dco_setting == 0) 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) void disable_sigma_delta(chanend c_sigma_delta)
{ {
c_sigma_delta <: 0; /* Stops SD */ outuint(c_sigma_delta, 0); /* Stops SD */
c_sigma_delta :> int _; /* Wait for ACK so we know reg write is complete */ inuint(c_sigma_delta); /* Wait for ACK so we know reg write is complete */
} }
#endif #endif
@@ -360,6 +425,8 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
timer t_external; timer t_external;
unsigned selected_mclk_rate = 0; unsigned selected_mclk_rate = 0;
unsigned selected_sample_rate = 0;
unsigned mclks_per_sample = 0;
unsigned short mclk_time_stamp = 0; unsigned short mclk_time_stamp = 0;
/* Get MCLK count */ /* Get MCLK count */
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud)); 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 #if USE_SW_PLL
chan c_sigma_delta; chan c_sigma_delta;
sw_pll_state_t sw_pll; sw_pll_state_t sw_pll;
InitSWPLL(sw_pll, MCLK_48); unsigned sdm_interval = InitSWPLL(sw_pll, MCLK_48);
par par
{ {
SigmaDeltaTask(c_sigma_delta); SigmaDeltaTask(c_sigma_delta, sdm_interval);
#else #else
{ {
#endif #endif
@@ -635,10 +702,14 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
case c_mclk_change :> selected_mclk_rate: 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 #if USE_SW_PLL
printstr("c_mclk_change: "); printuintln(selected_mclk_rate); 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); 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 #endif
break; break;
#endif #endif
@@ -684,12 +755,14 @@ void clockGen ( streaming chanend ?c_spdif_rx,
if(spdifSamps > MAX_SPDIF_SAMPLES-1) if(spdifSamps > MAX_SPDIF_SAMPLES-1)
{ {
spdifOverflow = 1; spdifOverflow = 1;
printstr("spo+\n"); // DELME
} }
/* Check for coming out of under flow */ /* Check for coming out of under flow */
if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1))) if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1)))
{ {
spdifUnderflow = 0; spdifUnderflow = 0;
printstr("spu-\n"); // DELME
} }
} }
break; break;
@@ -719,7 +792,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#if USE_SW_PLL #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 #else
/* Toggle edge */ /* Toggle edge */
i_pll_ref.toggle_timed(1); 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; timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#if USE_SW_PLL #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 #else
/* Toggle edge */ /* Toggle edge */
i_pll_ref.toggle_timed(1); 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 */ /* We're out of S/PDIF samples, mark underflow condition */
spdifUnderflow = 1; spdifUnderflow = 1;
printstr("spu-\n"); // DELME
spdifLeft = 0; spdifLeft = 0;
} }
@@ -884,6 +958,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1))) if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1)))
{ {
spdifOverflow = 0; spdifOverflow = 0;
printstr("spo-\n"); // DELME
} }
} }
#endif #endif