forked from PAWPAW-Mirror/lib_xua
Refactor sw_pll code into own source file
This commit is contained in:
@@ -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);
|
||||
|
||||
58
lib_xua/src/core/clocking/sw_pll_wrapper.h
Normal file
58
lib_xua/src/core/clocking/sw_pll_wrapper.h
Normal file
@@ -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);
|
||||
215
lib_xua/src/core/clocking/sw_pll_wrapper.xc
Normal file
215
lib_xua/src/core/clocking/sw_pll_wrapper.xc
Normal file
@@ -0,0 +1,215 @@
|
||||
// Copyright 2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <xs1.h>
|
||||
#include <assert.h>
|
||||
#include <print.h>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user