From e52771b15ee7e68944d70a64dcf48f8516212db7 Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Sun, 10 Jan 2010 09:25:02 +0000 Subject: xpp: Improved inter-Astribank drift calculation. Merged revisions 7625 via svnmerge from http://svn.digium.com/svn/dahdi/linux/trunk git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/branches/2.2@7806 a0bf4364-ded3-4de4-8d8a-66a801d63aff --- drivers/dahdi/xpp/xbus-pcm.c | 185 ++++++++++++++++++++++------------------- drivers/dahdi/xpp/xbus-pcm.h | 17 ++-- drivers/dahdi/xpp/xbus-sysfs.c | 53 ++++++++---- 3 files changed, 147 insertions(+), 108 deletions(-) diff --git a/drivers/dahdi/xpp/xbus-pcm.c b/drivers/dahdi/xpp/xbus-pcm.c index 81efba0..fb41761 100644 --- a/drivers/dahdi/xpp/xbus-pcm.c +++ b/drivers/dahdi/xpp/xbus-pcm.c @@ -61,8 +61,14 @@ static xbus_t *global_ticker; static struct xpp_ticker global_ticks_series; #define PROC_SYNC "sync" +#define SYNC_CYCLE 500 /* Sampling cycle in usec */ +#define SYNC_CYCLE_SAMPLE 100 /* Samples from end of SYNC_CYCLE */ +#define SYNC_CONVERGE 10 /* Number of SYNC_CYCLE's to converge speed */ +#define SYNC_CENTER 500 /* Offset from ref_ticker to other AB's */ +#define SYNC_DELTA 40 /* If within +/-SYNC_DELTA, try to stay there */ #define BIG_TICK_INTERVAL 1000 -#define SYNC_ADJ_MAX 63 /* maximal firmware drift unit (63) */ +#define SYNC_ADJ_MAX 20 /* maximal firmware drift unit (hardware limit 63) */ + /* * The USB bulk endpoints have a large jitter in the timing of frames * from the AB to the ehci-hcd. This is because we cannot predict @@ -123,12 +129,6 @@ static int xpp_ticker_step(struct xpp_ticker *ticker, const struct timeval *t) return cycled; } -static inline void driftinfo_recalc(struct xpp_drift *driftinfo) -{ - driftinfo->delta_max = INT_MIN; - driftinfo->delta_min = INT_MAX; -} - /* * No locking. It is called only from: * - update_sync_master() in a globall spinlock protected code. @@ -136,11 +136,12 @@ static inline void driftinfo_recalc(struct xpp_drift *driftinfo) */ static inline void xbus_drift_clear(xbus_t *xbus) { - struct xpp_drift *driftinfo = &xbus->drift; + struct xpp_drift *di = &xbus->drift; - driftinfo_recalc(driftinfo); - driftinfo->calc_drift = 0; + do_gettimeofday(&di->last_lost_tick.tv); ticker_set_cycle(&xbus->ticker, SYNC_ADJ_QUICK); + di->max_speed = -SYNC_ADJ_MAX; + di->min_speed = SYNC_ADJ_MAX; } void xpp_drift_init(xbus_t *xbus) @@ -148,7 +149,6 @@ void xpp_drift_init(xbus_t *xbus) memset(&xbus->drift, 0, sizeof(xbus->drift)); spin_lock_init(&xbus->drift.lock); xpp_ticker_init(&xbus->ticker); - xbus->drift.wanted_offset = 500; xbus_drift_clear(xbus); } @@ -168,110 +168,123 @@ static void sample_tick(xbus_t *xbus, int sample) #define sample_tick(x,y) #endif +/* + * The following function adjust the clock of an astribank according + * to our reference clock (another astribank or another DAHDI device). + * + * It is VERY hard to stabilise these corrections: + * - The measurments are affected by 125usec USB micro-frames. + * - However, if we are off by more than +/-500usec we are risking + * loosing frames. + * + * Every change must be tested rigorously with many device combinations + * including abrupt changes in sync -- to verify the stability of the + * algorithm. + */ static void xpp_drift_step(xbus_t *xbus, const struct timeval *tv) { - struct xpp_drift *driftinfo = &xbus->drift; + struct xpp_drift *di = &xbus->drift; struct xpp_ticker *ticker = &xbus->ticker; unsigned long flags; - bool cycled; - spin_lock_irqsave(&driftinfo->lock, flags); - cycled = xpp_ticker_step(&xbus->ticker, tv); + spin_lock_irqsave(&di->lock, flags); + xpp_ticker_step(&xbus->ticker, tv); + /* + * Do we need to be synchronized and is there an established reference + * ticker (another Astribank or another DAHDI device) already? + */ if(ref_ticker && ref_ticker != &xbus->ticker && syncer && xbus->sync_mode == SYNC_MODE_PLL) { int new_delta_tick = ticker->count - ref_ticker->count; - int lost_ticks = new_delta_tick - driftinfo->delta_tick; + int lost_ticks = new_delta_tick - di->delta_tick; + long usec_delta; - driftinfo->delta_tick = new_delta_tick; + di->delta_tick = new_delta_tick; if(lost_ticks) { static int rate_limit; - driftinfo->lost_ticks++; - driftinfo->lost_tick_count += abs(lost_ticks); - + /* + * We just lost some ticks. Just report it and don't + * try to adjust anything until we are stable again. + */ + di->lost_ticks++; + di->lost_tick_count += abs(lost_ticks); if((rate_limit++ % 1003) == 0) { - XBUS_DBG(SYNC, xbus, "Lost %d tick%s\n", + XBUS_NOTICE(xbus, "Lost %d tick%s\n", lost_ticks, (abs(lost_ticks) > 1) ? "s": ""); } - ticker_set_cycle(ticker, SYNC_ADJ_QUICK); + xbus_drift_clear(xbus); if(abs(lost_ticks) > 100) ticker->count = ref_ticker->count; } else { - long usec_delta; - bool nofix = 0; - + /* Sample a delta */ usec_delta = (long)usec_diff( &ticker->last_sample.tv, &ref_ticker->last_sample.tv); - usec_delta -= driftinfo->wanted_offset; sample_tick(xbus, usec_delta); - if(abs(usec_delta) > 300) { + if ((ticker->count % SYNC_CYCLE) > (SYNC_CYCLE - SYNC_CYCLE_SAMPLE)) + di->delta_sum += usec_delta; + + if((ticker->count % SYNC_CYCLE) == 0) { /* - * We are close to the edge, send a brutal - * fix, and skip calculation until next time. + * Full sampling cycle passed. Let's calculate */ - if(usec_delta > 0 && xbus->sync_adjustment > -SYNC_ADJ_MAX) { - XBUS_DBG(SYNC, xbus, "Pullback usec_delta=%ld\n", usec_delta); - driftinfo->kicks_down++; - send_drift(xbus, -SYNC_ADJ_MAX); /* emergency push */ - } - if(usec_delta < 0 && xbus->sync_adjustment < SYNC_ADJ_MAX) { - XBUS_DBG(SYNC, xbus, "Pushback usec_delta=%ld\n", usec_delta); - driftinfo->kicks_up++; - send_drift(xbus, SYNC_ADJ_MAX); /* emergency push */ - } - ticker_set_cycle(ticker, SYNC_ADJ_QUICK); - nofix = 1; - } else { - /* good data, use it */ - if(usec_delta > driftinfo->delta_max) - driftinfo->delta_max = usec_delta; - if(usec_delta < driftinfo->delta_min) - driftinfo->delta_min = usec_delta; - } - if(!nofix && cycled) { - int offset = 0; - - driftinfo->median = (driftinfo->delta_max + driftinfo->delta_min) / 2; - driftinfo->jitter = driftinfo->delta_max - driftinfo->delta_min; - if(abs(driftinfo->median) >= 150) { /* more than 1 usb uframe */ - int factor = abs(driftinfo->median) / 125; - - factor = 1 + (factor * 8000) / ticker->cycle; - if(driftinfo->median > 0) - offset = driftinfo->calc_drift - factor; - else - offset = driftinfo->calc_drift + factor; - /* for large median, push some more */ - if(abs(driftinfo->median) >= 300) { /* more than 2 usb uframes */ - ticker_set_cycle(ticker, SYNC_ADJ_QUICK); - XBUS_NOTICE(xbus, - "Back to quick: median=%d\n", - driftinfo->median); - } + int offset = di->delta_sum / SYNC_CYCLE_SAMPLE - SYNC_CENTER; + int offset_prev = di->offset_prev; + int speed = xbus->sync_adjustment; + int fix = 0; + int best_speed = (di->max_speed + di->min_speed) >> 1; + + if (offset > 0 && offset < SYNC_DELTA) { + speed = best_speed - 1; + } else if (offset < 0 && offset > -SYNC_DELTA) { + speed = best_speed + 1; } else { - //ticker_set_cycle(ticker, ticker->cycle + 500); + if (offset > 0) { + if (offset > offset_prev) + fix--; + } else { + if (offset < offset_prev) + fix++; + } + speed += fix; + } + if (speed < -SYNC_ADJ_MAX) + speed = -SYNC_ADJ_MAX; + if (speed > SYNC_ADJ_MAX) + speed = SYNC_ADJ_MAX; + if (speed < di->min_speed) + di->min_speed = speed; + if (speed > di->max_speed) + di->max_speed = speed; + if(offset > di->offset_max) + di->offset_max = offset; + if(offset < di->offset_min) + di->offset_min = offset; + + XBUS_DBG(SYNC, xbus, "offset: %d, min_speed=%d, max_speed=%d, usec_delta(last)=%ld\n", + offset_prev, di->min_speed, di->max_speed, usec_delta); + XBUS_DBG(SYNC, xbus, "ADJ: speed=%d (best_speed=%d) fix=%d\n", + speed, best_speed, fix); + xbus->sync_adjustment_offset = speed; + if(xbus != syncer && xbus->sync_adjustment != speed) + send_drift(xbus, speed); + di->sync_inaccuracy = abs(offset) + abs(di->offset_range) / 2; + if(ticker->count >= SYNC_CYCLE * SYNC_CONVERGE) { + di->offset_range = di->offset_max - di->offset_min; + di->offset_min = INT_MAX; + di->offset_max = -INT_MAX; + if(di->max_speed > best_speed) + di->max_speed--; + if(di->min_speed < best_speed) + di->min_speed++; } - driftinfo->calc_drift = offset; - XBUS_DBG(SYNC, xbus, - "ADJ: min=%d max=%d jitter=%d median=%d offset=%d\n", - driftinfo->delta_min, - driftinfo->delta_max, - driftinfo->jitter, - driftinfo->median, - offset); - if(offset < -SYNC_ADJ_MAX) - offset = -SYNC_ADJ_MAX; - if(offset > SYNC_ADJ_MAX) - offset = SYNC_ADJ_MAX; - xbus->sync_adjustment_offset = offset; - if(xbus != syncer && xbus->sync_adjustment != offset) - send_drift(xbus, offset); - driftinfo_recalc(driftinfo); + di->offset_prev = offset; + di->delta_sum = 0; } } } - spin_unlock_irqrestore(&driftinfo->lock, flags); + spin_unlock_irqrestore(&di->lock, flags); } const char *sync_mode_name(enum sync_mode mode) diff --git a/drivers/dahdi/xpp/xbus-pcm.h b/drivers/dahdi/xpp/xbus-pcm.h index bd9e5fa..23e701a 100644 --- a/drivers/dahdi/xpp/xbus-pcm.h +++ b/drivers/dahdi/xpp/xbus-pcm.h @@ -71,17 +71,18 @@ struct xpp_ticker { /* for rate calculation */ * xbus ticker to a reference ticker. */ struct xpp_drift { - int wanted_offset; /* fixed */ int delta_tick; /* from ref_ticker */ int lost_ticks; /* occurances */ int lost_tick_count; - int delta_max; - int delta_min; - int median; /* (max + min) / 2 */ - int jitter; /* max - min */ - int calc_drift; - int kicks_up; - int kicks_down; + int sync_inaccuracy; + struct xpp_timestamp last_lost_tick; + long delta_sum; + int offset_prev; + int offset_range; + int offset_min; + int offset_max; + int min_speed; + int max_speed; spinlock_t lock; }; diff --git a/drivers/dahdi/xpp/xbus-sysfs.c b/drivers/dahdi/xpp/xbus-sysfs.c index 35895ca..a48b806 100644 --- a/drivers/dahdi/xpp/xbus-sysfs.c +++ b/drivers/dahdi/xpp/xbus-sysfs.c @@ -121,10 +121,8 @@ static DEVICE_ATTR_READER(timing_show, dev, buf) len += snprintf(buf + len, PAGE_SIZE - len, "%-3s", sync_mode_name(xbus->sync_mode)); if(xbus->sync_mode == SYNC_MODE_PLL) { len += snprintf(buf + len, PAGE_SIZE - len, - " %5d: jitter %4d median %4d calc_drift %3d lost (%4d,%4d) : ", + " %5d: lost (%4d,%4d) : ", xbus->ticker.cycle, - driftinfo->jitter, driftinfo->median, - driftinfo->calc_drift, driftinfo->lost_ticks, driftinfo->lost_tick_count); len += snprintf(buf + len, PAGE_SIZE - len, "DRIFT %3d %ld sec ago", @@ -212,25 +210,52 @@ static DEVICE_ATTR_READER(driftinfo_show, dev, buf) xbus_t *xbus; struct xpp_drift *di; struct xpp_ticker *ticker; + struct timeval now; int len = 0; + int hours; + int minutes; + int seconds; + int speed_range; + int uframes_inaccuracy; + int i; xbus = dev_to_xbus(dev); di = &xbus->drift; ticker = &xbus->ticker; -#define SHOW(ptr,item) len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %d\n", #item, (ptr)->item) + /* + * Calculate lost ticks time + */ + do_gettimeofday(&now); + seconds = now.tv_sec - di->last_lost_tick.tv.tv_sec; + minutes = seconds / 60; + seconds = seconds % 60; + hours = minutes / 60; + minutes = minutes % 60; +#define SHOW(ptr,item) len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d\n", #item, (ptr)->item) + len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d (was %d:%02d:%02d ago)\n", + "lost_ticks", di->lost_ticks, hours, minutes, seconds); + speed_range = abs(di->max_speed - di->min_speed); + uframes_inaccuracy = di->sync_inaccuracy / 125; + len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d ", + "instability", speed_range + uframes_inaccuracy); + if(xbus->sync_mode == SYNC_MODE_AB) { + buf[len++] = '-'; + } else { + for(i = 0; len < PAGE_SIZE - 1 && i < speed_range + uframes_inaccuracy; i++) + buf[len++] = '#'; + } + buf[len++] = '\n'; + len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d (uframes)\n", "inaccuracy", uframes_inaccuracy); + len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d\n", "speed_range", speed_range); SHOW(xbus, sync_adjustment); - SHOW(di, wanted_offset); - SHOW(di, delta_tick); - SHOW(di, lost_ticks); - SHOW(di, kicks_up); - SHOW(di, kicks_down); - SHOW(di, delta_min); - SHOW(di, delta_max); - SHOW(di, median); - SHOW(di, jitter); - SHOW(di, calc_drift); + len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d\n", "offset (usec)", di->offset_prev); + SHOW(di, offset_range); + len += snprintf(buf + len, PAGE_SIZE - len, "%-15s: %8d\n", "best_speed", (di->max_speed + di->min_speed) / 2); + SHOW(di, min_speed); + SHOW(di, max_speed); SHOW(ticker, cycle); SHOW(ticker, tick_period); + SHOW(ticker, count); #undef SHOW return len; } -- cgit v1.2.3