From d6fdae5f1d133026f48b434dcd24ceb70dc30e63 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 22 Sep 2015 17:12:15 +0200 Subject: [PATCH] sys_generic: allow fast slewing with system driver The system drivers may implement their own slewing which the generic driver can use to slew faster than the maximum frequency the driver is allowed to set directly. --- sys_generic.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++---- sys_generic.h | 3 ++ sys_linux.c | 3 +- sys_timex.c | 13 ++++++-- sys_timex.h | 5 ++- 5 files changed, 105 insertions(+), 11 deletions(-) diff --git a/sys_generic.c b/sys_generic.c index db343e55..49f5df23 100644 --- a/sys_generic.c +++ b/sys_generic.c @@ -43,6 +43,8 @@ static lcl_ReadFrequencyDriver drv_read_freq; static lcl_SetFrequencyDriver drv_set_freq; static lcl_SetSyncStatusDriver drv_set_sync_status; +static lcl_AccrueOffsetDriver drv_accrue_offset; +static lcl_OffsetCorrectionDriver drv_get_offset_correction; /* Current frequency as requested by the local module (in ppm) */ static double base_freq; @@ -85,6 +87,16 @@ static double correction_rate; real frequency of the clock */ static double slew_error; +/* Minimum offset that the system driver can slew faster than the maximum + frequency offset that it allows to be set directly */ +static double fastslew_min_offset; + +/* Maximum slew rate of the system driver */ +static double fastslew_max_rate; + +/* Flag indicating that the system driver is currently slewing */ +static int fastslew_active; + /* ================================================== */ static void handle_end_of_slew(void *anything); @@ -109,6 +121,38 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq, /* ================================================== */ +static void +start_fastslew(void) +{ + if (!drv_accrue_offset) + return; + + drv_accrue_offset(offset_register, 0.0); + + DEBUG_LOG(LOGF_SysGeneric, "fastslew offset=%e", offset_register); + + offset_register = 0.0; + fastslew_active = 1; +} + +/* ================================================== */ + +static void +stop_fastslew(struct timeval *now) +{ + double corr; + + if (!drv_get_offset_correction || !fastslew_active) + return; + + /* Cancel the remaining offset */ + drv_get_offset_correction(now, &corr, NULL); + drv_accrue_offset(corr, 0.0); + offset_register -= corr; +} + +/* ================================================== */ + static double clamp_freq(double freq) { @@ -138,6 +182,8 @@ update_slew(void) UTI_DiffTimevalsToDouble(&duration, &now, &slew_start); offset_register -= slew_freq * duration; + stop_fastslew(&now); + /* Estimate how long should the next slew take */ if (fabs(offset_register) < MIN_OFFSET_CORRECTION) { duration = MAX_SLEW_TIMEOUT; @@ -155,6 +201,14 @@ update_slew(void) else if (corr_freq > max_corr_freq) corr_freq = max_corr_freq; + /* Let the system driver perform the slew if the requested frequency + offset is too large for the frequency driver */ + if (drv_accrue_offset && fabs(corr_freq) >= fastslew_max_rate && + fabs(offset_register) > fastslew_min_offset) { + start_fastslew(); + corr_freq = 0.0; + } + /* Get the new real frequency and clamp it */ total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq)); @@ -175,8 +229,8 @@ update_slew(void) /* Compute the duration of the slew and clamp it. If the slewing frequency is zero or has wrong sign (e.g. due to rounding in the frequency driver or - when base_freq is larger than max_freq), use maximum timeout and try again - on the next update. */ + when base_freq is larger than max_freq, or fast slew is active), use the + maximum timeout and try again on the next update. */ if (fabs(offset_register) < MIN_OFFSET_CORRECTION || offset_register * slew_freq <= 0.0) { duration = MAX_SLEW_TIMEOUT; @@ -246,13 +300,25 @@ static void offset_convert(struct timeval *raw, double *corr, double *err) { - double duration; + double duration, fastslew_corr, fastslew_err; UTI_DiffTimevalsToDouble(&duration, raw, &slew_start); - *corr = slew_freq * duration - offset_register; - if (err) - *err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0; + if (drv_get_offset_correction && fastslew_active) { + drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err); + if (fastslew_corr == 0.0 && fastslew_err == 0.0) + fastslew_active = 0; + } else { + fastslew_corr = fastslew_err = 0.0; + } + + *corr = slew_freq * duration + fastslew_corr - offset_register; + + if (err) { + *err = fastslew_err; + if (fabs(duration) <= max_freq_change_delay) + *err += slew_error; + } } /* ================================================== */ @@ -303,6 +369,9 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela lcl_ReadFrequencyDriver sys_read_freq, lcl_SetFrequencyDriver sys_set_freq, lcl_ApplyStepOffsetDriver sys_apply_step_offset, + double min_fastslew_offset, double max_fastslew_rate, + lcl_AccrueOffsetDriver sys_accrue_offset, + lcl_OffsetCorrectionDriver sys_get_offset_correction, lcl_SetLeapDriver sys_set_leap, lcl_SetSyncStatusDriver sys_set_sync_status) { @@ -310,6 +379,8 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6); drv_read_freq = sys_read_freq; drv_set_freq = sys_set_freq; + drv_accrue_offset = sys_accrue_offset; + drv_get_offset_correction = sys_get_offset_correction; drv_set_sync_status = sys_set_sync_status; base_freq = (*drv_read_freq)(); @@ -318,6 +389,10 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6; + fastslew_min_offset = min_fastslew_offset; + fastslew_max_rate = max_fastslew_rate / 1.0e6; + fastslew_active = 0; + lcl_RegisterSystemDrivers(read_frequency, set_frequency, accrue_offset, sys_apply_step_offset ? sys_apply_step_offset : apply_step_offset, @@ -331,6 +406,8 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela void SYS_Generic_Finalise(void) { + struct timeval now; + /* Must *NOT* leave a slew running - clock could drift way off if the daemon is not restarted */ if (slew_timer_running) { @@ -339,6 +416,9 @@ SYS_Generic_Finalise(void) } (*drv_set_freq)(clamp_freq(base_freq)); + + LCL_ReadRawTime(&now); + stop_fastslew(&now); } /* ================================================== */ diff --git a/sys_generic.h b/sys_generic.h index 45320838..d9b252e5 100644 --- a/sys_generic.h +++ b/sys_generic.h @@ -35,6 +35,9 @@ extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_s lcl_ReadFrequencyDriver sys_read_freq, lcl_SetFrequencyDriver sys_set_freq, lcl_ApplyStepOffsetDriver sys_apply_step_offset, + double min_fastslew_offset, double max_fastslew_rate, + lcl_AccrueOffsetDriver sys_accrue_offset, + lcl_OffsetCorrectionDriver sys_get_offset_correction, lcl_SetLeapDriver sys_set_leap, lcl_SetSyncStatusDriver sys_set_sync_status); diff --git a/sys_linux.c b/sys_linux.c index f5543319..24fcbbc4 100644 --- a/sys_linux.c +++ b/sys_linux.c @@ -384,7 +384,8 @@ SYS_Linux_Initialise(void) SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick, 1.0 / tick_update_hz, read_frequency, set_frequency, - have_setoffset ? apply_step_offset : NULL); + have_setoffset ? apply_step_offset : NULL, + 0.0, 0.0, NULL, NULL); } /* ================================================== */ diff --git a/sys_timex.c b/sys_timex.c index b2b53994..60018f15 100644 --- a/sys_timex.c +++ b/sys_timex.c @@ -181,7 +181,8 @@ initialise_timex(void) void SYS_Timex_Initialise(void) { - SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL, NULL, NULL); + SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL, NULL, NULL, + 0.0, 0.0, NULL, NULL); } /* ================================================== */ @@ -190,14 +191,20 @@ void SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay, lcl_ReadFrequencyDriver sys_read_freq, lcl_SetFrequencyDriver sys_set_freq, - lcl_ApplyStepOffsetDriver sys_apply_step_offset) + lcl_ApplyStepOffsetDriver sys_apply_step_offset, + double min_fastslew_offset, double max_fastslew_rate, + lcl_AccrueOffsetDriver sys_accrue_offset, + lcl_OffsetCorrectionDriver sys_get_offset_correction) { initialise_timex(); SYS_Generic_CompleteFreqDriver(max_set_freq_ppm, max_set_freq_delay, sys_read_freq ? sys_read_freq : read_frequency, sys_set_freq ? sys_set_freq : set_frequency, - sys_apply_step_offset, set_leap, set_sync_status); + sys_apply_step_offset, + min_fastslew_offset, max_fastslew_rate, + sys_accrue_offset, sys_get_offset_correction, + set_leap, set_sync_status); } /* ================================================== */ diff --git a/sys_timex.h b/sys_timex.h index 9f8544b9..c15b16c6 100644 --- a/sys_timex.h +++ b/sys_timex.h @@ -37,7 +37,10 @@ extern void SYS_Timex_Initialise(void); extern void SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay, lcl_ReadFrequencyDriver sys_read_freq, lcl_SetFrequencyDriver sys_set_freq, - lcl_ApplyStepOffsetDriver sys_apply_step_offset); + lcl_ApplyStepOffsetDriver sys_apply_step_offset, + double min_fastslew_offset, double max_fastslew_rate, + lcl_AccrueOffsetDriver sys_accrue_offset, + lcl_OffsetCorrectionDriver sys_get_offset_correction); extern void SYS_Timex_Finalise(void); -- 2.47.2