Commit 4bfaea77 authored by Jean-Claude BAU's avatar Jean-Claude BAU Committed by Adam Wujek

High Accuracy - first implementation

This implementation works on a WR Switch but does not yet integrate all
required functionalities. List of current modifications :
- Implementation of the L1SYNC protocol
- Modifications of the standard protocol: new hook, TS calculation
- Change WRS to integrate HA
- HAL adaptations
- ...
parent 41bedca5
......@@ -116,7 +116,7 @@ config PROFILE_WR
config PROFILE_HA
bool
default n if ARCH_WRS || ARCH_WRPC
default y if ARCH_WRS || ARCH_WRPC
config PROFILE_PTP
bool
......
......@@ -80,7 +80,6 @@ typedef struct {
#define HEXP_PORT_MODE_WR_SLAVE 2
#define HEXP_PORT_MODE_NON_WR 3
#define FIX_ALPHA_FRACBITS 40
/*
#define HEXP_PORT_TSC_RISING 1
#define HEXP_PORT_TSC_FALLING 2
......
......@@ -39,6 +39,9 @@ typedef struct hal_port_calibration {
uint32_t delta_tx_phy;
uint32_t delta_rx_phy;
/* bit slide expresse in picos */
uint32_t bitslide_ps;
/* Current board routing delays (between the DDMTD inputs to
the PHY clock inputs/outputs), in picoseconds */
uint32_t delta_tx_board;
......
......@@ -40,17 +40,19 @@ extern void wrs_main_loop(struct pp_globals *ppg);
extern void wrs_init_ipcserver(struct minipc_ch *ppsi_ch);
/* wrs-calibration.c */
int wrs_read_calibration_data(struct pp_instance *ppi,
uint32_t *delta_tx, uint32_t *delta_rx,
int32_t *fix_alpha, int32_t *clock_period);
int wrs_read_calibration_data(struct pp_instance *ppi, uint32_t *delta_tx,
uint32_t *delta_rx, int32_t *fix_alpha, int32_t *clock_period,
uint32_t *bit_slide_ps);
int wrs_calibrating_disable(struct pp_instance *ppi, int txrx);
int wrs_calibrating_enable(struct pp_instance *ppi, int txrx);
int wrs_calibrating_poll(struct pp_instance *ppi, int txrx, uint32_t *delta);
int wrs_calibration_pattern_enable(struct pp_instance *ppi,
unsigned int calib_period,
unsigned int calib_pattern,
unsigned int calib_pattern_len);
unsigned int calib_period, unsigned int calib_pattern,
unsigned int calib_pattern_len);
int wrs_calibration_pattern_disable(struct pp_instance *ppi);
int wrs_read_correction_data(struct pp_instance *ppi, int64_t *fixAlpha,
int32_t *clock_period_ps, uint32_t *bit_slide_ps);
/* wrs-time.c (some should moce to wrs-spll.c) */
int wrs_locking_enable(struct pp_instance *ppi);
......
......@@ -50,7 +50,15 @@ static int run_all_state_machines(struct pp_globals *ppg)
ppi->iface_name, ppi->link_up ? "up":"down");
if (ppi->link_up) {
uint32_t bit_slide_ps;
ppi->state = PPS_INITIALIZING;
if ( wrs_read_correction_data(ppi,NULL,NULL,&bit_slide_ps)!= WRH_HW_CALIB_OK ) {
pp_diag(ppi, fsm, 1, "Cannot read bit_slide value values\n");
bit_slide_ps=0;
}
pp_diag(ppi, fsm, 1, "semistaticLatency(bit-slide)=%u [ps]\n",bit_slide_ps);
ppi->timestampCorrectionPortDS.semistaticLatency= picos_to_interval(bit_slide_ps);
}
else {
ppi->next_state = PPS_DISABLED;
......
......@@ -15,7 +15,7 @@
int wrs_read_calibration_data(struct pp_instance *ppi,
uint32_t *delta_tx, uint32_t *delta_rx, int32_t *fix_alpha,
int32_t *clock_period)
int32_t *clock_period, uint32_t *bit_slide_ps)
{
struct hal_port_state *p;
/* The following fields come from struct hexp_port_state */
......@@ -35,17 +35,21 @@ int wrs_read_calibration_data(struct pp_instance *ppi,
* way as the HAL itself was doing to fill the RPC structure.
* Formulas copied from libwr/hal_shmem.c (get_exported_state).
*/
port_delta_tx = p->calib.delta_tx_phy
+ p->calib.sfp.delta_tx_ps + p->calib.delta_tx_board;
port_delta_rx = p->calib.delta_rx_phy
+ p->calib.sfp.delta_rx_ps + p->calib.delta_rx_board;
port_fix_alpha = (double)pow(2.0, 40.0) *
((p->calib.sfp.alpha + 1.0) / (p->calib.sfp.alpha + 2.0)
- 0.5);
pp_diag(ppi, servo, 1, "deltas: tx=%d, rx=%d\n",
port_delta_tx, port_delta_rx);
if ( delta_tx || delta_rx) {
port_delta_tx = p->calib.delta_tx_phy
+ p->calib.sfp.delta_tx_ps + p->calib.delta_tx_board;
port_delta_rx = p->calib.delta_rx_phy
+ p->calib.sfp.delta_rx_ps + p->calib.delta_rx_board;
pp_diag(ppi, servo, 1, "deltas: tx=%d, rx=%d\n",
port_delta_tx, port_delta_rx);
}
if ( bit_slide_ps )
*bit_slide_ps=p->calib.bitslide_ps;
if(delta_tx)
*delta_tx = port_delta_tx;
if(delta_rx)
......@@ -57,6 +61,24 @@ int wrs_read_calibration_data(struct pp_instance *ppi,
return WRH_HW_CALIB_OK;
}
int wrs_read_correction_data(struct pp_instance *ppi, int64_t *fiber_fix_alpha,
int32_t *clock_period_ps, uint32_t *bit_slide_ps) {
double alpha;
int32_t port_cP;
wrs_read_calibration_data(ppi, NULL, NULL,NULL, &port_cP, bit_slide_ps);
if(fiber_fix_alpha) {
alpha = ((double) ppi->asymmetryCorrectionPortDS.scaledDelayCoefficient)/(double)pow(2.0, REL_DIFF_FRACBITS_AS_FLOAT);
*fiber_fix_alpha = (double)pow(2.0, FIX_ALPHA_FRACBITS_AS_FLOAT) * ((alpha + 1.0) / (alpha + 2.0) - 0.5);
}
if(clock_period_ps)
*clock_period_ps = port_cP; /* REF_CLOCK_PERIOD_PS */
return WRH_HW_CALIB_OK;
}
int wrs_calibrating_disable(struct pp_instance *ppi, int txrx)
{
return 0;
......@@ -71,7 +93,7 @@ int wrs_calibrating_poll(struct pp_instance *ppi, int txrx, uint32_t *delta)
{
uint32_t delta_tx, delta_rx;
wrs_read_calibration_data(ppi, &delta_tx, &delta_rx, NULL, NULL);
wrs_read_calibration_data(ppi, &delta_tx, &delta_rx, NULL, NULL, NULL);
*delta = (txrx == WRH_HW_CALIB_TX) ? delta_tx : delta_rx;
......
......@@ -45,7 +45,7 @@ struct wrh_operations wrh_oper = {
.adjust_in_progress = wrs_adjust_in_progress,
.adjust_counters = wrs_adjust_counters,
.adjust_phase = wrs_adjust_phase,
.read_corr_data = wrs_read_correction_data,
.read_calib_data = wrs_read_calibration_data,
.calib_disable = wrs_calibrating_disable,
.calib_enable = wrs_calibrating_enable,
......@@ -210,13 +210,22 @@ int main(int argc, char **argv)
if (ppg->cfg.cfg_items == 0)
pp_config_file(ppg, 0, PP_DEFAULT_CONFIGFILE);
if (ppg->cfg.cfg_items == 0) {
/* Default configuration for WR switch is all ports */
/* Default configuration for switch is all ports - Priority given to HA */
char s[128];
int i;
for (i = 0; i < 18; i++) {
Boolean configured=FALSE;
#if CONFIG_EXT_L1SYNC == 1
sprintf(s, "port %i; iface wri%i; proto raw;"
"extension whiterabbit; role auto", i + 1, i + 1);
"profile ha; role auto", i + 1, i + 1);
configured=TRUE;
#endif
#if CONFIG_EXT_WR == 1
if ( ! configured )
sprintf(s, "port %i; iface wri%i; proto raw;"
"profile wr; role auto", i + 1, i + 1);
#endif
pp_config_string(ppg, s);
}
}
......@@ -254,7 +263,7 @@ int main(int argc, char **argv)
if ( ppi->cfg.profile==PPSI_PROFILE_HA ) {
ppi->protocol_extension=PPSI_EXT_L1S;
/* Add L1E extension portDS */
if ( !(ppi->portDS->ext_dsport =alloc_fn(ppsi_head, sizeof(struct l1e_ext_portDS))) ) {
if ( !(ppi->portDS->ext_dsport =alloc_fn(ppsi_head, sizeof(l1e_ext_portDS_t))) ) {
goto exit_out_of_memory;
}
......@@ -262,7 +271,13 @@ int main(int argc, char **argv)
if (! (ppi->ext_data = alloc_fn(ppsi_head,sizeof(struct l1e_data))) ) {
goto exit_out_of_memory;
}
/* Set ingress/egress latencies */
ppi->timestampCorrectionPortDS.egressLatency=picos_to_interval(ppi->cfg.egressLatency_ps);
ppi->timestampCorrectionPortDS.ingressLatency=picos_to_interval(ppi->cfg.ingressLatency_ps);
ppi->timestampCorrectionPortDS.messageTimestampPointLatency=0;
ppi->asymmetryCorrectionPortDS.constantAsymmetry=picos_to_interval(ppi->cfg.constantAsymmetry_ps);
ppi->asymmetryCorrectionPortDS.scaledDelayCoefficient=
(RelativeDifference)(ppi->cfg.delayCoefficient * (double)pow(2.0, REL_DIFF_FRACBITS_AS_FLOAT));
/* Set L1SYNC extension hooks */
ppi->ext_hooks=&l1e_ext_hooks;
/* Set default profile parameters */
......
......@@ -32,7 +32,17 @@ void __pp_diag(struct pp_instance *ppi, enum pp_diag_things th,
return;
/* Use the normal output channel for diagnostics */
pp_printf("%s-%i-%s: ", thing_name[th], level, name);
if (PP_DIAG_EXTRA_PRINT_TIME) {
int hours, minutes, seconds;
if (ppi && ppi->t_ops && ppi->t_ops->get_utc_time )
ppi->t_ops->get_utc_time(ppi, &hours, &minutes, &seconds);
else
hours=minutes=seconds=0;
pp_printf("%02d:%02d:%02d %s-%i-%s: ", hours, minutes,seconds,thing_name[th], level, name);
} else {
pp_printf("%s-%i-%s: ", thing_name[th], level, name);
}
va_start(args, fmt);
pp_vprintf(fmt, args);
va_end(args);
......
......@@ -65,7 +65,7 @@ static void pp_diag_fsm(struct pp_instance *ppi, char *name, int sequence,
static struct pp_state_table_item *
get_current_state_table_item(struct pp_instance *ppi)
{
struct pp_state_table_item *ip = ppi->current_state_item;;
struct pp_state_table_item *ip = ppi->current_state_item;
/* Avoid searching if we already know where we are */
ppi->is_new_state = 0;
......@@ -82,6 +82,17 @@ get_current_state_table_item(struct pp_instance *ppi)
return NULL;
}
char *get_state_as_string(struct pp_instance *ppi, int state) {
static char *def="INVALID";
struct pp_state_table_item *ip = ppi->current_state_item;
for (ip = pp_state_table; ip->state != PPS_END_OF_TABLE; ip++)
if (ip->state == state) {
return ip->name;
}
return def;
}
/*
* Returns delay to next state, which is always zero.
*/
......@@ -293,5 +304,11 @@ int pp_state_machine(struct pp_instance *ppi, void *buf, int len)
if (pp_next_delay_1(ppi, PP_TO_BMC) < ppi->next_delay)
ppi->next_delay = pp_next_delay_1(ppi, PP_TO_BMC);
/* Run the extension state machine. The extension can provide its own time-out */
if ( ppi->ext_hooks->run_ext_state_machine) {
int delay = ppi->ext_hooks->run_ext_state_machine(ppi);
ppi->next_delay= (delay < ppi->next_delay) ? delay : ppi->next_delay;
}
return ppi->next_delay;
}
......@@ -74,7 +74,7 @@ struct wrh_operations {
int (*read_calib_data)(struct pp_instance *ppi,
uint32_t *deltaTx, uint32_t *deltaRx,
int32_t *fix_alpha, int32_t *clock_period);
int32_t *fix_alpha, int32_t *clock_period, uint32_t *bit_slide_ps);
int (*calib_disable)(struct pp_instance *ppi, int txrx);
int (*calib_enable)(struct pp_instance *ppi, int txrx);
int (*calib_poll)(struct pp_instance *ppi, int txrx, uint32_t *delta);
......@@ -85,10 +85,8 @@ struct wrh_operations {
int (*calib_pattern_disable)(struct pp_instance *ppi);
int (*enable_timing_output)(struct pp_instance *ppi, int enable);
int (*servo_hook)(struct pp_instance *ppi, int action);
int (*read_corr_data)(struct pp_instance *ppi, int64_t *delayCoeff,
int64_t *ingressLatency, int64_t *egressLatency,
int64_t *msgTPointLatency, int64_t *delayAsymmetry,
int64_t *fixAlpha, int32_t *clock_period );
int (*read_corr_data)(struct pp_instance *ppi, int64_t *fixAlpha,
int32_t *clock_period, uint32_t *bit_slide_ps);
};
......
......@@ -69,6 +69,9 @@ extern void __pp_diag(struct pp_instance *ppi, enum pp_diag_things th,
# define PP_HAS_DIAG 1
#endif
/* Constant used to enable extra diagnostic information - Inserted at compilation time */
#define PP_DIAG_EXTRA_PRINT_TIME (1 && PP_HAS_DIAG) /* Print time on each pp_diag calls */
/* So, this is the function that is to be used by real ppsi code */
#define pp_diag(ppi_, th_, level_, ...) \
({ \
......
......@@ -134,6 +134,10 @@ struct pp_instance_cfg {
char iface_name[16];
int profile; /* PPSI_PROFILE_PTP, PPSI_PROFILE_WR, PPSI_PROFILE_HA */
int delayMechanism; /* Should be enum ENDelayMechanism but forced to int for configuration parsing */
int64_t egressLatency_ps;
int64_t ingressLatency_ps;
int64_t constantAsymmetry_ps;
double delayCoefficient;
};
/*
......
......@@ -190,6 +190,7 @@ struct pp_ext_hooks {
int (*handle_signaling) (struct pp_instance * ppi, void *buf, int len);
int (*pack_announce)(struct pp_instance *ppi);
void (*unpack_announce)(void *buf, MsgAnnounce *ann);
int (*ready_for_slave)(struct pp_instance *ppi); /* returns: 0=Not ready 1=ready */
int (*state_decision)(struct pp_instance *ppi, int next_state);
void (*state_change)(struct pp_instance *ppi);
int (*run_ext_state_machine) (struct pp_instance *ppi);
......@@ -286,6 +287,8 @@ struct pp_cfg_time {
union pp_cfg_arg {
int i;
int i2[2];
int64_t i64;
double d;
char *s;
struct pp_cfg_time ts;
};
......@@ -309,6 +312,8 @@ enum pp_argtype {
ARG_STR,
ARG_NAMES,
ARG_TIME,
ARG_DOUBLE,
ARG_INT64
};
struct pp_argline {
cfg_handler f;
......@@ -458,6 +463,8 @@ extern void pp_time_sub(struct pp_time *t1, struct pp_time *t2);
extern void pp_time_div2(struct pp_time *t);
extern TimeInterval pp_time_to_interval(struct pp_time *ts);
extern TimeInterval picos_to_interval(int64_t picos);
extern void pp_time_add_interval(struct pp_time *t1, TimeInterval t2);
extern void pp_time_sub_interval(struct pp_time *t1, TimeInterval t2);
/* Function for time conversion */
extern int64_t pp_time_to_picos(struct pp_time *ts);
......
......@@ -82,6 +82,42 @@ int f_simple_int(struct pp_argline *l, int lineno,
return 0;
}
static inline void ASSIGN_INT64_FIELD(struct pp_argline *l,
struct pp_globals *ppg,
int64_t v)
{
if (l->needs_port)
*(int64_t *)(((void *)CUR_PPI(ppg)) + l->field_offset) = v;
else
*(int64_t *)(((void *)GOPTS(ppg)) + l->field_offset) = v;
}
static int f_simple_int64(struct pp_argline *l, int lineno,
struct pp_globals *ppg, union pp_cfg_arg *arg)
{
CHECK_PPI(l->needs_port);
ASSIGN_INT64_FIELD(l, ppg, arg->i64);
return 0;
}
static inline void ASSIGN_DOUBLE_FIELD(struct pp_argline *l,
struct pp_globals *ppg,
double v)
{
if (l->needs_port)
*(double *)(((void *)CUR_PPI(ppg)) + l->field_offset) = v;
else
*(double *)(((void *)GOPTS(ppg)) + l->field_offset) = v;
}
static int f_simple_double( struct pp_argline *l, int lineno,
struct pp_globals *ppg, union pp_cfg_arg *arg)
{
CHECK_PPI(l->needs_port);
ASSIGN_DOUBLE_FIELD(l, ppg, arg->d);
return 0;
}
static int f_if(struct pp_argline *l, int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
......@@ -195,7 +231,7 @@ static int f_announce_intvl(struct pp_argline *l, int lineno,
pp_printf("config line %i: announce interval out of range: %i, "
"forced to %i\n", lineno, arg->i, i);
}
GOPTS(ppg)->announce_intvl = i;
GOPTS(ppg)->logAnnounceInterval = i;
return 0;
}
......@@ -246,6 +282,10 @@ static struct pp_argline pp_global_arglines[] = {
INST_OPTION_INT("extension", ARG_NAMES, arg_profile, cfg.profile), /* TODO: stay for backward compatibility. Should be removed in the future */
INST_OPTION_INT("profile", ARG_NAMES, arg_profile, cfg.profile),
INST_OPTION_INT("mechanism", ARG_NAMES, arg_delayMechanism, cfg.delayMechanism),
INST_OPTION_INT64("egressLatency", ARG_INT64, NULL,cfg.egressLatency_ps),
INST_OPTION_INT64("ingressLatency", ARG_INT64, NULL,cfg.ingressLatency_ps),
INST_OPTION_DOUBLE("delayCoefficient", ARG_DOUBLE, NULL,cfg.delayCoefficient),
INST_OPTION_INT64("constantAsymmetry", ARG_INT64, NULL,cfg.constantAsymmetry_ps),
LEGACY_OPTION(f_vlan, "vlan", ARG_STR),
LEGACY_OPTION(f_diag, "diagnostic", ARG_STR),
RT_OPTION_INT("clock-class", ARG_INT, NULL, clock_quality.clockClass),
......@@ -254,11 +294,11 @@ static struct pp_argline pp_global_arglines[] = {
RT_OPTION_INT("clock-allan-variance", ARG_INT, NULL,
clock_quality.offsetScaledLogVariance),
LEGACY_OPTION(f_servo_pi, "servo-pi", ARG_INT2),
RT_OPTION_INT("domain-number", ARG_INT, NULL, domain_number),
RT_OPTION_INT("domain-number", ARG_INT, NULL, domainNumber),
LEGACY_OPTION(f_announce_intvl, "announce-interval", ARG_INT),
RT_OPTION_INT("sync-interval", ARG_INT, NULL, sync_intvl),
RT_OPTION_INT("priority1", ARG_INT, NULL, prio1),
RT_OPTION_INT("priority2", ARG_INT, NULL, prio2),
RT_OPTION_INT("sync-interval", ARG_INT, NULL, logSyncInterval),
RT_OPTION_INT("priority1", ARG_INT, NULL, priority1),
RT_OPTION_INT("priority2", ARG_INT, NULL, priority2),
{}
};
......@@ -425,6 +465,13 @@ static int pp_config_line(struct pp_globals *ppg, char *line, int lineno)
}
break;
case ARG_INT64:
if (sscanf(line, "%lli", &(cfg_arg.i64)) != 1) {
pp_error("line %i: \"%s\"[%s]: not int64\n", lineno, word,line);
return -1;
}
break;
case ARG_INT2:
if (sscanf(line, "%i,%i", cfg_arg.i2, &cfg_arg.i2[1]) < 0) {
pp_error("line %i: wrong arg \"%s\" for \"%s\"\n",
......@@ -433,6 +480,13 @@ static int pp_config_line(struct pp_globals *ppg, char *line, int lineno)
}
break;
case ARG_DOUBLE:
if (sscanf(line, "%lf", &cfg_arg.d) < 0) {
pp_error("line %i: wrong arg \"%s\" for \"%s\"\n",
lineno, line, word);
return -1;
}
break;
case ARG_STR:
while (*line && blank(*line))
line++;
......
......@@ -35,6 +35,20 @@ void normalize_pp_time(struct pp_time *t)
t->scaled_nsecs *= sign;
}
/* Add a TimeInterval to a pp_time */
void pp_time_add_interval(struct pp_time *t1, TimeInterval t2)
{
t1->scaled_nsecs += t2;
normalize_pp_time(t1);
}
/* Substract a TimeInterval to a pp_time */
void pp_time_sub_interval(struct pp_time *t1, TimeInterval t2)
{
t1->scaled_nsecs -= t2;
normalize_pp_time(t1);
}
void pp_time_add(struct pp_time *t1, struct pp_time *t2)
{
t1->secs += t2->secs;
......
# All files are under directory D: I'm lazy
D := proto-ext-l1sync
OBJ-y += $D/l1e-hooks.o \
$D/l1e-servo.o\
$D/l1e-msg.o\
$D/l1e-state-machine.o
\ No newline at end of file
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Jean-Claude BAU & Maciej Lipinski
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#ifndef __L1SYNC_EXT_API_H__
#define __L1SYNC_EXT_API_H__
#if CONFIG_EXT_L1SYNC == 1
#define PROTO_EXT_L1SYNC (1)
#else
#define PROTO_EXT_L1SYNC (0)
#endif
#if CONFIG_EXT_L1SYNC == 1
#include <ppsi/lib.h>
#include "../include/hw-specific/wrh.h"
#include "l1e-constants.h"
#include <math.h>
/* Rename the timeouts, for readability */
#define L1E_TIMEOUT_TX_SYNC PP_TO_EXT_0
#define L1E_TIMEOUT_RX_SYNC PP_TO_EXT_1
#define L1E_DEFAULT_L1SYNCRECEIPTTIMEOUT 5 /* was 3: need more for pll lock */
/*
* We don't have ha_dsport, but rather rely on wr_dsport with our fields added.
*
*
* Fortunately (and strangely (thanks Maciej), here the spec *is* consistent.
* These 3 bits are valid 4 times, in 4 different bytes (conf/active, me/peer)
*/
#define L1E_TX_COHERENT 0x01
#define L1E_RX_COHERENT 0x02
#define L1E_CONGRUENT 0x04
#define L1E_OPT_PARAMS 0x08
enum l1_sync_states { /*draft P1588_v_29: page 334 */
__L1SYNC_MISSING = 0, /* my addition... */ //TODO: std-error->report in ballout
L1SYNC_DISABLED = 1,
L1SYNC_IDLE,
L1SYNC_LINK_ALIVE,
L1SYNC_CONFIG_MATCH,
L1SYNC_UP,
};
/* Pack/Unkpack L1SYNC messages (all of them are signalling messages) */
int l1e_pack_signal(struct pp_instance *ppi);
int l1e_unpack_signal(struct pp_instance *ppi, void *pkt, int plen);
/*
* These structures are used as extension-specific data in the DSPort
* ()
*/
typedef struct { /*draft P1588_v_29: page 100 and 333-335 */
/* configurable members */
Boolean L1SyncEnabled;
Boolean txCoherentIsRequired;
Boolean rxCoherentIsRequired;
Boolean congruentIsRequired;
Boolean optParamsEnabled;
Integer8 logL1SyncInterval;
Integer8 L1SyncReceiptTimeout;
/* dynamic members */
Boolean L1SyncLinkAlive;
Boolean isTxCoherent;
Boolean isRxCoherent;
Boolean isCongruent;
Enumeration8 L1SyncState;
Boolean peerTxCoherentIsRequired;
Boolean peerRxCoherentIsRequired;
Boolean peerCongruentIsRequired;
Boolean peerIsTxCoherent;
Boolean peerIsRxCoherent;
Boolean peerIsCongruent;
/* None compliant members with IEEE1558-2018 */
Enumeration8 next_state;
} L1SyncBasicPortDS_t;
typedef struct { /*draft P1588_v_29: page 101 and 340-341 */
/* configurable members */
Boolean timestampsCorrectedTx;
/* dynamic members */
Boolean phaseOffsetTxValid;
Boolean frequencyOffsetTxValid;
struct pp_time phaseOffsetTx;
Timestamp phaseOffsetTxTimesatmp;
struct pp_time frequencyOffsetTx;
Timestamp frequencyOffsetTxTimesatmp;
} L1SyncOptParamsPortDS_t;
/* Add all extension port DS related structure must be store here */
typedef struct {
wrh_portds_head_t head; /* Must be always at the first place */
L1SyncBasicPortDS_t basic;
L1SyncOptParamsPortDS_t opt_params;
Boolean parentExtModeOn;
}l1e_ext_portDS_t;
static inline l1e_ext_portDS_t *L1E_DSPOR(struct pp_instance *ppi)
{
return (l1e_ext_portDS_t *) ppi->portDS->ext_dsport;
}
static inline L1SyncBasicPortDS_t *L1E_DSPOR_BS(struct pp_instance *ppi)
{
return &L1E_DSPOR(ppi)->basic;
}
static inline L1SyncOptParamsPortDS_t *L1E_DSPOR_OP(struct pp_instance *ppi)
{
return &L1E_DSPOR(ppi)->opt_params;
}
/****************************************************************************************/
/* l1e_servo interface */
struct l1e_servo_state {
int state;
/* These fields are used by servo code, after setting at init time */
int64_t fiber_fix_alpha;
int32_t clock_period_ps;
/* Following fields are for monitoring/diagnostics (use w/ shmem) */
struct pp_time delayMM;
int64_t delayMM_ps;
int32_t cur_setpoint_ps;
int64_t delayMS_ps;
int tracking_enabled;
int64_t skew_ps;
int64_t offsetMS_ps;
/* Values used by snmp. Values are increased at servo update when
* erroneous condition occurs. */
uint32_t n_err_state;
uint32_t n_err_offset;
uint32_t n_err_delta_rtt;
struct pp_time update_time;
/* These fields are used by servo code, across iterations */
struct pp_time t1, t2, t3, t4, t5, t6;
int64_t prev_delayMS_ps;
int missed_iters;
};
/* Prototypes */
int l1e_servo_init(struct pp_instance *ppi);
void l1e_servo_reset(struct pp_instance *ppi);
void l1e_servo_enable_tracking(int enable);
int l1e_servo_got_sync(struct pp_instance *ppi, struct pp_time *t1,
struct pp_time *t2);
int l1e_servo_got_delay(struct pp_instance *ppi);
int l1e_servo_update(struct pp_instance *ppi);
uint8_t l1e_creat_L1Sync_bitmask(int tx_coh, int rx_coh, int congru);
void l1e_print_L1Sync_basic_bitmaps(struct pp_instance *ppi,
uint8_t configed, uint8_t active, char* text);
void l1e_servo_enable_tracking(int enable);
int l1e_update_correction_values(struct pp_instance *ppi);
int l1e_run_state_machine(struct pp_instance *ppi);
/* All data used as extension ppsi l1sync must be put here */
struct l1e_data {
struct l1e_servo_state servo_state;
};
static inline struct l1e_servo_state *L1E_SRV(struct pp_instance *ppi)
{
return &((struct l1e_data *)ppi->ext_data)->servo_state;
}
extern struct pp_ext_hooks l1e_ext_hooks;
#endif /* CONFIG_EXT_L1SYNC == 1 */
#endif /* __L1SYNC_EXT_API_H__ */
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Jean-Claude BAU & Maciej Lipinski
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#ifndef __L1SYNC_EXT_CONSTANTS_H__
#define __L1SYNC_EXT_CONSTANTS_H__
/* Define threshold values for SNMP */
#define SNMP_MAX_OFFSET_PS 500
#define SNMP_MAX_DELTA_RTT_PS 1000
/* L1SYNC Servo */
enum {
L1E_UNINITIALIZED = 0,
L1E_SYNC_NSEC,
L1E_SYNC_TAI,
L1E_SYNC_PHASE,
L1E_TRACK_PHASE,
L1E_WAIT_OFFSET_STABLE,
};
#endif /* __L1SYNC_EXT_CONSTANTS_H__ */
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Jean-Claude BAU & Maciej Lipinski
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#include <inttypes.h>
#include <ppsi/ppsi.h>
#include <common-fun.h>
#include "l1e-api.h"
#include "l1e-constants.h"
#include <math.h>
char *l1e_state_name[] = {
[L1SYNC_DISABLED] = "L1SYNC_DISABLED",
[L1SYNC_IDLE] = "L1SYNC_IDLE",
[L1SYNC_LINK_ALIVE] = "L1SYNC_LINK_ALIVE",
[L1SYNC_CONFIG_MATCH] = "L1SYNC_CONFIG_MATCH",
[L1SYNC_UP] = "L1SYNC_UP",
};
void l1e_print_L1Sync_basic_bitmaps(struct pp_instance *ppi, uint8_t configed,
uint8_t active, char* text)
{
pp_diag(ppi, ext, 2, "ML: L1Sync %s\n", text);
pp_diag(ppi, ext, 2, "ML: \tConfig: TxC=%d RxC=%d Cong=%d Param=%d\n",
((configed & L1E_TX_COHERENT) == L1E_TX_COHERENT),
((configed & L1E_RX_COHERENT) == L1E_RX_COHERENT),
((configed & L1E_CONGRUENT) == L1E_CONGRUENT),
((configed & L1E_OPT_PARAMS) == L1E_OPT_PARAMS));
pp_diag(ppi, ext, 2, "ML: \tActive: TxC=%d RxC=%d Cong=%d\n",
((active & L1E_TX_COHERENT) == L1E_TX_COHERENT),
((active & L1E_RX_COHERENT) == L1E_RX_COHERENT),
((active & L1E_CONGRUENT) == L1E_CONGRUENT));
}
/* update DS values of latencies and delay coefficient
* - these values are provided by HW (i.e. HAL) depending on SFPs, wavelenghts, etc
* - these values are stored in configurable data sets
* - the values from data sets are used in calculations
*/
int l1e_update_correction_values(struct pp_instance *ppi)
{
struct l1e_servo_state *s = L1E_SRV(ppi);
pp_diag(ppi, ext, 2, "hook: %s -- ext %i\n", __func__, ppi->protocol_extension);
/* read the interesting values from HW (i.e. HAL)*/
if ( WRH_OPER()->read_corr_data(ppi,
&s->fiber_fix_alpha,
&s->clock_period_ps,
NULL) != WRH_HW_CALIB_OK){
pp_diag(ppi, ext, 2, "hook: %s -- cannot read calib values\n",
__func__);
return -1;
}
pp_diag(ppi, ext, 2, "ML- Updated correction values:"
" fiber_fix_alpha=%"PRId64" , Clock period=%d [ps]",
s->fiber_fix_alpha,
s->clock_period_ps
);
return 0;
}
/* open is global; called from "pp_init_globals" */
static int l1e_open(struct pp_instance *ppi, struct pp_runtime_opts *rt_opts)
{
pp_diag(NULL, ext, 2, "hook: %s -- ext %i\n", __func__,
ppi->protocol_extension);
return 0;
}
/* initialize one specific port */
static int l1e_init(struct pp_instance *ppi, void *buf, int len)
{
L1SyncBasicPortDS_t * bds=L1E_DSPOR_BS(ppi);
pp_diag(ppi, ext, 2, "hook: %s -- ext %i\n", __func__,
ppi->protocol_extension);
// init configurable data set members with proper config values
bds->L1SyncEnabled = TRUE;
bds->txCoherentIsRequired = TRUE;
bds->rxCoherentIsRequired = TRUE;
bds->congruentIsRequired = TRUE;
bds->optParamsEnabled = FALSE;
bds->logL1SyncInterval = 0;
bds->L1SyncReceiptTimeout = L1E_DEFAULT_L1SYNCRECEIPTTIMEOUT;
// init dynamic data set members with zeros/defaults
bds->L1SyncLinkAlive = FALSE;
bds->isTxCoherent = FALSE;
bds->isRxCoherent = FALSE;
bds->isCongruent = FALSE;
bds->L1SyncState = L1SYNC_DISABLED;
bds->peerTxCoherentIsRequired = FALSE;
bds->peerRxCoherentIsRequired = FALSE;
bds->peerCongruentIsRequired = FALSE;
bds->peerIsTxCoherent = FALSE;
bds->peerIsRxCoherent = FALSE;
bds->peerIsCongruent = FALSE;
/* Init other specific members */
bds->next_state=bds->L1SyncState;
/* Init configuration members of L1SyncOptParamsPortDS */
L1E_DSPOR_OP(ppi)->timestampsCorrectedTx=FALSE;
return 0;
}
/* This hook is called whenever a signaling message is received */
static int l1e_handle_signaling(struct pp_instance * ppi, void *buf, int len)
{
L1SyncBasicPortDS_t * bds=L1E_DSPOR_BS(ppi);
pp_diag(ppi, ext, 2, "hook: %s (%i:%s) -- plen %i\n", __func__,
ppi->state, l1e_state_name[bds->L1SyncState], len);
if ( l1e_unpack_signal(ppi, buf, len)==0 ) {
/* Valid Sync message */
int timeout_tx_sync;
/* Reset reception timeout */
timeout_tx_sync = (4 << (bds->logL1SyncInterval + 8)) * bds->L1SyncReceiptTimeout;
__pp_timeout_set(ppi, L1E_TIMEOUT_RX_SYNC, timeout_tx_sync);
bds->L1SyncLinkAlive = TRUE;
}
return 0;
}
uint8_t l1e_creat_L1Sync_bitmask(int tx_coh, int rx_coh, int congru)
{
uint8_t outputMask=0;
if(tx_coh) outputMask |= L1E_TX_COHERENT;
if(rx_coh) outputMask |= L1E_RX_COHERENT;
if(congru) outputMask |= L1E_CONGRUENT;
return outputMask;
}
static int l1e_handle_resp(struct pp_instance *ppi)
{
struct pp_time *ofm = &SRV(ppi)->offsetFromMaster;
l1e_ext_portDS_t *l1epds = L1E_DSPOR(ppi);
pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
/* This correction_field we received is already part of t4 */
/*
* If no WR mode is on, run normal code, if T2/T3 are valid.
* After we adjusted the pps counter, stamps are invalid, so
* we'll have the Unix time instead, marked by "correct"
*/
if (!l1epds->head.extModeOn) {
if (is_incorrect(&ppi->t2) || is_incorrect(&ppi->t3)) {
pp_diag(ppi, servo, 1,
"T2 or T3 incorrect, discarding tuple\n");
return 0;
}
pp_servo_got_resp(ppi);
/*
* pps always on if offset less than 1 second,
* until ve have a configurable threshold */
WRH_OPER()->enable_timing_output(ppi, ofm->secs==0);
}
l1e_servo_got_delay(ppi);
l1e_servo_update(ppi);
return 0;
}
/* Hmm... "execute_slave" should look for errors; but it's off in WR too */
static int l1e_handle_followup(struct pp_instance *ppi,
struct pp_time *t1)
{
l1e_ext_portDS_t *pds=L1E_DSPOR(ppi);
pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
if ((pds->basic.L1SyncState != L1SYNC_UP) || !pds->head.extModeOn )
return 0;
l1e_servo_got_sync(ppi, t1, &ppi->t2);
if (CONFIG_HAS_P2P && ppi->delayMechanism == P2P)
l1e_servo_update(ppi);
return 1; /* the caller returns too */
}
static __attribute__((used)) int l1e_handle_presp(struct pp_instance *ppi)
{
struct pp_time *ofm = &SRV(ppi)->offsetFromMaster;
/*
* If no WR mode is on, run normal code, if T2/T3 are valid.
* After we adjusted the pps counter, stamps are invalid, so
* we'll have the Unix time instead, marked by "correct"
*/
if (!WRH_DSPOR_HEAD(ppi)->extModeOn) {
if (is_incorrect(&ppi->t3) || is_incorrect(&ppi->t6)) {
pp_diag(ppi, servo, 1,
"T3 or T6 incorrect, discarding tuple\n");
return 0;
}
pp_servo_got_presp(ppi);
/*
* pps always on if offset less than 1 second,
* until ve have a configurable threshold */
WRH_OPER()->enable_timing_output(ppi, ofm->secs==0);
return 0;
}
/* FIXME: verify that last-received cField is already accounted for */
l1e_servo_got_delay(ppi);
return 0;
}
/* Check if ready to go to SLAVE state */
static int l1e_ready_for_slave(struct pp_instance *ppi)
{
pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
/* Current state is UNCALIBRATED. Can we go to SLAVE state ? */
/* YES if L1SYNC state is UP. It means that the PPL is locked*/
if ( L1E_DSPOR_BS(ppi)->L1SyncState!=L1SYNC_UP &&
L1E_DSPOR_BS(ppi)->L1SyncEnabled == TRUE) {
return 0; /* Not ready */
}
return 1; /* Ready for slave */
}
/* The global structure used by ppsi */
struct pp_ext_hooks l1e_ext_hooks = {
.open = l1e_open,
.init = l1e_init,
.handle_signaling = l1e_handle_signaling,
.run_ext_state_machine = l1e_run_state_machine,
.ready_for_slave = l1e_ready_for_slave,
.handle_resp = l1e_handle_resp,
.handle_followup = l1e_handle_followup,
#if CONFIG_HAS_P2P
.handle_presp = l1e_handle_presp,
#endif
};
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Jean-Claude BAU & Maciej Lipinski
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#include <ppsi/ppsi.h>
#include "l1e-api.h"
#define MSG_OFFSET_HEADER 0
#define MSG_OFFSET_HEADER_MESSAGE_LENGTH (MSG_OFFSET_HEADER+2)
#define MSG_OFFSET_HEADER_CONTROL_FIELD (MSG_OFFSET_HEADER+32)
#define MSG_OFFSET_TARGET_PORT_IDENTITY 34
#define MSG_OFFSET_TARGET_PORT_IDENTITY_CLOCK_IDENTITY (MSG_OFFSET_TARGET_PORT_IDENTITY+0)
#define MSG_OFFSET_TARGET_PORT_IDENTITY_PORT_NUMBER (MSG_OFFSET_TARGET_PORT_IDENTITY+8)
#define MSG_OFFSET_TLV 44
#define MSG_OFFSET_TLV_TYPE (MSG_OFFSET_TLV )
#define MSG_OFFSET_TLV_LENGTH_FIELD (MSG_OFFSET_TLV+2)
#define MSG_OFFSET_TLV_L1SYNC_PEER_CONF (MSG_OFFSET_TLV+4)
#define MSG_OFFSET_TLV_L1SYNC_PEER_ACTIVE (MSG_OFFSET_TLV+5)
#define MSG_OFFSET_HEADER_TYPE (MSG_OFFSET_HEADER+0)
#define MSG_TYPE_SIGNALING 0xC
#define MSG_HEADER_CONTROL_FIELD_ALL_OTHERS 5
#define MSG_TYPE_
#define MSG_GET_16(buf,off) (*(UInteger16 *)(buf+off))
#define MSG_GET_8(buf,off ) *(UInteger8 *)(buf+off)
#define MSG_GET_HEADER_TYPE(buf) (MSG_GET_8(buf,MSG_OFFSET_HEADER_TYPE) & 0x0f)
#define MSG_GET_TLV_TYPE(buf) ntohs(MSG_GET_16(buf,MSG_OFFSET_TLV_TYPE))
#define MSG_GET_TLV_LENGTH_FIELD(buf) ntohs(MSG_GET_16(buf,MSG_OFFSET_TLV_LENGTH_FIELD))
#define MSG_GET_TLV_L1SYNC_PEER_CONF(buf) MSG_GET_8(buf,MSG_OFFSET_TLV_L1SYNC_PEER_CONF)
#define MSG_GET_TLV_L1SYNC_PEER_ACTIVE(buf) MSG_GET_8(buf,MSG_OFFSET_TLV_L1SYNC_PEER_ACTIVE)
#define MSG_SET_HEADER_TYPE(buf,val) MSG_GET_8(buf,MSG_OFFSET_HEADER_TYPE) = \
(MSG_GET_8(buf,MSG_OFFSET_HEADER_TYPE) & 0xF0) | val
#define MSG_SET_HEADER_MESSAGE_LENGTH(buf,val) MSG_GET_16(buf,MSG_OFFSET_HEADER_MESSAGE_LENGTH) = htons(val)
#define MSG_SET_HEADER_CONTROL_FIELD(buf, val) MSG_GET_8(buf,MSG_OFFSET_HEADER_CONTROL_FIELD) = val
#define MSG_SET_TARGET_PORT_IDENTITY(buf,clock,port) *(ClockIdentity *)(buf+MSG_OFFSET_TARGET_PORT_IDENTITY) = clock;\
MSG_GET_16(buf,MSG_OFFSET_TARGET_PORT_IDENTITY_PORT_NUMBER) = htons(port);
#define MSG_SET_TLV_TYPE(buf,val) MSG_GET_16(buf,MSG_OFFSET_TLV_TYPE)=htons(val)
#define MSG_SET_TLV_LENGTH_FIELD(buf,val) MSG_GET_16(buf,MSG_OFFSET_TLV_LENGTH_FIELD)=htons(val)
#define MSG_SET_TLV_L1SYNC_PEER_CONF(buf,val) MSG_GET_8(buf,MSG_OFFSET_TLV_L1SYNC_PEER_CONF)= val
#define MSG_SET_TLV_L1SYNC_PEER_ACTIVE(buf,val) MSG_GET_8(buf,MSG_OFFSET_TLV_L1SYNC_PEER_ACTIVE)=val
#define TLV_TYPE_L1_SYNC 0x8001
#define MSG_L1SYNC_LEN 50
int l1e_pack_signal(struct pp_instance *ppi)
{
void *buf;
uint8_t local_config, local_active;
L1SyncBasicPortDS_t * bds=L1E_DSPOR_BS(ppi);
buf = ppi->tx_ptp;
/* Changes in header */
MSG_SET_HEADER_TYPE(buf,MSG_TYPE_SIGNALING);
MSG_SET_HEADER_CONTROL_FIELD(buf,MSG_HEADER_CONTROL_FIELD_ALL_OTHERS);
/* target portIdentity */
MSG_SET_TARGET_PORT_IDENTITY(buf,
DSPAR(ppi)->parentPortIdentity.clockIdentity,
DSPAR(ppi)->parentPortIdentity.portNumber);
/* L1SyncTLV */
MSG_SET_TLV_TYPE(buf,TLV_TYPE_L1_SYNC);
MSG_SET_TLV_LENGTH_FIELD(buf,2);
/* O.6.4 */
local_config = l1e_creat_L1Sync_bitmask(bds->txCoherentIsRequired,
bds->rxCoherentIsRequired,
bds->congruentIsRequired);
if(bds->optParamsEnabled)
local_config |= L1E_OPT_PARAMS;
local_active = l1e_creat_L1Sync_bitmask(bds->isTxCoherent,
bds->isRxCoherent,
bds->isCongruent);
MSG_SET_TLV_L1SYNC_PEER_CONF(buf,local_config);
MSG_SET_TLV_L1SYNC_PEER_ACTIVE(buf,local_active);
l1e_print_L1Sync_basic_bitmaps(ppi, local_config,local_active, "Sent");
/* header len */
MSG_SET_HEADER_MESSAGE_LENGTH(buf,MSG_L1SYNC_LEN);
return MSG_L1SYNC_LEN;
}
int l1e_unpack_signal(struct pp_instance *ppi, void *buf, int plen)
{
L1SyncBasicPortDS_t * basicDS=L1E_DSPOR_BS(ppi);
int l1sync_peer_conf, l1sync_peer_acti;
if ( MSG_GET_HEADER_TYPE(buf) != MSG_TYPE_SIGNALING) {
pp_diag(ppi, ext, 1, "Not a signaling message, ignore\n");
return -1;
}
if (MSG_GET_TLV_TYPE(buf) != TLV_TYPE_L1_SYNC) {
pp_diag(ppi, ext, 1, "Not L1Sync TLV, ignore\n");
return -1;
}
if ( MSG_GET_TLV_LENGTH_FIELD(buf) != 2 || plen != MSG_L1SYNC_LEN) {
pp_diag(ppi, ext, 1, "L1Sync TLV wrong length, ignore\n");
return -1;
}
l1sync_peer_conf = MSG_GET_TLV_L1SYNC_PEER_CONF(buf);
l1sync_peer_acti = MSG_GET_TLV_L1SYNC_PEER_ACTIVE(buf);
l1e_print_L1Sync_basic_bitmaps(ppi, l1sync_peer_conf,l1sync_peer_acti, "Received");
basicDS->peerTxCoherentIsRequired = (l1sync_peer_conf & L1E_TX_COHERENT) == L1E_TX_COHERENT;
basicDS->peerRxCoherentIsRequired = (l1sync_peer_conf & L1E_RX_COHERENT) == L1E_RX_COHERENT;
basicDS->peerCongruentIsRequired = (l1sync_peer_conf & L1E_CONGRUENT) == L1E_CONGRUENT;
basicDS->peerIsTxCoherent = (l1sync_peer_acti & L1E_TX_COHERENT) == L1E_TX_COHERENT;
basicDS->peerIsRxCoherent = (l1sync_peer_acti & L1E_RX_COHERENT) == L1E_RX_COHERENT;
basicDS->peerIsCongruent = (l1sync_peer_acti & L1E_CONGRUENT) == L1E_CONGRUENT;
return 0;
}
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Jean-Claude BAU & Maciej Lipinski
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#include <ppsi/ppsi.h>
#include "l1e-api.h"
#include "l1e-constants.h"
#include <libwr/shmem.h>
static const char *l1e_servo_state_name[] = {
[L1E_UNINITIALIZED] = "Uninitialized",
[L1E_SYNC_NSEC] = "SYNC_NSEC",
[L1E_SYNC_TAI] = "SYNC_SEC",
[L1E_SYNC_PHASE] = "SYNC_PHASE",
[L1E_TRACK_PHASE] = "TRACK_PHASE",
[L1E_WAIT_OFFSET_STABLE] = "WAIT_OFFSET_STABLE",
};
/* Enable tracking by default. Disabling the tracking is used for demos. */
static int l1e_tracking_enabled = 1;
static struct pp_time l1e_faulty_stamps[6]; /* if unused, dropped at link time */
static int l1e_got_sync = 0;
/* prototypes */
static int l1e_p2p_delay(struct pp_instance *ppi, struct l1e_servo_state *s);
static void l1e_apply_faulty_stamp(struct l1e_servo_state *s, int index);
static void l1e_dump_timestamp(struct pp_instance *ppi, char *what, struct pp_time ts);
/* External data */
extern struct wrs_shm_head *ppsi_head;
static void l1e_apply_faulty_stamp(struct l1e_servo_state *s, int index)
{
if (PROTO_EXT_HAS_FAULTS) {
assert(index >= 1 && index <= 6, "Wrong T index %i\n", index);
pp_time_add(&s->t1 + index - 1, l1e_faulty_stamps + index - 1);
}
}
static void l1e_dump_timestamp(struct pp_instance *ppi, char *what,struct pp_time ts)
{
pp_diag(ppi, servo, 2, "%s = %ld:%09ld:%03ld\n", what, (long)ts.secs,
(long)(ts.scaled_nsecs >> 16),
/* unlikely what we had earlier, third field is not phase */
((long)(ts.scaled_nsecs & 0xffff) * 1000 + 0x8000) >> 16);
}
void l1e_servo_enable_tracking(int enable)
{
l1e_tracking_enabled = enable;
}
int l1e_servo_init(struct pp_instance *ppi)
{
struct l1e_servo_state *s=L1E_SRV(ppi);
/* shmem lock */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_BEGIN);
/* Update correction data in data sets*/
if (l1e_update_correction_values(ppi) < 0)
return -1;
WRH_OPER()->enable_timing_output(ppi, 0);
/*
* Do not reset cur_setpoint, but trim it to be less than one tick.
* The softpll code uses the module anyways, but if we unplug-replug
* the fiber it will always increase, so don't scare the user
*/
if (s->cur_setpoint_ps > s->clock_period_ps)
s->cur_setpoint_ps %= s->clock_period_ps;
pp_diag(ppi, servo, 3, "%s.%d: Adjust_phase: %d\n",__func__,__LINE__,s->cur_setpoint_ps);
WRH_OPER()->adjust_phase(s->cur_setpoint_ps);
s->missed_iters = 0;
s->state = L1E_SYNC_TAI;
strcpy(SRV(ppi)->servo_state_name, l1e_servo_state_name[s->state]);
SRV(ppi)->flags |= PP_SERVO_FLAG_VALID;
SRV(ppi)->update_count = 0;
ppi->t_ops->get(ppi, &s->update_time);
s->tracking_enabled = l1e_tracking_enabled;
l1e_got_sync = 0;
/* shmem unlock */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_END);
return 0;
}
void l1e_servo_reset(struct pp_instance *ppi)
{
/* values from servo_state to be preserved */
uint32_t n_err_state;
uint32_t n_err_offset;
uint32_t n_err_delta_rtt;
struct l1e_servo_state *s=L1E_SRV(ppi);
if (!s) {
/* Don't clean servo state when is not available */
return;
}
/* shmem lock */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_BEGIN);
ppi->flags = 0;
/* preserve some values from servo_state */
n_err_state = s->n_err_state;
n_err_offset = s->n_err_offset;
n_err_delta_rtt = s->n_err_delta_rtt;
/* clear servo_state to display empty fields in wr_mon and SNMP */
memset(s, 0, sizeof(struct l1e_servo_state));
/* restore values from servo_state */
s->n_err_state = n_err_state;
s->n_err_offset = n_err_offset;
s->n_err_delta_rtt = n_err_delta_rtt;
/* shmem unlock */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_END);
}
int l1e_servo_got_sync(struct pp_instance *ppi, struct pp_time *t1,
struct pp_time *t2)
{
struct l1e_servo_state *s=L1E_SRV(ppi);
s->t1 = *t1; l1e_apply_faulty_stamp(s, 1);
s->t2 = *t2; l1e_apply_faulty_stamp(s, 2);
l1e_got_sync = 1;
return 0;
}
int l1e_servo_got_delay(struct pp_instance *ppi)
{
struct l1e_servo_state *s=L1E_SRV(ppi);
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_BEGIN);
s->t3 = ppi->t3; l1e_apply_faulty_stamp(s, 3);
s->t4 = ppi->t4; l1e_apply_faulty_stamp(s, 4);
if (CONFIG_HAS_P2P && ppi->delayMechanism == P2P) {
s->t5 = ppi->t5; l1e_apply_faulty_stamp(s, 5);
s->t6 = ppi->t6; l1e_apply_faulty_stamp(s, 6);
l1e_p2p_delay(ppi, s);
}
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_END);
return 0;
}
/* update currentDS.meanDelay */
static void l1e_update_meanDelay(struct pp_instance *ppi,struct l1e_servo_state *s) {
struct pp_time mtime;
mtime=s->delayMM;
pp_time_div2(&mtime);
DSCUR(ppi)->meanDelay=pp_time_to_interval(&mtime);
}
/* update currentDS.delayAsymmetry */
static void l1e_update_delayAsymmetry(struct pp_instance *ppi,struct l1e_servo_state *s) {
DSPOR(ppi)->delayAsymmetry=picos_to_interval(s->delayMM_ps - 2LL * s->delayMS_ps);
}
static void l1e_update_offsetFromMaster (struct pp_instance *ppi,struct pp_time *offsetMS) {
DSCUR(ppi)->offsetFromMaster = pp_time_to_interval(offsetMS);
}
static int l1e_p2p_delay(struct pp_instance *ppi, struct l1e_servo_state *s)
{
static int errcount;
if (is_incorrect(&s->t3) || is_incorrect(&s->t4)
|| is_incorrect(&s->t5) || is_incorrect(&s->t6)) {
errcount++;
if (errcount > 5) /* a 2-3 in a row are expected */
pp_error("%s: TimestampsIncorrect: %d %d %d %d\n",
__func__, !is_incorrect(&s->t3),
!is_incorrect(&s->t4), !is_incorrect(&s->t5),
!is_incorrect(&s->t6));
return 0;
}
errcount = 0;
SRV(ppi)->update_count++;
ppi->t_ops->get(ppi, &s->update_time);
{ /* avoid modifying stamps in place */
struct pp_time mtime, stime;
stime = s->t6; pp_time_sub(&stime, &s->t3);
mtime = s->t5; pp_time_sub(&mtime, &s->t4);
s->delayMM = stime; pp_time_sub(&s->delayMM, &mtime);
}
if (__PP_DIAG_ALLOW(ppi, pp_dt_servo, 1)) {
l1e_dump_timestamp(ppi, "servo:t1", s->t1);
l1e_dump_timestamp(ppi, "servo:t2", s->t2);
l1e_dump_timestamp(ppi, "servo:t3", s->t3);
l1e_dump_timestamp(ppi, "servo:t4", s->t4);
l1e_dump_timestamp(ppi, "servo:t5", s->t5);
l1e_dump_timestamp(ppi, "servo:t6", s->t6);
l1e_dump_timestamp(ppi, "->delayMM", s->delayMM);
}
s->delayMM_ps = pp_time_to_picos(&s->delayMM);
/* delayMS[ps] = (fix_alpha*delayMM[ps])/2e40 +delayMM[ps]/2 */
s->delayMS_ps =
((s->delayMM_ps * s->fiber_fix_alpha) >> FIX_ALPHA_FRACBITS)
+ (s->delayMM_ps >> 1);
return 1;
}
static int l1e_p2p_offset(struct pp_instance *ppi,
struct l1e_servo_state *s, struct pp_time *offsetMS)
{
static int errcount;
if (is_incorrect(&s->t1) || is_incorrect(&s->t2)) {
errcount++;
if (errcount > 5) /* a 2-3 in a row are expected */
pp_error("%s: TimestampsIncorrect: %d %d \n",
__func__, !is_incorrect(&s->t1),
!is_incorrect(&s->t2));
return 0;
}
errcount = 0;
l1e_got_sync = 0;
SRV(ppi)->update_count++;
ppi->t_ops->get(ppi, &s->update_time);
*offsetMS = s->t1;
pp_time_sub(offsetMS, &s->t2);
pp_time_add(offsetMS, &SRV(ppi)->delayMS);
/* is it possible to calculate it in client,
* but then t1 and t2 require shmem locks */
s->tracking_enabled = l1e_tracking_enabled;
return 1;
}
static int l1e_e2e_offset(struct pp_instance *ppi,
struct l1e_servo_state *s, struct pp_time *offsetMS)
{
static int errcount;
if (is_incorrect(&s->t1) || is_incorrect(&s->t2)
|| is_incorrect(&s->t3) || is_incorrect(&s->t4)) {
errcount++;
if (errcount > 5) /* a 2-3 in a row are expected */
pp_error("%s: TimestampsIncorrect: %d %d %d %d\n",
__func__, !is_incorrect(&s->t1),
!is_incorrect(&s->t2), !is_incorrect(&s->t3),
!is_incorrect(&s->t4));
return 0;
}
if (WRH_OPER()->servo_hook)
WRH_OPER()->servo_hook(ppi, WRH_SERVO_ENTER);
errcount = 0;
SRV(ppi)->update_count++;
ppi->t_ops->get(ppi, &s->update_time);
l1e_got_sync = 0;
{ /* avoid modifying stamps in place */
struct pp_time mtime, stime;
mtime = s->t4; pp_time_sub(&mtime, &s->t1);
stime = s->t3; pp_time_sub(&stime, &s->t2);
s->delayMM = mtime; pp_time_sub(&s->delayMM, &stime);
}
s->delayMM_ps = pp_time_to_picos(&s->delayMM);
/* avoid negatives in calculations */
if ( s->delayMM_ps < 0 ) {
s->delayMM_ps=0;
picos_to_pp_time(s->delayMM_ps,&s->delayMM);
}
if (__PP_DIAG_ALLOW(ppi, pp_dt_servo, 1)) {
l1e_dump_timestamp(ppi, "t1", s->t1);
l1e_dump_timestamp(ppi, "t2", s->t2);
l1e_dump_timestamp(ppi, "t3", s->t3);
l1e_dump_timestamp(ppi, "t4", s->t4);
l1e_dump_timestamp(ppi, "delayMM", s->delayMM);
}
/* delayMS = (delayMM * fix_alpha) + delayMM/2*/
s->delayMS_ps = ((s->delayMM_ps * s->fiber_fix_alpha) >> FIX_ALPHA_FRACBITS) + (s->delayMM_ps >> 1);
{ /* offsetMS = t1-t2 + delayMS */
struct pp_time tmp1;
*offsetMS = s->t1; pp_time_sub(offsetMS, &s->t2);
picos_to_pp_time(s->delayMS_ps,&tmp1);
pp_time_add(offsetMS, &tmp1);
}
s->tracking_enabled = l1e_tracking_enabled;
picos_to_pp_time(s->delayMS_ps,&SRV(ppi)->delayMS);
l1e_update_meanDelay(ppi,s); /* update currentDS.meanDelay */
l1e_update_delayAsymmetry(ppi,s); /* update currentDS.delayAsymmetry */
return 1;
}
int l1e_servo_update(struct pp_instance *ppi)
{
struct l1e_servo_state *s=L1E_SRV(ppi);
int remaining_offset;
int32_t offset_ticks;
int64_t prev_delayMM_ps = 0;
int locking_poll_ret;
struct pp_time offsetMS ;
int32_t offset_ps;
if (!l1e_got_sync)
return 0;
/* shmem lock */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_BEGIN);
if ( s->state==L1E_UNINITIALIZED )
goto out;
prev_delayMM_ps = s->delayMM_ps;
if (CONFIG_HAS_P2P && ppi->delayMechanism == P2P) {
if (!l1e_p2p_offset(ppi, s, &offsetMS))
goto out;
} else {
if (!l1e_e2e_offset(ppi, s, &offsetMS))
goto out;
}
l1e_update_offsetFromMaster(ppi,&offsetMS); /* Update currentDS.offsetFromMaster */
s->offsetMS_ps=pp_time_to_picos(&offsetMS);
pp_diag(ppi, servo, 2,
"ML: fiber_fix_alpha = %lld, delayMS = %lld, offsetMS = %lld [ps]\n",
(long long )s->fiber_fix_alpha,
(long long )s->delayMS_ps,
(long long )s->offsetMS_ps);
l1e_dump_timestamp(ppi,"ML: offsetMS",offsetMS);
pp_time_hardwarize(&offsetMS, s->clock_period_ps,
&offset_ticks, &offset_ps);
pp_diag(ppi, servo, 2, "offsetMS: %li.%09li (+%li)\n",
(long)offsetMS.secs, (long)offset_ticks,
(long)offset_ps);
locking_poll_ret = WRH_OPER()->locking_poll(ppi, 0);
if (locking_poll_ret != WRH_SPLL_READY
&& locking_poll_ret != WRH_SPLL_CALIB_NOT_READY) {
pp_diag(ppi, servo, 1, "PLL OutOfLock, should restart sync\n");
WRH_OPER()->enable_timing_output(ppi, 0);
/* TODO check
* DSPOR(ppi)->doRestart = TRUE; */
}
/* After each action on the hardware, we must verify if it is over. */
if (!WRH_OPER()->adjust_in_progress()) {
SRV(ppi)->flags &= ~PP_SERVO_FLAG_WAIT_HW;
} else {
pp_diag(ppi, servo, 1, "servo:busy\n");
goto out;
}
/* So, we didn't return. Choose the right state */
if (offsetMS.secs) /* so bad... */
s->state = L1E_SYNC_TAI;
else if (offset_ticks) /* not that bad */
s->state = L1E_SYNC_NSEC;
/* else, let the states below choose the sequence */
pp_diag(ppi, servo, 2, "offsetMS: %li.%09li (+%li)\n",
(long)offsetMS.secs, (long)offset_ticks,
(long)offset_ps);
pp_diag(ppi, servo, 1, "l1e_servo state: %s%s\n",
l1e_servo_state_name[s->state],
SRV(ppi)->flags & PP_SERVO_FLAG_WAIT_HW ? " (wait for hw)" : "");
/* update string state name */
strcpy(SRV(ppi)->servo_state_name, l1e_servo_state_name[s->state]);
switch (s->state) {
case L1E_SYNC_TAI:
WRH_OPER()->adjust_counters(offsetMS.secs, 0);
SRV(ppi)->flags |= PP_SERVO_FLAG_WAIT_HW;
/*
* If nsec wrong, code above forces SYNC_NSEC,
* Else, we must ensure we leave this status towards
* fine tuning
*/
s->state = L1E_SYNC_PHASE;
break;
case L1E_SYNC_NSEC:
WRH_OPER()->adjust_counters(0, offset_ticks);
SRV(ppi)->flags |= PP_SERVO_FLAG_WAIT_HW;
s->state = L1E_SYNC_PHASE;
break;
case L1E_SYNC_PHASE:
pp_diag(ppi, servo, 2, "oldsetp %i, offset %i:%04i\n",
s->cur_setpoint_ps, offset_ticks,
offset_ps);
s->cur_setpoint_ps += offset_ps;
pp_diag(ppi, servo, 3, "%s.%d: Adjust_phase: %d\n",__func__,__LINE__,s->cur_setpoint_ps);
WRH_OPER()->adjust_phase(s->cur_setpoint_ps);
SRV(ppi)->flags |= PP_SERVO_FLAG_WAIT_HW;
s->state = L1E_WAIT_OFFSET_STABLE;
if (ARCH_IS_WRS) {
/*
* Now, let's fix system time. We pass here
* once only, so that's the best place to do
* it. We can't use current WR time, as we
* still miss the method to get it (through IPC).
* So use T4, which is a good approximation.
*/
unix_time_ops.set(ppi, &ppi->t4);
pp_diag(ppi, time, 1, "system time set to %li TAI\n",
(long)ppi->t4.secs);
}
break;
case L1E_WAIT_OFFSET_STABLE:
/* ts_to_picos() below returns phase alone */
remaining_offset = abs(pp_time_to_picos(&offsetMS));
if(remaining_offset < WRH_SERVO_OFFSET_STABILITY_THRESHOLD) {
WRH_OPER()->enable_timing_output(ppi, 1);
s->prev_delayMS_ps = s->delayMS_ps;
s->state = L1E_TRACK_PHASE;
} else {
s->missed_iters++;
}
if (s->missed_iters >= 10) {
s->missed_iters = 0;
s->state = L1E_SYNC_PHASE;
}
break;
case L1E_TRACK_PHASE:
s->skew_ps = s->delayMS_ps - s->prev_delayMS_ps;
/* Can be disabled for manually tweaking and testing */
if(l1e_tracking_enabled) {
if (abs(offset_ps) >
2 * WRH_SERVO_OFFSET_STABILITY_THRESHOLD) {
s->state = WR_SYNC_PHASE;
break;
}
// adjust phase towards offset = 0 make ck0 0
s->cur_setpoint_ps += (offset_ps / 4);
pp_diag(ppi, servo, 3, "%s.%d: Adjust_phase: %d\n",__func__,__LINE__,s->cur_setpoint_ps);
WRH_OPER()->adjust_phase(s->cur_setpoint_ps);
pp_diag(ppi, time, 1, "adjust phase %i\n",
s->cur_setpoint_ps);
s->prev_delayMS_ps = s->delayMS_ps;
}
break;
}
SRV(ppi)->servo_locked=s->state==L1E_TRACK_PHASE;
/* Increase number of servo updates with state different than
* L1E_TRACK_PHASE. (Used by SNMP) */
if (s->state != L1E_TRACK_PHASE)
s->n_err_state++;
/* Increase number of servo updates with offset exceeded
* SNMP_MAX_OFFSET_PS (Used by SNMP) */
if (abs(s->offsetMS_ps) > SNMP_MAX_OFFSET_PS)
s->n_err_offset++;
/* Increase number of servo updates with delta rtt exceeded
* SNMP_MAX_DELTA_RTT_PS (Used by SNMP) */
if (abs(prev_delayMM_ps - s->delayMM_ps) > SNMP_MAX_DELTA_RTT_PS)
s->n_err_delta_rtt++;
out:
/* shmem unlock */
wrs_shm_write(ppsi_head, WRS_SHM_WRITE_END);
if (WRH_OPER()->servo_hook)
WRH_OPER()->servo_hook(ppi, WRH_SERVO_LEAVE);
return 0;
}
/*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Jean-Claude BAU & Maciej Lipinski
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#include <ppsi/ppsi.h>
#include <common-fun.h>
#include "l1e-api.h"
#include "l1e-constants.h"
#include <math.h>
typedef struct {
int (*action)(struct pp_instance *ppi, Boolean newState);
}l1e_state_machine_t;
#define L1E_DEFAULT_DELAY 500
extern char *l1e_state_name[];
#define MAX_STATE_ACTIONS (sizeof(le1_state_actions)/sizeof(l1e_state_machine_t))
/* prototypes */
static int l1e_empty_action(struct pp_instance *ppi, Boolean new_state);
static int l1e_handle_state_disabled(struct pp_instance *ppi, Boolean new_state);
static int l1e_handle_state_idle(struct pp_instance *ppi, Boolean new_state);
static int l1e_handle_state_link_alive(struct pp_instance *ppi, Boolean new_state);
static int l1e_handle_state_config_match(struct pp_instance *ppi, Boolean new_state);
static int l1e_handle_state_up(struct pp_instance *ppi, Boolean new_state);
static l1e_state_machine_t le1_state_actions[] ={
[0] { /* Not used */
.action=l1e_empty_action,
},
[L1SYNC_DISABLED] {
.action=l1e_handle_state_disabled,
},
[L1SYNC_IDLE] {
.action=l1e_handle_state_idle,
},
[L1SYNC_LINK_ALIVE] {
.action=l1e_handle_state_link_alive,
},
[L1SYNC_CONFIG_MATCH] {
.action=l1e_handle_state_config_match,
},
[L1SYNC_UP] {
.action=l1e_handle_state_up,
},
};
/*
* This hook is called by fsm to run the extension state machine.
* It is used to send signaling messages.
* It returns the ext-specific timeout value
*/
int l1e_run_state_machine(struct pp_instance *ppi) {
L1SyncBasicPortDS_t * basicDS=L1E_DSPOR_BS(ppi);
Enumeration8 nextState=basicDS->next_state;
Boolean newState=nextState!=basicDS->L1SyncState;
int delay;
if ( nextState>=MAX_STATE_ACTIONS)
return L1E_DEFAULT_DELAY; /* return default delay */
/*
* Update the L1SYNC dynamic data independent of the state machine
*/
/* By design, when link is up, it is always tx coherent and congruent*/
basicDS->isTxCoherent= ppi->link_up ? 1 : 0;
/* Check SYNC reception Time-out */
if ( pp_timeout(ppi, L1E_TIMEOUT_RX_SYNC) ) {
/* Time-out detected */
basicDS->L1SyncLinkAlive = FALSE;
}
/*
* Evaluation of events common to all states
*/
if ( newState ) {
basicDS->L1SyncState=nextState;
pp_diag(ppi, ext, 2, "L1SYNC state: Enter %s\n", l1e_state_name[nextState]);
}
delay=(*le1_state_actions[basicDS->L1SyncState].action) (ppi,newState);
if ( basicDS->L1SyncState != basicDS->next_state )
pp_diag(ppi, ext, 2, "L1SYNC state: Exit %s\n", l1e_state_name[basicDS->L1SyncState]);
return delay;
}
static int l1e_empty_action(struct pp_instance *ppi, Boolean new_state){
return L1E_DEFAULT_DELAY; /* return default delay */
}
/* L1_SYNC_RESET event */
static inline Boolean le1_evt_L1_SYNC_RESET(struct pp_instance *ppi) {
return ppi->link_up == 0;
}
/* L1_SYNC_ENABLED event */
static inline Boolean le1_evt_L1_SYNC_ENABLED(struct pp_instance *ppi) {
return L1E_DSPOR_BS(ppi)->L1SyncEnabled == TRUE;
}
/* CONFIG_OK event */
static inline Boolean le1_evt_CONFIG_OK(struct pp_instance *ppi) {
L1SyncBasicPortDS_t *basicDS=L1E_DSPOR_BS(ppi);
return basicDS->txCoherentIsRequired==basicDS->peerTxCoherentIsRequired
&& basicDS->rxCoherentIsRequired==basicDS->peerRxCoherentIsRequired
&& basicDS->congruentIsRequired==basicDS->peerCongruentIsRequired;
}
/* STATE_OK event */
static Boolean le1_evt_STATE_OK(struct pp_instance *ppi) {
L1SyncBasicPortDS_t *basicDS=L1E_DSPOR_BS(ppi);
int pll_state;
/* Update isRxCoherent and isCongruant : it must follow the PTP state machine */
switch (ppi->state) {
case PPS_SLAVE :
case PPS_UNCALIBRATED :
pll_state= WRH_OPER()->locking_poll(ppi, 0); /* Get the PPL state */
basicDS->isCongruent =
basicDS->isRxCoherent= pll_state == WRH_SPLL_READY ? 1 : 0;
break;
case PPS_MASTER :
basicDS->isRxCoherent=
basicDS->peerIsTxCoherent &&
basicDS->peerIsRxCoherent &&
basicDS->isTxCoherent ? 1 : 0;
/** TODO: in principle, the PLL should report unlocked when it is not locked,
* regardless of the state. Thus, when we check in MASTER state, it should report
* unlocked - this is not the case for some reasons
* Ideally, we want to check whether PLL is locked at any time. This is to cover
* the case of network re-arrangement. In such case, a port in SLAVE state can
* transition to MASTER state and after such transition the port in MASTER state
* might remain locked. We want to detect this case.
*
* Thus, the condition should be:
* basicDS->isCongruent= pll_state == !WRH_SPLL_READY ? 1 : 0;
*/
basicDS->isCongruent = 1;
break;
default :
basicDS->isCongruent = 0;
}
return ( !basicDS->txCoherentIsRequired || (basicDS->isTxCoherent && basicDS->peerIsTxCoherent ))
&& ( !basicDS->rxCoherentIsRequired || (basicDS->isRxCoherent && basicDS->peerIsRxCoherent ))
&& ( !basicDS->congruentIsRequired || (basicDS->isCongruent && basicDS->peerIsCongruent));
}
/* LINK_OK event */
static inline Boolean le1_evt_LINK_OK(struct pp_instance *ppi) {
return L1E_DSPOR(ppi)->basic.L1SyncLinkAlive == TRUE;
}
static void l1e_send_sync_msg(struct pp_instance *ppi, Boolean immediatSend) {
if (pp_timeout(ppi, L1E_TIMEOUT_TX_SYNC) || immediatSend) {
int len;
pp_diag(ppi, ext, 1, "Sending L1SYNC_TLV signaling msg\n");
len = l1e_pack_signal(ppi);
/* FIXME: check the destination MAC address */
__send_and_log(ppi, len, PP_NP_GEN);
/* Calculate when the next message should be sent */
__pp_timeout_set(ppi, L1E_TIMEOUT_TX_SYNC,
4 << (L1E_DSPOR_BS(ppi)->logL1SyncInterval + 8)); /* loop ever since */
}
}
/* DISABLED state */
static int l1e_handle_state_disabled(struct pp_instance *ppi, Boolean new_state){
l1e_ext_portDS_t * l1e_portDS=L1E_DSPOR(ppi);
/* State initialization */
if ( new_state ) {
/* Table 157 - page 449
* All dynamic members of L1SyncBasicPortDS and L1SyncOptPortDS data sets are set
* to initialization values
*/
l1e_portDS->basic.isTxCoherent =
l1e_portDS->basic.isRxCoherent =
l1e_portDS->basic.isCongruent =
l1e_portDS->basic.peerTxCoherentIsRequired =
l1e_portDS->basic.peerRxCoherentIsRequired =
l1e_portDS->basic.peerCongruentIsRequired =
l1e_portDS->basic.peerIsTxCoherent =
l1e_portDS->basic.peerIsRxCoherent =
l1e_portDS->basic.peerIsCongruent =
FALSE;
/* Set extension mode disabled */
l1e_portDS->head.extModeOn = l1e_portDS->parentExtModeOn = 0;
}
/* Check if state transition needed */
if ( le1_evt_L1_SYNC_ENABLED(ppi) && !le1_evt_L1_SYNC_RESET(ppi) ) {
/* Go the IDLE state */
L1E_DSPOR_BS(ppi)->next_state=L1SYNC_IDLE;
return 0; /* no wait to evaluate next state */
}
return L1E_DEFAULT_DELAY; /* return default delay */
}
/* IDLE state */
static int l1e_handle_state_idle(struct pp_instance *ppi, Boolean new_state){
l1e_ext_portDS_t * l1e_portDS=L1E_DSPOR(ppi);
/* State initialization */
if ( new_state ) {
/* Table 157 - page 449
* The dynamic members listed in Table 155 are set to initialization
* values when entering this state.
*/
l1e_portDS->basic.peerTxCoherentIsRequired =
l1e_portDS->basic.peerRxCoherentIsRequired =
l1e_portDS->basic.peerCongruentIsRequired =
l1e_portDS->basic.peerIsTxCoherent =
l1e_portDS->basic.peerIsRxCoherent=
l1e_portDS->basic.peerIsCongruent = FALSE;
/* Set extension mode disabled */
l1e_portDS->head.extModeOn = l1e_portDS->parentExtModeOn = 0;
l1e_send_sync_msg(ppi,1); /* Send immediately a message */
}
/* Check if state transition needed */
if ( !le1_evt_L1_SYNC_ENABLED(ppi) || le1_evt_L1_SYNC_RESET(ppi) ) {
/* Go to DISABLE state */
l1e_portDS->basic.next_state=L1SYNC_DISABLED;
return L1E_DEFAULT_DELAY; /* return default delay */
}
if ( le1_evt_LINK_OK(ppi) ) {
l1e_portDS->basic.next_state=L1SYNC_LINK_ALIVE;
return L1E_DEFAULT_DELAY; /* return default delay */
}
/* Iterative treatment */
l1e_send_sync_msg(ppi,0);
return L1E_DEFAULT_DELAY; /* return default delay */
}
/* LINK_ALIVE state */
static int l1e_handle_state_link_alive(struct pp_instance *ppi, Boolean new_state){
L1SyncBasicPortDS_t *basic= L1E_DSPOR_BS(ppi);
/* State initialization */
if ( new_state ) {
/* Initialize time-out peer L1SYNC reception */
int timeout_tx_sync = (4 << (basic->logL1SyncInterval + 8)) * basic->L1SyncReceiptTimeout;
__pp_timeout_set(ppi, L1E_TIMEOUT_RX_SYNC, timeout_tx_sync);
}
/* Check if state transition needed */
if ( !le1_evt_LINK_OK(ppi) ) {
/* Go to IDLE state */
basic->next_state=L1SYNC_IDLE;
return L1E_DEFAULT_DELAY; /* return default delay */
}
/* we will allow to go to CONFIG_MATH only for UNCALIBRATED and MASTER states */
if ( le1_evt_CONFIG_OK(ppi) && (ppi->state==PPS_UNCALIBRATED || ppi->state==PPS_MASTER)) {
basic->next_state=L1SYNC_CONFIG_MATCH;
return 0; /* Try to treat next state asap */
}
/* Iterative treatment */
l1e_send_sync_msg(ppi,0);
return pp_next_delay_1(ppi, L1E_TIMEOUT_TX_SYNC); /* Return the timeout for next invocation */
}
/* CONFIG_MATCH state */
static int l1e_handle_state_config_match(struct pp_instance *ppi, Boolean new_state){
L1SyncBasicPortDS_t *basic= L1E_DSPOR_BS(ppi);
/* State initialization */
if (new_state) {
switch ( ppi->state ) {
case PPS_SLAVE :
case PPS_UNCALIBRATED :
if ( basic->congruentIsRequired==1 && basic->isRxCoherent==0) {
pp_diag(ppi, ext, 1, "Locking PLL\n");
WRH_OPER()->locking_enable(ppi);
}
break;
case PPS_MASTER :
if ( basic->congruentIsRequired == 1) {
WRH_OPER()->locking_disable(ppi);
}
break;
default:
break;
}
}
/* Check if state transition needed */
if ( !le1_evt_LINK_OK(ppi) ) {
/* Go to IDLE state */
L1E_DSPOR_BS(ppi)->next_state=L1SYNC_IDLE;
return L1E_DEFAULT_DELAY; /* return default delay */
}
if ( !le1_evt_CONFIG_OK(ppi) ) {
/* Return to LINK_ALIVE state */
L1E_DSPOR_BS(ppi)->next_state=L1SYNC_LINK_ALIVE;
return L1E_DEFAULT_DELAY; /* return default delay */
}
if ( le1_evt_STATE_OK(ppi) ) {
/* Return to UP state */
L1E_DSPOR_BS(ppi)->next_state=L1SYNC_UP;
return 0; /* Treat next state asap */
}
/* Iterative treatment */
l1e_send_sync_msg(ppi,0);
return pp_next_delay_1(ppi, L1E_TIMEOUT_TX_SYNC); /* Return the timeout for next invocation */
}
/* UP state */
static int l1e_handle_state_up(struct pp_instance *ppi, Boolean new_state){
l1e_ext_portDS_t * l1e_portDS=L1E_DSPOR(ppi);
Enumeration8 next_state=0;
/* State initialization */
if ( new_state ) {
l1e_servo_init(ppi); /* The servo can be initialized because the PPL is locked */
l1e_portDS->head.extModeOn =
l1e_portDS->parentExtModeOn = 1;
WRH_OPER()->enable_ptracker(ppi);
}
/* Check if state transition needed */
if ( !le1_evt_LINK_OK(ppi) ) {
/* Go to IDLE state */
next_state=L1SYNC_IDLE;
}
if ( !le1_evt_CONFIG_OK(ppi) ) {
/* Return to LINK_ALIVE state */
next_state=L1SYNC_LINK_ALIVE;
}
if ( !le1_evt_STATE_OK(ppi) ) {
/* Return to CONFIG_MATCH state */
next_state=L1SYNC_CONFIG_MATCH;
}
if (next_state!=0 ) {
l1e_portDS->basic.next_state=next_state;
l1e_portDS->head.extModeOn =
l1e_portDS->parentExtModeOn = 0;
WRH_OPER()->locking_disable(ppi); /* Unlock the PLL */
l1e_servo_reset(ppi);
return 0; /* Treat the next state asap */
}
/* Iterative treatment */
l1e_update_correction_values(ppi);
l1e_send_sync_msg(ppi,0);
return pp_next_delay_1(ppi, L1E_TIMEOUT_TX_SYNC); /* Return the timeout for next invocation */
}
......@@ -220,6 +220,14 @@ int st_com_handle_announce(struct pp_instance *ppi, void *buf, int len)
return 0;
}
int st_com_handle_signaling(struct pp_instance *ppi, void *buf, int len)
{
if (ppi->ext_hooks->handle_signaling)
return ppi->ext_hooks->handle_signaling(ppi,buf,len);
return 0;
}
int __send_and_log(struct pp_instance *ppi, int msglen, int chtype)
{
int msgtype = ((char *)ppi->tx_ptp)[0] & 0xf;
......@@ -235,7 +243,11 @@ int __send_and_log(struct pp_instance *ppi, int msglen, int chtype)
pp_msgtype_info[msgtype].name, msgtype);
return PP_SEND_ERROR;
}
/* FIXME: diagnosticst should be looped back in the send method */
/* The send method updates ppi->last_snt_time with the Tx timestamp. */
/* This timestamp must be corrected with the egressLatency */
pp_time_add_interval(t,ppi->timestampCorrectionPortDS.egressLatency);
/* FIXME: diagnostics should be looped back in the send method */
pp_diag(ppi, frames, 1, "SENT %02d bytes at %d.%09d.%03d (%s)\n",
msglen, (int)t->secs, (int)(t->scaled_nsecs >> 16),
((int)(t->scaled_nsecs & 0xffff) * 1000) >> 16,
......
......@@ -13,20 +13,11 @@
/* Contains all functions common to more than one state */
int st_com_check_announce_receive_timeout(struct pp_instance *ppi);
int st_com_peer_handle_preq(struct pp_instance *ppi, void *buf,
int len);
int st_com_peer_handle_pres(struct pp_instance *ppi, void *buf,
int len);
int st_com_peer_handle_pres_followup(struct pp_instance *ppi,
void *buf, int len);
int st_com_handle_announce(struct pp_instance *ppi, void *buf,
int len);
int st_com_peer_handle_preq(struct pp_instance *ppi, void *buf, int len);
int st_com_peer_handle_pres(struct pp_instance *ppi, void *buf, int len);
int st_com_peer_handle_pres_followup(struct pp_instance *ppi, void *buf, int len);
int st_com_handle_announce(struct pp_instance *ppi, void *buf, int len);
int st_com_handle_signaling(struct pp_instance *ppi, void *buf, int len);
int __send_and_log(struct pp_instance *ppi, int msglen, int chtype);
......@@ -36,8 +27,13 @@ static inline int __recv_and_count(struct pp_instance *ppi, void *buf, int len,
{
int ret;
ret = ppi->n_ops->recv(ppi, buf, len, t);
if (ret > 0)
if (ret > 0) {
/* Adjust reception timestamp: ts'= ts - ingressLatency - semistaticLatency*/
TimeInterval adjust=ppi->timestampCorrectionPortDS.ingressLatency;
adjust+=ppi->timestampCorrectionPortDS.semistaticLatency;
pp_time_sub_interval(t,adjust);
ppi->ptp_rx_count++;
}
return ret;
}
......
......@@ -206,9 +206,13 @@ int pp_slave(struct pp_instance *ppi, void *buf, int len)
/* upgrade from uncalibrated to slave or back*/
if (uncalibrated) {
/* TODO add implementation specific MASTER_CLOCK_SELECTED event
for now just change directly to new state on next round */
ppi->next_state = PPS_SLAVE;
if ( ppi->ext_hooks->ready_for_slave != NULL ) {
if ( (*ppi->ext_hooks->ready_for_slave)(ppi) ) {
ppi->next_state = PPS_SLAVE;
}
} else {
ppi->next_state = PPS_SLAVE;
}
} else {
/* TODO add implementation specific SYNCHRONIZATION FAULT
* event */
......@@ -217,7 +221,7 @@ int pp_slave(struct pp_instance *ppi, void *buf, int len)
}
/* when entering uncalibrated init servo */
if ((ppi->state == PPS_UNCALIBRATED) && (ppi->is_new_state)) {
if (uncalibrated && (ppi->is_new_state)) {
memset(&ppi->t1, 0, sizeof(ppi->t1));
pp_diag(ppi, bmc, 2, "Entered to uncalibrated, reset servo\n");
pp_servo_init(ppi);
......
......@@ -74,3 +74,11 @@ struct ptp_tlv { /* page 135 (155) */
uint8_t subtype[3];
uint8_t data[0];
} __attribute__((packed));
struct l1sync_tlv { /* page 135 (155) */
uint16_t type;
uint16_t len;
uint8_t config;
uint8_t active;
uint8_t data[0];
} __attribute__((packed));
......@@ -166,7 +166,7 @@ static void dump_msg_resp_etc(char *prefix, char *s, struct ptp_sync_etc *p)
}
/* TLV dumper, now white-rabbit aware */
static int dump_tlv(char *prefix, struct ptp_tlv *tlv, int totallen)
static int wr_dump_tlv(char *prefix, struct ptp_tlv *tlv, int totallen)
{
/* the field includes 6 bytes of the header, ecludes 4 of them. Bah! */
int explen = ntohs(tlv->len) + 4;
......@@ -276,6 +276,29 @@ static int dump_tlv(char *prefix, struct ptp_tlv *tlv, int totallen)
return explen;
}
static int l1sync_dump_tlv(char *prefix, struct l1sync_tlv *tlv, int totallen)
{
/* the field includes 6 bytes of the header, ecludes 4 of them. Bah! */
int explen = ntohs(tlv->len) + 4;
printf("TLV: type %04x len %i conf %02x act %02x ",
ntohs(tlv->type), explen,
(int) tlv->config,
(int) tlv->active);
if (explen > totallen) {
printf("%sTLV: too short (expected %i, total %i)\n", prefix,
explen, totallen);
return totallen;
}
/* later: if (memcmp(tlv->oui, "\x08\x00\x30", 3)) ... */
/* Now dump non-wr tlv in binary, count only payload */
dumpstruct(prefix, "TLV: ", "tlv-content", tlv->data,
explen - sizeof(*tlv));
return explen;
}
/* A big function to dump the ptp information */
static void dump_payload(char *prefix, void *pl, int len)
{
......@@ -285,6 +308,7 @@ static void dump_payload(char *prefix, void *pl, int len)
int version = h->versionPTP_and_reserved & 0xf;
int messageType = h->type_and_transport_specific & 0xf;
char *cfptr = (void *)&h->correctionField;
int tlv_size=0;
if (version != 2) {
printf("%sVERSION: unsupported (%i)\n", prefix, version);
......@@ -326,11 +350,13 @@ static void dump_payload(char *prefix, void *pl, int len)
CASE(G, ANNOUNCE);
dump_msg_announce(prefix, msg_specific);
donelen = 64;
tlv_size=sizeof(struct ptp_tlv);
break;
CASE(G, SIGNALING);
dump_1port(prefix, "MSG-SIGNALING: target-port ", msg_specific);
donelen = 44;
tlv_size=sizeof(struct l1sync_tlv);
break;
#if __STDC_HOSTED__ /* Avoid pdelay dump within ppsi, we don't use it */
......@@ -360,14 +386,22 @@ static void dump_payload(char *prefix, void *pl, int len)
* Dump any trailing TLV, but ignore a trailing 2-long data hunk.
* The trailing zeroes appear with less-than-minimum Eth messages.
*/
while (donelen < len && len - donelen > 2) {
int n = len - donelen;
if (n < sizeof(struct ptp_tlv)) {
if (n < tlv_size) {
printf("%sTLV: too short (%i - %i = %i)\n", prefix,
len, donelen, n);
break;
}
donelen += dump_tlv(prefix, pl + donelen, n);
switch ( messageType) {
case PPM_ANNOUNCE :
donelen += wr_dump_tlv(prefix, pl + donelen, n);
break;
case PPM_SIGNALING :
donelen += l1sync_dump_tlv(prefix, pl + donelen, n);
break;
}
}
out:
/* Finally, binary dump of it all */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment