Commit f132291c authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

Merge branch 'tom-multichannel-softpll'

Conflicts:
	wrc_main.c
parents fbfde2ae 3f16eb1a
......@@ -20,8 +20,6 @@ obj-y = arch/lm32/crt0.o arch/lm32/irq.o arch/lm32/debug.o
LDS = arch/lm32/ram.ld
obj-y += wrc_main.o
obj-y += softpll/softpll_ng.o
# our linker script is preprocessed, so have a rule here
%.ld: %.ld.S $(AUTOCONF)
......@@ -93,6 +91,7 @@ include lib/lib.mk
include pp_printf/printf.mk
include sockitowm/sockitowm.mk
include dev/dev.mk
include softpll/softpll.mk
obj-y += check-error.o
......@@ -139,6 +138,7 @@ config.o: .config
%.bin: %.elf
${OBJCOPY} -O binary $^ $@
scp $@ twlostow@cs-ccr-dev3:/user/twlostow
%.ram: tools %.bin
./tools/genraminit $*.bin 0 > $@
......
This diff is collapsed.
......@@ -8,11 +8,13 @@ WARNING: These parameters must be in sync with the generics of the HDL instantia
#include <stdio.h>
#include "board.h"
/* Reference clock frequency, in [Hz] */
#define CLOCK_FREQ 125000000
#define CLOCK_FREQ REF_CLOCK_FREQ_HZ
/* Reference clock period, in picoseconds */
#define CLOCK_PERIOD_PICOSECONDS 8000
#define CLOCK_PERIOD_PICOSECONDS REF_CLOCK_PERIOD_PS
/* optional DMTD clock division to improve FPGA timing closure by avoiding
clock nets directly driving FD inputs. Must be consistent with the
......@@ -35,7 +37,7 @@ WARNING: These parameters must be in sync with the generics of the HDL instantia
#define MAX_CHAN_REF 1
/* Max. allowed number of auxillary channels */
#define MAX_CHAN_AUX 1
#define MAX_CHAN_AUX 2
/* Max. allowed number of phase trackers */
#define MAX_PTRACKERS 1
......
......@@ -31,7 +31,6 @@ extern int abs(int val);
/* The following from ptp-noposix */
extern void wr_servo_reset(void);
void update_rx_queues(void);
void spll_enable_ptracker(int ref_channel, int enable);
#endif /* __WRC_H__ */
......@@ -117,13 +117,16 @@ void wrc_mon_gui(void)
cprintf(C_GREY, "Aux clock status: ");
aux_stat = spll_get_aux_status(0);
int n_ref, n_out, i;
spll_get_num_channels(&n_ref, &n_out);
if (aux_stat & SPLL_AUX_ENABLED)
cprintf(C_GREEN, "enabled");
for(i = 0; i<n_out - 1; i++)
{
const char *aux_stat = spll_get_aux_status_string(i);
if (aux_stat & SPLL_AUX_LOCKED)
cprintf(C_GREEN, ", locked");
cprintf(C_GREY,"%d:", i);
cprintf(C_GREEN,"%s ", aux_stat);
}
mprintf("\n");
cprintf(C_BLUE, "\nTiming parameters:\n\n");
......
......@@ -3,7 +3,7 @@
* File : softpll_regs.h
* Author : auto-generated by wbgen2 from spll_wb_slave.wb
* Created : Mon Jul 23 15:02:57 2012
* Created : Wed Mar 20 14:50:14 2013
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE spll_wb_slave.wb
......@@ -14,7 +14,7 @@
#ifndef __WBGEN2_REGDEFS_SPLL_WB_SLAVE_WB
#define __WBGEN2_REGDEFS_SPLL_WB_SLAVE_WB
#include <stdint.h>
#include <inttypes.h>
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
......@@ -33,26 +33,20 @@
/* definitions for register: SPLL Control/Status Register */
/* definitions for field: Period detector reference select in reg: SPLL Control/Status Register */
#define SPLL_CSR_PER_SEL_MASK WBGEN2_GEN_MASK(0, 6)
#define SPLL_CSR_PER_SEL_SHIFT 0
#define SPLL_CSR_PER_SEL_W(value) WBGEN2_GEN_WRITE(value, 0, 6)
#define SPLL_CSR_PER_SEL_R(reg) WBGEN2_GEN_READ(reg, 0, 6)
/* definitions for field: Number of reference channels (max: 32) in reg: SPLL Control/Status Register */
#define SPLL_CSR_N_REF_MASK WBGEN2_GEN_MASK(8, 6)
#define SPLL_CSR_N_REF_SHIFT 8
#define SPLL_CSR_N_REF_W(value) WBGEN2_GEN_WRITE(value, 8, 6)
#define SPLL_CSR_N_REF_R(reg) WBGEN2_GEN_READ(reg, 8, 6)
#define SPLL_CSR_N_REF_MASK WBGEN2_GEN_MASK(0, 6)
#define SPLL_CSR_N_REF_SHIFT 0
#define SPLL_CSR_N_REF_W(value) WBGEN2_GEN_WRITE(value, 0, 6)
#define SPLL_CSR_N_REF_R(reg) WBGEN2_GEN_READ(reg, 0, 6)
/* definitions for field: Number of output channels (max: 8) in reg: SPLL Control/Status Register */
#define SPLL_CSR_N_OUT_MASK WBGEN2_GEN_MASK(16, 3)
#define SPLL_CSR_N_OUT_SHIFT 16
#define SPLL_CSR_N_OUT_W(value) WBGEN2_GEN_WRITE(value, 16, 3)
#define SPLL_CSR_N_OUT_R(reg) WBGEN2_GEN_READ(reg, 16, 3)
#define SPLL_CSR_N_OUT_MASK WBGEN2_GEN_MASK(8, 3)
#define SPLL_CSR_N_OUT_SHIFT 8
#define SPLL_CSR_N_OUT_W(value) WBGEN2_GEN_WRITE(value, 8, 3)
#define SPLL_CSR_N_OUT_R(reg) WBGEN2_GEN_READ(reg, 8, 3)
/* definitions for field: Enable Period Measurement in reg: SPLL Control/Status Register */
#define SPLL_CSR_PER_EN WBGEN2_GEN_MASK(19, 1)
/* definitions for field: Debug queue supported in reg: SPLL Control/Status Register */
#define SPLL_CSR_DBG_SUPPORTED WBGEN2_GEN_MASK(11, 1)
/* definitions for register: External Clock Control Register */
......@@ -71,22 +65,6 @@
/* definitions for field: External Clock Reference Present in reg: External Clock Control Register */
#define SPLL_ECCR_EXT_REF_PRESENT WBGEN2_GEN_MASK(4, 1)
/* definitions for register: DMTD Clock Control Register */
/* definitions for field: DMTD Clock Undersampling Divider in reg: DMTD Clock Control Register */
#define SPLL_DCCR_GATE_DIV_MASK WBGEN2_GEN_MASK(0, 6)
#define SPLL_DCCR_GATE_DIV_SHIFT 0
#define SPLL_DCCR_GATE_DIV_W(value) WBGEN2_GEN_WRITE(value, 0, 6)
#define SPLL_DCCR_GATE_DIV_R(reg) WBGEN2_GEN_READ(reg, 0, 6)
/* definitions for register: Reference Channel Undersampling Enable Register */
/* definitions for field: Reference Channel Undersampling Enable in reg: Reference Channel Undersampling Enable Register */
#define SPLL_RCGER_GATE_SEL_MASK WBGEN2_GEN_MASK(0, 32)
#define SPLL_RCGER_GATE_SEL_SHIFT 0
#define SPLL_RCGER_GATE_SEL_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define SPLL_RCGER_GATE_SEL_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: Output Channel Control Register */
/* definitions for field: Output Channel HW enable flag in reg: Output Channel Control Register */
......@@ -101,20 +79,15 @@
#define SPLL_OCCR_OUT_LOCK_W(value) WBGEN2_GEN_WRITE(value, 8, 8)
#define SPLL_OCCR_OUT_LOCK_R(reg) WBGEN2_GEN_READ(reg, 8, 8)
/* definitions for register: Reference Channel Enable Register */
/* definitions for register: Output Channel Enable Register */
/* definitions for field: Output Channel Phase Detector Type in reg: Output Channel Control Register */
#define SPLL_OCCR_OUT_DET_TYPE_MASK WBGEN2_GEN_MASK(16, 8)
#define SPLL_OCCR_OUT_DET_TYPE_SHIFT 16
#define SPLL_OCCR_OUT_DET_TYPE_W(value) WBGEN2_GEN_WRITE(value, 16, 8)
#define SPLL_OCCR_OUT_DET_TYPE_R(reg) WBGEN2_GEN_READ(reg, 16, 8)
/* definitions for register: HPLL Period Error */
/* definitions for register: Reference Channel Tagging Enable Register */
/* definitions for field: Period error value in reg: HPLL Period Error */
#define SPLL_PER_HPLL_ERROR_MASK WBGEN2_GEN_MASK(0, 16)
#define SPLL_PER_HPLL_ERROR_SHIFT 0
#define SPLL_PER_HPLL_ERROR_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define SPLL_PER_HPLL_ERROR_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for field: Period Error Valid in reg: HPLL Period Error */
#define SPLL_PER_HPLL_VALID WBGEN2_GEN_MASK(16, 1)
/* definitions for register: Output Channel Tagging Enable Register */
/* definitions for register: Helper DAC Output */
......@@ -132,7 +105,7 @@
#define SPLL_DAC_MAIN_DAC_SEL_W(value) WBGEN2_GEN_WRITE(value, 16, 4)
#define SPLL_DAC_MAIN_DAC_SEL_R(reg) WBGEN2_GEN_READ(reg, 16, 4)
/* definitions for register: Deglitcher threshold */
/* definitions for register: DDMTD Deglitcher threshold */
/* definitions for register: Debug FIFO Register - SPLL side */
......@@ -152,6 +125,32 @@
/* definitions for register: Counter Resync Register - output channels */
/* definitions for register: Aux clock configuration register */
/* definitions for field: Aux output select in reg: Aux clock configuration register */
#define SPLL_AUX_CR_AUX_SEL_MASK WBGEN2_GEN_MASK(0, 3)
#define SPLL_AUX_CR_AUX_SEL_SHIFT 0
#define SPLL_AUX_CR_AUX_SEL_W(value) WBGEN2_GEN_WRITE(value, 0, 3)
#define SPLL_AUX_CR_AUX_SEL_R(reg) WBGEN2_GEN_READ(reg, 0, 3)
/* definitions for field: BB reference divider in reg: Aux clock configuration register */
#define SPLL_AUX_CR_DIV_REF_MASK WBGEN2_GEN_MASK(3, 6)
#define SPLL_AUX_CR_DIV_REF_SHIFT 3
#define SPLL_AUX_CR_DIV_REF_W(value) WBGEN2_GEN_WRITE(value, 3, 6)
#define SPLL_AUX_CR_DIV_REF_R(reg) WBGEN2_GEN_READ(reg, 3, 6)
/* definitions for field: BB feedback divider in reg: Aux clock configuration register */
#define SPLL_AUX_CR_DIV_FB_MASK WBGEN2_GEN_MASK(9, 6)
#define SPLL_AUX_CR_DIV_FB_SHIFT 9
#define SPLL_AUX_CR_DIV_FB_W(value) WBGEN2_GEN_WRITE(value, 9, 6)
#define SPLL_AUX_CR_DIV_FB_R(reg) WBGEN2_GEN_READ(reg, 9, 6)
/* definitions for field: BB gating frequency select in reg: Aux clock configuration register */
#define SPLL_AUX_CR_GATE_MASK WBGEN2_GEN_MASK(15, 4)
#define SPLL_AUX_CR_GATE_SHIFT 15
#define SPLL_AUX_CR_GATE_W(value) WBGEN2_GEN_WRITE(value, 15, 4)
#define SPLL_AUX_CR_GATE_R(reg) WBGEN2_GEN_READ(reg, 15, 4)
/* definitions for register: Interrupt disable register */
/* definitions for field: Got a tag in reg: Interrupt disable register */
......@@ -229,32 +228,28 @@ PACKED struct SPLL_WB {
uint32_t CSR;
/* [0x4]: REG External Clock Control Register */
uint32_t ECCR;
/* [0x8]: REG DMTD Clock Control Register */
uint32_t DCCR;
/* [0xc]: REG Reference Channel Undersampling Enable Register */
uint32_t RCGER;
/* [0x10]: REG Output Channel Control Register */
/* [0x8]: REG Output Channel Control Register */
uint32_t OCCR;
/* [0x14]: REG Reference Channel Enable Register */
/* [0xc]: REG Reference Channel Tagging Enable Register */
uint32_t RCER;
/* [0x18]: REG Output Channel Enable Register */
/* [0x10]: REG Output Channel Tagging Enable Register */
uint32_t OCER;
/* [0x1c]: REG HPLL Period Error */
uint32_t PER_HPLL;
/* [0x20]: REG Helper DAC Output */
/* [0x14]: REG Helper DAC Output */
uint32_t DAC_HPLL;
/* [0x24]: REG Main DAC Output */
/* [0x18]: REG Main DAC Output */
uint32_t DAC_MAIN;
/* [0x28]: REG Deglitcher threshold */
/* [0x1c]: REG DDMTD Deglitcher threshold */
uint32_t DEGLITCH_THR;
/* [0x2c]: REG Debug FIFO Register - SPLL side */
/* [0x20]: REG Debug FIFO Register - SPLL side */
uint32_t DFR_SPLL;
/* [0x30]: REG Counter Resync Register - input channels */
/* [0x24]: REG Counter Resync Register - input channels */
uint32_t CRR_IN;
/* [0x34]: REG Counter Resync Register - output channels */
/* [0x28]: REG Counter Resync Register - output channels */
uint32_t CRR_OUT;
/* [0x2c]: REG Aux clock configuration register */
uint32_t AUX_CR;
/* padding to: 16 words */
uint32_t __padding_0[2];
uint32_t __padding_0[4];
/* [0x40]: REG Interrupt disable register */
uint32_t EIC_IDR;
/* [0x44]: REG Interrupt enable register */
......
obj-y += \
softpll/spll_common.o \
softpll/spll_external.o \
softpll/spll_helper.o \
softpll/spll_main.o \
softpll/spll_ptracker.o \
softpll/softpll_ng.o
\ No newline at end of file
This diff is collapsed.
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* softpll_ng.h: public SoftPLL API header */
#ifndef __SOFTPLL_NG_H
#define __SOFTPLL_NG_H
#include <stdio.h>
#include <stdint.h>
/* Modes */
/* SoftPLL operating modes, for mode parameter of spll_init(). */
/* Grand Master - lock to 10 MHz external reference */
#define SPLL_MODE_GRAND_MASTER 1
/* Free running master - 125 MHz refrence free running, DDMTD locked to it */
#define SPLL_MODE_FREE_RUNNING_MASTER 2
/* Slave mode - 125 MHz reference locked to one of the input clocks */
#define SPLL_MODE_SLAVE 3
/* Disabled mode: SoftPLL inactive */
#define SPLL_MODE_DISABLED 4
#define SPLL_ALL_CHANNELS 0xffff
/* Shortcut for 'channels' parameter in various API functions to perform operation on all channels */
#define SPLL_ALL_CHANNELS 0xffffffff
/* Aux clock flags */
#define SPLL_AUX_ENABLED (1<<0) /* Locking the particular aux channel to the WR reference is enabled */
#define SPLL_AUX_LOCKED (1<<1) /* The particular aux clock is already locked to WR reference */
/* Phase detector types */
#define SPLL_PD_DDMTD 0
#define SPLL_PD_BANGBANG 1
#define SPLL_AUX_ENABLED (1<<0)
#define SPLL_AUX_LOCKED (1<<1)
void spll_init(int mode, int slave_ref_channel, int align_pps);
/* Note on channel naming:
- ref_channel means a PHY recovered clock input. There can be one (as in WR core) or more (WR switch).
- out_channel means an output channel, which represents PLL feedback signal from a local, tunable oscillator. Every SPLL implementation
has at least one output channel, connected to the 125 / 62.5 MHz transceiver (WR) reference. This channel has always
index 0 and is compared against all reference channels by the phase tracking mechanism.
*/
/* PUBLIC API */
/*
Initializes the SoftPLL to work in mode (mode). Extra parameters depend on choice of the mode:
- for SPLL_MODE_GRAND_MASTER: non-zero (align_pps) value enables realignment of the WR reference rising edge to the
rising edge of 10 MHz external clock that comes immediately after a PPS pulse
- for SPLL_MODE_SLAVE: (ref_channel) indicates the reference channel to which we are locking our PLL.
*/
void spll_init(int mode, int ref_channel, int align_pps);
/* Disables the SoftPLL and cleans up stuff */
void spll_shutdown();
void spll_start_channel(int channel);
void spll_stop_channel(int channel);
int spll_check_lock(int channel);
void spll_set_phase_shift(int channel, int32_t value_picoseconds);
void spll_get_phase_shift(int channel, int32_t *current, int32_t *target);
void spll_enable_ptracker(int ref_channel, int enable);
int spll_read_ptracker(int channel, int32_t *phase_ps, int *enabled);
/* Returns number of reference and output channels implemented in HW. */
void spll_get_num_channels(int *n_ref, int *n_out);
int spll_shifter_busy(int channel);
int spll_get_delock_count();
/* Starts locking output channel (out_channel) */
void spll_start_channel(int out_channel);
/* Stops locking output channel (out_channel) */
void spll_stop_channel(int out_channel);
/* Returns non-zero if output channel (out_channel) is locked to a WR reference */
int spll_check_lock(int out_channel);
/* Sets phase setpoint for given output channel. */
void spll_set_phase_shift(int out_channel, int32_t value_picoseconds);
/* Retreives the current phase shift and desired setpoint for given output channel */
void spll_get_phase_shift(int out_channel, int32_t *current, int32_t *target);
/* Returns non-zero if the given output channel is busy phase shifting to a new preset */
int spll_shifter_busy(int out_channel);
/* Returns phase detector type used by particular output channel. There are two phase detectors available:
- DDMTD: locks only 62.5 / 125 MHz. Provides independent phase shift control for each output.
- Bang-Bang: locks to any frequency that is a result of rational (M/N) multiplication of the reference frequency.
The frequency can be set by spll_set_aux_frequency(). BB detector follows phase setpoint of channel 0 (WR reference),
there is no per-output shift control.
*/
int spll_get_phase_detector_type(int out_channel);
/* Sets the aux clock freuency when a BB detector is in use.
Must be called prior to spll_start_channel(). If the frequency is out of available range,
returns negative value */
int spll_set_aux_frequency(int out_channel, int32_t frequency);
/* Enables/disables phase tracking on channel (ref_channel). Phase is always measured between
the WR local reference (out_channel 0) and ref_channel */
void spll_enable_ptracker(int ref_channel, int enable);
/* Reads tracked phase shift value for given reference channel */
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)
in the main loop of the program if aux clocks are used in the design. */
int spll_update_aux_clocks();
int spll_get_aux_status(int channel);
void spll_set_dac(int index, int value);
int spll_get_dac(int index);
/* Returns the status of given aux clock output (SPLL_AUX_) */
int spll_get_aux_status(int out_channel);
/* Debug/testing functions */
/* Returns how many time the PLL has de-locked since last call of spll_init() */
int spll_get_delock_count();
/* Sets VCXO tuning DAC corresponding to output (out_channel) to a given value */
void spll_set_dac(int out_channel, int value);
/* Returns current DAC sample value for output (out_channel) */
int spll_get_dac(int out_channel);
#endif // __SOFTPLL_NG_H
#endif
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_common.c - common data structures and functions used by the SoftPLL */
#include "spll_defs.h"
#include "spll_common.h"
int pi_update(spll_pi_t *pi, int x)
{
int i_new, y;
pi->x = x;
i_new = pi->integrator + x;
y = ((i_new * pi->ki + x * pi->kp) >> PI_FRACBITS) + pi->bias;
/* clamping (output has to be in <y_min, y_max>) and
anti-windup: stop the integrator if the output is already
out of range and the output is going further away from
y_min/y_max. */
if (y < pi->y_min) {
y = pi->y_min;
if ((pi->anti_windup && (i_new > pi->integrator))
|| !pi->anti_windup)
pi->integrator = i_new;
} else if (y > pi->y_max) {
y = pi->y_max;
if ((pi->anti_windup && (i_new < pi->integrator))
|| !pi->anti_windup)
pi->integrator = i_new;
} else /* No antiwindup/clamping? */
pi->integrator = i_new;
pi->y = y;
return y;
}
void pi_init(spll_pi_t *pi)
{
pi->integrator = 0;
}
/* Lock detector state machine. Takes an error sample (y) and checks
if it's withing an acceptable range (i.e. <-ld.threshold,
ld.threshold>. If it has been inside the range for
(ld.lock_samples) cyckes, the FSM assumes the PLL is locked.
Return value:
0: PLL not locked
1: PLL locked
-1: PLL just got out of lock
*/
int ld_update(spll_lock_det_t *ld, int y)
{
ld->lock_changed = 0;
if (abs(y) <= ld->threshold) {
if (ld->lock_cnt < ld->lock_samples)
ld->lock_cnt++;
if (ld->lock_cnt == ld->lock_samples) {
ld->lock_changed = 1;
ld->locked = 1;
return 1;
}
} else {
if (ld->lock_cnt > ld->delock_samples)
ld->lock_cnt--;
if (ld->lock_cnt == ld->delock_samples) {
ld->lock_cnt = 0;
ld->lock_changed = 1;
ld->locked = 0;
return -1;
}
}
return ld->locked;
}
void ld_init(spll_lock_det_t *ld)
{
ld->locked = 0;
ld->lock_cnt = 0;
ld->lock_changed = 0;
}
void lowpass_init(spll_lowpass_t *lp, int alpha)
{
lp->y_d = 0x80000000;
lp->alpha = alpha;
}
int lowpass_update(spll_lowpass_t *lp, int x)
{
if (lp->y_d == 0x80000000) {
lp->y_d = x;
return x;
} else {
int scaled = (lp->alpha * (x - lp->y_d)) >> 15;
lp->y_d = lp->y_d + (scaled >> 1) + (scaled & 1);
return lp->y_d;
}
}
/* Enables/disables DDMTD tag generation on a given (channel).
Channels (0 ... splL_n_chan_ref - 1) are the reference channels
(e.g. transceivers' RX clocks or a local reference)
Channels (spll_n_chan_ref ... spll_n_chan_out + spll_n_chan_ref-1) are the output
channels (local voltage controlled oscillators). One output
(usually the first one) is always used to drive the oscillator
which produces the reference clock for the transceiver. Other
outputs can be used to discipline external oscillators
(e.g. on FMCs).
*/
void spll_enable_tagger(int channel, int enable)
{
if (channel >= spll_n_chan_ref) { /* Output channel? */
if (enable)
SPLL->OCER |= 1 << (channel - spll_n_chan_ref);
else
SPLL->OCER &= ~(1 << (channel - spll_n_chan_ref));
} else { /* Reference channel */
if (enable)
SPLL->RCER |= 1 << channel;
else
SPLL->RCER &= ~(1 << channel);
}
// TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
}
void spll_resync_dmtd_counter(int channel)
{
if (channel >= spll_n_chan_ref) /* Output channel? */
SPLL->CRR_OUT = 1 << (channel - spll_n_chan_ref);
else
SPLL->CRR_IN = 1 << channel;
}
int spll_check_dmtd_resync(int channel)
{
if (channel >= spll_n_chan_ref) /* Output channel? */
return (SPLL->CRR_OUT & (1 << (channel - spll_n_chan_ref))) ? 1 : 0;
else
return (SPLL->CRR_IN & (1 << channel)) ? 1 : 0;
}
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_common.h - common data structures and functions used by the SoftPLL */
#ifndef __SPLL_COMMON_H
#define __SPLL_COMMON_H
White Rabbit Softcore PLL (SoftPLL) - common definitions
#include <stdint.h>
#include <stdlib.h>
#include <syscon.h>
Copyright (c) 2010 - 2012 CERN / BE-CO-HT (Tomasz Włostowski)
Licensed under LGPL 2.1.
#include <hw/softpll_regs.h>
#include <hw/pps_gen_regs.h>
spll_common.h - common data structures and functions
#include "spll_defs.h"
*/
#define SPLL_LOCKED 1
#define SPLL_LOCKING 0
/* Number of reference/output channels. We don't plan to have more than one
SoftPLL instantiation per project, so these can remain global. */
extern int spll_n_chan_ref, spll_n_chan_out;
extern volatile struct SPLL_WB *SPLL;
extern volatile struct PPSG_WB *PPSG;
/* Number of reference/output channels. Currently we support only one
SoftPLL instantiation per project, so these can remain static. */
static int n_chan_ref, n_chan_out;
/* PI regulator state */
typedef struct {
......@@ -32,6 +51,7 @@ typedef struct {
delock_samples < lock_samples. */
int threshold; /* Error threshold */
int locked; /* Non-zero: we are locked */
int lock_changed;
} spll_lock_det_t;
/* simple, 1st-order lowpass filter */
......@@ -40,143 +60,20 @@ typedef struct {
int y_d;
} spll_lowpass_t;
/* initializes the PI controller state. Currently almost a stub. */
void pi_init(spll_pi_t *pi);
/* Processes a single sample (x) with PI control algorithm
(pi). Returns the value (y) to drive the actuator. */
static inline int pi_update(spll_pi_t *pi, int x)
{
int i_new, y;
pi->x = x;
i_new = pi->integrator + x;
y = ((i_new * pi->ki + x * pi->kp) >> PI_FRACBITS) + pi->bias;
/* clamping (output has to be in <y_min, y_max>) and
anti-windup: stop the integrator if the output is already
out of range and the output is going further away from
y_min/y_max. */
if (y < pi->y_min) {
y = pi->y_min;
if ((pi->anti_windup && (i_new > pi->integrator))
|| !pi->anti_windup)
pi->integrator = i_new;
} else if (y > pi->y_max) {
y = pi->y_max;
if ((pi->anti_windup && (i_new < pi->integrator))
|| !pi->anti_windup)
pi->integrator = i_new;
} else /* No antiwindup/clamping? */
pi->integrator = i_new;
pi->y = y;
return y;
}
int pi_update(spll_pi_t *pi, int x);
/* initializes the PI controller state. Currently almost a stub. */
static inline void pi_init(spll_pi_t *pi)
{
pi->integrator = 0;
}
/* Lock detector state machine. Takes an error sample (y) and checks
if it's withing an acceptable range (i.e. <-ld.threshold,
ld.threshold>. If it has been inside the range for
(ld.lock_samples) cyckes, the FSM assumes the PLL is locked.
Return value:
0: PLL not locked
1: PLL locked
-1: PLL just got out of lock
*/
static inline int ld_update(spll_lock_det_t *ld, int y)
{
if (abs(y) <= ld->threshold) {
if (ld->lock_cnt < ld->lock_samples)
ld->lock_cnt++;
if (ld->lock_cnt == ld->lock_samples) {
ld->locked = 1;
return 1;
}
} else {
if (ld->lock_cnt > ld->delock_samples)
ld->lock_cnt--;
if (ld->lock_cnt == ld->delock_samples) {
ld->lock_cnt = 0;
ld->locked = 0;
return -1;
}
}
return ld->locked;
}
static void ld_init(spll_lock_det_t *ld)
{
ld->locked = 0;
ld->lock_cnt = 0;
}
static void lowpass_init(spll_lowpass_t *lp, int alpha)
{
lp->y_d = 0x80000000;
lp->alpha = alpha;
}
static int lowpass_update(spll_lowpass_t *lp, int x)
{
if (lp->y_d == 0x80000000) {
lp->y_d = x;
return x;
} else {
int scaled = (lp->alpha * (x - lp->y_d)) >> 15;
lp->y_d = lp->y_d + (scaled >> 1) + (scaled & 1);
return lp->y_d;
}
}
/* Enables/disables DDMTD tag generation on a given (channel).
Channels (0 ... n_chan_ref - 1) are the reference channels
(e.g. transceivers' RX clocks or a local reference)
Channels (n_chan_ref ... n_chan_out + n_chan_ref-1) are the output
channels (local voltage controlled oscillators). One output
(usually the first one) is always used to drive the oscillator
which produces the reference clock for the transceiver. Other
outputs can be used to discipline external oscillators
(e.g. on FMCs).
*/
static void spll_enable_tagger(int channel, int enable)
{
if (channel >= n_chan_ref) { /* Output channel? */
if (enable)
SPLL->OCER |= 1 << (channel - n_chan_ref);
else
SPLL->OCER &= ~(1 << (channel - n_chan_ref));
} else { /* Reference channel */
if (enable)
SPLL->RCER |= 1 << channel;
else
SPLL->RCER &= ~(1 << channel);
}
// TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
}
static void spll_resync_dmtd_counter(int channel)
{
if (channel >= n_chan_ref) /* Output channel? */
SPLL->CRR_OUT = 1 << (channel - n_chan_ref);
else
SPLL->CRR_IN = 1 << channel;
}
static int spll_check_dmtd_resync(int channel)
{
if (channel >= n_chan_ref) /* Output channel? */
return (SPLL->CRR_OUT & (1 << (channel - n_chan_ref))) ? 1 : 0;
else
return (SPLL->CRR_IN & (1 << channel)) ? 1 : 0;
}
void ld_init(spll_lock_det_t *ld);
int ld_update(spll_lock_det_t *ld, int y);
void lowpass_init(spll_lowpass_t *lp, int alpha);
int lowpass_update(spll_lowpass_t *lp, int x);
void spll_enable_tagger(int channel, int enable);
void spll_resync_dmtd_counter(int channel);
int spll_check_dmtd_resync(int channel);
#endif // __SPLL_COMMON_H
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_external.h - implementation of SoftPLL servo for the
external (10 MHz - Grandmaster mode) reference channel */
#include "spll_external.h"
#include "spll_debug.h"
#define BB_ERROR_BITS 10
void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
s->pi.y_min = 5;
s->pi.y_max = (1 << DAC_BITS) - 5;
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 = 250;
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)
{
switch (s->realign_state) {
case REALIGN_STAGE1:
SPLL->ECCR |= SPLL_ECCR_ALIGN_EN;
s->realign_state = REALIGN_STAGE1_WAIT;
s->realign_timer = timer_get_tics();
break;
case REALIGN_STAGE1_WAIT:
if (SPLL->ECCR & SPLL_ECCR_ALIGN_DONE)
s->realign_state = REALIGN_STAGE2;
else if (timer_get_tics() - s->realign_timer >
2 * TICS_PER_SECOND) {
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;
s->realign_timer = timer_get_tics();
}
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 (timer_get_tics() - s->realign_timer >
2 * TICS_PER_SECOND) {
PPSG->ESCR = 0;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
case REALIGN_PPS_INVALID:
case REALIGN_DISABLED:
case REALIGN_DONE:
return;
}
}
int external_update(struct spll_external_state *s, int tag, int source)
{
int err, y, y2, ylt;
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;
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);
spll_debug(DBG_SAMPLE_ID | DBG_EXT, s->sample_n++, 0);
spll_debug(DBG_Y | DBG_EXT, y2, 1);
if (ld_update(&s->ld, y2 - ylt))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
void external_start(struct spll_external_state *s)
{
SPLL->ECCR = 0;
s->sample_n = 0;
s->realign_state =
(s->realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED);
SPLL->ECCR = SPLL_ECCR_EXT_EN;
spll_debug(DBG_EVENT | DBG_EXT, DBG_EVT_START, 1);
}
int external_locked(struct spll_external_state *s)
{
return (s->ld.locked
&& (s->realign_clocks ? s->realign_state == REALIGN_DONE : 1));
}
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
#include <syscon.h>
/* spll_external.h - definitions & prototypes for the
external (10 MHz - Grandmaster mode) reference channel */
/* Number of bits of the BB phase detector error counter. Bit [BB_ERROR_BITS] is the wrap-around bit */
#define BB_ERROR_BITS 16
#ifndef __SPLL_EXTERNAL_H
#define __SPLL_EXTERNAL_H
#include "spll_common.h"
/* Alignment FSM states */
......@@ -58,152 +69,14 @@ struct spll_external_state {
spll_lock_det_t ld;
};
static void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
s->pi.y_min = 5;
s->pi.y_max = (1 << DAC_BITS) - 5;
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 = 250;
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)
{
switch (s->realign_state) {
case REALIGN_STAGE1:
SPLL->ECCR |= SPLL_ECCR_ALIGN_EN;
s->realign_state = REALIGN_STAGE1_WAIT;
s->realign_timer = timer_get_tics();
break;
case REALIGN_STAGE1_WAIT:
if (SPLL->ECCR & SPLL_ECCR_ALIGN_DONE)
s->realign_state = REALIGN_STAGE2;
else if (timer_get_tics() - s->realign_timer >
2 * TICS_PER_SECOND) {
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;
s->realign_timer = timer_get_tics();
}
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 (timer_get_tics() - s->realign_timer >
2 * TICS_PER_SECOND) {
PPSG->ESCR = 0;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
case REALIGN_PPS_INVALID:
case REALIGN_DISABLED:
case REALIGN_DONE:
return;
}
}
static int external_update(struct spll_external_state *s, int tag, int source)
{
int err, y, y2, ylt;
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;
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);
spll_debug(DBG_SAMPLE_ID | DBG_EXT, s->sample_n++, 0);
spll_debug(DBG_Y | DBG_EXT, y2, 1);
if (ld_update(&s->ld, y2 - ylt))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
static void external_start(struct spll_external_state *s)
{
// mprintf("ExtStartup\n");
SPLL->ECCR = 0;
s->sample_n = 0;
s->realign_state =
(s->realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED);
SPLL->ECCR = SPLL_ECCR_EXT_EN;
spll_debug(DBG_EVENT | DBG_EXT, DBG_EVT_START, 1);
}
static inline int external_locked(struct spll_external_state *s)
{
return (s->ld.locked
&& (s->realign_clocks ? s->realign_state == REALIGN_DONE : 1));
}
void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks);
int external_update(struct spll_external_state *s, int tag, int source);
void external_start(struct spll_external_state *s);
int external_locked(struct spll_external_state *s);
#endif // __SPLL_EXTERNAL_H
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_helper.c - implmentation of the Helper PLL servo algorithm. */
#include "spll_helper.h"
#include "spll_debug.h"
void helper_init(struct spll_helper_state *s, int ref_channel)
{
/* Phase branch PI controller */
s->pi.y_min = 5;
s->pi.y_max = (1 << DAC_BITS) - 5;
s->pi.kp = (int)(0.3 * 32.0 * 16.0); // / 2;
s->pi.ki = (int)(0.03 * 32.0 * 3.0); // / 2;
s->pi.anti_windup = 1;
/* Phase branch lock detection */
s->ld.threshold = 200;
s->ld.lock_samples = 10000;
s->ld.delock_samples = 100;
s->ref_src = ref_channel;
s->delock_count = 0;
}
int helper_update(struct spll_helper_state *s, int tag,
int source)
{
int err, y;
if (source == s->ref_src) {
spll_debug(DBG_TAG | DBG_HELPER, tag, 0);
spll_debug(DBG_REF | DBG_HELPER, s->p_setpoint, 0);
if (s->tag_d0 < 0) {
s->p_setpoint = tag;
s->tag_d0 = tag;
return SPLL_LOCKING;
}
if (s->tag_d0 > tag)
s->p_adder += (1 << TAG_BITS);
err = (tag + s->p_adder) - s->p_setpoint;
if (HELPER_ERROR_CLAMP) {
if (err < -HELPER_ERROR_CLAMP)
err = -HELPER_ERROR_CLAMP;
if (err > HELPER_ERROR_CLAMP)
err = HELPER_ERROR_CLAMP;
}
if ((tag + s->p_adder) > HELPER_TAG_WRAPAROUND
&& s->p_setpoint > HELPER_TAG_WRAPAROUND) {
s->p_adder -= HELPER_TAG_WRAPAROUND;
s->p_setpoint -= HELPER_TAG_WRAPAROUND;
}
s->p_setpoint += (1 << HPLL_N);
s->tag_d0 = tag;
y = pi_update((spll_pi_t *)&s->pi, err);
SPLL->DAC_HPLL = y;
spll_debug(DBG_SAMPLE_ID | DBG_HELPER, s->sample_n++, 0);
spll_debug(DBG_Y | DBG_HELPER, y, 0);
spll_debug(DBG_ERR | DBG_HELPER, err, 1);
if (ld_update((spll_lock_det_t *)&s->ld, err))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
void helper_start(struct spll_helper_state *s)
{
/* Set the bias to the upper end of tuning range. This is to ensure that
the HPLL will always lock on positive frequency offset. */
s->pi.bias = s->pi.y_max;
s->p_setpoint = 0;
s->p_adder = 0;
s->sample_n = 0;
s->tag_d0 = -1;
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
spll_enable_tagger(s->ref_src, 1);
spll_debug(DBG_EVENT | DBG_HELPER, DBG_EVT_START, 1);
}
/* State of the Helper PLL producing a clock (clk_dmtd_i) which is
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_helper.h - the helper PLL producing a clock (clk_dmtd_i) which is
slightly offset in frequency from the recovered/reference clock
(clk_rx_i or clk_ref_i), so the Main PLL can use it to perform
linear phase measurements.
*/
linear phase measurements. */
#ifndef __SPLL_HELPER_H
#define __SPLL_HELPER_H
#include "spll_common.h"
#define SPLL_LOCKED 1
#define SPLL_LOCKING 0
#define HELPER_TAG_WRAPAROUND 100000000
......@@ -24,89 +35,10 @@ struct spll_helper_state {
spll_lock_det_t ld;
};
static void helper_init(volatile struct spll_helper_state *s, int ref_channel)
{
/* Phase branch PI controller */
s->pi.y_min = 5;
s->pi.y_max = (1 << DAC_BITS) - 5;
s->pi.kp = (int)(0.3 * 32.0 * 16.0); // / 2;
s->pi.ki = (int)(0.03 * 32.0 * 3.0); // / 2;
s->pi.anti_windup = 1;
/* Phase branch lock detection */
s->ld.threshold = 200;
s->ld.lock_samples = 10000;
s->ld.delock_samples = 100;
s->ref_src = ref_channel;
s->delock_count = 0;
}
static int helper_update(volatile struct spll_helper_state *s, int tag,
int source)
{
int err, y;
if (source == s->ref_src) {
spll_debug(DBG_TAG | DBG_HELPER, tag, 0);
spll_debug(DBG_REF | DBG_HELPER, s->p_setpoint, 0);
if (s->tag_d0 < 0) {
s->p_setpoint = tag;
s->tag_d0 = tag;
return SPLL_LOCKING;
}
if (s->tag_d0 > tag)
s->p_adder += (1 << TAG_BITS);
err = (tag + s->p_adder) - s->p_setpoint;
if (HELPER_ERROR_CLAMP) {
if (err < -HELPER_ERROR_CLAMP)
err = -HELPER_ERROR_CLAMP;
if (err > HELPER_ERROR_CLAMP)
err = HELPER_ERROR_CLAMP;
}
if ((tag + s->p_adder) > HELPER_TAG_WRAPAROUND
&& s->p_setpoint > HELPER_TAG_WRAPAROUND) {
s->p_adder -= HELPER_TAG_WRAPAROUND;
s->p_setpoint -= HELPER_TAG_WRAPAROUND;
}
s->p_setpoint += (1 << HPLL_N);
s->tag_d0 = tag;
y = pi_update((spll_pi_t *)&s->pi, err);
SPLL->DAC_HPLL = y;
spll_debug(DBG_SAMPLE_ID | DBG_HELPER, s->sample_n++, 0);
spll_debug(DBG_Y | DBG_HELPER, y, 0);
spll_debug(DBG_ERR | DBG_HELPER, err, 1);
if (ld_update((spll_lock_det_t *)&s->ld, err))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
static void helper_start(volatile struct spll_helper_state *s)
{
/* Set the bias to the upper end of tuning range. This is to ensure that
the HPLL will always lock on positive frequency offset. */
s->pi.bias = s->pi.y_max;
s->p_setpoint = 0;
s->p_adder = 0;
s->sample_n = 0;
s->tag_d0 = -1;
void helper_init(struct spll_helper_state *s, int ref_channel);
int helper_update(struct spll_helper_state *s, int tag,
int source);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
void helper_start(struct spll_helper_state *s);
spll_enable_tagger(s->ref_src, 1);
spll_debug(DBG_EVENT | DBG_HELPER, DBG_EVT_START, 1);
}
#endif // __SPLL_HELPER_H
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_main.c - Implementation of the main DDMTD PLL. */
#include "spll_main.h"
#include "spll_debug.h"
#define MPLL_TAG_WRAPAROUND 100000000
#define MATCH_NEXT_TAG 0
#define MATCH_WAIT_REF 1
#define MATCH_WAIT_OUT 2
#undef WITH_SEQUENCING
void mpll_init(struct spll_main_state *s, int id_ref,
int id_out)
{
/* Frequency branch PI controller */
s->pi.y_min = 5;
s->pi.y_max = 65530;
s->pi.anti_windup = 1;
s->pi.bias = 65000;
s->pi.kp = 1100; // / 2;
s->pi.ki = 30; // / 2;
s->delock_count = 0;
/* Freqency branch lock detection */
s->ld.threshold = 1200;
s->ld.lock_samples = 1000;
s->ld.delock_samples = 100;
s->id_ref = id_ref;
s->id_out = id_out;
s->dac_index = id_out - spll_n_chan_ref;
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
}
void mpll_start(struct spll_main_state *s)
{
s->adder_ref = s->adder_out = 0;
s->tag_ref = -1;
s->tag_out = -1;
s->tag_ref_d = -1;
s->tag_out_d = -1;
s->seq_ref = 0;
s->seq_out = 0;
s->match_state = MATCH_NEXT_TAG;
s->phase_shift_target = 0;
s->phase_shift_current = 0;
s->sample_n = 0;
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
spll_enable_tagger(s->id_ref, 1);
spll_enable_tagger(s->id_out, 1);
spll_debug(DBG_EVENT | DBG_MAIN, DBG_EVT_START, 1);
}
void mpll_stop(struct spll_main_state *s)
{
spll_enable_tagger(s->id_out, 0);
}
int mpll_update(struct spll_main_state *s, int tag, int source)
{
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)
s->tag_ref = tag;
if (source == s->id_out)
s->tag_out = tag;
#endif
if (s->tag_ref >= 0 && s->tag_out >= 0) {
if (s->tag_ref_d >= 0 && s->tag_ref_d > s->tag_ref)
s->adder_ref += (1 << TAG_BITS);
if (s->tag_out_d >= 0 && s->tag_out_d > s->tag_out)
s->adder_out += (1 << TAG_BITS);
s->tag_ref_d = s->tag_ref;
s->tag_out_d = s->tag_out;
err = s->adder_ref + s->tag_ref - s->adder_out - s->tag_out;
#ifndef WITH_SEQUENCING
/* Hack: the PLL is locked, so the tags are close to
each other. But when we start phase shifting, after
reaching full clock period, one of the reference
tags will flip before the other, causing a suddent
2**HPLL_N jump in the error. So, once the PLL is
locked, we just mask out everything above
2**HPLL_N.
Proper solution: tag sequence numbers */
if (s->ld.locked) {
err &= (1 << HPLL_N) - 1;
if (err & (1 << (HPLL_N - 1)))
err |= ~((1 << HPLL_N) - 1);
}
#endif
y = pi_update((spll_pi_t *)&s->pi, err);
SPLL->DAC_MAIN = SPLL_DAC_MAIN_VALUE_W(y)
| SPLL_DAC_MAIN_DAC_SEL_W(s->dac_index);
spll_debug(DBG_MAIN | DBG_REF, s->tag_ref + s->adder_ref, 0);
spll_debug(DBG_MAIN | DBG_TAG, s->tag_out + s->adder_out, 0);
spll_debug(DBG_MAIN | DBG_ERR, err, 0);
spll_debug(DBG_MAIN | DBG_SAMPLE_ID, s->sample_n++, 0);
spll_debug(DBG_MAIN | DBG_Y, y, 1);
s->tag_out = -1;
s->tag_ref = -1;
if (s->adder_ref > 2 * MPLL_TAG_WRAPAROUND
&& s->adder_out > 2 * MPLL_TAG_WRAPAROUND) {
s->adder_ref -= MPLL_TAG_WRAPAROUND;
s->adder_out -= MPLL_TAG_WRAPAROUND;
}
if (s->ld.locked) {
if (s->phase_shift_current < s->phase_shift_target) {
s->phase_shift_current++;
s->adder_ref++;
} else if (s->phase_shift_current >
s->phase_shift_target) {
s->phase_shift_current--;
s->adder_ref--;
}
}
if (ld_update((spll_lock_det_t *)&s->ld, err))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
int mpll_set_phase_shift(struct spll_main_state *s,
int desired_shift)
{
s->phase_shift_target = desired_shift;
return 0;
}
int mpll_shifter_busy(struct spll_main_state *s)
{
return s->phase_shift_target != s->phase_shift_current;
}
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
#define MPLL_TAG_WRAPAROUND 100000000
/* spll_main.h - the main DDMTD PLL. Locks output clock to any reference
with programmable phase shift. */
#define MATCH_NEXT_TAG 0
#define MATCH_WAIT_REF 1
#define MATCH_WAIT_OUT 2
#ifndef __SPLL_MAIN_H
#define __SPLL_MAIN_H
#undef WITH_SEQUENCING
#include "spll_common.h"
/* State of the Main PLL */
struct spll_main_state {
......@@ -29,185 +37,18 @@ struct spll_main_state {
int dac_index;
};
static void mpll_init(volatile struct spll_main_state *s, int id_ref,
int id_out)
{
/* Frequency branch PI controller */
s->pi.y_min = 5;
s->pi.y_max = 65530;
s->pi.anti_windup = 1;
s->pi.bias = 65000;
s->pi.kp = 1100; // / 2;
s->pi.ki = 30; // / 2;
s->delock_count = 0;
void mpll_init(struct spll_main_state *s, int id_ref,
int id_out);
/* Freqency branch lock detection */
s->ld.threshold = 1200;
s->ld.lock_samples = 1000;
s->ld.delock_samples = 100;
s->id_ref = id_ref;
s->id_out = id_out;
s->dac_index = id_out - n_chan_ref;
void mpll_stop(struct spll_main_state *s);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
}
void mpll_start(struct spll_main_state *s);
static void mpll_start(volatile struct spll_main_state *s)
{
s->adder_ref = s->adder_out = 0;
s->tag_ref = -1;
s->tag_out = -1;
s->tag_ref_d = -1;
s->tag_out_d = -1;
s->seq_ref = 0;
s->seq_out = 0;
s->match_state = MATCH_NEXT_TAG;
int mpll_update(struct spll_main_state *s, int tag, int source);
s->phase_shift_target = 0;
s->phase_shift_current = 0;
s->sample_n = 0;
int mpll_set_phase_shift(struct spll_main_state *s,
int desired_shift);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
int mpll_shifter_busy(struct spll_main_state *s);
spll_enable_tagger(s->id_ref, 1);
spll_enable_tagger(s->id_out, 1);
spll_debug(DBG_EVENT | DBG_MAIN, DBG_EVT_START, 1);
}
static void mpll_stop(volatile struct spll_main_state *s)
{
spll_enable_tagger(s->id_out, 0);
}
static int mpll_update(volatile struct spll_main_state *s, int tag, int source)
{
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)
s->tag_ref = tag;
if (source == s->id_out)
s->tag_out = tag;
#endif
if (s->tag_ref >= 0 && s->tag_out >= 0) {
if (s->tag_ref_d >= 0 && s->tag_ref_d > s->tag_ref)
s->adder_ref += (1 << TAG_BITS);
if (s->tag_out_d >= 0 && s->tag_out_d > s->tag_out)
s->adder_out += (1 << TAG_BITS);
s->tag_ref_d = s->tag_ref;
s->tag_out_d = s->tag_out;
err = s->adder_ref + s->tag_ref - s->adder_out - s->tag_out;
#ifndef WITH_SEQUENCING
/* Hack: the PLL is locked, so the tags are close to
each other. But when we start phase shifting, after
reaching full clock period, one of the reference
tags will flip before the other, causing a suddent
2**HPLL_N jump in the error. So, once the PLL is
locked, we just mask out everything above
2**HPLL_N.
Proper solution: tag sequence numbers */
if (s->ld.locked) {
err &= (1 << HPLL_N) - 1;
if (err & (1 << (HPLL_N - 1)))
err |= ~((1 << HPLL_N) - 1);
}
#endif
y = pi_update((spll_pi_t *)&s->pi, err);
SPLL->DAC_MAIN = SPLL_DAC_MAIN_VALUE_W(y)
| SPLL_DAC_MAIN_DAC_SEL_W(s->dac_index);
spll_debug(DBG_MAIN | DBG_REF, s->tag_ref + s->adder_ref, 0);
spll_debug(DBG_MAIN | DBG_TAG, s->tag_out + s->adder_out, 0);
spll_debug(DBG_MAIN | DBG_ERR, err, 0);
spll_debug(DBG_MAIN | DBG_SAMPLE_ID, s->sample_n++, 0);
spll_debug(DBG_MAIN | DBG_Y, y, 1);
s->tag_out = -1;
s->tag_ref = -1;
if (s->adder_ref > 2 * MPLL_TAG_WRAPAROUND
&& s->adder_out > 2 * MPLL_TAG_WRAPAROUND) {
s->adder_ref -= MPLL_TAG_WRAPAROUND;
s->adder_out -= MPLL_TAG_WRAPAROUND;
}
if (s->ld.locked) {
if (s->phase_shift_current < s->phase_shift_target) {
s->phase_shift_current++;
s->adder_ref++;
} else if (s->phase_shift_current >
s->phase_shift_target) {
s->phase_shift_current--;
s->adder_ref--;
}
}
if (ld_update((spll_lock_det_t *)&s->ld, err))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
static int mpll_set_phase_shift(volatile struct spll_main_state *s,
int desired_shift)
{
s->phase_shift_target = desired_shift;
return 0;
}
static int mpll_shifter_busy(volatile struct spll_main_state *s)
{
return s->phase_shift_target != s->phase_shift_current;
}
#endif // __SPLL_MAIN_H
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* spll_ptracker.c - implementation of phase trackers. */
#include "spll_ptracker.h"
void ptracker_init(struct spll_ptracker_state *s, int id_a,
int id_b, int num_avgs)
{
s->tag_a = s->tag_b = -1;
s->id_a = id_a;
s->id_b = id_b;
s->ready = 0;
s->n_avg = num_avgs;
s->acc = 0;
s->avg_count = 0;
s->sample_n = 0;
s->preserve_sign = 0;
}
void ptracker_start(struct spll_ptracker_state *s)
{
s->tag_a = s->tag_b = -1;
s->ready = 0;
s->acc = 0;
s->avg_count = 0;
s->sample_n = 0;
s->preserve_sign = 0;
spll_resync_dmtd_counter(s->id_b);
spll_enable_tagger(s->id_a, 1);
spll_enable_tagger(s->id_b, 1);
}
#define PTRACK_WRAP_LO (1<<(HPLL_N-2))
#define PTRACK_WRAP_HI (3*(1<<(HPLL_N-2)))
int ptracker_update(struct spll_ptracker_state *s, int tag,
int source)
{
if (source == s->id_a)
s->tag_a = tag;
if (source == s->id_b)
s->tag_b = tag;
if (s->tag_a >= 0 && s->tag_b >= 0) {
int delta = (s->tag_a - s->tag_b) & ((1 << HPLL_N) - 1);
s->sample_n++;
if (s->avg_count == 0) {
if (delta <= PTRACK_WRAP_LO)
s->preserve_sign = -1;
else if (delta >= PTRACK_WRAP_HI)
s->preserve_sign = 1;
else
s->preserve_sign = 0;
s->avg_count++;
s->acc = delta;
} else {
if (delta <= PTRACK_WRAP_LO && s->preserve_sign > 0)
s->acc += delta + (1 << HPLL_N);
else if (delta >= PTRACK_WRAP_HI
&& s->preserve_sign < 0)
s->acc += delta - (1 << HPLL_N);
else
s->acc += delta;
s->avg_count++;
if (s->avg_count == s->n_avg) {
s->phase_val = s->acc / s->n_avg;
s->ready = 1;
s->acc = 0;
s->avg_count = 0;
}
}
s->tag_b = s->tag_a = -1;
}
return SPLL_LOCKING;
}
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2010 - 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
/* State of a Phase Tracker */
/* spll_ptracker.h - data structures & prototypes for phase trackers. */
#ifndef __SPLL_PTRACKER_H
#define __SPLL_PTRACKER_H
#include "spll_common.h"
struct spll_ptracker_state {
int id_a, id_b;
......@@ -10,86 +23,12 @@ struct spll_ptracker_state {
int preserve_sign;
};
static void ptracker_init(volatile struct spll_ptracker_state *s, int id_a,
int id_b, int num_avgs)
{
s->tag_a = s->tag_b = -1;
s->id_a = id_a;
s->id_b = id_b;
s->ready = 0;
s->n_avg = num_avgs;
s->acc = 0;
s->avg_count = 0;
s->sample_n = 0;
s->preserve_sign = 0;
}
static void ptracker_start(volatile struct spll_ptracker_state *s)
{
s->tag_a = s->tag_b = -1;
s->ready = 0;
s->acc = 0;
s->avg_count = 0;
s->sample_n = 0;
s->preserve_sign = 0;
spll_resync_dmtd_counter(s->id_b);
spll_enable_tagger(s->id_a, 1);
spll_enable_tagger(s->id_b, 1);
}
#define PTRACK_WRAP_LO (1<<(HPLL_N-2))
#define PTRACK_WRAP_HI (3*(1<<(HPLL_N-2)))
static int ptracker_update(volatile struct spll_ptracker_state *s, int tag,
int source)
{
if (source == s->id_a)
s->tag_a = tag;
if (source == s->id_b)
s->tag_b = tag;
if (s->tag_a >= 0 && s->tag_b >= 0) {
int delta = (s->tag_a - s->tag_b) & ((1 << HPLL_N) - 1);
s->sample_n++;
if (s->avg_count == 0) {
if (delta <= PTRACK_WRAP_LO)
s->preserve_sign = -1;
else if (delta >= PTRACK_WRAP_HI)
s->preserve_sign = 1;
else
s->preserve_sign = 0;
s->avg_count++;
s->acc = delta;
} else {
if (delta <= PTRACK_WRAP_LO && s->preserve_sign > 0)
s->acc += delta + (1 << HPLL_N);
else if (delta >= PTRACK_WRAP_HI
&& s->preserve_sign < 0)
s->acc += delta - (1 << HPLL_N);
else
s->acc += delta;
s->avg_count++;
if (s->avg_count == s->n_avg) {
s->phase_val = s->acc / s->n_avg;
s->ready = 1;
s->acc = 0;
s->avg_count = 0;
}
void ptracker_init(struct spll_ptracker_state *s, int id_a,
int id_b, int num_avgs);
}
void ptracker_start(struct spll_ptracker_state *s);
s->tag_b = s->tag_a = -1;
}
int ptracker_update(struct spll_ptracker_state *s, int tag,
int source);
return SPLL_LOCKING;
}
#endif // __SPLL_PTRACKER_H
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