port: Add interval update mechanism.

This commit adds functionality to increase the sync and pdelay request
intervals once gptp synchronization has been achieved. This is useful
while running Automotive Profile where the network is usually static.

Here, we are assuming that the gPTP synchronization is achieved whenever
the last 'n' offsets calculated using the Sync messages are below a
certain threshold. Both, the number of offsets to consider and the offset
threshold are configurable.

In order to support the interval update, a new clock servo state
CLOCK_SERVO_STABLE has been added. The clock will transition to this
state whenever the above condition is satisfied. When this transition
occurs, the slave will send an "interval request" signaling message
asking the master to change the sync interval. It will also update the
pdelay request interval locally.

Four new config options have been added to provide this functionality:

- servo_offset_threshold: All the offset values being considered should
  be below this value.
- servo_num_offset_values: number of previously received offset values
  to consider.
- operLogSyncInterval: slave will ask the master to switch to this
  interval for sending sync messages when the offset stabilizes.
- operLogPdelayReqInterval: the slave will set this interval for pdelay
  request messages when the offset stabilizes.

Signed-off-by: Vedang Patel <vedang.patel@intel.com>
master
Vedang Patel 2019-03-28 20:32:30 -07:00 committed by Richard Cochran
parent 78b4b780da
commit 3f5f5653d7
11 changed files with 145 additions and 6 deletions

View File

@ -1690,6 +1690,7 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin)
tsproc_reset(c->tsproc, 0); tsproc_reset(c->tsproc, 0);
break; break;
case SERVO_LOCKED: case SERVO_LOCKED:
case SERVO_LOCKED_STABLE:
clockadj_set_freq(c->clkid, -adj); clockadj_set_freq(c->clkid, -adj);
if (c->clkid == CLOCK_REALTIME) { if (c->clkid == CLOCK_REALTIME) {
sysclk_set_sync(); sysclk_set_sync();

View File

@ -263,6 +263,8 @@ struct config_item config_tab[] = {
PORT_ITEM_ENU("network_transport", TRANS_UDP_IPV4, nw_trans_enu), PORT_ITEM_ENU("network_transport", TRANS_UDP_IPV4, nw_trans_enu),
GLOB_ITEM_INT("ntpshm_segment", 0, INT_MIN, INT_MAX), GLOB_ITEM_INT("ntpshm_segment", 0, INT_MIN, INT_MAX),
GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX), GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX),
PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_MAX),
PORT_ITEM_INT("path_trace_enabled", 0, 0, 1), PORT_ITEM_INT("path_trace_enabled", 0, 0, 1),
GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX), GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX),
GLOB_ITEM_DBL("pi_integral_exponent", 0.4, -DBL_MAX, DBL_MAX), GLOB_ITEM_DBL("pi_integral_exponent", 0.4, -DBL_MAX, DBL_MAX),
@ -279,6 +281,8 @@ struct config_item config_tab[] = {
PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"), PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
GLOB_ITEM_STR("revisionData", ";;"), GLOB_ITEM_STR("revisionData", ";;"),
GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX), GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX),
GLOB_ITEM_INT("servo_num_offset_values", 10, 0, INT_MAX),
GLOB_ITEM_INT("servo_offset_threshold", 0, 0, INT_MAX),
GLOB_ITEM_INT("slaveOnly", 0, 0, 1), GLOB_ITEM_INT("slaveOnly", 0, 0, 1),
GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX), GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX),
GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX), GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX),

View File

@ -31,3 +31,7 @@ asCapable true
ignore_source_id 1 ignore_source_id 1
# Required to quickly correct Time Jumps in master # Required to quickly correct Time Jumps in master
step_threshold 1 step_threshold 1
operLogSyncInterval 0
operLogPdelayReqInterval 2
servo_offset_threshold 30
servo_num_offset_values 10

View File

@ -23,8 +23,10 @@ maxStepsRemoved 255
# #
logAnnounceInterval 1 logAnnounceInterval 1
logSyncInterval 0 logSyncInterval 0
operLogSyncInterval 0
logMinDelayReqInterval 0 logMinDelayReqInterval 0
logMinPdelayReqInterval 0 logMinPdelayReqInterval 0
operLogPdelayReqInterval 0
announceReceiptTimeout 3 announceReceiptTimeout 3
syncReceiptTimeout 0 syncReceiptTimeout 0
delayAsymmetry 0 delayAsymmetry 0
@ -73,6 +75,8 @@ max_frequency 900000000
clock_servo pi clock_servo pi
sanity_freq_limit 200000000 sanity_freq_limit 200000000
ntpshm_segment 0 ntpshm_segment 0
servo_num_offset_values 10
servo_offset_threshold 0
# #
# Transport options # Transport options
# #

View File

@ -619,6 +619,7 @@ static void update_clock(struct node *node, struct clock *clock,
clockcheck_step(clock->sanity_check, -offset); clockcheck_step(clock->sanity_check, -offset);
/* Fall through. */ /* Fall through. */
case SERVO_LOCKED: case SERVO_LOCKED:
case SERVO_LOCKED_STABLE:
clockadj_set_freq(clock->clkid, -ppb); clockadj_set_freq(clock->clkid, -ppb);
if (clock->clkid == CLOCK_REALTIME) if (clock->clkid == CLOCK_REALTIME)
sysclk_set_sync(); sysclk_set_sync();

44
port.c
View File

@ -1110,10 +1110,12 @@ static void port_slave_priority_warning(struct port *p)
static void port_synchronize(struct port *p, static void port_synchronize(struct port *p,
tmv_t ingress_ts, tmv_t ingress_ts,
struct timestamp origin_ts, struct timestamp origin_ts,
Integer64 correction1, Integer64 correction2) Integer64 correction1, Integer64 correction2,
Integer8 sync_interval)
{ {
enum servo_state state; enum servo_state state, last_state;
tmv_t t1, t1c, t2, c1, c2; tmv_t t1, t1c, t2, c1, c2;
struct servo *s;
port_set_sync_rx_tmo(p); port_set_sync_rx_tmo(p);
@ -1123,10 +1125,20 @@ static void port_synchronize(struct port *p,
c2 = correction_to_tmv(correction2); c2 = correction_to_tmv(correction2);
t1c = tmv_add(t1, tmv_add(c1, c2)); t1c = tmv_add(t1, tmv_add(c1, c2));
s = clock_servo(p->clock);
last_state = clock_servo_state(p->clock);
state = clock_synchronize(p->clock, t2, t1c); state = clock_synchronize(p->clock, t2, t1c);
switch (state) { switch (state) {
case SERVO_UNLOCKED: case SERVO_UNLOCKED:
port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0);
if (servo_offset_threshold(s) != 0 &&
sync_interval != p->initialLogSyncInterval) {
p->logPdelayReqInterval = p->logMinPdelayReqInterval;
p->logSyncInterval = p->initialLogSyncInterval;
port_tx_interval_request(p, SIGNAL_NO_CHANGE,
SIGNAL_SET_INITIAL,
SIGNAL_NO_CHANGE);
}
break; break;
case SERVO_JUMP: case SERVO_JUMP:
port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0);
@ -1139,6 +1151,23 @@ static void port_synchronize(struct port *p,
case SERVO_LOCKED: case SERVO_LOCKED:
port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0); port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0);
break; break;
case SERVO_LOCKED_STABLE:
if (last_state == SERVO_LOCKED) {
p->logPdelayReqInterval = p->operLogPdelayReqInterval;
p->logSyncInterval = p->operLogSyncInterval;
port_tx_interval_request(p, SIGNAL_NO_CHANGE,
p->logSyncInterval,
SIGNAL_NO_CHANGE);
port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0);
} else if (sync_interval != p->operLogSyncInterval) {
/*
* The most likely reason for this to happen is the
* master daemon re-initialized due to some fault.
*/
servo_reset(s);
port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0);
}
break;
} }
} }
@ -1192,7 +1221,8 @@ static void port_syfufsm(struct port *p, enum syfu_event event,
syn = p->last_syncfup; syn = p->last_syncfup;
port_synchronize(p, syn->hwts.ts, m->ts.pdu, port_synchronize(p, syn->hwts.ts, m->ts.pdu,
syn->header.correction, syn->header.correction,
m->header.correction); m->header.correction,
m->header.logMessageInterval);
msg_put(p->last_syncfup); msg_put(p->last_syncfup);
p->syfu = SF_EMPTY; p->syfu = SF_EMPTY;
break; break;
@ -1211,7 +1241,8 @@ static void port_syfufsm(struct port *p, enum syfu_event event,
fup = p->last_syncfup; fup = p->last_syncfup;
port_synchronize(p, m->hwts.ts, fup->ts.pdu, port_synchronize(p, m->hwts.ts, fup->ts.pdu,
m->header.correction, m->header.correction,
fup->header.correction); fup->header.correction,
m->header.logMessageInterval);
msg_put(p->last_syncfup); msg_put(p->last_syncfup);
p->syfu = SF_EMPTY; p->syfu = SF_EMPTY;
break; break;
@ -1613,8 +1644,10 @@ int port_initialize(struct port *p)
p->localPriority = config_get_int(cfg, p->name, "G.8275.portDS.localPriority"); p->localPriority = config_get_int(cfg, p->name, "G.8275.portDS.localPriority");
p->initialLogSyncInterval = config_get_int(cfg, p->name, "logSyncInterval"); p->initialLogSyncInterval = config_get_int(cfg, p->name, "logSyncInterval");
p->logSyncInterval = p->initialLogSyncInterval; p->logSyncInterval = p->initialLogSyncInterval;
p->operLogSyncInterval = config_get_int(cfg, p->name, "operLogSyncInterval");
p->logMinPdelayReqInterval = config_get_int(cfg, p->name, "logMinPdelayReqInterval"); p->logMinPdelayReqInterval = config_get_int(cfg, p->name, "logMinPdelayReqInterval");
p->logPdelayReqInterval = p->logMinPdelayReqInterval; p->logPdelayReqInterval = p->logMinPdelayReqInterval;
p->operLogPdelayReqInterval = config_get_int(cfg, p->name, "operLogPdelayReqInterval");
p->neighborPropDelayThresh = config_get_int(cfg, p->name, "neighborPropDelayThresh"); p->neighborPropDelayThresh = config_get_int(cfg, p->name, "neighborPropDelayThresh");
p->min_neighbor_prop_delay = config_get_int(cfg, p->name, "min_neighbor_prop_delay"); p->min_neighbor_prop_delay = config_get_int(cfg, p->name, "min_neighbor_prop_delay");
@ -2224,7 +2257,8 @@ void process_sync(struct port *p, struct ptp_message *m)
if (one_step(m)) { if (one_step(m)) {
port_synchronize(p, m->hwts.ts, m->ts.pdu, port_synchronize(p, m->hwts.ts, m->ts.pdu,
m->header.correction, 0); m->header.correction, 0,
m->header.logMessageInterval);
flush_last_sync(p); flush_last_sync(p);
return; return;
} }

View File

@ -115,9 +115,11 @@ struct port {
UInteger8 transportSpecific; UInteger8 transportSpecific;
UInteger8 localPriority; UInteger8 localPriority;
Integer8 initialLogSyncInterval; Integer8 initialLogSyncInterval;
Integer8 operLogSyncInterval;
Integer8 logSyncInterval; Integer8 logSyncInterval;
Enumeration8 delayMechanism; Enumeration8 delayMechanism;
Integer8 logMinPdelayReqInterval; Integer8 logMinPdelayReqInterval;
Integer8 operLogPdelayReqInterval;
Integer8 logPdelayReqInterval; Integer8 logPdelayReqInterval;
UInteger32 neighborPropDelayThresh; UInteger32 neighborPropDelayThresh;
int follow_up_info; int follow_up_info;

30
ptp4l.8
View File

@ -161,6 +161,13 @@ The mean time interval between Sync messages. A shorter interval may improve
accuracy of the local clock. It's specified as a power of two in seconds. accuracy of the local clock. It's specified as a power of two in seconds.
The default is 0 (1 second). The default is 0 (1 second).
.TP .TP
.B operLogSyncInterval
The mean time interval between Sync messages. This value is only used by the
slave device when interval_update_timer is enabled. Slave will send this
interval to the master to switch to. This is done via a signaling message after
interval_update_timer expires. It's specified as a power of two in seconds. The
default value is 0 (1 second).
.TP
.B logMinDelayReqInterval .B logMinDelayReqInterval
The minimum permitted mean time interval between Delay_Req messages. A shorter The minimum permitted mean time interval between Delay_Req messages. A shorter
interval makes ptp4l react faster to the changes in the path delay. It's interval makes ptp4l react faster to the changes in the path delay. It's
@ -172,6 +179,13 @@ The minimum permitted mean time interval between Pdelay_Req messages. It's
specified as a power of two in seconds. specified as a power of two in seconds.
The default is 0 (1 second). The default is 0 (1 second).
.TP .TP
.B operLogPdelayReqInterval
The mean time interval between Pdelay Request messages. This value is only used
by the slave device when interval_update_timer is enabled. Slave will switch to
the interval specified by this config option after the interval_update_timer
expires. It's specified as a power of two in seconds. The default value is 0 (1
second).
.TP
.B announceReceiptTimeout .B announceReceiptTimeout
The number of missed Announce messages before the last Announce messages The number of missed Announce messages before the last Announce messages
expires. expires.
@ -715,6 +729,22 @@ This will disable source port identity checking for Sync and Follow_Up
messages. This is useful when the announce messages are disabled in the master messages. This is useful when the announce messages are disabled in the master
and the slave does not have any way to know it's identity. The default is 0 and the slave does not have any way to know it's identity. The default is 0
(disabled). (disabled).
.TP
.B servo_num_offset_values
The number of offset values calculated in previously received Sync messages to
consider when adjusting the Sync and Pdelay request intervals. More information
provided in the description of 'offset_threshold'. The default value is 10.
.TP
.B servo_offset_threshold
This value is used by the slave for adjusting the intervals for Sync and Pdelay
request messages. The slave will check the last 'num_offset_values' offsets and
if all those offsets are less than the offset_threshold, it will adjust both
the intervals. The Sync interval is adjusted via the signaling mechanism and
the pdelay request interval is just adjusted locally. The new values to use for
sync message intervals and pdelay request intervals can be indicated by
operLogSyncInterval and operLogPdelayReqInterval respectively. This mechanism
is currently only supported when BMCA == 'noop'. The default
value of offset_threshold is 0 (disabled).
.SH UNICAST DISCOVERY OPTIONS .SH UNICAST DISCOVERY OPTIONS

45
servo.c
View File

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "config.h" #include "config.h"
#include "linreg.h" #include "linreg.h"
@ -25,6 +26,8 @@
#include "pi.h" #include "pi.h"
#include "servo_private.h" #include "servo_private.h"
#include "print.h"
#define NSEC_PER_SEC 1000000000 #define NSEC_PER_SEC 1000000000
struct servo *servo_create(struct config *cfg, enum servo_type type, struct servo *servo_create(struct config *cfg, enum servo_type type,
@ -79,6 +82,9 @@ struct servo *servo_create(struct config *cfg, enum servo_type type,
} }
servo->first_update = 1; servo->first_update = 1;
servo->offset_threshold = config_get_int(cfg, NULL, "servo_offset_threshold");
servo->num_offset_values = config_get_int(cfg, NULL, "servo_num_offset_values");
servo->curr_offset_values = servo->num_offset_values;
return servo; return servo;
} }
@ -88,6 +94,18 @@ void servo_destroy(struct servo *servo)
servo->destroy(servo); servo->destroy(servo);
} }
static int check_offset_threshold(struct servo *s, int64_t offset)
{
long long int abs_offset = llabs(offset);
if (s->offset_threshold) {
if (abs_offset < s->offset_threshold && s->curr_offset_values)
s->curr_offset_values--;
return s->curr_offset_values ? 0 : 1;
}
return 0;
}
double servo_sample(struct servo *servo, double servo_sample(struct servo *servo,
int64_t offset, int64_t offset,
uint64_t local_ts, uint64_t local_ts,
@ -98,8 +116,28 @@ double servo_sample(struct servo *servo,
r = servo->sample(servo, offset, local_ts, weight, state); r = servo->sample(servo, offset, local_ts, weight, state);
if (*state != SERVO_UNLOCKED) switch (*state) {
case SERVO_UNLOCKED:
servo->curr_offset_values = servo->num_offset_values;
break;
case SERVO_JUMP:
servo->curr_offset_values = servo->num_offset_values;
servo->first_update = 0; servo->first_update = 0;
break;
case SERVO_LOCKED:
if (check_offset_threshold(servo, offset)) {
*state = SERVO_LOCKED_STABLE;
}
servo->first_update = 0;
break;
case SERVO_LOCKED_STABLE:
/*
* This case will never occur since the only place
* SERVO_LOCKED_STABLE is set is in this switch/case block
* (case SERVO_LOCKED).
*/
break;
}
return r; return r;
} }
@ -127,3 +165,8 @@ void servo_leap(struct servo *servo, int leap)
if (servo->leap) if (servo->leap)
servo->leap(servo, leap); servo->leap(servo, leap);
} }
int servo_offset_threshold(struct servo *servo)
{
return servo->offset_threshold;
}

13
servo.h
View File

@ -57,6 +57,12 @@ enum servo_state {
* The servo is tracking the master clock. * The servo is tracking the master clock.
*/ */
SERVO_LOCKED, SERVO_LOCKED,
/**
* The Servo has stabilized. The last 'servo_num_offset_values' values
* of the estimated threshold are less than servo_offset_threshold.
*/
SERVO_LOCKED_STABLE,
}; };
/** /**
@ -123,4 +129,11 @@ double servo_rate_ratio(struct servo *servo);
*/ */
void servo_leap(struct servo *servo, int leap); void servo_leap(struct servo *servo, int leap);
/**
* Get the offset threshold for triggering the interval change request.
* @param servo Pointer to a servo obtained via @ref servo_create().
* @return The offset threshold set by the user.
*/
int servo_offset_threshold(struct servo *servo);
#endif #endif

View File

@ -29,6 +29,9 @@ struct servo {
double step_threshold; double step_threshold;
double first_step_threshold; double first_step_threshold;
int first_update; int first_update;
int64_t offset_threshold;
int num_offset_values;
int curr_offset_values;
void (*destroy)(struct servo *servo); void (*destroy)(struct servo *servo);