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.
This commit is contained in:
Miroslav Lichvar
2015-09-22 17:12:15 +02:00
parent 8feb37df2b
commit d6fdae5f1d
5 changed files with 105 additions and 11 deletions

View File

@@ -43,6 +43,8 @@
static lcl_ReadFrequencyDriver drv_read_freq; static lcl_ReadFrequencyDriver drv_read_freq;
static lcl_SetFrequencyDriver drv_set_freq; static lcl_SetFrequencyDriver drv_set_freq;
static lcl_SetSyncStatusDriver drv_set_sync_status; 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) */ /* Current frequency as requested by the local module (in ppm) */
static double base_freq; static double base_freq;
@@ -85,6 +87,16 @@ static double correction_rate;
real frequency of the clock */ real frequency of the clock */
static double slew_error; 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); 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 static double
clamp_freq(double freq) clamp_freq(double freq)
{ {
@@ -138,6 +182,8 @@ update_slew(void)
UTI_DiffTimevalsToDouble(&duration, &now, &slew_start); UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
offset_register -= slew_freq * duration; offset_register -= slew_freq * duration;
stop_fastslew(&now);
/* Estimate how long should the next slew take */ /* Estimate how long should the next slew take */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) { if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
duration = MAX_SLEW_TIMEOUT; duration = MAX_SLEW_TIMEOUT;
@@ -155,6 +201,14 @@ update_slew(void)
else if (corr_freq > max_corr_freq) else if (corr_freq > max_corr_freq)
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 */ /* Get the new real frequency and clamp it */
total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq)); 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 /* 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 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 when base_freq is larger than max_freq, or fast slew is active), use the
on the next update. */ maximum timeout and try again on the next update. */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION || if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
offset_register * slew_freq <= 0.0) { offset_register * slew_freq <= 0.0) {
duration = MAX_SLEW_TIMEOUT; duration = MAX_SLEW_TIMEOUT;
@@ -246,13 +300,25 @@ static void
offset_convert(struct timeval *raw, offset_convert(struct timeval *raw,
double *corr, double *err) double *corr, double *err)
{ {
double duration; double duration, fastslew_corr, fastslew_err;
UTI_DiffTimevalsToDouble(&duration, raw, &slew_start); UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
*corr = slew_freq * duration - offset_register; if (drv_get_offset_correction && fastslew_active) {
if (err) drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
*err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0; 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_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_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,
lcl_SetLeapDriver sys_set_leap, lcl_SetLeapDriver sys_set_leap,
lcl_SetSyncStatusDriver sys_set_sync_status) 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); max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
drv_read_freq = sys_read_freq; drv_read_freq = sys_read_freq;
drv_set_freq = sys_set_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; drv_set_sync_status = sys_set_sync_status;
base_freq = (*drv_read_freq)(); 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; 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, lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, sys_apply_step_offset ? accrue_offset, sys_apply_step_offset ?
sys_apply_step_offset : 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 void
SYS_Generic_Finalise(void) SYS_Generic_Finalise(void)
{ {
struct timeval now;
/* Must *NOT* leave a slew running - clock could drift way off /* Must *NOT* leave a slew running - clock could drift way off
if the daemon is not restarted */ if the daemon is not restarted */
if (slew_timer_running) { if (slew_timer_running) {
@@ -339,6 +416,9 @@ SYS_Generic_Finalise(void)
} }
(*drv_set_freq)(clamp_freq(base_freq)); (*drv_set_freq)(clamp_freq(base_freq));
LCL_ReadRawTime(&now);
stop_fastslew(&now);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -35,6 +35,9 @@ extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_s
lcl_ReadFrequencyDriver sys_read_freq, lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_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,
lcl_SetLeapDriver sys_set_leap, lcl_SetLeapDriver sys_set_leap,
lcl_SetSyncStatusDriver sys_set_sync_status); lcl_SetSyncStatusDriver sys_set_sync_status);

View File

@@ -384,7 +384,8 @@ SYS_Linux_Initialise(void)
SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick, SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
1.0 / tick_update_hz, 1.0 / tick_update_hz,
read_frequency, set_frequency, read_frequency, set_frequency,
have_setoffset ? apply_step_offset : NULL); have_setoffset ? apply_step_offset : NULL,
0.0, 0.0, NULL, NULL);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -181,7 +181,8 @@ initialise_timex(void)
void void
SYS_Timex_Initialise(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, SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay,
lcl_ReadFrequencyDriver sys_read_freq, lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_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(); initialise_timex();
SYS_Generic_CompleteFreqDriver(max_set_freq_ppm, max_set_freq_delay, SYS_Generic_CompleteFreqDriver(max_set_freq_ppm, max_set_freq_delay,
sys_read_freq ? sys_read_freq : read_frequency, sys_read_freq ? sys_read_freq : read_frequency,
sys_set_freq ? sys_set_freq : set_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);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -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, extern void SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay,
lcl_ReadFrequencyDriver sys_read_freq, lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_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); extern void SYS_Timex_Finalise(void);