Subject: Re: [RFC PATCH v2 1/2] macb: Add 1588 support in Cadence GEM.

From: Richard Cochran <[email-masked]>
Date: Wed, 23 Nov 2016 22:03:18 +0100

On Wed, Nov 23, 2016 at 02:34:03PM +0100, Andrei Pistirica wrote:
> From what I understand, your suggestion is:
> (ns | frac) * ppb = (total_ns | total_frac)
> (total_ns | total_frac) / 10^9 = (adj_ns | adj_frac)
> This is correct iff total_ns/10^9 >= 1, but the problem is that there are
> missed fractions due to the following approximation:
> frac*ppb =~ (ns*ppb+frac*ppb*2^16)*2^16-10^9*2^16*flor(ns*ppb+frac*ppb*2^16,
> 10^9).

-ENOPARSE;
 
> An example which uses values from a real test:
> let ppb=4891, ns=12 and frac=3158

That is a very strange example for nominal frequency.  The clock
period is 12.048187255859375 nanoseconds, and so the frequency is
83000037.99 Hz.

But hey, let's go with it...

> - using suggested algorithm, yields: adj_ns = 0 and adj_frac = 0
> - using in-place algorithm, yields: adj_ns = 0, adj_frac = 4
> You can check the calculus.

The test program, below, shows you what I meant.  (Of course, you
should adjust this to fit the adjfine() method.)

Unfortunately, this device has a very coarse frequency resolution.
Using a nominal period of ns=12 as an example, the resolution is
2^-16 / 12 or 1.27 ppm.  The 24 bit device is much better in this
repect.

The output using your example numbers is:

   $ ./a.out 12 3158 4891
   ns=12 frac=3158
   ns=12 frac=3162

   $ ./a.out 12 3158 -4891
   ns=12 frac=3158
   ns=12 frac=3154

See how you get a result of +/- 4 with just one division?

Thanks,
Richard

---
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

static void adjfreq(uint32_t ns, uint32_t frac, int32_t ppb)
{
	uint64_t adj;
	uint32_t diff, word;
	int neg_adj = 0;

	printf("ns=%u frac=%u\n", ns, frac);

	if (ppb < 0) {
		neg_adj = 1;
		ppb = -ppb;
	}
	word = (ns << 16) + frac;
	adj = word;
	adj *= ppb;
	adj += 500000000UL;
	diff = adj / 1000000000UL;

	word = neg_adj ? word - diff : word + diff;
	printf("ns=%u frac=%u\n", word >> 16, word & 0xffff);
}

int main(int argc, char *argv[])
{
	uint32_t ns, frac;
	int32_t ppb;

	if (argc != 4) {
		puts("need ns, frac, and ppb");
		return -1;
	}
	ns = atoi(argv[1]);
	frac = atoi(argv[2]);
	ppb = atoi(argv[3]);
	adjfreq(ns, frac, ppb);
	return 0;
}