Commit f0953590 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Grzegorz Daniluk

softpll: rewritten external channel, we don't use bang-bang phase detector any more

parent 245d3efb
...@@ -153,7 +153,6 @@ int ad9516_set_output_divider(int output, int ratio, int phase_offset) ...@@ -153,7 +153,6 @@ int ad9516_set_output_divider(int output, int ratio, int phase_offset)
ad9516_write_reg(base + 1, div_ctl | (1<<7) | (phase_offset & 0xf)); ad9516_write_reg(base + 1, div_ctl | (1<<7) | (phase_offset & 0xf));
} else { } else {
uint8_t div_ctl = ad9516_read_reg(base + 1); uint8_t div_ctl = ad9516_read_reg(base + 1);
TRACE("DivCtl: %x\n", div_ctl);
ad9516_write_reg(base + 1, (div_ctl & (~(1<<7))) | (phase_offset & 0xf)); /* disable bypass bit */ ad9516_write_reg(base + 1, (div_ctl & (~(1<<7))) | (phase_offset & 0xf)); /* disable bypass bit */
ad9516_write_reg(base, (lcycles << 4) | hcycles); ad9516_write_reg(base, (lcycles << 4) | hcycles);
} }
......
This diff is collapsed.
...@@ -75,6 +75,21 @@ struct softpll_state { ...@@ -75,6 +75,21 @@ struct softpll_state {
struct spll_ptracker_state ptrackers[MAX_PTRACKERS]; struct spll_ptracker_state ptrackers[MAX_PTRACKERS];
}; };
static const struct stringlist_entry seq_states [] =
{
{ SEQ_START_EXT, "start-ext" },
{ SEQ_WAIT_EXT, "wait-ext" },
{ SEQ_START_HELPER, "start-helper" },
{ SEQ_WAIT_HELPER, "wait-helper" },
{ SEQ_START_MAIN, "start-main" },
{ SEQ_WAIT_MAIN, "wait-main" },
{ SEQ_DISABLED, "disabled" },
{ SEQ_READY, "ready" },
{ SEQ_CLEAR_DACS, "clear-dacs" },
{ SEQ_WAIT_CLEAR_DACS, "wait-clear-dacs" },
{ 0, NULL }
};
static volatile struct softpll_state softpll; static volatile struct softpll_state softpll;
static volatile int ptracker_mask = 0; static volatile int ptracker_mask = 0;
...@@ -153,8 +168,10 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta ...@@ -153,8 +168,10 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
/* State "Wait until we are locked to external 10MHz clock" */ /* State "Wait until we are locked to external 10MHz clock" */
case SEQ_WAIT_EXT: case SEQ_WAIT_EXT:
{ {
if (external_locked(&s->ext)) if (external_locked(&s->ext)) {
s->seq_state = SEQ_START_HELPER; start_ptrackers(s);
s->seq_state = SEQ_READY;
}
break; break;
} }
...@@ -190,7 +207,7 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta ...@@ -190,7 +207,7 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
case SEQ_WAIT_MAIN: case SEQ_WAIT_MAIN:
{ {
if (s->mpll.ld.locked) if (s->mpll.ld.locked)
{ {
start_ptrackers(s); start_ptrackers(s);
s->seq_state = SEQ_READY; s->seq_state = SEQ_READY;
...@@ -200,16 +217,13 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta ...@@ -200,16 +217,13 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
case SEQ_READY: case SEQ_READY:
{ {
if (!s->helper.ld.locked) if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext)) {
{
s->delock_count++; s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS; s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext)) } else if (!s->helper.ld.locked) {
{
s->delock_count++; s->delock_count++;
s->seq_state = SEQ_START_EXT; s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked) } else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked) {
{
s->delock_count++; s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS; s->seq_state = SEQ_CLEAR_DACS;
} }
...@@ -220,47 +234,22 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta ...@@ -220,47 +234,22 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
static inline void update_loops(struct softpll_state *s, int tag_value, int tag_source) static inline void update_loops(struct softpll_state *s, int tag_value, int tag_source)
{ {
if(s->mode == SPLL_MODE_GRAND_MASTER) {
switch(s->seq_state) {
case SEQ_WAIT_EXT:
case SEQ_START_HELPER:
case SEQ_WAIT_HELPER:
case SEQ_START_MAIN:
case SEQ_WAIT_MAIN:
case SEQ_READY:
external_update(&s->ext, tag_value, tag_source);
break;
}
}
switch(s->seq_state) {
case SEQ_WAIT_HELPER:
case SEQ_START_MAIN:
case SEQ_WAIT_MAIN:
case SEQ_READY:
helper_update(&s->helper, tag_value, tag_source);
break;
}
if(s->seq_state == SEQ_WAIT_MAIN) helper_update(&s->helper, tag_value, tag_source);
{
mpll_update(&s->mpll, tag_value, tag_source);
}
if(s->seq_state == SEQ_READY) if(s->helper.ld.locked)
{ {
if(s->mode == SPLL_MODE_SLAVE) mpll_update(&s->mpll, tag_value, tag_source);
{
int i;
mpll_update(&s->mpll, tag_value, tag_source); if(s->seq_state == SEQ_READY) {
for (i = 0; i < spll_n_chan_out - 1; i++) if(s->mode == SPLL_MODE_SLAVE) {
mpll_update(&s->aux[i].pll.dmtd, tag_value, tag_source); // fixme: bb hooks here int i;
for (i = 0; i < spll_n_chan_out - 1; i++)
mpll_update(&s->aux[i].pll.dmtd, tag_value, tag_source);
}
update_ptrackers(s, tag_value, tag_source);
} }
update_ptrackers(s, tag_value, tag_source);
} }
} }
...@@ -301,7 +290,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps) ...@@ -301,7 +290,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
spll_n_chan_ref = SPLL_CSR_N_REF_R(csr); spll_n_chan_ref = SPLL_CSR_N_REF_R(csr);
spll_n_chan_out = SPLL_CSR_N_OUT_R(csr); spll_n_chan_out = SPLL_CSR_N_OUT_R(csr);
s->mode = mode; s->mode = mode;
s->delock_count = 0; s->delock_count = 0;
...@@ -318,16 +306,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps) ...@@ -318,16 +306,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
PPSG->ESCR = 0; PPSG->ESCR = 0;
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_CNT_RST | PPSG_CR_PWIDTH_W(PPS_WIDTH); PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_CNT_RST | PPSG_CR_PWIDTH_W(PPS_WIDTH);
if(mode == SPLL_MODE_GRAND_MASTER)
{
if(SPLL->ECCR & SPLL_ECCR_EXT_SUPPORTED)
external_init(&s->ext, spll_n_chan_ref + spll_n_chan_out, align_pps);
else {
TRACE_DEV("softpll: attempting to enable GM mode on non-GM hardware.\n");
return;
}
}
if(mode == SPLL_MODE_DISABLED) if(mode == SPLL_MODE_DISABLED)
s->seq_state = SEQ_DISABLED; s->seq_state = SEQ_DISABLED;
else else
...@@ -354,6 +332,17 @@ void spll_init(int mode, int slave_ref_channel, int align_pps) ...@@ -354,6 +332,17 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
for (i = 0; i < spll_n_chan_ref; i++) for (i = 0; i < spll_n_chan_ref; i++)
ptracker_init(&s->ptrackers[i], i, PTRACKER_AVERAGE_SAMPLES); ptracker_init(&s->ptrackers[i], i, PTRACKER_AVERAGE_SAMPLES);
if(mode == SPLL_MODE_GRAND_MASTER) {
if(SPLL->ECCR & SPLL_ECCR_EXT_SUPPORTED) {
s->ext.helper = &s->helper;
s->ext.main = &s->mpll;
external_init(&s->ext, spll_n_chan_ref + spll_n_chan_out, align_pps);
} else {
TRACE_DEV("softpll: attempting to enable GM mode on non-GM hardware.\n");
return;
}
}
TRACE_DEV TRACE_DEV
("softpll: mode %s, %d ref channels, %d out channels\n", ("softpll: mode %s, %d ref channels, %d out channels\n",
modes[mode], spll_n_chan_ref, spll_n_chan_out); modes[mode], spll_n_chan_ref, spll_n_chan_out);
...@@ -400,6 +389,11 @@ void spll_stop_channel(int channel) ...@@ -400,6 +389,11 @@ void spll_stop_channel(int channel)
mpll_stop(&s->aux[channel - 1].pll.dmtd); mpll_stop(&s->aux[channel - 1].pll.dmtd);
} }
int spll_ext_locked()
{
return external_locked( (struct spll_external_state *) &softpll.ext);
}
int spll_check_lock(int channel) int spll_check_lock(int channel)
{ {
if (!channel) if (!channel)
...@@ -409,29 +403,6 @@ int spll_check_lock(int channel) ...@@ -409,29 +403,6 @@ int spll_check_lock(int channel)
&& softpll.aux[channel - 1].pll.dmtd.ld.locked; && softpll.aux[channel - 1].pll.dmtd.ld.locked;
} }
#ifdef CONFIG_PPSI /* use __div64_32 from ppsi library to save libgcc memory */
static int32_t from_picos(int32_t ps)
{
extern uint32_t __div64_32(uint64_t *n, uint32_t base);
uint64_t ups = ps;
if (ps >= 0) {
ups *= 1 << HPLL_N;
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return ups;
}
ups = -ps * (1 << HPLL_N);
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return -ups;
}
#else /* previous implementation: ptp-noposix has no __div64_32 available */
static int32_t from_picos(int32_t ps)
{
return (int32_t) ((int64_t) ps * (int64_t) (1 << HPLL_N) /
(int64_t) CLOCK_PERIOD_PICOSECONDS);
}
#endif
static int32_t to_picos(int32_t units) static int32_t to_picos(int32_t units)
{ {
return (int32_t) (((int64_t) units * return (int32_t) (((int64_t) units *
...@@ -443,8 +414,7 @@ static void set_phase_shift(int channel, int32_t value_picoseconds) ...@@ -443,8 +414,7 @@ static void set_phase_shift(int channel, int32_t value_picoseconds)
{ {
struct spll_main_state *st = (struct spll_main_state *) struct spll_main_state *st = (struct spll_main_state *)
(!channel ? &softpll.mpll : &softpll.aux[channel - 1].pll.dmtd); (!channel ? &softpll.mpll : &softpll.aux[channel - 1].pll.dmtd);
int div = (DIVIDE_DMTD_CLOCKS_BY_2 ? 2 : 1); mpll_set_phase_shift(st, value_picoseconds);
mpll_set_phase_shift(st, from_picos(value_picoseconds) / div);
softpll.mpll_shift_ps = value_picoseconds; softpll.mpll_shift_ps = value_picoseconds;
} }
...@@ -502,15 +472,12 @@ void spll_get_num_channels(int *n_ref, int *n_out) ...@@ -502,15 +472,12 @@ void spll_get_num_channels(int *n_ref, int *n_out)
void spll_show_stats() void spll_show_stats()
{ {
if (softpll.mode > 0) if (softpll.mode > 0)
TRACE_DEV TRACE_DEV("softpll: irqs %d seq %s mode %d "
("softpll: irq_count %d sequencer_state %d mode %d " "alignment_state %d HL%d ML%d HY=%d MY=%d DelCnt=%d\n",
"alignment_state %d HL%d EL%d ML%d HY=%d " irq_count, stringlist_lookup(seq_states, softpll.seq_state), softpll.mode,
"MY=%d EY=%d DelCnt=%d extsc=%d\n", softpll.ext.align_state, softpll.helper.ld.locked, softpll.mpll.ld.locked,
irq_count, softpll.seq_state, softpll.mode, softpll.helper.pi.y, softpll.mpll.pi.y,
softpll.ext.realign_state, softpll.helper.ld.locked, softpll.delock_count);
softpll.ext.ld.locked, softpll.mpll.ld.locked,
softpll.helper.pi.y, softpll.mpll.pi.y, softpll.ext.pi.y,
softpll.delock_count, softpll.ext.sample_n);
} }
int spll_shifter_busy(int channel) int spll_shifter_busy(int channel)
...@@ -666,3 +633,33 @@ void spll_set_dac(int index, int value) ...@@ -666,3 +633,33 @@ void spll_set_dac(int index, int value)
softpll.aux[index - 1].pll.dmtd.pi.y = value; softpll.aux[index - 1].pll.dmtd.pi.y = value;
} }
} }
void spll_update()
{
switch(softpll.mode) {
case SPLL_MODE_GRAND_MASTER:
external_align_fsm(&softpll.ext);
break;
}
spll_update_aux_clocks();
}
int spll_measure_frequency(int osc)
{
volatile uint32_t *reg;
switch(osc) {
case SPLL_OSC_REF:
reg = &SPLL->F_REF;
break;
case SPLL_OSC_DMTD:
reg = &SPLL->F_DMTD;
break;
case SPLL_OSC_EXT:
reg = &SPLL->F_EXT;
break;
}
timer_delay_ms(2000);
return (*reg ) & (0xfffffff);
}
...@@ -39,6 +39,10 @@ ...@@ -39,6 +39,10 @@
#define SPLL_PD_DDMTD 0 #define SPLL_PD_DDMTD 0
#define SPLL_PD_BANGBANG 1 #define SPLL_PD_BANGBANG 1
/* Channels for spll_measure_frequency() */
#define SPLL_OSC_REF 0
#define SPLL_OSC_DMTD 1
#define SPLL_OSC_EXT 2
/* Note on channel naming: /* Note on channel naming:
- ref_channel means a PHY recovered clock input. There can be one (as in WR core) or more (WR switch). - ref_channel means a PHY recovered clock input. There can be one (as in WR core) or more (WR switch).
...@@ -101,9 +105,10 @@ void spll_enable_ptracker(int ref_channel, int enable); ...@@ -101,9 +105,10 @@ void spll_enable_ptracker(int ref_channel, int enable);
/* Reads tracked phase shift value for given reference channel */ /* Reads tracked phase shift value for given reference channel */
int spll_read_ptracker(int ref_channel, int32_t *phase_ps, int *enabled); int spll_read_ptracker(int ref_channel, int32_t *phase_ps, int *enabled);
/* Calls aux clock handling state machine. Must be called regularly (although it is not time-critical) /* Calls non-realtime update state machine. Must be called regularly (although
in the main loop of the program if aux clocks are used in the design. */ * it is not time-critical) in the main loop of the program if aux clocks or
int spll_update_aux_clocks(); * external reference are used in the design. */
void spll_update();
/* Returns the status of given aux clock output (SPLL_AUX_) */ /* Returns the status of given aux clock output (SPLL_AUX_) */
int spll_get_aux_status(int out_channel); int spll_get_aux_status(int out_channel);
...@@ -122,5 +127,7 @@ void spll_set_dac(int out_channel, int value); ...@@ -122,5 +127,7 @@ void spll_set_dac(int out_channel, int value);
/* Returns current DAC sample value for output (out_channel) */ /* Returns current DAC sample value for output (out_channel) */
int spll_get_dac(int out_channel); int spll_get_dac(int out_channel);
int spll_measure_frequency(int osc);
#endif // __SOFTPLL_NG_H #endif // __SOFTPLL_NG_H
...@@ -125,6 +125,7 @@ Channels (spll_n_chan_ref ... spll_n_chan_out + spll_n_chan_ref-1) are the outpu ...@@ -125,6 +125,7 @@ Channels (spll_n_chan_ref ... spll_n_chan_out + spll_n_chan_ref-1) are the outpu
void spll_enable_tagger(int channel, int enable) void spll_enable_tagger(int channel, int enable)
{ {
TRACE("EnableTagger %d %d\n", channel, enable);
if (channel >= spll_n_chan_ref) { /* Output channel? */ if (channel >= spll_n_chan_ref) { /* Output channel? */
if (enable) if (enable)
SPLL->OCER |= 1 << (channel - spll_n_chan_ref); SPLL->OCER |= 1 << (channel - spll_n_chan_ref);
...@@ -137,7 +138,7 @@ void spll_enable_tagger(int channel, int enable) ...@@ -137,7 +138,7 @@ void spll_enable_tagger(int channel, int enable)
SPLL->RCER &= ~(1 << channel); SPLL->RCER &= ~(1 << channel);
} }
// TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER); TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
} }
void biquad_init(spll_biquad_t *bq, const int *coefs, int shift) void biquad_init(spll_biquad_t *bq, const int *coefs, int shift)
...@@ -167,3 +168,13 @@ int biquad_update(spll_biquad_t *bq, int x) ...@@ -167,3 +168,13 @@ int biquad_update(spll_biquad_t *bq, int x)
return y; return y;
} }
const char *stringlist_lookup(const struct stringlist_entry *slist, int id)
{
int i;
for(i=0; slist[i].str; i++) {
if(slist[i].id == id)
return slist[i].str;
}
return "<unknown>";
}
...@@ -66,6 +66,21 @@ typedef struct { ...@@ -66,6 +66,21 @@ typedef struct {
int yd[2], xd[2]; /* I/O delay lines */ int yd[2], xd[2]; /* I/O delay lines */
} spll_biquad_t; } spll_biquad_t;
/* long-term-average filter */
typedef struct {
int acc;
int n;
int window;
int pos;
int size;
int log[16];
} spll_lta_t;
struct stringlist_entry {
int id;
const char *str;
};
/* initializes the PI controller state. Currently almost a stub. */ /* initializes the PI controller state. Currently almost a stub. */
void pi_init(spll_pi_t *pi); void pi_init(spll_pi_t *pi);
...@@ -83,4 +98,6 @@ void spll_enable_tagger(int channel, int enable); ...@@ -83,4 +98,6 @@ void spll_enable_tagger(int channel, int enable);
void biquad_init(spll_biquad_t *bq, const int *coefs, int shift); void biquad_init(spll_biquad_t *bq, const int *coefs, int shift);
int biquad_update(spll_biquad_t *bq, int x); int biquad_update(spll_biquad_t *bq, int x);
const char *stringlist_lookup(const struct stringlist_entry *slist, int id);
#endif // __SPLL_COMMON_H #endif // __SPLL_COMMON_H
...@@ -13,152 +13,171 @@ ...@@ -13,152 +13,171 @@
#include "spll_external.h" #include "spll_external.h"
#include "spll_debug.h" #include "spll_debug.h"
#define BB_ERROR_BITS 16 #include "trace.h"
#include "irq.h"
void external_init(volatile struct spll_external_state *s, int ext_ref, #define ALIGN_STATE_EXT_OFF 0
int realign_clocks) #define ALIGN_STATE_START 1
{ #define ALIGN_STATE_INIT_CSYNC 2
#define ALIGN_STATE_WAIT_CSYNC 3
#define ALIGN_STATE_START_ALIGNMENT 7
#define ALIGN_STATE_WAIT_SAMPLE 4
#define ALIGN_STATE_COMPENSATE_DELAY 5
#define ALIGN_STATE_LOCKED 6
#define ALIGN_STATE_START_MAIN 8
s->pi.y_min = 5; #define ALIGN_SAMPLE_PERIOD 100000
s->pi.y_max = (1 << DAC_BITS) - 5; #define ALIGN_TARGET 0
s->pi.kp = (int)(300);
s->pi.ki = (int)(1);
s->pi.anti_windup = 1;
s->pi.bias = 32768;
/* Phase branch lock detection */
s->ld.threshold = 400;
s->ld.lock_samples = 10000;
s->ld.delock_samples = 9990;
s->ref_src = ext_ref;
s->ph_err_cur = 0;
s->ph_err_d0 = 0;
s->ph_raw_d0 = 0;
s->realign_clocks = realign_clocks;
s->realign_state = (realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
lowpass_init((spll_lowpass_t *)&s->lp_short, 4000);
lowpass_init((spll_lowpass_t *)&s->lp_long, 300);
}
static inline void realign_fsm(struct spll_external_state *s) #define EXT_PERIOD_NS 100
{ #define EXT_FREQ_HZ 10000000
switch (s->realign_state) { #define EXT_PPS_LATENCY_PS 30000 // fixme: make configurable
case REALIGN_STAGE1:
SPLL->ECCR |= SPLL_ECCR_ALIGN_EN;
s->realign_state = REALIGN_STAGE1_WAIT;
s->realign_timer = timer_get_tics() + 2 * TICS_PER_SECOND;
break;
case REALIGN_STAGE1_WAIT: void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
int idx = spll_n_chan_ref + spll_n_chan_out;
if (SPLL->ECCR & SPLL_ECCR_ALIGN_DONE)
s->realign_state = REALIGN_STAGE2;
else if (time_after(timer_get_tics(), s->realign_timer)) {
SPLL->ECCR &= ~SPLL_ECCR_ALIGN_EN;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
case REALIGN_STAGE2:
if (s->ld.locked) {
PPSG->CR = PPSG_CR_CNT_RST | PPSG_CR_CNT_EN;
PPSG->ADJ_UTCLO = 0;
PPSG->ADJ_UTCHI = 0;
PPSG->ADJ_NSEC = 0;
PPSG->ESCR = PPSG_ESCR_SYNC;
s->realign_state = REALIGN_STAGE2_WAIT; helper_init(s->helper, idx);
s->realign_timer = timer_get_tics() mpll_init(s->main, idx, spll_n_chan_ref);
+ 2 * TICS_PER_SECOND;
}
break;
case REALIGN_STAGE2_WAIT:
if (PPSG->ESCR & PPSG_ESCR_SYNC) {
PPSG->ESCR = PPSG_ESCR_PPS_VALID | PPSG_ESCR_TM_VALID;
s->realign_state = REALIGN_DONE;
} else if (time_after(timer_get_tics(), s->realign_timer)) {
PPSG->ESCR = 0;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
case REALIGN_PPS_INVALID: s->align_state = ALIGN_STATE_EXT_OFF;
case REALIGN_DISABLED: s->enabled = 0;
case REALIGN_DONE:
return;
}
} }
int external_update(struct spll_external_state *s, int tag, int source) void external_start(struct spll_external_state *s)
{ {
int err, y, y2, ylt; helper_start(s->helper);
if (source == s->ref_src) {
int wrap = tag & (1 << BB_ERROR_BITS) ? 1 : 0;
realign_fsm(s);
tag &= ((1 << BB_ERROR_BITS) - 1);
// mprintf("err %d\n", tag);
if (wrap) {
if (tag > s->ph_raw_d0)
s->ph_err_offset -= (1 << BB_ERROR_BITS);
else if (tag <= s->ph_raw_d0)
s->ph_err_offset += (1 << BB_ERROR_BITS);
}
s->ph_raw_d0 = tag;
err = (tag + s->ph_err_offset) - s->ph_err_d0; SPLL->ECCR = SPLL_ECCR_EXT_EN;
s->ph_err_d0 = (tag + s->ph_err_offset);
y = pi_update(&s->pi, err);
y2 = lowpass_update(&s->lp_short, y);
ylt = lowpass_update(&s->lp_long, y);
if (!(SPLL->ECCR & SPLL_ECCR_EXT_REF_PRESENT)) {
/* no reference? de-lock now */
ld_init(&s->ld);
y2 = 32000;
}
SPLL->DAC_MAIN = y2 & 0xffff;
spll_debug(DBG_ERR | DBG_EXT, ylt, 0); s->align_state = ALIGN_STATE_START;
spll_debug(DBG_SAMPLE_ID | DBG_EXT, s->sample_n++, 0); s->enabled = 1;
spll_debug(DBG_Y | DBG_EXT, y2, 1); spll_debug (DBG_EVENT | DBG_EXT, DBG_EVT_START, 1);
}
if (ld_update(&s->ld, y2 - ylt)) int external_locked(struct spll_external_state *s)
return SPLL_LOCKED; {
if (!s->helper->ld.locked || !s->main->ld.locked)
return 0;
switch(s->align_state) {
case ALIGN_STATE_EXT_OFF:
case ALIGN_STATE_START:
case ALIGN_STATE_START_MAIN:
case ALIGN_STATE_INIT_CSYNC:
case ALIGN_STATE_WAIT_CSYNC:
return 0;
default:
return 1;
} }
return SPLL_LOCKING;
} }
void external_start(struct spll_external_state *s) static int align_sample(int channel, int *v)
{ {
int mask = (1 << channel);
SPLL->ECCR = 0;
if(SPLL->AL_CR & mask) {
s->sample_n = 0; SPLL->AL_CR = mask; // ack
s->realign_state = int ci = SPLL->AL_CIN;
(s->realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED); if(ci > 100 && ci < (EXT_FREQ_HZ - 100) ) { // give some metastability margin, when the counter transitions from EXT_FREQ_HZ-1 to 0
*v = ci * EXT_PERIOD_NS;
SPLL->ECCR = SPLL_ECCR_EXT_EN; return 1;
}
spll_debug(DBG_EVENT | DBG_EXT, DBG_EVT_START, 1); }
return 0; // sample not valid
} }
int external_locked(struct spll_external_state *s) void external_align_fsm( struct spll_external_state *s )
{ {
return (s->ld.locked int v;
&& (s->realign_clocks ? s->realign_state == REALIGN_DONE : 1)); switch(s->align_state) {
case ALIGN_STATE_EXT_OFF:
break;
case ALIGN_STATE_START:
if(s->helper->ld.locked) {
disable_irq();
mpll_start(s->main);
enable_irq();
s->align_state = ALIGN_STATE_START_MAIN;
}
break;
case ALIGN_STATE_START_MAIN:
SPLL->AL_CR = 2;
if(s->helper->ld.locked && s->main->ld.locked) {
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_PWIDTH_W(10);
PPSG->ADJ_NSEC = 3;
PPSG->ESCR = PPSG_ESCR_SYNC;
s->align_state = ALIGN_STATE_INIT_CSYNC;
TRACE_DEV("EXT: DMTD locked.\n");
}
break;
case ALIGN_STATE_INIT_CSYNC:
if (PPSG->ESCR & PPSG_ESCR_SYNC) {
PPSG->ESCR = PPSG_ESCR_PPS_VALID; // enable PPS output (even though it's not aligned yet)
s->align_timer = timer_get_tics() + 2 * TICS_PER_SECOND;
s->align_state = ALIGN_STATE_WAIT_CSYNC;
}
break;
case ALIGN_STATE_WAIT_CSYNC:
if(timer_get_tics() >= s->align_timer) {
s->align_state = ALIGN_STATE_START_ALIGNMENT;
s->align_shift = 0;
TRACE_DEV("EXT: CSync complete.\n");
}
break;
case ALIGN_STATE_START_ALIGNMENT:
if(align_sample(1, &v)) {
v %= ALIGN_SAMPLE_PERIOD;
if(v == 0 || v >= ALIGN_SAMPLE_PERIOD / 2) {
s->align_target = EXT_PERIOD_NS;
s->align_step = -100;
} else if (s > 0) {
s->align_target = 0;
s->align_step = 100;
}
TRACE_DEV("EXT: Align target %d, step %d.\n", s->align_target, s->align_step);
s->align_state = ALIGN_STATE_WAIT_SAMPLE;
}
break;
case ALIGN_STATE_WAIT_SAMPLE:
if(!mpll_shifter_busy(s->main) && align_sample(1, &v)) {
v %= ALIGN_SAMPLE_PERIOD;
if(v != s->align_target) {
s->align_shift += s->align_step;
mpll_set_phase_shift(s->main, s->align_shift);
} else if (v == s->align_target) {
s->align_shift += EXT_PPS_LATENCY_PS;
mpll_set_phase_shift(s->main, s->align_shift);
s->align_state = ALIGN_STATE_COMPENSATE_DELAY;
}
}
break;
case ALIGN_STATE_COMPENSATE_DELAY:
if(!mpll_shifter_busy(s->main)) {
TRACE_DEV("EXT: Align done.\n");
s->align_state = ALIGN_STATE_LOCKED;
}
break;
case ALIGN_STATE_LOCKED:
if(!external_locked(s)) {
s->align_state = ALIGN_STATE_START;
}
break;
default:
break;
}
} }
...@@ -14,69 +14,29 @@ ...@@ -14,69 +14,29 @@
#define __SPLL_EXTERNAL_H #define __SPLL_EXTERNAL_H
#include "spll_common.h" #include "spll_common.h"
#include "spll_helper.h"
#include "spll_main.h"
/* Alignment FSM states */ struct spll_external_state {
struct spll_helper_state *helper;
/* 1st alignment stage, done before starting the ext channel PLL: struct spll_main_state *main;
alignment of the rising edge of the external clock (10 MHz), with
the rising edge of the local reference (62.5/125 MHz) and the PPS
signal. Because of non-integer ratio (6.25 or 12.5), the PLL must
know which edges shall be kept at phase==0. We align to the edge of
the 10 MHz clock which comes right after the edge of the PPS pulse
(see drawing below):
PLL reference (62.5 MHz) ____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|
External clock (10 MHz) ^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|___
External PPS ___________|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*/
#define REALIGN_STAGE1 1
#define REALIGN_STAGE1_WAIT 2
/* 2nd alignment stage, done after the ext channel PLL has locked. We
make sure that the switch's internal PPS signal is produced exactly
on the edge of PLL reference in-phase with 10 MHz clock edge, which
has come right after the PPS input
PLL reference (62.5 MHz) ____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|
External clock (10 MHz) ^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|___
External PPS ___________|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Internal PPS __________________________________|^^^^^^^^^|______________________________________________________________________
^ aligned clock edges and PPS
*/
#define REALIGN_STAGE2 3
#define REALIGN_STAGE2_WAIT 4
/* Error state - PPS signal missing or of bad frequency */
#define REALIGN_PPS_INVALID 5
/* Realignment is disabled (i.e. the switch inputs only the reference
* frequency, but not time) */
#define REALIGN_DISABLED 6
/* Realignment done */ int enabled;
#define REALIGN_DONE 7
struct spll_external_state { int align_state;
int ref_src; int align_timer;
int sample_n; int align_target;
int ph_err_offset, ph_err_cur, ph_err_d0, ph_raw_d0; int align_step;
int realign_clocks; int align_shift;
int realign_state;
int realign_timer;
spll_pi_t pi;
spll_lowpass_t lp_short, lp_long;
spll_lock_det_t ld;
}; };
void external_init(volatile struct spll_external_state *s, int ext_ref, void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks); int realign_clocks);
int external_update(struct spll_external_state *s, int tag, int source);
void external_start(struct spll_external_state *s); void external_start(struct spll_external_state *s);
int external_locked(struct spll_external_state *s); int external_locked(struct spll_external_state *s);
void external_align_fsm( struct spll_external_state *s );
#endif // __SPLL_EXTERNAL_H #endif // __SPLL_EXTERNAL_H
...@@ -65,7 +65,6 @@ int helper_update(struct spll_helper_state *s, int tag, ...@@ -65,7 +65,6 @@ int helper_update(struct spll_helper_state *s, int tag,
err = HELPER_ERROR_CLAMP; err = HELPER_ERROR_CLAMP;
} }
// err = biquad_update(&s->precomp, err);
if ((tag + s->p_adder) > HELPER_TAG_WRAPAROUND if ((tag + s->p_adder) > HELPER_TAG_WRAPAROUND
&& s->p_setpoint > HELPER_TAG_WRAPAROUND) { && s->p_setpoint > HELPER_TAG_WRAPAROUND) {
...@@ -108,3 +107,15 @@ void helper_start(struct spll_helper_state *s) ...@@ -108,3 +107,15 @@ void helper_start(struct spll_helper_state *s)
spll_enable_tagger(s->ref_src, 1); spll_enable_tagger(s->ref_src, 1);
spll_debug(DBG_EVENT | DBG_HELPER, DBG_EVT_START, 1); spll_debug(DBG_EVENT | DBG_HELPER, DBG_EVT_START, 1);
} }
void helper_switch_reference(struct spll_helper_state *s, int new_ref)
{
#if 0
disable_irq();
s->ref-src = 1;
s->tag_d0 = -1;
s->p-addr = 0;
enable_irq();
spll_enable_tagger(s->ref_src, 1);
#endif
}
...@@ -41,5 +41,6 @@ int helper_update(struct spll_helper_state *s, int tag, ...@@ -41,5 +41,6 @@ int helper_update(struct spll_helper_state *s, int tag,
int source); int source);
void helper_start(struct spll_helper_state *s); void helper_start(struct spll_helper_state *s);
void helper_switch_reference(struct spll_helper_state *s, int new_ref);
#endif // __SPLL_HELPER_H #endif // __SPLL_HELPER_H
...@@ -27,10 +27,10 @@ void mpll_init(struct spll_main_state *s, int id_ref, ...@@ -27,10 +27,10 @@ void mpll_init(struct spll_main_state *s, int id_ref,
s->pi.y_min = 5; s->pi.y_min = 5;
s->pi.y_max = 65530; s->pi.y_max = 65530;
s->pi.anti_windup = 1; s->pi.anti_windup = 1;
s->pi.bias = 65000; s->pi.bias = 30000;
#if defined(CONFIG_WR_SWITCH) #if defined(CONFIG_WR_SWITCH)
s->pi.kp = 1500; // / 2; s->pi.kp = 1100; // / 2;
s->pi.ki = 7; // / 2; s->pi.ki = 30; // / 2;
#elif defined(CONFIG_WR_NODE) #elif defined(CONFIG_WR_NODE)
s->pi.kp = 1100; // / 2; s->pi.kp = 1100; // / 2;
s->pi.ki = 30; // / 2; s->pi.ki = 30; // / 2;
...@@ -47,12 +47,16 @@ void mpll_init(struct spll_main_state *s, int id_ref, ...@@ -47,12 +47,16 @@ void mpll_init(struct spll_main_state *s, int id_ref,
s->id_out = id_out; s->id_out = id_out;
s->dac_index = id_out - spll_n_chan_ref; s->dac_index = id_out - spll_n_chan_ref;
TRACE("ref %d out %d idx %x", s->id_ref, s->id_out, s->dac_index);
pi_init((spll_pi_t *)&s->pi); pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld); ld_init((spll_lock_det_t *)&s->ld);
} }
void mpll_start(struct spll_main_state *s) void mpll_start(struct spll_main_state *s)
{ {
TRACE("MPLL_Start [dac %d]\n", s->dac_index);
s->adder_ref = s->adder_out = 0; s->adder_ref = s->adder_out = 0;
s->tag_ref = -1; s->tag_ref = -1;
s->tag_out = -1; s->tag_out = -1;
...@@ -83,63 +87,28 @@ int mpll_update(struct spll_main_state *s, int tag, int source) ...@@ -83,63 +87,28 @@ int mpll_update(struct spll_main_state *s, int tag, int source)
{ {
int err, y; int err, y;
#ifdef WITH_SEQUENCING
int new_ref = -1, new_out = -1;
if (source == s->id_ref) {
new_ref = tag;
s->seq_ref++;
} else if (source == s->id_out) {
new_out = tag;
s->seq_out++;
}
switch (s->match_state) {
case MATCH_NEXT_TAG:
if (new_ref > 0 && s->seq_out < s->seq_ref) {
s->tag_ref = new_ref;
s->match_seq = s->seq_ref;
s->match_state = MATCH_WAIT_OUT;
}
if (new_out > 0 && s->seq_out > s->seq_ref) {
s->tag_out = new_out;
s->match_seq = s->seq_out;
s->match_state = MATCH_WAIT_REF;
}
break;
case MATCH_WAIT_REF:
if (new_ref > 0 && s->seq_ref == s->match_seq) {
s->match_state = MATCH_NEXT_TAG;
s->tag_ref = new_ref;
}
break;
case MATCH_WAIT_OUT:
if (new_out > 0 && s->seq_out == s->match_seq) {
s->match_state = MATCH_NEXT_TAG;
s->tag_out = new_out;
}
break;
}
#else
if (source == s->id_ref) if (source == s->id_ref)
s->tag_ref = tag; s->tag_ref = tag;
if (source == s->id_out) if (source == s->id_out)
s->tag_out = tag; s->tag_out = tag;
#endif if (s->tag_ref >= 0) {
if(s->tag_ref_d >= 0 && s->tag_ref_d > s->tag_ref)
s->adder_ref += (1 << TAG_BITS);
if (s->tag_ref >= 0 && s->tag_out >= 0) { s->tag_ref_d = s->tag_ref;
}
if (s->tag_ref_d >= 0 && s->tag_ref_d > s->tag_ref)
s->adder_ref += (1 << TAG_BITS); if (s->tag_out >= 0) {
if (s->tag_out_d >= 0 && s->tag_out_d > s->tag_out) if(s->tag_out_d >= 0 && s->tag_out_d > s->tag_out)
s->adder_out += (1 << TAG_BITS); s->adder_out += (1 << TAG_BITS);
s->tag_ref_d = s->tag_ref;
s->tag_out_d = s->tag_out; s->tag_out_d = s->tag_out;
}
if (s->tag_ref >= 0 && s->tag_out >= 0) {
err = s->adder_ref + s->tag_ref - s->adder_out - s->tag_out; err = s->adder_ref + s->tag_ref - s->adder_out - s->tag_out;
#ifndef WITH_SEQUENCING #ifndef WITH_SEQUENCING
...@@ -192,16 +161,39 @@ int mpll_update(struct spll_main_state *s, int tag, int source) ...@@ -192,16 +161,39 @@ int mpll_update(struct spll_main_state *s, int tag, int source)
} }
if (ld_update((spll_lock_det_t *)&s->ld, err)) if (ld_update((spll_lock_det_t *)&s->ld, err))
return SPLL_LOCKED; return SPLL_LOCKED;
} }
return SPLL_LOCKING; return SPLL_LOCKING;
} }
#ifdef CONFIG_PPSI /* use __div64_32 from ppsi library to save libgcc memory */
static int32_t from_picos(int32_t ps)
{
extern uint32_t __div64_32(uint64_t *n, uint32_t base);
uint64_t ups = ps;
if (ps >= 0) {
ups *= 1 << HPLL_N;
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return ups;
}
ups = -ps * (1 << HPLL_N);
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return -ups;
}
#else /* previous implementation: ptp-noposix has no __div64_32 available */
static int32_t from_picos(int32_t ps)
{
return (int32_t) ((int64_t) ps * (int64_t) (1 << HPLL_N) /
(int64_t) CLOCK_PERIOD_PICOSECONDS);
}
#endif
int mpll_set_phase_shift(struct spll_main_state *s, int mpll_set_phase_shift(struct spll_main_state *s,
int desired_shift) int desired_shift_ps)
{ {
s->phase_shift_target = desired_shift; int div = (DIVIDE_DMTD_CLOCKS_BY_2 ? 2 : 1);
s->phase_shift_target = from_picos(desired_shift_ps) / div;
return 0; return 0;
} }
......
...@@ -47,7 +47,7 @@ void mpll_start(struct spll_main_state *s); ...@@ -47,7 +47,7 @@ void mpll_start(struct spll_main_state *s);
int mpll_update(struct spll_main_state *s, int tag, int source); int mpll_update(struct spll_main_state *s, int tag, int source);
int mpll_set_phase_shift(struct spll_main_state *s, int mpll_set_phase_shift(struct spll_main_state *s,
int desired_shift); int desired_shift_ps);
int mpll_shifter_busy(struct spll_main_state *s); int mpll_shifter_busy(struct spll_main_state *s);
......
...@@ -269,7 +269,7 @@ int main(void) ...@@ -269,7 +269,7 @@ int main(void)
ui_update(); ui_update();
wrc_ptp_update(); wrc_ptp_update();
spll_update_aux_clocks(); spll_update();
check_stack(); check_stack();
} }
} }
...@@ -126,6 +126,7 @@ int wrc_ptp_set_mode(int mode) ...@@ -126,6 +126,7 @@ int wrc_ptp_set_mode(int mode)
shw_pps_gen_enable_output(0); shw_pps_gen_enable_output(0);
while (!spll_check_lock(0) && lock_timeout) { while (!spll_check_lock(0) && lock_timeout) {
spll_update();
timer_delay_ms(1000); timer_delay_ms(1000);
mprintf("."); mprintf(".");
if (time_after(timer_get_tics(), start_tics + lock_timeout)) { if (time_after(timer_get_tics(), start_tics + lock_timeout)) {
......
...@@ -8,13 +8,56 @@ ...@@ -8,13 +8,56 @@
const char *build_revision; const char *build_revision;
const char *build_date; const char *build_date;
static int calc_apr(int meas_min, int meas_max, int f_center )
{
// apr_min is in PPM
int64_t delta_low = meas_min - f_center;
int64_t delta_hi = meas_max - f_center;
if(delta_low >= 0)
return -1;
if(delta_hi <= 0)
return -1;
int ppm_lo = -(int64_t)delta_low * 1000000LL / f_center;
int ppm_hi = (int64_t)delta_hi * 1000000LL / f_center;
return ppm_lo < ppm_hi ? ppm_lo : ppm_hi;
}
extern void disable_irq();
static void check_vco_frequencies()
{
disable_irq();
int f_min, f_max;
TRACE("SoftPLL VCO Frequency/APR test:\n");
spll_set_dac(-1, 0);
f_min = spll_measure_frequency(SPLL_OSC_DMTD);
spll_set_dac(-1, 65535);
f_max = spll_measure_frequency(SPLL_OSC_DMTD);
TRACE("DMTD VCO: Low=%d Hz Hi=%d Hz, APR = %d ppm.\n", f_min, f_max, calc_apr(f_min, f_max, 62500000));
spll_set_dac(0, 0);
f_min = spll_measure_frequency(SPLL_OSC_REF);
spll_set_dac(0, 65535);
f_max = spll_measure_frequency(SPLL_OSC_REF);
TRACE("REF VCO: Low=%d Hz Hi=%d Hz, APR = %d ppm.\n", f_min, f_max, calc_apr(f_min, f_max, 62500000));
f_min = spll_measure_frequency(SPLL_OSC_EXT);
TRACE("EXT clock: Freq=%d Hz\n", f_min);
}
int main(void) int main(void)
{ {
uint32_t start_tics = timer_get_tics(); uint32_t start_tics = timer_get_tics();
uart_init_hw(); uart_init_hw();
TRACE("WR Switch Real Time Subsystem (c) CERN 2011 - 2013\n"); TRACE("WR Switch Real Time Subsystem (c) CERN 2011 - 2014\n");
TRACE("Revision: %s, built %s.\n", build_revision, build_date); TRACE("Revision: %s, built %s.\n", build_revision, build_date);
TRACE("--"); TRACE("--");
...@@ -28,12 +71,12 @@ int main(void) ...@@ -28,12 +71,12 @@ int main(void)
if(time_after(tics, start_tics + TICS_PER_SECOND/5)) if(time_after(tics, start_tics + TICS_PER_SECOND/5))
{ {
// TRACE("tick!\n");
spll_show_stats(); spll_show_stats();
start_tics = tics; start_tics = tics;
} }
rts_update(); rts_update();
rtipc_action(); rtipc_action();
spll_update();
} }
return 0; return 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment