Wall clock accuracy

2 messages, 2 authors, 2005-08-09 · open the first message on its own page

Wall clock accuracy

From: Rune Torgersen <hidden>
Date: 2005-08-09 15:35:27

Hi

I have discovered that the accuracy of the wallclock (xtime) on ppc is
not very good.
I am running a custom telco board based on a 8266, and the main busclock
is derived off of a T1 reference clock.
I was noticing a huge number of logentries fron OpenNTPD about
adjustiing the clock, so I started to check.
The drift of the walltime was a little over 7 seconds in 15 hours (7
seconds slow) (equals about 130us per s)

When I checked around, I found that xtime is updated in kernel/time.c
and is updated by time_nsec nanoseconds per timer interrupt. 
This value is hardcoded (through some obscure macros) to be 999848
ns/timertick. Tis is 152ns slow per timertick. This works out to be
152us per second, almos the value I found.

I have tried a few workarounds, and made one that modifies time_nsec
before update_wall_time_one_tick() is called.
With this fix (which is 8260 specific at the time) I have less than 1 ms
drift after 5 days.

The fix works by calculating how many ns and fractions of ns there is in
one timer-tick (one jiffie).
Then, at each timer interrupt, I accumulate the ns fractions, and add
whole ns to the timer when needed.

This is tested on 8266 under 80.00MHz and 82.575360MHz main busclock. It
also works correct if HZ=1024 instead of 1000

It is not meant as a patch to be added to the kernel right now, but more
of a possible solution to a problem that it doesn't seem anybody has
noticed before.

Index: arch/ppc/kernel/time.c
===================================================================
--- arch/ppc/kernel/time.c	(revision 20)
+++ arch/ppc/kernel/time.c	(working copy)
@@ -119,6 +119,11 @@
 EXPORT_SYMBOL(profile_pc);
 #endif
 
+
+#ifdef CONFIG_8260
+extern unsigned long get_ns_delta(void); /*
arch/ppc/syslib/m8260_setup.c */
+extern unsigned long tick_nsec;          /* kernel/time.c */
+#endif
 /*
  * timer_interrupt - gets called when the decrementer overflows,
  * with interrupts disabled.
@@ -148,6 +153,10 @@
 		/* We are in an interrupt, no need to save/restore flags
*/
 		write_seqlock(&xtime_lock);
 		tb_last_stamp = jiffy_stamp;
+
+#ifdef CONFIG_8260		
+		tick_nsec = get_ns_delta();
+#endif		
 		do_timer(regs);
 
 		/*
@@ -179,7 +188,7 @@
 		write_sequnlock(&xtime_lock);
 	}
 	if ( !disarm_decr[smp_processor_id()] )
-		set_dec(next_dec);
+		set_dec(next_dec-1);
 	last_jiffy_stamp(cpu) = jiffy_stamp;
 
 	if (ppc_md.heartbeat && !ppc_md.heartbeat_count--)
Index: arch/ppc/syslib/m8260_setup.c
===================================================================
--- arch/ppc/syslib/m8260_setup.c	(revision 20)
+++ arch/ppc/syslib/m8260_setup.c	(working copy)
@@ -31,6 +31,8 @@
 
 #include "cpm2_pic.h"
 
+#include <linux/module.h>    
+
 unsigned char __res[sizeof(bd_t)];
 
 extern void cpm2_reset(void);
@@ -82,16 +84,63 @@
 /* The decrementer counts at the system (internal) clock frequency
  * divided by four.
  */
+unsigned long tb_time_nsec;
+unsigned long tb_frac_nsec;
+unsigned long tb_frac_limit;     
+ 
+extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
+ 
 static void __init
 m8260_calibrate_decr(void)
+{    
+    bd_t *binfo = (bd_t *)__res;
+    int freq, divisor, tick_freq;
+    uint64_t tmp;
+    printk("Calibrating Decrementer\n");
+
+    freq = binfo->bi_busfreq;
+    divisor = 4;
+    
+    /* get rounded off decrementer frequency */
+    tick_freq = (freq + 3) / 4;
+    
+    /* get number of timebase ticks per jiffy (timerint) */
+    tb_ticks_per_jiffy = (tick_freq + (HZ/2)) / HZ;
+    tb_to_us = mulhwu_scale_factor(tick_freq, 1000000);
+
+    /* calculate timer interrupt interval in nano seconds */
+    tmp = 1000000000ULL * (u64)tb_ticks_per_jiffy;
+    __div64_32(&tmp, tick_freq);
+    tb_time_nsec = tmp;
+    
+    /* calculate fractions of nanosecons per timer interrupt */
+    tb_frac_limit = 100000UL;
+    tmp = 1000000000ULL * (u64)tb_ticks_per_jiffy * tb_frac_limit;
+    __div64_32(&tmp, tick_freq);
+    tb_frac_nsec = (u32)(tmp - (u64)tb_time_nsec * (u64)tb_frac_limit);
+    
+    printk("busfreq = %u\n", freq);
+    printk("tb_ticks_per_jiffy = %u\ntb_to_us = %u\n",
tb_ticks_per_jiffy, tb_to_us);
+    printk("tick_freq = %u\ntime_nsec = %u\ntb_frac_nsec = %u\n",
tick_freq, tb_time_nsec, tb_frac_nsec);
+}
+
+unsigned long get_ns_delta(void)
 {
-	bd_t *binfo = (bd_t *)__res;
-	int freq, divisor;
+    static uint32_t frac = 0;
 
-	freq = binfo->bi_busfreq;
-        divisor = 4;
-        tb_ticks_per_jiffy = freq / HZ / divisor;
-	tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
+    unsigned long delta_ns;
+
+    delta_ns = tb_time_nsec;
+
+    /* adjust for inaccuracy in calculation */
+    frac += tb_frac_nsec;
+    if (frac >= tb_frac_limit)
+    {
+        delta_ns += frac / tb_frac_limit;
+        frac %= tb_frac_limit;
+    }
+    
+    return delta_ns;
 }
 
 /* The 8260 has an internal 1-second timer update register that

Rune Torgersen
System Developer
Innovative Systems LLC
1000 Innovative Drive
Mitchell, SD 57301
Ph: 605-995-6120
www.innovsys.com

Re: Wall clock accuracy

From: Eugene Surovegin <hidden>
Date: 2005-08-09 16:41:27

On Tue, Aug 09, 2005 at 10:23:19AM -0500, Rune Torgersen wrote:
Hi

I have discovered that the accuracy of the wallclock (xtime) on ppc is
not very good.
I am running a custom telco board based on a 8266, and the main busclock
is derived off of a T1 reference clock.
I was noticing a huge number of logentries fron OpenNTPD about
adjustiing the clock, so I started to check.
The drift of the walltime was a little over 7 seconds in 15 hours (7
seconds slow) (equals about 130us per s)
Hmm, if I'm correct this clock drift (130ppm) should be handled easily 
by NTPD without stepping clock but with slewing. This is why NTPD 
exists in the first place, so I don't see any reason to change 
the kernel.

It's not small drift (I usually have clock accuracy within +-50ppm), 
but still is much less than maximum 1024ppm NTPD can deal with.

Maybe it's an OpenNTPD problem? I use original NTPD (ntp.org) which 
handles such drifts quite well.

-- 
Eugene
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help