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;
}
......@@ -18,38 +18,55 @@
/* BEGIN OF HACKS */
#include <pp-printf.h>
/* <ctype.h> */
static inline int isdigit(int c)
{
return c >= '0' && c <= '9';
}
static inline int islower(int c)
{
return c >= 'a' && c <= 'z';
}
static inline int isupper(int c)
{
return c >= 'A' && c <= 'Z';
}
static inline int isalpha(int c)
{
return islower(c) || isupper(c);
}
static inline int isalnum(int c)
{
return isalpha(c) || isdigit(c);
}
/* <linux/types.h> -- but if we typedef we get redefined type when hosted */
#define u8 uint8_t
#define size_t unsigned long
#define ptrdiff_t unsigned long
#define noinline __attribute__((noinline))
/*
* We now have optional 64-bit support. It depends on __div64_32.
* The suggested implementaion is the one by Bernardo Innocenti, found
* in asm-generic in the kernel -- ARub
*/
#ifdef CONFIG_PRINTF_64BIT
#define NUMBER_TYPE uint64_t
#define SIGNED_NUMBER_TYPE int64_t
extern uint32_t __div64_32(uint64_t *n, uint32_t base);
/* The unnecessary pointer compare is there
* to check for type safety (n must be 64bit)
*/
#define do_div(n,base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
(void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
if (((n) >> 32) == 0) { \
__rem = (uint32_t)(n) % __base; \
(n) = (uint32_t)(n) / __base; \
} else \
__rem = __div64_32(&(n), __base); \
__rem; \
})
#else /* 32 bits (or native 64 bits): a reduced version of above */
#define NUMBER_TYPE unsigned long
#define SIGNED_NUMBER_TYPE signed long
#define do_div(n,base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
(void)(((typeof((n)) *)0) == ((unsigned long *)0)); \
__rem = (n) % __base; \
(n) = (n) / __base; \
__rem; \
})
#endif /* CONFIG_PRINTF_64BIT */
/* END OF HACKS */
const char hex_asc[] = "0123456789abcdef";
......@@ -76,106 +93,6 @@ static int skip_atoi(const char **s)
return i;
}
/* Decimal conversion is by far the most typical, and is used
* for /proc and /sys data. This directly impacts e.g. top performance
* with many processes running. We optimize it for speed
* using code from
* http://www.cs.uiowa.edu/~jones/bcd/decimal.html
* (with permission from the author, Douglas W. Jones). */
/* Formats correctly any integer in [0,99999].
* Outputs from one to five digits depending on input.
* On i386 gcc 4.1.2 -O2: ~250 bytes of code. */
static char* put_dec_trunc(char *buf, unsigned q)
{
unsigned d3, d2, d1, d0;
d1 = (q>>4) & 0xf;
d2 = (q>>8) & 0xf;
d3 = (q>>12);
d0 = 6*(d3 + d2 + d1) + (q & 0xf);
q = (d0 * 0xcd) >> 11;
d0 = d0 - 10*q;
*buf++ = d0 + '0'; /* least significant digit */
d1 = q + 9*d3 + 5*d2 + d1;
if (d1 != 0) {
q = (d1 * 0xcd) >> 11;
d1 = d1 - 10*q;
*buf++ = d1 + '0'; /* next digit */
d2 = q + 2*d2;
if ((d2 != 0) || (d3 != 0)) {
q = (d2 * 0xd) >> 7;
d2 = d2 - 10*q;
*buf++ = d2 + '0'; /* next digit */
d3 = q + 4*d3;
if (d3 != 0) {
q = (d3 * 0xcd) >> 11;
d3 = d3 - 10*q;
*buf++ = d3 + '0'; /* next digit */
if (q != 0)
*buf++ = q + '0'; /* most sign. digit */
}
}
}
return buf;
}
/* Same with if's removed. Always emits five digits */
static char* put_dec_full(char *buf, unsigned q)
{
/* BTW, if q is in [0,9999], 8-bit ints will be enough, */
/* but anyway, gcc produces better code with full-sized ints */
unsigned d3, d2, d1, d0;
d1 = (q>>4) & 0xf;
d2 = (q>>8) & 0xf;
d3 = (q>>12);
/*
* Possible ways to approx. divide by 10
* gcc -O2 replaces multiply with shifts and adds
* (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
* (x * 0x67) >> 10: 1100111
* (x * 0x34) >> 9: 110100 - same
* (x * 0x1a) >> 8: 11010 - same
* (x * 0x0d) >> 7: 1101 - same, shortest code (on i386)
*/
d0 = 6*(d3 + d2 + d1) + (q & 0xf);
q = (d0 * 0xcd) >> 11;
d0 = d0 - 10*q;
*buf++ = d0 + '0';
d1 = q + 9*d3 + 5*d2 + d1;
q = (d1 * 0xcd) >> 11;
d1 = d1 - 10*q;
*buf++ = d1 + '0';
d2 = q + 2*d2;
q = (d2 * 0xd) >> 7;
d2 = d2 - 10*q;
*buf++ = d2 + '0';
d3 = q + 4*d3;
q = (d3 * 0xcd) >> 11; /* - shorter code */
/* q = (d3 * 0x67) >> 10; - would also work */
d3 = d3 - 10*q;
*buf++ = d3 + '0';
*buf++ = q + '0';
return buf;
}
/* No inlining helps gcc to use registers better */
static noinline char* put_dec(char *buf, unsigned long num)
{
while (1) {
unsigned rem;
if (num < 100000)
return put_dec_trunc(buf, num);
rem = num % 100000;
num /= 100000;
buf = put_dec_full(buf, rem);
}
}
#define ZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
......@@ -184,7 +101,7 @@ static noinline char* put_dec(char *buf, unsigned long num)
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
static char *number(char *buf, unsigned long num, int base, int size, int precision, int type)
static char *number(char *buf, NUMBER_TYPE num, int base, int size, int precision, int type)
{
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
......@@ -202,9 +119,9 @@ static char *number(char *buf, unsigned long num, int base, int size, int precis
type &= ~ZEROPAD;
sign = 0;
if (type & SIGN) {
if ((signed long) num < 0) {
if ((SIGNED_NUMBER_TYPE) num < 0) {
sign = '-';
num = - (signed long) num;
num = - (SIGNED_NUMBER_TYPE) num;
size--;
} else if (type & PLUS) {
sign = '+';
......@@ -224,22 +141,10 @@ static char *number(char *buf, unsigned long num, int base, int size, int precis
i = 0;
if (num == 0)
tmp[i++] = '0';
/* Generic code, for any base:
/* Generic code, for any base: */
else do {
tmp[i++] = (digits[do_div(num,base)] | locase);
} while (num != 0);
*/
else if (base != 10) { /* 8 or 16 */
int mask = base - 1;
int shift = 3;
if (base == 16) shift = 4;
do {
tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
num >>= shift;
} while (num);
} else { /* base 10 */
i = put_dec(tmp, num) - tmp;
}
/* printing 100 using %2d gives "100", not "00" */
if (i > precision)
......@@ -295,112 +200,28 @@ static char *string(char *buf, char *s, int field_width, int precision, int flag
return buf;
}
#ifdef CONFIG_CMD_NET
static char *mac_address_string(char *buf, u8 *addr, int field_width,
int precision, int flags)
{
char mac_addr[6 * 3]; /* (6 * 2 hex digits), 5 colons and trailing zero */
char *p = mac_addr;
int i;
for (i = 0; i < 6; i++) {
p = pack_hex_byte(p, addr[i]);
if (!(flags & SPECIAL) && i != 5)
*p++ = ':';
}
*p = '\0';
return string(buf, mac_addr, field_width, precision, flags & ~SPECIAL);
}
static char *ip6_addr_string(char *buf, u8 *addr, int field_width,
int precision, int flags)
{
char ip6_addr[8 * 5]; /* (8 * 4 hex digits), 7 colons and trailing zero */
char *p = ip6_addr;
int i;
for (i = 0; i < 8; i++) {
p = pack_hex_byte(p, addr[2 * i]);
p = pack_hex_byte(p, addr[2 * i + 1]);
if (!(flags & SPECIAL) && i != 7)
*p++ = ':';
}
*p = '\0';
return string(buf, ip6_addr, field_width, precision, flags & ~SPECIAL);
}
static char *ip4_addr_string(char *buf, u8 *addr, int field_width,
int precision, int flags)
{
char ip4_addr[4 * 4]; /* (4 * 3 decimal digits), 3 dots and trailing zero */
char temp[3]; /* hold each IP quad in reverse order */
char *p = ip4_addr;
int i, digits;
for (i = 0; i < 4; i++) {
digits = put_dec_trunc(temp, addr[i]) - temp;
/* reverse the digits in the quad */
while (digits--)
*p++ = temp[digits];
if (i != 3)
*p++ = '.';
}
*p = '\0';
return string(buf, ip4_addr, field_width, precision, flags & ~SPECIAL);
}
#endif
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
* specifiers.
*
* Right now we handle:
*
* - 'M' For a 6-byte MAC address, it prints the address in the
* usual colon-separated hex notation
* - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated
* decimal for v4 and colon separated network-order 16 bit hex for v6)
* - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
* currently the same
*
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
* pointer to the real address.
* -- Such extension is removed in pp_printf
*/
static char *pointer(const char *fmt, char *buf, void *ptr, int field_width, int precision, int flags)
{
unsigned long plong;
if (!ptr)
return string(buf, "(null)", field_width, precision, flags);
#ifdef CONFIG_CMD_NET
switch (*fmt) {
case 'm':
flags |= SPECIAL;
/* Fallthrough */
case 'M':
return mac_address_string(buf, ptr, field_width, precision, flags);
case 'i':
flags |= SPECIAL;
/* Fallthrough */
case 'I':
if (fmt[1] == '6')
return ip6_addr_string(buf, ptr, field_width, precision, flags);
if (fmt[1] == '4')
return ip4_addr_string(buf, ptr, field_width, precision, flags);
flags &= ~SPECIAL;
break;
}
#endif
flags |= SMALL;
if (field_width == -1) {
field_width = 2*sizeof(void *);
flags |= ZEROPAD;
}
return number(buf, (unsigned long) ptr, 16, field_width, precision, flags);
plong = (intptr_t)ptr;
return number(buf, plong, 16, field_width, precision, flags);
}
/**
......@@ -422,7 +243,7 @@ static char *pointer(const char *fmt, char *buf, void *ptr, int field_width, int
*/
int pp_vsprintf(char *buf, const char *fmt, va_list args)
{
unsigned long num;
NUMBER_TYPE num;
int base;
char *str;
......@@ -518,9 +339,6 @@ int pp_vsprintf(char *buf, const char *fmt, va_list args)
str = pointer(fmt+1, str,
va_arg(args, void *),
field_width, precision, flags);
/* Skip all alphanumeric pointer suffixes */
while (isalnum(fmt[1]))
fmt++;
continue;
case 'n':
......@@ -562,8 +380,8 @@ int pp_vsprintf(char *buf, const char *fmt, va_list args)
--fmt;
continue;
}
#ifdef CONFIG_SYS_64BIT_VSPRINTF
if (qualifier == 'L') /* "quad" for 64 bit variables */
#ifdef CONFIG_PRINTF_64BIT
if (qualifier == 'L')
num = va_arg(args, unsigned long long);
else
#endif
......
......@@ -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;
......
......@@ -3,7 +3,7 @@
* File : softpll_regs.h
* Author : auto-generated by wbgen2 from spll_wb_slave.wb
* Created : Thu Jul 25 11:14:53 2013
* Created : Mon Jul 21 13:49:11 2014
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE spll_wb_slave.wb
......@@ -34,62 +34,101 @@
/* 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)
#define SPLL_CSR_UNUSED0_MASK WBGEN2_GEN_MASK(8, 6)
#define SPLL_CSR_UNUSED0_SHIFT 8
#define SPLL_CSR_UNUSED0_W(value) WBGEN2_GEN_WRITE(value, 8, 6)
#define SPLL_CSR_UNUSED0_R(reg) WBGEN2_GEN_READ(reg, 8, 6)
/* definitions for field: Number of reference channels (max: 32) in reg: SPLL Control/Status Register */
#define SPLL_CSR_N_REF_MASK WBGEN2_GEN_MASK(8, 6)
#define SPLL_CSR_N_REF_SHIFT 8
#define SPLL_CSR_N_REF_W(value) WBGEN2_GEN_WRITE(value, 8, 6)
#define SPLL_CSR_N_REF_R(reg) WBGEN2_GEN_READ(reg, 8, 6)
#define SPLL_CSR_N_REF_MASK WBGEN2_GEN_MASK(16, 6)
#define SPLL_CSR_N_REF_SHIFT 16
#define SPLL_CSR_N_REF_W(value) WBGEN2_GEN_WRITE(value, 16, 6)
#define SPLL_CSR_N_REF_R(reg) WBGEN2_GEN_READ(reg, 16, 6)
/* definitions for field: Number of output channels (max: 8) in reg: SPLL Control/Status Register */
#define SPLL_CSR_N_OUT_MASK WBGEN2_GEN_MASK(16, 3)
#define SPLL_CSR_N_OUT_SHIFT 16
#define SPLL_CSR_N_OUT_W(value) WBGEN2_GEN_WRITE(value, 16, 3)
#define SPLL_CSR_N_OUT_R(reg) WBGEN2_GEN_READ(reg, 16, 3)
#define SPLL_CSR_N_OUT_MASK WBGEN2_GEN_MASK(24, 3)
#define SPLL_CSR_N_OUT_SHIFT 24
#define SPLL_CSR_N_OUT_W(value) WBGEN2_GEN_WRITE(value, 24, 3)
#define SPLL_CSR_N_OUT_R(reg) WBGEN2_GEN_READ(reg, 24, 3)
/* definitions for field: Debug queue supported in reg: SPLL Control/Status Register */
#define SPLL_CSR_DBG_SUPPORTED WBGEN2_GEN_MASK(19, 1)
#define SPLL_CSR_DBG_SUPPORTED WBGEN2_GEN_MASK(27, 1)
/* definitions for register: External Clock Control Register */
/* definitions for field: Enable External Clock BB Detector in reg: External Clock Control Register */
/* definitions for field: Enable External Clock PLL 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: External Clock Reference Present in reg: External Clock Control Register */
#define SPLL_ECCR_EXT_REF_PRESENT 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 register: Aligner Control Register */
/* 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 field: Aligner sample valid/select on channel in reg: Aligner Control Register */
#define SPLL_AL_CR_VALID_MASK WBGEN2_GEN_MASK(0, 9)
#define SPLL_AL_CR_VALID_SHIFT 0
#define SPLL_AL_CR_VALID_W(value) WBGEN2_GEN_WRITE(value, 0, 9)
#define SPLL_AL_CR_VALID_R(reg) WBGEN2_GEN_READ(reg, 0, 9)
/* definitions for field: Aligner required on channel in reg: Aligner Control Register */
#define SPLL_AL_CR_REQUIRED_MASK WBGEN2_GEN_MASK(9, 9)
#define SPLL_AL_CR_REQUIRED_SHIFT 9
#define SPLL_AL_CR_REQUIRED_W(value) WBGEN2_GEN_WRITE(value, 9, 9)
#define SPLL_AL_CR_REQUIRED_R(reg) WBGEN2_GEN_READ(reg, 9, 9)
/* definitions for register: Aligner Counter REF register */
/* definitions for register: Aligner Counter IN register */
/* definitions for register: DMTD VCO Frequency */
/* definitions for field: FREQ in reg: DMTD VCO Frequency */
#define SPLL_F_DMTD_FREQ_MASK WBGEN2_GEN_MASK(0, 28)
#define SPLL_F_DMTD_FREQ_SHIFT 0
#define SPLL_F_DMTD_FREQ_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define SPLL_F_DMTD_FREQ_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for field: VALID in reg: DMTD VCO Frequency */
#define SPLL_F_DMTD_VALID WBGEN2_GEN_MASK(28, 1)
/* definitions for register: REF VCO Frequency */
/* definitions for field: FREQ in reg: REF VCO Frequency */
#define SPLL_F_REF_FREQ_MASK WBGEN2_GEN_MASK(0, 28)
#define SPLL_F_REF_FREQ_SHIFT 0
#define SPLL_F_REF_FREQ_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define SPLL_F_REF_FREQ_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for field: VALID in reg: REF VCO Frequency */
#define SPLL_F_REF_VALID WBGEN2_GEN_MASK(28, 1)
/* definitions for register: EXT VCO Frequency */
/* definitions for field: FREQ in reg: EXT VCO Frequency */
#define SPLL_F_EXT_FREQ_MASK WBGEN2_GEN_MASK(0, 28)
#define SPLL_F_EXT_FREQ_SHIFT 0
#define SPLL_F_EXT_FREQ_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define SPLL_F_EXT_FREQ_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for field: VALID in reg: EXT VCO Frequency */
#define SPLL_F_EXT_VALID WBGEN2_GEN_MASK(28, 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)
#define SPLL_OCCR_OUT_EN_MASK WBGEN2_GEN_MASK(8, 8)
#define SPLL_OCCR_OUT_EN_SHIFT 8
#define SPLL_OCCR_OUT_EN_W(value) WBGEN2_GEN_WRITE(value, 8, 8)
#define SPLL_OCCR_OUT_EN_R(reg) WBGEN2_GEN_READ(reg, 8, 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)
#define SPLL_OCCR_OUT_LOCK_MASK WBGEN2_GEN_MASK(16, 8)
#define SPLL_OCCR_OUT_LOCK_SHIFT 16
#define SPLL_OCCR_OUT_LOCK_W(value) WBGEN2_GEN_WRITE(value, 16, 8)
#define SPLL_OCCR_OUT_LOCK_R(reg) WBGEN2_GEN_READ(reg, 16, 8)
/* definitions for register: Reference Channel Tagging Enable Register */
......@@ -127,36 +166,6 @@
#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 */
......@@ -234,49 +243,53 @@ PACKED struct SPLL_WB {
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 */
/* [0x8]: REG Aligner Control Register */
uint32_t AL_CR;
/* [0xc]: REG Aligner Counter REF register */
uint32_t AL_CREF;
/* [0x10]: REG Aligner Counter IN register */
uint32_t AL_CIN;
/* [0x14]: REG DMTD VCO Frequency */
uint32_t F_DMTD;
/* [0x18]: REG REF VCO Frequency */
uint32_t F_REF;
/* [0x1c]: REG EXT VCO Frequency */
uint32_t F_EXT;
/* [0x20]: REG Output Channel Control Register */
uint32_t OCCR;
/* [0x14]: REG Reference Channel Tagging Enable Register */
/* [0x24]: REG Reference Channel Tagging Enable Register */
uint32_t RCER;
/* [0x18]: REG Output Channel Tagging Enable Register */
/* [0x28]: REG Output Channel Tagging Enable Register */
uint32_t OCER;
/* padding to: 8 words */
uint32_t __padding_1[1];
/* [0x20]: REG Helper DAC Output */
/* padding to: 16 words */
uint32_t __padding_0[5];
/* [0x40]: REG Helper DAC Output */
uint32_t DAC_HPLL;
/* [0x24]: REG Main DAC Output */
/* [0x44]: REG Main DAC Output */
uint32_t DAC_MAIN;
/* [0x28]: REG DDMTD Deglitcher threshold */
/* [0x48]: REG DDMTD Deglitcher threshold */
uint32_t DEGLITCH_THR;
/* [0x2c]: REG Debug FIFO Register - SPLL side */
/* [0x4c]: 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 */
/* padding to: 24 words */
uint32_t __padding_1[4];
/* [0x60]: REG Interrupt disable register */
uint32_t EIC_IDR;
/* [0x44]: REG Interrupt enable register */
/* [0x64]: REG Interrupt enable register */
uint32_t EIC_IER;
/* [0x48]: REG Interrupt mask register */
/* [0x68]: REG Interrupt mask register */
uint32_t EIC_IMR;
/* [0x4c]: REG Interrupt status register */
/* [0x6c]: REG Interrupt status register */
uint32_t EIC_ISR;
/* [0x50]: REG FIFO 'Debug FIFO Register - Host side' data output register 0 */
/* [0x70]: 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 */
/* [0x74]: 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 */
/* [0x78]: 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 */
/* [0x7c]: REG FIFO 'Tag Readout Register' data output register 0 */
uint32_t TRR_R0;
/* [0x60]: REG FIFO 'Tag Readout Register' control/status register */
/* [0x80]: REG FIFO 'Tag Readout Register' control/status register */
uint32_t TRR_CSR;
};
......
......@@ -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