Commit fcaf3bed authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Alessandro Rubini

softpll: rewritten phase tracker stuff, now it easily handles 18 ports. To be…

softpll: rewritten phase tracker stuff, now it easily handles 18 ports. To be merged with wrpc-sw softpll branch.
parent 65da3053
CROSS_COMPILE ?= lm32-elf- CROSS_COMPILE ?= lm32-elf-
LIBSOFTPLL_DIR = ../userspace/ptp-noposix/softpll LIBSOFTPLL_DIR = softpll
OBJS = main.o dev/uart.o dev/timer.o lib/mprintf.o dev/ad9516.o ipc/minipc-mem-server.o ipc/rt_ipc.o $(LIBSOFTPLL_DIR)/softpll_ng.o
OBJS = main.o dev/uart.o dev/timer.o lib/mprintf.o dev/ad9516.o ipc/minipc-mem-server.o ipc/rt_ipc.o
include softpll/softpll.mk
CFLAGS_PLATFORM = -mmultiply-enabled -mbarrel-shift-enabled -Idev CFLAGS_PLATFORM = -mmultiply-enabled -mbarrel-shift-enabled -Idev
LDFLAGS_PLATFORM = -mmultiply-enabled -mbarrel-shift-enabled -nostdlib -T target/lm32/ram.ld LDFLAGS_PLATFORM = -mmultiply-enabled -mbarrel-shift-enabled -nostdlib -T target/lm32/ram.ld
......
...@@ -151,8 +151,6 @@ static int rts_get_state_func(const struct minipc_pd *pd, uint32_t *args, void * ...@@ -151,8 +151,6 @@ static int rts_get_state_func(const struct minipc_pd *pd, uint32_t *args, void *
struct rts_pll_state *tmp = (struct rts_pll_state *)ret; struct rts_pll_state *tmp = (struct rts_pll_state *)ret;
int i; int i;
// TRACE("IPC Call: %s [rv at %x]\n", __FUNCTION__, ret);
pstate.ipc_count++; pstate.ipc_count++;
/* gaaaah, somebody should write a SWIG plugin for generating this stuff. */ /* gaaaah, somebody should write a SWIG plugin for generating this stuff. */
...@@ -163,8 +161,6 @@ static int rts_get_state_func(const struct minipc_pd *pd, uint32_t *args, void * ...@@ -163,8 +161,6 @@ static int rts_get_state_func(const struct minipc_pd *pd, uint32_t *args, void *
tmp->delock_count = spll_get_delock_count(); tmp->delock_count = spll_get_delock_count();
tmp->ipc_count = pstate.ipc_count; tmp->ipc_count = pstate.ipc_count;
softpll_copy_debug_data(&tmp->debug_data[0]);
for(i=0; i<RTS_PLL_CHANNELS;i++) for(i=0; i<RTS_PLL_CHANNELS;i++)
{ {
tmp->channels[i].priority = htonl(pstate.channels[i].priority); tmp->channels[i].priority = htonl(pstate.channels[i].priority);
...@@ -199,7 +195,8 @@ static int rts_adjust_phase_func(const struct minipc_pd *pd, uint32_t *args, voi ...@@ -199,7 +195,8 @@ static int rts_adjust_phase_func(const struct minipc_pd *pd, uint32_t *args, voi
static int rts_enable_ptracker_func(const struct minipc_pd *pd, uint32_t *args, void *ret) static int rts_enable_ptracker_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
{ {
pstate.ipc_count++; pstate.ipc_count++;
*(int *) ret = spll_enable_ptracker((int)args[0], (int)args[1]); spll_enable_ptracker((int)args[0], (int)args[1]);
*(int *) ret = 0;
} }
static int rts_debug_command_func(const struct minipc_pd *pd, uint32_t *args, void *ret) static int rts_debug_command_func(const struct minipc_pd *pd, uint32_t *args, void *ret)
......
/*
Register definitions for slave core: WR Switch PPS generator and RTC
* File : pps_gen_regs.h
* Author : auto-generated by wbgen2 from pps_gen_wb.wb
* Created : Fri Jul 26 15:09:09 2013
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE pps_gen_wb.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_PPS_GEN_WB_WB
#define __WBGEN2_REGDEFS_PPS_GEN_WB_WB
#include <stdint.h>
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Control Register */
/* definitions for field: Reset counter in reg: Control Register */
#define PPSG_CR_CNT_RST WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Enable counter in reg: Control Register */
#define PPSG_CR_CNT_EN WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Adjust offset in reg: Control Register */
#define PPSG_CR_CNT_ADJ WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Set time in reg: Control Register */
#define PPSG_CR_CNT_SET WBGEN2_GEN_MASK(3, 1)
/* definitions for field: PPS Pulse width in reg: Control Register */
#define PPSG_CR_PWIDTH_MASK WBGEN2_GEN_MASK(4, 28)
#define PPSG_CR_PWIDTH_SHIFT 4
#define PPSG_CR_PWIDTH_W(value) WBGEN2_GEN_WRITE(value, 4, 28)
#define PPSG_CR_PWIDTH_R(reg) WBGEN2_GEN_READ(reg, 4, 28)
/* definitions for register: Nanosecond counter register */
/* definitions for register: UTC Counter register (least-significant part) */
/* definitions for register: UTC Counter register (most-significant part) */
/* definitions for register: Nanosecond adjustment register */
/* definitions for register: UTC Adjustment register (least-significant part) */
/* definitions for register: UTC Adjustment register (most-significant part) */
/* definitions for register: External sync control register */
/* definitions for field: Sync to external PPS input in reg: External sync control register */
#define PPSG_ESCR_SYNC WBGEN2_GEN_MASK(0, 1)
/* definitions for field: PPS output valid in reg: External sync control register */
#define PPSG_ESCR_PPS_VALID WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Timecode output(UTC+cycles) valid in reg: External sync control register */
#define PPSG_ESCR_TM_VALID WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Set seconds counter in reg: External sync control register */
#define PPSG_ESCR_SEC_SET WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Set nanoseconds counter in reg: External sync control register */
#define PPSG_ESCR_NSEC_SET WBGEN2_GEN_MASK(4, 1)
PACKED struct PPSG_WB {
/* [0x0]: REG Control Register */
uint32_t CR;
/* [0x4]: REG Nanosecond counter register */
uint32_t CNTR_NSEC;
/* [0x8]: REG UTC Counter register (least-significant part) */
uint32_t CNTR_UTCLO;
/* [0xc]: REG UTC Counter register (most-significant part) */
uint32_t CNTR_UTCHI;
/* [0x10]: REG Nanosecond adjustment register */
uint32_t ADJ_NSEC;
/* [0x14]: REG UTC Adjustment register (least-significant part) */
uint32_t ADJ_UTCLO;
/* [0x18]: REG UTC Adjustment register (most-significant part) */
uint32_t ADJ_UTCHI;
/* [0x1c]: REG External sync control register */
uint32_t ESCR;
};
#endif
/*
Register definitions for slave core: WR Softcore PLL
* File : softpll_regs.h
* Author : auto-generated by wbgen2 from spll_wb_slave.wb
* Created : Thu Jul 25 11:14:53 2013
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE spll_wb_slave.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_SPLL_WB_SLAVE_WB
#define __WBGEN2_REGDEFS_SPLL_WB_SLAVE_WB
#include <stdint.h>
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: SPLL Control/Status Register */
/* definitions for field: Unused (kept for software compatibility). in reg: SPLL Control/Status Register */
#define SPLL_CSR_UNUSED0_MASK WBGEN2_GEN_MASK(0, 6)
#define SPLL_CSR_UNUSED0_SHIFT 0
#define SPLL_CSR_UNUSED0_W(value) WBGEN2_GEN_WRITE(value, 0, 6)
#define SPLL_CSR_UNUSED0_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)
/* 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)
/* definitions for field: Debug queue supported in reg: SPLL Control/Status Register */
#define SPLL_CSR_DBG_SUPPORTED WBGEN2_GEN_MASK(19, 1)
/* definitions for register: External Clock Control Register */
/* definitions for field: Enable External Clock BB Detector in reg: External Clock Control Register */
#define SPLL_ECCR_EXT_EN WBGEN2_GEN_MASK(0, 1)
/* definitions for field: External Clock Input Available in reg: External Clock Control Register */
#define SPLL_ECCR_EXT_SUPPORTED WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Enable PPS/phase alignment in reg: External Clock Control Register */
#define SPLL_ECCR_ALIGN_EN WBGEN2_GEN_MASK(2, 1)
/* definitions for field: PPS/phase alignment done in reg: External Clock Control Register */
#define SPLL_ECCR_ALIGN_DONE WBGEN2_GEN_MASK(3, 1)
/* 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: Output Channel Control Register */
/* definitions for field: Output Channel HW enable flag in reg: Output Channel Control Register */
#define SPLL_OCCR_OUT_EN_MASK WBGEN2_GEN_MASK(0, 8)
#define SPLL_OCCR_OUT_EN_SHIFT 0
#define SPLL_OCCR_OUT_EN_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define SPLL_OCCR_OUT_EN_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for field: Output Channel locked flag in reg: Output Channel Control Register */
#define SPLL_OCCR_OUT_LOCK_MASK WBGEN2_GEN_MASK(8, 8)
#define SPLL_OCCR_OUT_LOCK_SHIFT 8
#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 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: Reference Channel Tagging Enable Register */
/* definitions for register: Output Channel Tagging Enable Register */
/* definitions for register: Helper DAC Output */
/* definitions for register: Main DAC Output */
/* definitions for field: DAC value in reg: Main DAC Output */
#define SPLL_DAC_MAIN_VALUE_MASK WBGEN2_GEN_MASK(0, 16)
#define SPLL_DAC_MAIN_VALUE_SHIFT 0
#define SPLL_DAC_MAIN_VALUE_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define SPLL_DAC_MAIN_VALUE_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for field: DAC select in reg: Main DAC Output */
#define SPLL_DAC_MAIN_DAC_SEL_MASK WBGEN2_GEN_MASK(16, 4)
#define SPLL_DAC_MAIN_DAC_SEL_SHIFT 16
#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: DDMTD Deglitcher threshold */
/* definitions for register: Debug FIFO Register - SPLL side */
/* definitions for field: Debug Value in reg: Debug FIFO Register - SPLL side */
#define SPLL_DFR_SPLL_VALUE_MASK WBGEN2_GEN_MASK(0, 31)
#define SPLL_DFR_SPLL_VALUE_SHIFT 0
#define SPLL_DFR_SPLL_VALUE_W(value) WBGEN2_GEN_WRITE(value, 0, 31)
#define SPLL_DFR_SPLL_VALUE_R(reg) WBGEN2_GEN_READ(reg, 0, 31)
/* definitions for field: End-of-Sample in reg: Debug FIFO Register - SPLL side */
#define SPLL_DFR_SPLL_EOS_MASK WBGEN2_GEN_MASK(31, 1)
#define SPLL_DFR_SPLL_EOS_SHIFT 31
#define SPLL_DFR_SPLL_EOS_W(value) WBGEN2_GEN_WRITE(value, 31, 1)
#define SPLL_DFR_SPLL_EOS_R(reg) WBGEN2_GEN_READ(reg, 31, 1)
/* definitions for register: Counter Resync Register - input channels */
/* 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 */
#define SPLL_EIC_IDR_TAG WBGEN2_GEN_MASK(0, 1)
/* definitions for register: Interrupt enable register */
/* definitions for field: Got a tag in reg: Interrupt enable register */
#define SPLL_EIC_IER_TAG WBGEN2_GEN_MASK(0, 1)
/* definitions for register: Interrupt mask register */
/* definitions for field: Got a tag in reg: Interrupt mask register */
#define SPLL_EIC_IMR_TAG WBGEN2_GEN_MASK(0, 1)
/* definitions for register: Interrupt status register */
/* definitions for field: Got a tag in reg: Interrupt status register */
#define SPLL_EIC_ISR_TAG WBGEN2_GEN_MASK(0, 1)
/* definitions for register: FIFO 'Debug FIFO Register - Host side' data output register 0 */
/* definitions for field: Value in reg: FIFO 'Debug FIFO Register - Host side' data output register 0 */
#define SPLL_DFR_HOST_R0_VALUE_MASK WBGEN2_GEN_MASK(0, 32)
#define SPLL_DFR_HOST_R0_VALUE_SHIFT 0
#define SPLL_DFR_HOST_R0_VALUE_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define SPLL_DFR_HOST_R0_VALUE_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: FIFO 'Debug FIFO Register - Host side' data output register 1 */
/* definitions for field: Seq ID in reg: FIFO 'Debug FIFO Register - Host side' data output register 1 */
#define SPLL_DFR_HOST_R1_SEQ_ID_MASK WBGEN2_GEN_MASK(0, 16)
#define SPLL_DFR_HOST_R1_SEQ_ID_SHIFT 0
#define SPLL_DFR_HOST_R1_SEQ_ID_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define SPLL_DFR_HOST_R1_SEQ_ID_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for register: FIFO 'Debug FIFO Register - Host side' control/status register */
/* definitions for field: FIFO full flag in reg: FIFO 'Debug FIFO Register - Host side' control/status register */
#define SPLL_DFR_HOST_CSR_FULL WBGEN2_GEN_MASK(16, 1)
/* definitions for field: FIFO empty flag in reg: FIFO 'Debug FIFO Register - Host side' control/status register */
#define SPLL_DFR_HOST_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
/* definitions for field: FIFO counter in reg: FIFO 'Debug FIFO Register - Host side' control/status register */
#define SPLL_DFR_HOST_CSR_USEDW_MASK WBGEN2_GEN_MASK(0, 13)
#define SPLL_DFR_HOST_CSR_USEDW_SHIFT 0
#define SPLL_DFR_HOST_CSR_USEDW_W(value) WBGEN2_GEN_WRITE(value, 0, 13)
#define SPLL_DFR_HOST_CSR_USEDW_R(reg) WBGEN2_GEN_READ(reg, 0, 13)
/* definitions for register: FIFO 'Tag Readout Register' data output register 0 */
/* definitions for field: Tag value in reg: FIFO 'Tag Readout Register' data output register 0 */
#define SPLL_TRR_R0_VALUE_MASK WBGEN2_GEN_MASK(0, 24)
#define SPLL_TRR_R0_VALUE_SHIFT 0
#define SPLL_TRR_R0_VALUE_W(value) WBGEN2_GEN_WRITE(value, 0, 24)
#define SPLL_TRR_R0_VALUE_R(reg) WBGEN2_GEN_READ(reg, 0, 24)
/* definitions for field: Channel ID in reg: FIFO 'Tag Readout Register' data output register 0 */
#define SPLL_TRR_R0_CHAN_ID_MASK WBGEN2_GEN_MASK(24, 7)
#define SPLL_TRR_R0_CHAN_ID_SHIFT 24
#define SPLL_TRR_R0_CHAN_ID_W(value) WBGEN2_GEN_WRITE(value, 24, 7)
#define SPLL_TRR_R0_CHAN_ID_R(reg) WBGEN2_GEN_READ(reg, 24, 7)
/* definitions for field: Discontinuous bit in reg: FIFO 'Tag Readout Register' data output register 0 */
#define SPLL_TRR_R0_DISC WBGEN2_GEN_MASK(31, 1)
/* definitions for register: FIFO 'Tag Readout Register' control/status register */
/* definitions for field: FIFO empty flag in reg: FIFO 'Tag Readout Register' control/status register */
#define SPLL_TRR_CSR_EMPTY WBGEN2_GEN_MASK(17, 1)
PACKED struct SPLL_WB {
/* [0x0]: REG SPLL Control/Status Register */
uint32_t CSR;
/* [0x4]: REG External Clock Control Register */
uint32_t ECCR;
/* padding to: 4 words */
uint32_t __padding_0[2];
/* [0x10]: REG Output Channel Control Register */
uint32_t OCCR;
/* [0x14]: REG Reference Channel Tagging Enable Register */
uint32_t RCER;
/* [0x18]: REG Output Channel Tagging Enable Register */
uint32_t OCER;
/* padding to: 8 words */
uint32_t __padding_1[1];
/* [0x20]: REG Helper DAC Output */
uint32_t DAC_HPLL;
/* [0x24]: REG Main DAC Output */
uint32_t DAC_MAIN;
/* [0x28]: REG DDMTD Deglitcher threshold */
uint32_t DEGLITCH_THR;
/* [0x2c]: REG Debug FIFO Register - SPLL side */
uint32_t DFR_SPLL;
/* [0x30]: REG Counter Resync Register - input channels */
uint32_t CRR_IN;
/* [0x34]: REG Counter Resync Register - output channels */
uint32_t CRR_OUT;
/* [0x38]: REG Aux clock configuration register */
uint32_t AUX_CR;
/* padding to: 16 words */
uint32_t __padding_2[1];
/* [0x40]: REG Interrupt disable register */
uint32_t EIC_IDR;
/* [0x44]: REG Interrupt enable register */
uint32_t EIC_IER;
/* [0x48]: REG Interrupt mask register */
uint32_t EIC_IMR;
/* [0x4c]: REG Interrupt status register */
uint32_t EIC_ISR;
/* [0x50]: REG FIFO 'Debug FIFO Register - Host side' data output register 0 */
uint32_t DFR_HOST_R0;
/* [0x54]: REG FIFO 'Debug FIFO Register - Host side' data output register 1 */
uint32_t DFR_HOST_R1;
/* [0x58]: REG FIFO 'Debug FIFO Register - Host side' control/status register */
uint32_t DFR_HOST_CSR;
/* [0x5c]: REG FIFO 'Tag Readout Register' data output register 0 */
uint32_t TRR_R0;
/* [0x60]: REG FIFO 'Tag Readout Register' control/status register */
uint32_t TRR_CSR;
};
#endif
OBJS += \
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "board.h"
#include "trace.h"
#include "hw/softpll_regs.h"
#include "hw/pps_gen_regs.h"
#include "softpll_ng.h"
#include "irq.h"
#include "timer.h"
volatile int irq_count = 0;
volatile struct SPLL_WB *SPLL;
volatile struct PPSG_WB *PPSG;
int spll_n_chan_ref, spll_n_chan_out;
/*
* The includes below contain code (not only declarations) to enable
* the compiler to inline functions where necessary and save some CPU
* cycles
*/
#include "spll_defs.h"
#include "spll_common.h"
#include "spll_debug.h"
#include "spll_helper.h"
#include "spll_main.h"
#include "spll_ptracker.h"
#include "spll_external.h"
#define MAIN_CHANNEL (spll_n_chan_ref)
#define SEQ_START_EXT 1
#define SEQ_WAIT_EXT 2
#define SEQ_START_HELPER 3
#define SEQ_WAIT_HELPER 4
#define SEQ_START_MAIN 5
#define SEQ_WAIT_MAIN 6
#define SEQ_DISABLED 7
#define SEQ_READY 8
#define SEQ_CLEAR_DACS 9
#define SEQ_WAIT_CLEAR_DACS 10
#define AUX_DISABLED 1
#define AUX_LOCK_PLL 2
#define AUX_ALIGN_PHASE 3
#define AUX_READY 4
struct spll_aux_state {
int seq_state;
int32_t phase_target;
union {
struct spll_main_state dmtd;
/* spll_external_state ch_bb */
} pll;
};
struct softpll_state {
int mode;
int seq_state;
int dac_timeout;
int default_dac_main;
int delock_count;
int32_t mpll_shift_ps;
struct spll_helper_state helper;
struct spll_external_state ext;
struct spll_main_state mpll;
struct spll_aux_state aux[MAX_CHAN_AUX];
struct spll_ptracker_state ptrackers[MAX_PTRACKERS];
};
static volatile struct softpll_state softpll;
static volatile int ptracker_mask = 0;
/* fixme: should be done by spll_init() but spll_init is called to
* switch modes (and we won't like messing around with ptrackers
* there) */
static inline void start_ptrackers(struct softpll_state *s)
{
int i;
for (i = 0; i < spll_n_chan_ref; i++)
if (ptracker_mask & (1 << i))
ptracker_start(&s->ptrackers[i]);
}
static inline void update_ptrackers(struct softpll_state *s, int tag_value, int tag_source)
{
ptracker_mask |= (1<<spll_n_chan_ref);
if(tag_source >= spll_n_chan_ref)
return;
ptrackers_update(s->ptrackers, tag_value, tag_source);
}
static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int tag_source)
{
switch (s->seq_state) {
/* State "Clear DACs": initial SPLL sequnencer state. Brings both DACs (not the AUXs) to the default values
prior to starting the SPLL. */
case SEQ_CLEAR_DACS:
{
/* Helper always starts at the maximum value (to make sure it locks on positive offset */
SPLL->DAC_HPLL = s->helper.pi.y_max;
/* Main starts at midscale */
SPLL->DAC_MAIN = (s->mpll.pi.y_max + s->mpll.pi.y_min) / 2;
/* we need tags from at least one channel, so that the IRQ that calls this function
gets called again */
spll_enable_tagger(MAIN_CHANNEL, 1);
softpll.dac_timeout = timer_get_tics();
softpll.seq_state = SEQ_WAIT_CLEAR_DACS;
break;
}
/* State "Wait until DACs have been cleared". Makes sure the VCO control inputs have stabilized before starting the PLL. */
case SEQ_WAIT_CLEAR_DACS:
{
if (timer_get_tics() - softpll.dac_timeout >
TICS_PER_SECOND / 20)
{
if(s->mode == SPLL_MODE_GRAND_MASTER)
s->seq_state = SEQ_START_EXT;
else
s->seq_state = SEQ_START_HELPER;
}
break;
}
/* State "Disabled". Entered when the whole PLL is off */
case SEQ_DISABLED:
break;
/* State "Start external reference PLL": starts up BB PLL for locking local reference to 10 MHz input */
case SEQ_START_EXT:
{
spll_enable_tagger(MAIN_CHANNEL, 0);
external_start(&s->ext);
s->seq_state = SEQ_WAIT_EXT;
break;
}
/* State "Wait until we are locked to external 10MHz clock" */
case SEQ_WAIT_EXT:
{
if (external_locked(&s->ext))
s->seq_state = SEQ_START_HELPER;
break;
}
case SEQ_START_HELPER:
{
helper_start(&s->helper);
s->seq_state = SEQ_WAIT_HELPER;
break;
}
case SEQ_WAIT_HELPER:
{
if (s->helper.ld.locked && s->helper.ld.lock_changed)
{
if (s->mode == SPLL_MODE_SLAVE)
{
s->seq_state = SEQ_START_MAIN;
} else {
start_ptrackers(s);
s->seq_state = SEQ_READY;
}
}
break;
}
case SEQ_START_MAIN:
{
mpll_start(&s->mpll);
s->seq_state = SEQ_WAIT_MAIN;
break;
}
case SEQ_WAIT_MAIN:
{
if (s->mpll.ld.locked)
{
start_ptrackers(s);
s->seq_state = SEQ_READY;
}
break;
}
case SEQ_READY:
{
if (!s->helper.ld.locked)
{
s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext))
{
s->delock_count++;
s->seq_state = SEQ_START_EXT;
} else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked)
{
s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS;
}
break;
}
}
}
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)
{
mpll_update(&s->mpll, tag_value, tag_source);
}
if(s->seq_state == SEQ_READY)
{
if(s->mode == SPLL_MODE_SLAVE)
{
int i;
mpll_update(&s->mpll, tag_value, tag_source);
for (i = 0; i < spll_n_chan_out - 1; i++)
mpll_update(&s->aux[i].pll.dmtd, tag_value, tag_source); // fixme: bb hooks here
}
update_ptrackers(s, tag_value, tag_source);
}
}
void _irq_entry()
{
struct softpll_state *s = (struct softpll_state *)&softpll;
/* check if there are more tags in the FIFO */
while (!(SPLL->TRR_CSR & SPLL_TRR_CSR_EMPTY)) {
volatile uint32_t trr = SPLL->TRR_R0;
int tag_source = SPLL_TRR_R0_CHAN_ID_R(trr);
int tag_value = SPLL_TRR_R0_VALUE_R(trr);
sequencing_fsm(s, tag_value, tag_source);
update_loops(s, tag_value, tag_source);
}
irq_count++;
clear_irq();
}
void spll_clear_dacs()
{
SPLL->DAC_HPLL = 0;
SPLL->DAC_MAIN = 0;
timer_delay(100);
}
void spll_init(int mode, int slave_ref_channel, int align_pps)
{
static const char *modes[] = { "", "grandmaster", "freemaster", "slave", "disabled" };
volatile int dummy;
int i;
struct softpll_state *s = (struct softpll_state *) &softpll;
disable_irq();
SPLL = (volatile struct SPLL_WB *)BASE_SOFTPLL;
PPSG = (volatile struct PPSG_WB *)BASE_PPS_GEN;
uint32_t csr = SPLL->CSR;
spll_n_chan_ref = SPLL_CSR_N_REF_R(csr);
spll_n_chan_out = SPLL_CSR_N_OUT_R(csr);
s->mode = mode;
s->delock_count = 0;
SPLL->DAC_HPLL = 0;
SPLL->DAC_MAIN = 0;
SPLL->CSR = 0;
SPLL->OCER = 0;
SPLL->RCER = 0;
SPLL->ECCR = 0;
SPLL->OCCR = 0;
SPLL->DEGLITCH_THR = 1000;
PPSG->ESCR = 0;
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_CNT_RST | PPSG_CR_PWIDTH_W(100);
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)
s->seq_state = SEQ_DISABLED;
else
s->seq_state = SEQ_CLEAR_DACS;
helper_init(&s->helper, spll_n_chan_ref);
mpll_init(&s->mpll, slave_ref_channel, spll_n_chan_ref);
for (i = 0; i < spll_n_chan_out - 1; i++) {
mpll_init(&s->aux[i].pll.dmtd, slave_ref_channel, spll_n_chan_ref + i + 1);
s->aux[i].seq_state = AUX_DISABLED;
}
if(mode == SPLL_MODE_FREE_RUNNING_MASTER)
PPSG->ESCR = PPSG_ESCR_PPS_VALID | PPSG_ESCR_TM_VALID;
for (i = 0; i < spll_n_chan_ref; i++)
ptracker_init(&s->ptrackers[i], i, PTRACKER_AVERAGE_SAMPLES);
TRACE_DEV
("softpll: mode %s, %d ref channels, %d out channels\n",
modes[mode], spll_n_chan_ref, spll_n_chan_out);
/* Purge tag buffer */
while (!(SPLL->TRR_CSR & SPLL_TRR_CSR_EMPTY))
dummy = SPLL->TRR_R0;
SPLL->EIC_IER = 1;
SPLL->OCER |= 1;
enable_irq();
}
void spll_shutdown()
{
disable_irq();
SPLL->OCER = 0;
SPLL->RCER = 0;
SPLL->ECCR = 0;
SPLL->EIC_IDR = 1;
}
void spll_start_channel(int channel)
{
struct softpll_state *s = (struct softpll_state *) &softpll;
if (s->seq_state != SEQ_READY || !channel) {
TRACE_DEV("Can't start channel %d, the PLL is not ready\n",
channel);
return;
}
mpll_start(&s->aux[channel - 1].pll.dmtd);
}
void spll_stop_channel(int channel)
{
struct softpll_state *s = (struct softpll_state *) &softpll;
if (!channel)
return;
mpll_stop(&s->aux[channel - 1].pll.dmtd);
}
int spll_check_lock(int channel)
{
if (!channel)
return (softpll.seq_state == SEQ_READY);
else
return (softpll.seq_state == SEQ_READY)
&& 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)
{
return (int32_t) (((int64_t) units *
(int64_t) CLOCK_PERIOD_PICOSECONDS) >> HPLL_N);
}
/* Channel 0 = local PLL reference, 1...N = aux oscillators */
static void set_phase_shift(int channel, int32_t value_picoseconds)
{
struct spll_main_state *st = (struct spll_main_state *)
(!channel ? &softpll.mpll : &softpll.aux[channel - 1].pll.dmtd);
int div = (DIVIDE_DMTD_CLOCKS_BY_2 ? 2 : 1);
mpll_set_phase_shift(st, from_picos(value_picoseconds) / div);
softpll.mpll_shift_ps = value_picoseconds;
}
void spll_set_phase_shift(int channel, int32_t value_picoseconds)
{
int i;
if (channel == SPLL_ALL_CHANNELS) {
spll_set_phase_shift(0, value_picoseconds);
for (i = 0; i < spll_n_chan_out - 1; i++)
if (softpll.aux[i].seq_state == AUX_READY)
set_phase_shift(i + 1, value_picoseconds);
} else
set_phase_shift(channel, value_picoseconds);
}
void spll_get_phase_shift(int channel, int32_t *current, int32_t *target)
{
volatile struct spll_main_state *st = (struct spll_main_state *)
(!channel ? &softpll.mpll : &softpll.aux[channel - 1].pll.dmtd);
int div = (DIVIDE_DMTD_CLOCKS_BY_2 ? 2 : 1);
if (current)
*current = to_picos(st->phase_shift_current * div);
if (target)
*target = to_picos(st->phase_shift_target * div);
}
int spll_read_ptracker(int channel, int32_t *phase_ps, int *enabled)
{
volatile struct spll_ptracker_state *st = &softpll.ptrackers[channel];
int phase = st->phase_val;
if (phase < 0)
phase += (1 << HPLL_N);
else if (phase >= (1 << HPLL_N))
phase -= (1 << HPLL_N);
if (DIVIDE_DMTD_CLOCKS_BY_2) {
phase <<= 1;
phase &= (1 << HPLL_N) - 1;
}
*phase_ps = to_picos(phase);
if (enabled)
*enabled = ptracker_mask & (1 << st->id) ? 1 : 0;
return st->ready;
}
void spll_get_num_channels(int *n_ref, int *n_out)
{
if (n_ref)
*n_ref = spll_n_chan_ref;
if (n_out)
*n_out = spll_n_chan_out;
}
void spll_show_stats()
{
if (softpll.mode > 0)
TRACE_DEV
("softpll: irq_count %d sequencer_state %d mode %d "
"alignment_state %d HL%d EL%d ML%d HY=%d "
"MY=%d EY=%d DelCnt=%d extsc=%d\n",
irq_count, softpll.seq_state, softpll.mode,
softpll.ext.realign_state, softpll.helper.ld.locked,
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)
{
if (!channel)
return mpll_shifter_busy((struct spll_main_state *)&softpll.mpll);
else
return mpll_shifter_busy((struct spll_main_state *)&softpll.aux[channel - 1].pll.dmtd);
}
void spll_enable_ptracker(int ref_channel, int enable)
{
if (enable) {
spll_enable_tagger(ref_channel, 1);
ptracker_start((struct spll_ptracker_state *)&softpll.
ptrackers[ref_channel]);
ptracker_mask |= (1 << ref_channel);
TRACE_DEV("Enabling ptracker channel: %d\n", ref_channel);
} else {
ptracker_mask &= ~(1 << ref_channel);
if (ref_channel != softpll.mpll.id_ref)
spll_enable_tagger(ref_channel, 0);
TRACE_DEV("Disabling ptracker tagger: %d\n", ref_channel);
}
}
int spll_get_delock_count()
{
return softpll.delock_count;
}
static inline int aux_locking_enabled(int channel)
{
uint32_t occr_aux_en = SPLL_OCCR_OUT_EN_R(SPLL->OCCR);
return occr_aux_en & (1 << channel);
}
static inline void aux_set_channel_status(int channel, int locked)
{
if(!locked)
SPLL->OCCR &= ~(SPLL_OCCR_OUT_LOCK_W((1 << channel)));
else
SPLL->OCCR |= (SPLL_OCCR_OUT_LOCK_W((1 << channel)));
}
int spll_update_aux_clocks()
{
int ch;
for (ch = 1; ch < spll_n_chan_out; ch++)
{
struct spll_aux_state *s = (struct spll_aux_state *) &softpll.aux[ch - 1];
if(s->seq_state != AUX_DISABLED && !aux_locking_enabled(ch))
{
TRACE_DEV("softpll: disabled aux channel %d\n", ch);
spll_stop_channel(ch);
aux_set_channel_status(ch, 0);
s->seq_state = AUX_DISABLED;
}
switch (s->seq_state) {
case AUX_DISABLED:
if (softpll.mpll.ld.locked && aux_locking_enabled(ch)) {
TRACE_DEV("softpll: enabled aux channel %d\n", ch);
spll_start_channel(ch);
s->seq_state = AUX_LOCK_PLL;
}
break;
case AUX_LOCK_PLL:
if (s->pll.dmtd.ld.locked) {
TRACE_DEV ("softpll: channel %d locked [aligning @ %d ps]\n", ch, softpll.mpll_shift_ps);
set_phase_shift(ch, softpll.mpll_shift_ps);
s->seq_state = AUX_ALIGN_PHASE;
}
break;
case AUX_ALIGN_PHASE:
if (!mpll_shifter_busy(&s->pll.dmtd)) {
TRACE_DEV("softpll: channel %d phase aligned\n", ch);
aux_set_channel_status(ch, 1);
s->seq_state = AUX_READY;
}
break;
case AUX_READY:
if (!softpll.mpll.ld.locked || !s->pll.dmtd.ld.locked) {
TRACE_DEV("softpll: aux channel %d or mpll lost lock\n", ch);
aux_set_channel_status(ch, 0);
s->seq_state = AUX_DISABLED;
}
break;
}
}
return 0;
}
int spll_get_aux_status(int channel)
{
int rval = 0;
if (softpll.aux[channel].seq_state != AUX_DISABLED)
rval |= SPLL_AUX_ENABLED;
if (softpll.aux[channel].seq_state == AUX_READY)
rval |= SPLL_AUX_LOCKED;
return rval;
}
const char *spll_get_aux_status_string(int channel)
{
const char *aux_stat[] = {"disabled", "locking", "aligning", "locked"};
struct spll_aux_state *s = (struct spll_aux_state* )&softpll.aux[channel];
switch(s->seq_state)
{
case AUX_DISABLED: return aux_stat[0];
case AUX_LOCK_PLL: return aux_stat[1];
case AUX_ALIGN_PHASE: return aux_stat[2];
case AUX_READY: return aux_stat[3];
}
return "";
}
int spll_get_dac(int index)
{
if (index < 0)
return softpll.helper.pi.y;
else if (index == 0)
return softpll.mpll.pi.y;
else if (index > 0)
return softpll.aux[index - 1].pll.dmtd.pi.y;
return 0;
}
void spll_set_dac(int index, int value)
{
if (index < 0) {
softpll.helper.pi.y = value;
SPLL->DAC_HPLL = value;
} else {
SPLL->DAC_MAIN =
SPLL_DAC_MAIN_DAC_SEL_W(index) | (value & 0xffff);
if (index == 0)
softpll.mpll.pi.y = value;
else if (index > 0)
softpll.aux[index - 1].pll.dmtd.pi.y = value;
}
}
/*
* 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 <stdint.h>
/* 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
/* 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
/* 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();
/* Returns number of reference and output channels implemented in HW. */
void spll_get_num_channels(int *n_ref, int *n_out);
/* 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();
/* 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
/*
* 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;
pi->y = pi->bias;
}
/* 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);
}
/*
* 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
#include <stdint.h>
#include <stdlib.h>
#include <hw/softpll_regs.h>
#include <hw/pps_gen_regs.h>
#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;
/* PI regulator state */
typedef struct {
int ki, kp; /* integral and proportional gains (1<<PI_FRACBITS == 1.0f) */
int integrator; /* current integrator value */
int bias; /* DC offset always added to the output */
int anti_windup; /* when non-zero, anti-windup is enabled */
int y_min; /* min/max output range, used by clapming and antiwindup algorithms */
int y_max;
int x, y; /* Current input (x) and output value (y) */
} spll_pi_t;
/* lock detector state */
typedef struct {
int lock_cnt; /* Lock sample counter */
int lock_samples; /* Number of samples below the (threshold) to assume that we are locked */
int delock_samples; /* Accumulated number of samples that causes the PLL go get out of lock.
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 */
typedef struct {
int alpha;
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. */
int pi_update(spll_pi_t *pi, int x);
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);
#endif // __SPLL_COMMON_H
/*
White Rabbit Softcore PLL (SoftPLL) - common definitions
Copyright (c) 2010 - 2012 CERN / BE-CO-HT (Tomasz Włostowski)
Licensed under LGPL 2.1.
spll_debug.h - debugging/diagnostic interface
The so-called debug inteface is a large, interrupt-driven FIFO which
passes various realtime parameters (e.g. error value, tags, DAC drive)
to an external application where they are further analyzed. It's very
useful for optimizing PI coefficients and/or lock thresholds.
The data is organized as a stream of samples, where each sample can
store a number of parameters. For example, a stream samples with Y
and ERR parameters can be used to evaluate the impact of
integral/proportional gains on the response of the system.
*/
#define DBG_Y 0
#define DBG_ERR 1
#define DBG_TAG 2
#define DBG_REF 5
#define DBG_PERIOD 3
#define DBG_EVENT 4
#define DBG_SAMPLE_ID 6
#define DBG_HELPER 0x20 /* Sample source: Helper PLL */
#define DBG_EXT 0x40 /* Sample source: External Reference PLL */
#define DBG_MAIN 0x0 /* ... : Main PLL */
#define DBG_EVT_START 1 /* PLL has just started */
#define DBG_EVT_LOCKED 2 /* PLL has just become locked */
/* Writes a parameter to the debug FIFO.
value: value of the parameter.
what: type of the parameter and its' source. For example,
- DBG_ERR | DBG_HELPER means that (value) contains the phase error of the helper PLL.
- DBG_EVENT indicates an asynchronous event. (value) must contain the event type (DBG_EVT_xxx)
last: when non-zero, indicates the last parameter in a sample.
*/
static inline void spll_debug(int what, int value, int last)
{
SPLL->DFR_SPLL =
(last ? 0x80000000 : 0) | (value & 0xffffff) | (what << 24);
}
/*
* 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"
#include "timer.h"
#define BB_ERROR_BITS 16
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.
*/
/* spll_external.h - definitions & prototypes for the
external (10 MHz - Grandmaster mode) reference channel */
#ifndef __SPLL_EXTERNAL_H
#define __SPLL_EXTERNAL_H
#include "spll_common.h"
/* Alignment FSM states */
/* 1st alignment stage, done before starting the ext channel PLL:
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 */
#define REALIGN_DONE 7
struct spll_external_state {
int ref_src;
int sample_n;
int ph_err_offset, ph_err_cur, ph_err_d0, ph_raw_d0;
int realign_clocks;
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,
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);
}
/*
* 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. */
#ifndef __SPLL_HELPER_H
#define __SPLL_HELPER_H
#include "spll_common.h"
#define HELPER_TAG_WRAPAROUND 100000000
/* Maximum abs value of the phase error. If the error is bigger, it's
* clamped to this value. */
#define HELPER_ERROR_CLAMP 150000
struct spll_helper_state {
int p_adder; /* anti wrap-around adder */
int p_setpoint, tag_d0;
int ref_src;
int sample_n;
int delock_count;
spll_pi_t pi;
spll_lock_det_t ld;
};
void helper_init(struct spll_helper_state *s, int ref_channel);
int helper_update(struct spll_helper_state *s, int tag,
int source);
void helper_start(struct spll_helper_state *s);
#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.
*/
/* spll_main.h - the main DDMTD PLL. Locks output clock to any reference
with programmable phase shift. */
#ifndef __SPLL_MAIN_H
#define __SPLL_MAIN_H
#include "spll_common.h"
/* State of the Main PLL */
struct spll_main_state {
int state;
spll_pi_t pi;
spll_lock_det_t ld;
int adder_ref, adder_out, tag_ref, tag_out, tag_ref_d, tag_out_d;
// tag sequencing stuff
uint32_t seq_ref, seq_out;
int match_state;
int match_seq;
int phase_shift_target;
int phase_shift_current;
int id_ref, id_out; /* IDs of the reference and the output channel */
int sample_n;
int delock_count;
int dac_index;
};
void mpll_init(struct spll_main_state *s, int id_ref,
int id_out);
void mpll_stop(struct spll_main_state *s);
void mpll_start(struct spll_main_state *s);
int mpll_update(struct spll_main_state *s, int tag, int source);
int mpll_set_phase_shift(struct spll_main_state *s,
int desired_shift);
int mpll_shifter_busy(struct spll_main_state *s);
#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"
static int tag_ref = -1;
void ptracker_init(struct spll_ptracker_state *s, int id, int num_avgs)
{
s->id = id;
s->ready = 0;
s->n_avg = num_avgs;
s->acc = 0;
s->avg_count = 0;
s->enabled = 0;
}
void ptracker_start(struct spll_ptracker_state *s)
{
s->preserve_sign = 0;
s->enabled = 1;
s->ready = 0;
s->acc = 0;
s->avg_count = 0;
spll_enable_tagger(s->id, 1);
}
int ptrackers_update(struct spll_ptracker_state *ptrackers, int tag,
int source)
{
const int adj_tab[16] = { /* psign */
/* 0 - 1/4 */ 0, 0, 0, (1<<HPLL_N),
/* 1/4 - 1/2 */ 0, 0, 0, 0,
/* 1/2 - 3/4 */ 0, 0, 0, 0,
/* 3/4 - 1 */ (1<<HPLL_N), 0, 0, 0};
if (source > spll_n_chan_ref)
return 0;
if(source == spll_n_chan_ref)
{
tag_ref = tag;
return 0;
}
register struct spll_ptracker_state *s = ptrackers + source;
if(!s->enabled)
return 0;
register int delta = (tag_ref - tag) & ((1 << HPLL_N) - 1);
register int index = delta >> (HPLL_N - 2);
if (s->avg_count == 0) {
/* hack: two since PTRACK_WRAP_LO/HI are in 1/4 and 3/4 of the scale,
we can use the two MSBs of delta and a trivial LUT instead, removing 2 branches */
s->preserve_sign = index << 2;
s->acc = delta;
s->avg_count ++;
} else {
/* same hack again, using another lookup table to adjust for wraparound */
s->acc += delta + adj_tab[ index + s->preserve_sign ];
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;
}
}
return 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_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 enabled, id;
int n_avg, acc, avg_count, preserve_sign;
int phase_val, ready;
};
void ptracker_init(struct spll_ptracker_state *s, int id, int num_avgs);
void ptracker_start(struct spll_ptracker_state *s);
int ptrackers_update(struct spll_ptracker_state *ptrackers, int tag, int source);
#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