Commit fc63d9cf authored by Grzegorz Daniluk's avatar Grzegorz Daniluk

Merge branch 'switch-v4.1' into pmaster

Conflicts:
	softpll/softpll_ng.c
parents 1a08dc04 740be374
......@@ -61,7 +61,12 @@ include pp_printf/printf.mk
include dev/dev.mk
include softpll/softpll.mk
obj-$(CONFIG_WR_NODE) += check-error.o
# ppsi already has div64 (the same one), so only pick it if not using ppsi
ifndef CONFIG_PPSI
obj-y += pp_printf/div64.o
endif
# And always complain if we pick the libgcc division: 64/32 = 32 is enough here.
obj-y += check-error.o
obj-$(CONFIG_WR_NODE) += sdb-lib/libsdbfs.a
cflags-$(CONFIG_WR_NODE) += -Isdb-lib
......
......@@ -153,7 +153,6 @@ int ad9516_set_output_divider(int output, int ratio, int phase_offset)
ad9516_write_reg(base + 1, div_ctl | (1<<7) | (phase_offset & 0xf));
} else {
uint8_t div_ctl = ad9516_read_reg(base + 1);
TRACE("DivCtl: %x\n", div_ctl);
ad9516_write_reg(base + 1, (div_ctl & (~(1<<7))) | (phase_offset & 0xf)); /* disable bypass bit */
ad9516_write_reg(base, (lcycles << 4) | hcycles);
}
......@@ -212,7 +211,7 @@ void ad9516_sync_outputs()
ad9516_write_reg(0x232, 1);
}
int ad9516_init(void)
int ad9516_init(int scb_version)
{
TRACE("Initializing AD9516 PLL...\n");
......@@ -235,14 +234,41 @@ int ad9516_init(void)
return -1;
}
ad9516_load_regset(ad9516_base_config, ARRAY_SIZE(ad9516_base_config), 0);
if( scb_version >= 34) //New SCB v3.4. 10MHz Output.
ad9516_load_regset(ad9516_base_config_34, ARRAY_SIZE(ad9516_base_config_34), 0);
else //Old one
ad9516_load_regset(ad9516_base_config_33, ARRAY_SIZE(ad9516_base_config_33), 0);
ad9516_load_regset(ad9516_ref_tcxo, ARRAY_SIZE(ad9516_ref_tcxo), 1);
ad9516_wait_lock();
ad9516_sync_outputs();
ad9516_set_output_divider(9, 4, 0); /* AUX/SWCore = 187.5 MHz */
ad9516_set_output_divider(7, 12, 0); /* REF = 62.5 MHz */
ad9516_set_output_divider(4, 12, 0); /* GTX = 62.5 MHz */
if( scb_version >= 34) { //New SCB v3.4. 10MHz Output.
ad9516_set_output_divider(2, 4, 0); // OUT2. 187.5 MHz.
ad9516_set_output_divider(3, 4, 0); // OUT3. 187.5 MHz.
ad9516_set_output_divider(4, 3, 0); // OUT4. 250 MHz.
ad9516_set_output_divider(5, 3, 0); // OUT5. 250 MHz.
/*The following PLL outputs have been configured through the ad9516_base_config_34 register,
* so it doesn't need to replicate the configuration:
*
* Output 6 => 62.5 MHz
* Output 7 => 62.5 MHz
* Output 8 => 10 MHz
* Output 9 => 10 MHz
*/
ad9516_write_reg(0x143, 0x1); //Temporary Powerdown 10MHz output
} else { //Old one
ad9516_set_output_divider(9, 4, 0); /* AUX/SWCore = 187.5 MHz */
ad9516_set_output_divider(7, 12, 0); /* REF = 62.5 MHz */
ad9516_set_output_divider(4, 12, 0); /* GTX = 62.5 MHz */
}
ad9516_sync_outputs();
ad9516_set_vco_divider(2);
......
/* Base configuration (global dividers, output config, reference-independent) */
const struct ad9516_reg ad9516_base_config[] = {
/* Base configuration for the SCB version lower than 3.4 (global dividers, output config, reference-independent) */
const struct ad9516_reg ad9516_base_config_33[] = {
{0x0000, 0x99},
{0x0001, 0x00},
{0x0002, 0x10},
......@@ -70,6 +70,79 @@ const struct ad9516_reg ad9516_base_config[] = {
{0x0231, 0x00},
};
/* Configuration for the SCB version greater than or equal 3.4: Base + 6, 7, 8, 9 outputs*/
const struct ad9516_reg ad9516_base_config_34[] = {
{0x0000, 0x99},
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
{0x0010, 0x7C},
{0x0011, 0x05},
{0x0012, 0x00},
{0x0013, 0x0C},
{0x0014, 0x12},
{0x0015, 0x00},
{0x0016, 0x05},
{0x0017, 0x88},
{0x0018, 0x07},
{0x0019, 0x00},
{0x001A, 0x00},
{0x001B, 0x00},
{0x001C, 0x02},
{0x001D, 0x00},
{0x001E, 0x00},
{0x001F, 0x0E},
{0x00A0, 0x01},
{0x00A1, 0x00},
{0x00A2, 0x00},
{0x00A3, 0x01},
{0x00A4, 0x00},
{0x00A5, 0x00},
{0x00A6, 0x01},
{0x00A7, 0x00},
{0x00A8, 0x00},
{0x00A9, 0x01},
{0x00AA, 0x00},
{0x00AB, 0x00},
{0x00F0, 0x0A},
{0x00F1, 0x0A},
{0x00F2, 0x0A},
{0x00F3, 0x0A},
{0x00F4, 0x08},
{0x00F5, 0x08},
// The following registers configure the PLL outputs from 6 to 9.
{0x0140, 0x42},
{0x0141, 0x42},
{0x0142, 0x43},
{0x0143, 0x4E},
{0x0190, 0x55},
{0x0191, 0x00},
{0x0192, 0x00},
{0x0193, 0x11},
{0x0194, 0x00},
{0x0195, 0x00},
{0x0196, 0x10},
{0x0197, 0x00},
{0x0198, 0x00},
{0x0199, 0x55},
{0x019A, 0x00},
{0x019B, 0x11},
{0x019C, 0x20},
{0x019D, 0x00},
{0x019E, 0x10},
{0x019F, 0x00},
{0x01A0, 0xCB},
{0x01A1, 0x00},
{0x01A2, 0x00},
{0x01A3, 0x00},
//
{0x01E0, 0x04},
{0x01E1, 0x02},
{0x0230, 0x00},
{0x0231, 0x00},
};
/* Config for 25 MHz VCTCXO reference (RDiv = 5, use REF1) */
const struct ad9516_reg ad9516_ref_tcxo[] = {
{0x0011, 0x05},
......
......@@ -2,12 +2,14 @@
#define __FREESTANDING_TRACE_H__
#ifdef CONFIG_WR_NODE
#include <wrc.h>
#define TRACE_WRAP(...)
#define TRACE_DEV(...) wrc_debug_printf(0, __VA_ARGS__)
#else /* WR_SWITCH */
#include <pp-printf.h>
#define TRACE(...) pp_printf(__VA_ARGS__)
#define TRACE_DEV(...) pp_printf(__VA_ARGS__)
......
......@@ -45,10 +45,13 @@ void update_rx_queues(void);
extern int wrc_ui_refperiod;
/* Init functions for the wrs build */
int ad9516_init(void);
int ad9516_init(int scb_ver);
void rts_init(void);
int rtipc_init(void);
void rts_update(void);
void rtipc_action(void);
/* div64.c, lifted from the linux kernel through pp_printf or ppsi */
extern uint32_t __div64_32(uint64_t *n, uint32_t base);
#endif /* __WRC_H__ */
*.o
example-printf
example-printf64
......@@ -20,12 +20,18 @@ obj-$(CONFIG_PRINTF_XINT) += vsprintf-xint.o
# set full as a default if nothing is selected
obj-y ?= vsprintf-full.o
# If you want to pick the local div64.c, define this as "y"
obj-$(CONFIG_PRINTF_LOCAL_DIV64) += div64.o
obj-y += printf.o
# There is a static variable in pp-printf.c to accumulate stuff
CONFIG_PRINT_BUFSIZE ?= 256
CFLAGS += -DCONFIG_PRINT_BUFSIZE=$(CONFIG_PRINT_BUFSIZE)
ifdef CONFIG_PRINTF_64BIT
CFLAGS += -DCONFIG_PRINTF_64BIT
endif
# Targets. You may want to make them different in your package
......@@ -37,6 +43,9 @@ pp-printf.o: $(obj-y)
example-printf: example-printf.c pp-printf.o
$(CC) $(CFLAGS) $^ -o $@
# build a special example/test for 64-bit prints (not built by default)
example-printf64: example-printf64.o pp-printf.o div64.o
.c.o:
$(CC) -c $(CFLAGS) $< -o $@
......
......@@ -16,6 +16,7 @@ The printf engine, vsprintf(), comes in four flavours:
In summary:
- the "full" version is a normal printf (GPL2, from older Linux kernel)
(as a build-time option, it supports int64_t items, as "ll" or "L")
- the "xint" version accepts all formats and prints hex and int only
......@@ -42,6 +43,10 @@ used in the Linux kernel. It is licensed according to the GNU GPL
version 2. It includes all formats and prefixes and so on. It is
clearly bugless because everybody is using it.
In July 2014 I made some changes, which includes adding 64-bit items,
but the feature is disabled by default. There is a whole section later
in this file about this.
It is selected at compile time by setting the make variable
"CONFIG_PRINTF_FULL" to "y". You can do that in the environment,
or use Kconfig in your application.
......@@ -72,6 +77,10 @@ Result (as you see, format modifiers are respected):
Footprint: 1400-3200 bytes, plus 100-400 bytes for the frontend.
(With the 2014 changes the footprint is a few hundred bytes less, but
I lacked the time to make comprehensive builds; if you enable 64-bit
support, you'll get a size impact of a few hunderd bytes: for ARM,
around 200 bytes in vsprintf-full plus 200 for div64.o).
The xint implementation in detail
================================
......@@ -164,10 +173,83 @@ the constant strings in .rodata. I don't support this in the package,
though, and I discourage from doing it, for the usual
preprocessor-related reasons.
int64_t support
===============
vsprintf-full.c now supports printing 64-bit items. It works in all
platforms, including the 16-bit AVR CPU. The feature is disabled by
default because it has run-time impact (all integer conversions are
performed using 64-bit division.
Moreover, 64-bit supports requires 64-bit division. As customary in
the kernel/bootloader world we avoid picking the libgcc
implementation, and rely on the smaller __div64_32() function,
instead, which only supports a 32-bit divisor and thus a 32-bit
remainder; but this is all we need. The function is available in
"lib64.c", by Bernardo Innocenti, which is used in the kernel and many
other projects.
So, to enable 64-bit support in pp-printf, please use
CONFIG_PRINTF_64BIT=y
in your environment or command-line of make. If your project uses
Kconfig, you'll rely on it instead (I'm running pp-printf in 3
Kconfig-based projects and it works great).
If you already have __div64_32() in your project, you are done:
vsprintf-full.o will have an undefined symbol, that your final link
will resolve. If you miss the function, you can use the local
version. To do that, set
CONFIG_PRINTF_LOCAL_DIV64=y
in your environment or the command line of make.
The test program for 64-bit formats is not built by default. To build
it, you should
make CONFIG_PRINTF_64BIT=y example-printf64
This automatically picks div64.o: you'll get a link error if you also
set CONFIG_PRINTF_LOCAL_DIV64=y. This is not a problem as the test
is meant to be run locally, only once or so.
The test prints one thousand, one million and so on using the various
formats, ending with the maximum positive and maximum negative::
positive: 1000
negative: -1000
neg-unsigned: 18446744073709550616
pos-hex: 0x00000000000003e8
pos-HEX: 0x00000000000003E8
neg-hex: 0xfffffffffffffc18
[...]
positive: 1000000000000000000
negative: -1000000000000000000
neg-unsigned: 17446744073709551616
pos-hex: 0x0de0b6b3a7640000
pos-HEX: 0x0DE0B6B3A7640000
neg-hex: 0xf21f494c589c0000
max positive: 9223372036854775807
max negative: -9223372036854775808
You may want to verify with your own target platform, before using the
code in production (you can avoid that if you trust me, but I wouldn't
blindly trust programmers, in general).
Footprint of the various implementations
========================================
NOTE: these figures refer to the 2012 master. The "full"
implementation is now a few hundred bytes smaller.
Also, I made no measures for the 64-bit version, which adds a few
hundred bytes (and run-time overhead of every integer print).
This table excludes the static buffer (256 in .bss by default) and
only lists the code size (command "size", column "text"), compiled
with -Os as for this Makefile.
......
/* This file in ppsi is a subset of lib/div64.c in Linux source code */
/*
* Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
*
* Based on former do_div() implementation from asm-parisc/div64.h:
* Copyright (C) 1999 Hewlett-Packard Co
* Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
*
*
* Generic C version of 64bit/32bit division and modulo, with
* 64bit result and 32bit remainder.
*
* The fast case for (n>>32 == 0) is handled inline by do_div().
*
* Code generated for this function might be very inefficient
* for some CPUs. __div64_32() can be overridden by linking arch-specific
* assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
*/
#include <stdint.h>
uint32_t __div64_32(uint64_t *n, uint32_t base)
{
uint64_t rem = *n;
uint64_t b = base;
uint64_t res, d = 1;
uint32_t high = rem >> 32;
/* Reduce the thing a bit first */
res = 0;
if (high >= base) {
high /= base;
res = (uint64_t) high << 32;
rem -= (uint64_t) (high*base) << 32;
}
while ((int64_t)b > 0 && b < rem) {
b = b+b;
d = d+d;
}
do {
if (rem >= b) {
rem -= b;
res += d;
}
b >>= 1;
d >>= 1;
} while (d);
*n = res;
return rem;
}
#include <stdint.h>
#include <pp-printf.h>
int main(int argc, char **argv)
{
long long ll; /* I'd use "int64_t" but gcc wars about formats */
for (ll = 1000; ll < (1LL << 61); ll *= 1000) {
pp_printf("positive: %20lli\n", ll);
pp_printf("negative: %20lli\n", -ll);
pp_printf("neg-unsigned: %20llu\n", -ll);
pp_printf("pos-hex: 0x%016llx\n", ll);
pp_printf("pos-HEX: 0x%016llX\n", ll);
pp_printf("neg-hex: 0x%016Lx\n", -ll);
pp_printf("\n");
}
ll = (1LL <<63) - 1;
pp_printf("max positive: %20lli\n", ll);
ll = (1LL << 63);
pp_printf("max negative: %20lli\n", ll);
return 0;
}
This diff is collapsed.
......@@ -53,7 +53,9 @@ static int cmd_pll(const char *args[])
if (!args[1])
return -EINVAL;
mprintf("%d\n", spll_get_dac(atoi(args[1])));
} else
} else if(!strcasecmp(args[0], "checkvco"))
check_vco_frequencies();
else
return -EINVAL;
return 0;
......
This diff is collapsed.
......@@ -75,6 +75,21 @@ struct softpll_state {
struct spll_ptracker_state ptrackers[MAX_PTRACKERS];
};
static const struct stringlist_entry seq_states [] =
{
{ SEQ_START_EXT, "start-ext" },
{ SEQ_WAIT_EXT, "wait-ext" },
{ SEQ_START_HELPER, "start-helper" },
{ SEQ_WAIT_HELPER, "wait-helper" },
{ SEQ_START_MAIN, "start-main" },
{ SEQ_WAIT_MAIN, "wait-main" },
{ SEQ_DISABLED, "disabled" },
{ SEQ_READY, "ready" },
{ SEQ_CLEAR_DACS, "clear-dacs" },
{ SEQ_WAIT_CLEAR_DACS, "wait-clear-dacs" },
{ 0, NULL }
};
static volatile struct softpll_state softpll;
static volatile int ptracker_mask = 0;
......@@ -153,8 +168,10 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
/* State "Wait until we are locked to external 10MHz clock" */
case SEQ_WAIT_EXT:
{
if (external_locked(&s->ext))
s->seq_state = SEQ_START_HELPER;
if (external_locked(&s->ext)) {
start_ptrackers(s);
s->seq_state = SEQ_READY;
}
break;
}
......@@ -190,7 +207,7 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
case SEQ_WAIT_MAIN:
{
if (s->mpll.ld.locked)
if (s->mpll.ld.locked)
{
start_ptrackers(s);
s->seq_state = SEQ_READY;
......@@ -200,16 +217,13 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
case SEQ_READY:
{
if (!s->helper.ld.locked)
{
if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext)) {
s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext))
{
} else if (!s->helper.ld.locked) {
s->delock_count++;
s->seq_state = SEQ_START_EXT;
} else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked)
{
s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked) {
s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS;
}
......@@ -220,47 +234,22 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
static inline void update_loops(struct softpll_state *s, int tag_value, int tag_source)
{
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);
}
helper_update(&s->helper, tag_value, tag_source);
if(s->seq_state == SEQ_READY)
if(s->helper.ld.locked)
{
if(s->mode == SPLL_MODE_SLAVE)
{
int i;
mpll_update(&s->mpll, tag_value, tag_source);
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
if(s->seq_state == SEQ_READY) {
if(s->mode == SPLL_MODE_SLAVE) {
int i;
for (i = 0; i < spll_n_chan_out - 1; i++)
mpll_update(&s->aux[i].pll.dmtd, tag_value, tag_source);
}
update_ptrackers(s, tag_value, tag_source);
}
update_ptrackers(s, tag_value, tag_source);
}
}
......@@ -301,7 +290,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
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;
......@@ -318,16 +306,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
PPSG->ESCR = 0;
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_CNT_RST | PPSG_CR_PWIDTH_W(PPS_WIDTH);
if(mode == SPLL_MODE_GRAND_MASTER)
{
if(SPLL->ECCR & SPLL_ECCR_EXT_SUPPORTED)
external_init(&s->ext, spll_n_chan_ref + spll_n_chan_out, align_pps);
else {
TRACE_DEV("softpll: attempting to enable GM mode on non-GM hardware.\n");
return;
}
}
if(mode == SPLL_MODE_DISABLED)
s->seq_state = SEQ_DISABLED;
else
......@@ -354,6 +332,17 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
for (i = 0; i < spll_n_chan_ref; i++)
ptracker_init(&s->ptrackers[i], i, PTRACKER_AVERAGE_SAMPLES);
if(mode == SPLL_MODE_GRAND_MASTER) {
if(SPLL->ECCR & SPLL_ECCR_EXT_SUPPORTED) {
s->ext.helper = &s->helper;
s->ext.main = &s->mpll;
external_init(&s->ext, spll_n_chan_ref + spll_n_chan_out, align_pps);
} else {
TRACE_DEV("softpll: attempting to enable GM mode on non-GM hardware.\n");
return;
}
}
TRACE_DEV
("softpll: mode %s, %d ref channels, %d out channels\n",
modes[mode], spll_n_chan_ref, spll_n_chan_out);
......@@ -400,6 +389,11 @@ void spll_stop_channel(int channel)
mpll_stop(&s->aux[channel - 1].pll.dmtd);
}
int spll_ext_locked()
{
return external_locked( (struct spll_external_state *) &softpll.ext);
}
int spll_check_lock(int channel)
{
if (!channel)
......@@ -409,29 +403,6 @@ int spll_check_lock(int channel)
&& 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 *
......@@ -443,11 +414,7 @@ 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);
int32_t desired_shift = from_picos(value_picoseconds);
if (DIVIDE_DMTD_CLOCKS_BY_2)
desired_shift >>= 1;
mpll_set_phase_shift(st, desired_shift);
mpll_set_phase_shift(st, value_picoseconds);
softpll.mpll_shift_ps = value_picoseconds;
}
......@@ -505,15 +472,12 @@ void spll_get_num_channels(int *n_ref, int *n_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);
TRACE_DEV("softpll: irqs %d seq %s mode %d "
"alignment_state %d HL%d ML%d HY=%d MY=%d DelCnt=%d\n",
irq_count, stringlist_lookup(seq_states, softpll.seq_state), softpll.mode,
softpll.ext.align_state, softpll.helper.ld.locked, softpll.mpll.ld.locked,
softpll.helper.pi.y, softpll.mpll.pi.y,
softpll.delock_count);
}
int spll_shifter_busy(int channel)
......@@ -669,3 +633,83 @@ void spll_set_dac(int index, int value)
softpll.aux[index - 1].pll.dmtd.pi.y = value;
}
}
void spll_update()
{
switch(softpll.mode) {
case SPLL_MODE_GRAND_MASTER:
external_align_fsm(&softpll.ext);
break;
}
spll_update_aux_clocks();
}
static int spll_measure_frequency(int osc)
{
volatile uint32_t *reg;
switch(osc) {
case SPLL_OSC_REF:
reg = &SPLL->F_REF;
break;
case SPLL_OSC_DMTD:
reg = &SPLL->F_DMTD;
break;
case SPLL_OSC_EXT:
reg = &SPLL->F_EXT;
break;
default:
return 0;
}
timer_delay_ms(2000);
return (*reg ) & (0xfffffff);
}
static int calc_apr(int meas_min, int meas_max, int f_center )
{
// apr_min is in PPM
int64_t delta_low = meas_min - f_center;
int64_t delta_hi = meas_max - f_center;
int ppm_lo, ppm_hi;
if(delta_low >= 0)
return -1;
if(delta_hi <= 0)
return -1;
/* __div64_32 divides 64 by 32; result is in the 64 argument. */
delta_low = -(int64_t)delta_low * 1000000LL;
__div64_32(&delta_low, f_center);
ppm_lo = delta_low;
delta_hi = delta_hi * 1000000LL;
__div64_32(&delta_hi, f_center);
ppm_hi = delta_hi;
return ppm_lo < ppm_hi ? ppm_lo : ppm_hi;
}
void check_vco_frequencies()
{
disable_irq();
int f_min, f_max;
TRACE_DEV("SoftPLL VCO Frequency/APR test:\n");
spll_set_dac(-1, 0);
f_min = spll_measure_frequency(SPLL_OSC_DMTD);
spll_set_dac(-1, 65535);
f_max = spll_measure_frequency(SPLL_OSC_DMTD);
TRACE_DEV("DMTD VCO: Low=%d Hz Hi=%d Hz, APR = %d ppm.\n", f_min, f_max, calc_apr(f_min, f_max, 62500000));
spll_set_dac(0, 0);
f_min = spll_measure_frequency(SPLL_OSC_REF);
spll_set_dac(0, 65535);
f_max = spll_measure_frequency(SPLL_OSC_REF);
TRACE_DEV("REF VCO: Low=%d Hz Hi=%d Hz, APR = %d ppm.\n", f_min, f_max, calc_apr(f_min, f_max, REF_CLOCK_FREQ_HZ));
f_min = spll_measure_frequency(SPLL_OSC_EXT);
TRACE_DEV("EXT clock: Freq=%d Hz\n", f_min);
}
......@@ -39,6 +39,10 @@
#define SPLL_PD_DDMTD 0
#define SPLL_PD_BANGBANG 1
/* Channels for spll_measure_frequency() */
#define SPLL_OSC_REF 0
#define SPLL_OSC_DMTD 1
#define SPLL_OSC_EXT 2
/* Note on channel naming:
- ref_channel means a PHY recovered clock input. There can be one (as in WR core) or more (WR switch).
......@@ -101,9 +105,10 @@ void spll_enable_ptracker(int ref_channel, int enable);
/* 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();
/* Calls non-realtime update state machine. Must be called regularly (although
* it is not time-critical) in the main loop of the program if aux clocks or
* external reference are used in the design. */
void spll_update();
/* Returns the status of given aux clock output (SPLL_AUX_) */
int spll_get_aux_status(int out_channel);
......@@ -122,5 +127,7 @@ 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);
void check_vco_frequencies();
#endif // __SOFTPLL_NG_H
......@@ -10,6 +10,8 @@
/* spll_common.c - common data structures and functions used by the SoftPLL */
#include <string.h>
#include <pp-printf.h>
#include "trace.h"
#include "spll_defs.h"
#include "spll_common.h"
......@@ -125,6 +127,7 @@ Channels (spll_n_chan_ref ... spll_n_chan_out + spll_n_chan_ref-1) are the outpu
void spll_enable_tagger(int channel, int enable)
{
TRACE_DEV("EnableTagger %d %d\n", channel, enable);
if (channel >= spll_n_chan_ref) { /* Output channel? */
if (enable)
SPLL->OCER |= 1 << (channel - spll_n_chan_ref);
......@@ -137,7 +140,7 @@ void spll_enable_tagger(int channel, int enable)
SPLL->RCER &= ~(1 << channel);
}
// TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
TRACE_DEV("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
}
void biquad_init(spll_biquad_t *bq, const int *coefs, int shift)
......@@ -167,3 +170,13 @@ int biquad_update(spll_biquad_t *bq, int x)
return y;
}
const char *stringlist_lookup(const struct stringlist_entry *slist, int id)
{
int i;
for(i=0; slist[i].str; i++) {
if(slist[i].id == id)
return slist[i].str;
}
return "<unknown>";
}
......@@ -66,6 +66,21 @@ typedef struct {
int yd[2], xd[2]; /* I/O delay lines */
} spll_biquad_t;
/* long-term-average filter */
typedef struct {
int acc;
int n;
int window;
int pos;
int size;
int log[16];
} spll_lta_t;
struct stringlist_entry {
int id;
const char *str;
};
/* initializes the PI controller state. Currently almost a stub. */
void pi_init(spll_pi_t *pi);
......@@ -83,4 +98,6 @@ void spll_enable_tagger(int channel, int enable);
void biquad_init(spll_biquad_t *bq, const int *coefs, int shift);
int biquad_update(spll_biquad_t *bq, int x);
const char *stringlist_lookup(const struct stringlist_entry *slist, int id);
#endif // __SPLL_COMMON_H
......@@ -13,152 +13,172 @@
#include "spll_external.h"
#include "spll_debug.h"
#define BB_ERROR_BITS 16
#include <pp-printf.h>
#include "trace.h"
#include "irq.h"
void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
#define ALIGN_STATE_EXT_OFF 0
#define ALIGN_STATE_START 1
#define ALIGN_STATE_INIT_CSYNC 2
#define ALIGN_STATE_WAIT_CSYNC 3
#define ALIGN_STATE_START_ALIGNMENT 7
#define ALIGN_STATE_WAIT_SAMPLE 4
#define ALIGN_STATE_COMPENSATE_DELAY 5
#define ALIGN_STATE_LOCKED 6
#define ALIGN_STATE_START_MAIN 8
s->pi.y_min = 5;
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 = 400;
s->ld.lock_samples = 10000;
s->ld.delock_samples = 9990;
s->ref_src = ext_ref;
s->ph_err_cur = 0;
s->ph_err_d0 = 0;
s->ph_raw_d0 = 0;
s->realign_clocks = realign_clocks;
s->realign_state = (realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
lowpass_init((spll_lowpass_t *)&s->lp_short, 4000);
lowpass_init((spll_lowpass_t *)&s->lp_long, 300);
}
#define ALIGN_SAMPLE_PERIOD 100000
#define ALIGN_TARGET 0
static inline void realign_fsm(struct spll_external_state *s)
{
switch (s->realign_state) {
case REALIGN_STAGE1:
SPLL->ECCR |= SPLL_ECCR_ALIGN_EN;
#define EXT_PERIOD_NS 100
#define EXT_FREQ_HZ 10000000
#define EXT_PPS_LATENCY_PS 30000 // fixme: make configurable
s->realign_state = REALIGN_STAGE1_WAIT;
s->realign_timer = timer_get_tics() + 2 * TICS_PER_SECOND;
break;
case REALIGN_STAGE1_WAIT:
void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
int idx = spll_n_chan_ref + spll_n_chan_out;
if (SPLL->ECCR & SPLL_ECCR_ALIGN_DONE)
s->realign_state = REALIGN_STAGE2;
else if (time_after(timer_get_tics(), s->realign_timer)) {
SPLL->ECCR &= ~SPLL_ECCR_ALIGN_EN;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
case REALIGN_STAGE2:
if (s->ld.locked) {
PPSG->CR = PPSG_CR_CNT_RST | PPSG_CR_CNT_EN;
PPSG->ADJ_UTCLO = 0;
PPSG->ADJ_UTCHI = 0;
PPSG->ADJ_NSEC = 0;
PPSG->ESCR = PPSG_ESCR_SYNC;
s->realign_state = REALIGN_STAGE2_WAIT;
s->realign_timer = timer_get_tics()
+ 2 * TICS_PER_SECOND;
}
break;
case REALIGN_STAGE2_WAIT:
if (PPSG->ESCR & PPSG_ESCR_SYNC) {
PPSG->ESCR = PPSG_ESCR_PPS_VALID | PPSG_ESCR_TM_VALID;
s->realign_state = REALIGN_DONE;
} else if (time_after(timer_get_tics(), s->realign_timer)) {
PPSG->ESCR = 0;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
helper_init(s->helper, idx);
mpll_init(s->main, idx, spll_n_chan_ref);
case REALIGN_PPS_INVALID:
case REALIGN_DISABLED:
case REALIGN_DONE:
return;
}
s->align_state = ALIGN_STATE_EXT_OFF;
s->enabled = 0;
}
int external_update(struct spll_external_state *s, int tag, int source)
void external_start(struct spll_external_state *s)
{
int err, y, y2, ylt;
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);
}
helper_start(s->helper);
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->ECCR = SPLL_ECCR_EXT_EN;
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);
s->align_state = ALIGN_STATE_START;
s->enabled = 1;
spll_debug (DBG_EVENT | DBG_EXT, DBG_EVT_START, 1);
}
if (ld_update(&s->ld, y2 - ylt))
return SPLL_LOCKED;
int external_locked(volatile struct spll_external_state *s)
{
if (!s->helper->ld.locked || !s->main->ld.locked)
return 0;
switch(s->align_state) {
case ALIGN_STATE_EXT_OFF:
case ALIGN_STATE_START:
case ALIGN_STATE_START_MAIN:
case ALIGN_STATE_INIT_CSYNC:
case ALIGN_STATE_WAIT_CSYNC:
return 0;
default:
return 1;
}
return SPLL_LOCKING;
}
void external_start(struct spll_external_state *s)
static int align_sample(int channel, int *v)
{
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 mask = (1 << channel);
if(SPLL->AL_CR & mask) {
SPLL->AL_CR = mask; // ack
int ci = SPLL->AL_CIN;
if(ci > 100 && ci < (EXT_FREQ_HZ - 100) ) { // give some metastability margin, when the counter transitions from EXT_FREQ_HZ-1 to 0
*v = ci * EXT_PERIOD_NS;
return 1;
}
}
return 0; // sample not valid
}
int external_locked(struct spll_external_state *s)
void external_align_fsm(volatile struct spll_external_state *s)
{
return (s->ld.locked
&& (s->realign_clocks ? s->realign_state == REALIGN_DONE : 1));
int v;
switch(s->align_state) {
case ALIGN_STATE_EXT_OFF:
break;
case ALIGN_STATE_START:
if(s->helper->ld.locked) {
disable_irq();
mpll_start(s->main);
enable_irq();
s->align_state = ALIGN_STATE_START_MAIN;
}
break;
case ALIGN_STATE_START_MAIN:
SPLL->AL_CR = 2;
if(s->helper->ld.locked && s->main->ld.locked) {
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_PWIDTH_W(10);
PPSG->ADJ_NSEC = 3;
PPSG->ESCR = PPSG_ESCR_SYNC;
s->align_state = ALIGN_STATE_INIT_CSYNC;
TRACE_DEV("EXT: DMTD locked.\n");
}
break;
case ALIGN_STATE_INIT_CSYNC:
if (PPSG->ESCR & PPSG_ESCR_SYNC) {
PPSG->ESCR = PPSG_ESCR_PPS_VALID; // enable PPS output (even though it's not aligned yet)
s->align_timer = timer_get_tics() + 2 * TICS_PER_SECOND;
s->align_state = ALIGN_STATE_WAIT_CSYNC;
}
break;
case ALIGN_STATE_WAIT_CSYNC:
if(timer_get_tics() >= s->align_timer) {
s->align_state = ALIGN_STATE_START_ALIGNMENT;
s->align_shift = 0;
TRACE_DEV("EXT: CSync complete.\n");
}
break;
case ALIGN_STATE_START_ALIGNMENT:
if(align_sample(1, &v)) {
v %= ALIGN_SAMPLE_PERIOD;
if(v == 0 || v >= ALIGN_SAMPLE_PERIOD / 2) {
s->align_target = EXT_PERIOD_NS;
s->align_step = -100;
} else if (s > 0) {
s->align_target = 0;
s->align_step = 100;
}
TRACE_DEV("EXT: Align target %d, step %d.\n", s->align_target, s->align_step);
s->align_state = ALIGN_STATE_WAIT_SAMPLE;
}
break;
case ALIGN_STATE_WAIT_SAMPLE:
if(!mpll_shifter_busy(s->main) && align_sample(1, &v)) {
v %= ALIGN_SAMPLE_PERIOD;
if(v != s->align_target) {
s->align_shift += s->align_step;
mpll_set_phase_shift(s->main, s->align_shift);
} else if (v == s->align_target) {
s->align_shift += EXT_PPS_LATENCY_PS;
mpll_set_phase_shift(s->main, s->align_shift);
s->align_state = ALIGN_STATE_COMPENSATE_DELAY;
}
}
break;
case ALIGN_STATE_COMPENSATE_DELAY:
if(!mpll_shifter_busy(s->main)) {
TRACE_DEV("EXT: Align done.\n");
s->align_state = ALIGN_STATE_LOCKED;
}
break;
case ALIGN_STATE_LOCKED:
if(!external_locked(s)) {
s->align_state = ALIGN_STATE_START;
}
break;
default:
break;
}
}
......@@ -14,69 +14,29 @@
#define __SPLL_EXTERNAL_H
#include "spll_common.h"
#include "spll_helper.h"
#include "spll_main.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
struct spll_external_state {
struct spll_helper_state *helper;
struct spll_main_state *main;
/* Realignment done */
#define REALIGN_DONE 7
int enabled;
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;
int align_state;
int align_timer;
int align_target;
int align_step;
int align_shift;
};
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);
int external_locked(volatile struct spll_external_state *s);
void external_align_fsm(volatile struct spll_external_state *s);
#endif // __SPLL_EXTERNAL_H
......@@ -65,7 +65,6 @@ int helper_update(struct spll_helper_state *s, int tag,
err = HELPER_ERROR_CLAMP;
}
// err = biquad_update(&s->precomp, err);
if ((tag + s->p_adder) > HELPER_TAG_WRAPAROUND
&& s->p_setpoint > HELPER_TAG_WRAPAROUND) {
......@@ -108,3 +107,15 @@ 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);
}
void helper_switch_reference(struct spll_helper_state *s, int new_ref)
{
#if 0
disable_irq();
s->ref-src = 1;
s->tag_d0 = -1;
s->p-addr = 0;
enable_irq();
spll_enable_tagger(s->ref_src, 1);
#endif
}
......@@ -41,5 +41,6 @@ int helper_update(struct spll_helper_state *s, int tag,
int source);
void helper_start(struct spll_helper_state *s);
void helper_switch_reference(struct spll_helper_state *s, int new_ref);
#endif // __SPLL_HELPER_H
......@@ -11,6 +11,8 @@
#include "spll_main.h"
#include "spll_debug.h"
#include <pp-printf.h>
#include "trace.h"
#define MPLL_TAG_WRAPAROUND 100000000
......@@ -27,10 +29,10 @@ void mpll_init(struct spll_main_state *s, int id_ref,
s->pi.y_min = 5;
s->pi.y_max = 65530;
s->pi.anti_windup = 1;
s->pi.bias = 65000;
s->pi.bias = 30000;
#if defined(CONFIG_WR_SWITCH)
s->pi.kp = 1500; // / 2;
s->pi.ki = 7; // / 2;
s->pi.kp = 1100; // / 2;
s->pi.ki = 30; // / 2;
#elif defined(CONFIG_WR_NODE)
s->pi.kp = 1100; // / 2;
s->pi.ki = 30; // / 2;
......@@ -38,6 +40,7 @@ void mpll_init(struct spll_main_state *s, int id_ref,
#error "Please set CONFIG for wr switch or wr node"
#endif
s->delock_count = 0;
s->enabled = 0;
/* Freqency branch lock detection */
s->ld.threshold = 1200;
......@@ -47,12 +50,16 @@ void mpll_init(struct spll_main_state *s, int id_ref,
s->id_out = id_out;
s->dac_index = id_out - spll_n_chan_ref;
TRACE_DEV("ref %d out %d idx %x \n", s->id_ref, s->id_out, s->dac_index);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
}
void mpll_start(struct spll_main_state *s)
{
TRACE_DEV("MPLL_Start [dac %d]\n", s->dac_index);
s->adder_ref = s->adder_out = 0;
s->tag_ref = -1;
s->tag_out = -1;
......@@ -65,7 +72,7 @@ void mpll_start(struct spll_main_state *s)
s->phase_shift_target = 0;
s->phase_shift_current = 0;
s->sample_n = 0;
s->enabled = 1;
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
......@@ -77,69 +84,38 @@ void mpll_start(struct spll_main_state *s)
void mpll_stop(struct spll_main_state *s)
{
spll_enable_tagger(s->id_out, 0);
s->enabled = 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(!s->enabled)
return SPLL_LOCKED;
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;
int err, y;
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) {
if(s->tag_ref_d >= 0 && s->tag_ref_d > s->tag_ref)
s->adder_ref += (1 << TAG_BITS);
if (s->tag_ref >= 0 && s->tag_out >= 0) {
s->tag_ref_d = s->tag_ref;
}
if (s->tag_ref_d >= 0 && s->tag_ref_d > s->tag_ref)
s->adder_ref += (1 << TAG_BITS);
if (s->tag_out_d >= 0 && s->tag_out_d > s->tag_out)
if (s->tag_out >= 0) {
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;
}
if (s->tag_ref >= 0 && s->tag_out >= 0) {
err = s->adder_ref + s->tag_ref - s->adder_out - s->tag_out;
#ifndef WITH_SEQUENCING
......@@ -192,16 +168,39 @@ int mpll_update(struct spll_main_state *s, int tag, int source)
}
if (ld_update((spll_lock_det_t *)&s->ld, err))
return SPLL_LOCKED;
}
return SPLL_LOCKING;
}
#ifdef CONFIG_PPSI /* use __div64_32 from ppsi library to save libgcc memory */
static int32_t from_picos(int32_t ps)
{
extern uint32_t __div64_32(uint64_t *n, uint32_t base);
uint64_t ups = ps;
if (ps >= 0) {
ups *= 1 << HPLL_N;
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return ups;
}
ups = -ps * (1 << HPLL_N);
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return -ups;
}
#else /* previous implementation: ptp-noposix has no __div64_32 available */
static int32_t from_picos(int32_t ps)
{
return (int32_t) ((int64_t) ps * (int64_t) (1 << HPLL_N) /
(int64_t) CLOCK_PERIOD_PICOSECONDS);
}
#endif
int mpll_set_phase_shift(struct spll_main_state *s,
int desired_shift)
int desired_shift_ps)
{
s->phase_shift_target = desired_shift;
int div = (DIVIDE_DMTD_CLOCKS_BY_2 ? 2 : 1);
s->phase_shift_target = from_picos(desired_shift_ps) / div;
return 0;
}
......
......@@ -35,6 +35,7 @@ struct spll_main_state {
int sample_n;
int delock_count;
int dac_index;
int enabled;
};
void mpll_init(struct spll_main_state *s, int id_ref,
......@@ -47,7 +48,7 @@ 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 desired_shift_ps);
int mpll_shifter_busy(struct spll_main_state *s);
......
......@@ -269,7 +269,7 @@ int main(void)
ui_update();
wrc_ptp_update();
spll_update_aux_clocks();
spll_update();
check_stack();
}
}
......@@ -126,6 +126,7 @@ int wrc_ptp_set_mode(int mode)
shw_pps_gen_enable_output(0);
while (!spll_check_lock(0) && lock_timeout) {
spll_update();
timer_delay_ms(1000);
mprintf(".");
if (time_after(timer_get_tics(), start_tics + lock_timeout)) {
......
......@@ -8,17 +8,21 @@
const char *build_revision;
const char *build_date;
int scb_ver = 33; //SCB version.
int main(void)
{
uint32_t start_tics = timer_get_tics();
uart_init_hw();
TRACE("WR Switch Real Time Subsystem (c) CERN 2011 - 2013\n");
TRACE("");
TRACE("WR Switch Real Time Subsystem (c) CERN 2011 - 2014\n");
TRACE("Revision: %s, built %s.\n", build_revision, build_date);
TRACE("SCB version: %d. %s\n", scb_ver,(scb_ver>=34)?"10 MHz SMC Output.":"" );
TRACE("--");
ad9516_init();
ad9516_init( scb_ver );
rts_init();
rtipc_init();
......@@ -28,12 +32,12 @@ int main(void)
if(time_after(tics, start_tics + TICS_PER_SECOND/5))
{
// TRACE("tick!\n");
spll_show_stats();
start_tics = tics;
}
rts_update();
rtipc_action();
spll_update();
}
return 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment