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

View File

@@ -8,6 +8,7 @@
#include "xua_commands.h"
#include "xua_clocking.h"
#include <stdio.h> // 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