flexPTP 1.0
An IEEE 1588 PTP implementation designed for microcontrollers
Loading...
Searching...
No Matches
slave.c
Go to the documentation of this file.
1#include "slave.h"
2#include "common.h"
3
4#include "format_utils.h"
5#include "msg_utils.h"
6#include "portmacro.h"
7#include "ptp_types.h"
9#include "stats.h"
10#include "task_ptp.h"
11#include "timeutils.h"
12
13#include "ptp_core.h"
14#include "ptp_defs.h"
15#include <math.h>
16
17#ifdef MIN
18#undef MIN
19#endif
20
21#define MIN(a, b) (((a) < (b)) ? (a) : (b))
22
23#ifdef MAX
24#undef MAX
25#endif
26
27#define MAX(a, b) (((a) > (b)) ? (a) : (b))
28
30#define S (gPtpCoreState)
32
33// --------------
34
39 // don't do any processing if no delay_request data is present
40 if (!nonZeroI(&S.network.meanPathDelay)) {
41 return;
42 }
43
44 // timestamps and time intervals
45 TimestampI d, syncMa, syncSl, delReqSl, delReqMa;
46
47 // copy timestamps to assign them with meaningful names
48 syncMa = S.slave.scd.t[T1];
49 syncSl = S.slave.scd.t[T2];
50 delReqSl = S.slave.scd.t[T3];
51 delReqMa = S.slave.scd.t[T4];
52
53 // log timestamps (if enabled)
54 if (S.profile.delayMechanism == PTP_DM_E2E) {
55 CLILOG(S.logging.timestamps,
56 "seqID: %u\n"
57 "T1: %d.%09d <- Sync TX (master)\n"
58 "T2: %d.%09d <- Sync RX (slave) \n"
59 "T3: %d.%09d <- Del_Req TX (slave) \n"
60 "T4: %d.%09d <- Del_Req RX (master)\n\n",
61 (uint32_t)S.slave.messaging.sequenceID,
62 (int32_t)syncMa.sec, syncMa.nanosec,
63 (int32_t)syncSl.sec, syncSl.nanosec,
64 (int32_t)delReqSl.sec, delReqSl.nanosec,
65 (int32_t)delReqMa.sec, delReqMa.nanosec);
66 } else if (S.profile.delayMechanism == PTP_DM_P2P) {
67 CLILOG(S.logging.timestamps,
68 "seqID: %u\n"
69 "T1: %d.%09d <- Sync TX (master)\n"
70 "T2: %d.%09d <- Sync RX (slave)\n"
71 "t1: %d.%09d <- PDel_Req TX (our clock)\n"
72 "t2: %d.%09d <- PDel_Req RX (their clock)\n"
73 "t3: %d.%09d <- PDel_Resp TX (their clock)\n"
74 "t4: %d.%09d <- PDel_Resp RX (our clock)\n\n",
75 (uint32_t)S.slave.messaging.sequenceID,
76 (int32_t)S.slave.scd.t[0].sec, S.slave.scd.t[0].nanosec,
77 (int32_t)S.slave.scd.t[1].sec, S.slave.scd.t[1].nanosec,
78 (int32_t)S.slave.scd.t[2].sec, S.slave.scd.t[2].nanosec,
79 (int32_t)S.slave.scd.t[3].sec, S.slave.scd.t[3].nanosec,
80 (int32_t)S.slave.scd.t[4].sec, S.slave.scd.t[4].nanosec,
81 (int32_t)S.slave.scd.t[5].sec, S.slave.scd.t[5].nanosec);
82 }
83
84 // variable for later substraction of summed correction fields
85 TimestampI cf = {0, 0};
86 nsToTsI(&cf, S.slave.scd.cf[T1] + S.slave.scd.cf[T2]);
87
88 // compute difference between master and slave clocks
89 subTime(&d, &syncSl, &syncMa); // t2 - t1 ...
90 subTime(&d, &d, &S.network.meanPathDelay); // - MPD
91 subTime(&d, &d, &cf); // - CF of (Sync + Follow_Up)
92
93 // substract offset
94 subTime(&d, &d, &S.hwoptions.offset);
95
96 // normalize time difference (eliminate malformed time value issues)
97 normTime(&d);
98
99 // translate time difference into clock tick unit
100 int32_t d_ticks = tsToTick(&d, PTP_CLOCK_TICK_FREQ_HZ);
101
102 // if time difference is at least one second, then jump the clock
103 int64_t d_ns = nsI(&d);
104 if (llabs(d_ns) > S.slave.coarseLimit) {
105 PTP_SET_CLOCK((int32_t)syncMa.sec, syncMa.nanosec);
106
107 CLILOG(S.logging.info, "Time difference has exceeded the coarse correction threshold [%ldns], executing coarse correction!\n", d_ns);
108
109 S.slave.prevSyncMa = syncMa;
110
111 return;
112 }
113
114 // prepare data to pass to the controller
115 double measSyncPeriod_ns;
116 // if momentarily sync interval is not directly measurable, use the nominal value
117 if (S.profile.logDelayReqPeriod == PTP_LOGPER_SYNCMATCHED && S.profile.delayMechanism == PTP_DM_E2E) {
118 measSyncPeriod_ns = S.slave.messaging.syncPeriodMs * 1E+06;
119 } else { // if accurately measurable...
120 TimestampI measSyncPeriod;
121 subTime(&measSyncPeriod, &syncMa, &(S.slave.prevSyncMa));
122 measSyncPeriod_ns = nsI(&measSyncPeriod);
123 }
124
125 PtpServoAuxInput saux = {S.slave.scd, S.slave.messaging.logSyncPeriod, S.slave.messaging.syncPeriodMs, measSyncPeriod_ns};
126
127 // run controller
128 float corr_ppb = PTP_SERVO_RUN(nsI(&d), &saux);
129
130 // compute addend value
131 int64_t compAddend = (int64_t)S.hwclock.addend + (int64_t)(corr_ppb * PTP_ADDEND_CORR_PER_PPB_F); // compute addend value
132 S.hwclock.addend = MIN(compAddend, 0xFFFFFFFF); // limit to 32-bit range
133
134 // write addend into hardware
135 PTP_SET_ADDEND(S.hwclock.addend);
136
137 // collect statistics
139
140 // log on cli (if enabled)
141 CLILOG(S.logging.def, "%d %09d %d %09d %d " PTP_COLOR_BYELLOW "% 9d" PTP_COLOR_RESET " %d %u %f %ld %09lu\n",
142 (int32_t)syncMa.sec, syncMa.nanosec, (int32_t)delReqMa.sec, delReqMa.nanosec,
143 (int32_t)d.sec, d.nanosec, d_ticks,
144 S.hwclock.addend, corr_ppb, nsI(&S.network.meanPathDelay), (uint64_t)measSyncPeriod_ns);
145
146 // call sync callback if defined
147 if (S.slave.syncCb != NULL) {
148 S.slave.syncCb(nsI(&d), &S.slave.scd, S.hwclock.addend);
149 }
150
151 S.slave.prevSyncMa = syncMa;
152}
153
159 // send Delay_Req message
160 if (S.profile.logDelayReqPeriod == PTP_LOGPER_SYNCMATCHED) {
161 // send (P)Delay_Req message
163
164 // dispatch (P)DELAY_REQ_SENT event
165 PTP_IUEV((S.profile.delayMechanism == PTP_DM_E2E) ? PTP_UEV_DELAY_REQ_SENT : PTP_UEV_PDELAY_REQ_SENT);
166 }
167
168 // jump clock if error is way too big...
169 TimestampI d;
170 subTime(&d, &S.slave.scd.t[T2], &S.slave.scd.t[T1]);
171 if (d.sec != 0) {
172 PTP_SET_CLOCK((int32_t)S.slave.scd.t[T1].sec, S.slave.scd.t[T1].nanosec);
173 }
174
175 // run servo only if issuing Delay_Requests is not syncmatched
176 if (S.profile.logDelayReqPeriod != PTP_LOGPER_SYNCMATCHED) {
178 }
179}
180
187static void ptp_commence_p2p_correction(uint32_t pdelRespSeqId) {
188 // compute mean path delay
189 ptp_compute_mean_path_delay_p2p(S.slave.scd.t + 2, S.slave.scd.cf + 2, &S.network.meanPathDelay);
190
191 // store last response ID
192 S.slave.messaging.lastRespondedDelReqId = pdelRespSeqId;
193
194 if (S.profile.logDelayReqPeriod == PTP_LOGPER_SYNCMATCHED) {
196 }
197}
198
199// packet processing
201 PtpMessageType mt = pHeader->messageType;
202 PtpDelayMechanism dm = S.profile.delayMechanism;
203
204 // process non-Announce messages
205 if (mt == PTP_MT_Sync || mt == PTP_MT_Follow_Up) {
206 switch (S.slave.messaging.m2sState) {
207 // wait for Sync message
208 case SIdle: {
209 // switch into next state if Sync packet has arrived
210 if (mt == PTP_MT_Sync) {
211 // save sync interval
212 S.slave.messaging.logSyncPeriod = pHeader->logMessagePeriod;
213 S.slave.messaging.syncPeriodMs = ptp_logi2ms(pHeader->logMessagePeriod);
214
215 // MSG("%d\n", header.logMessagePeriod);
216
217 // save reception time
218 S.slave.scd.t[T2] = pRawMsg->ts;
219
220 // switch to next syncState
221 S.slave.messaging.sequenceID = pHeader->sequenceID;
222
223 // save correction field
224 S.slave.scd.cf[T1] = pHeader->correction_ns;
225
226 // handle two step/one step messaging
227 if (pHeader->flags.PTP_TWO_STEP) {
228 S.slave.messaging.m2sState = SWaitFollowUp;
229 } else {
230 ptp_extract_timestamps(&S.slave.scd.t[T1], pRawMsg->data, 1); // extract t1
231 S.slave.scd.cf[T2] = 0; // clear Follow_Up correction field, since no Follow_Up is expected
232 ptp_commence_e2e_correction(); // commence executing the E2E correction
233 }
234
235 // dispatch SYNC_RECVED event
237 }
238
239 break;
240 }
241 // wait for Follow_Up message
242 case SWaitFollowUp:
243 if (mt == PTP_MT_Follow_Up) {
244 // check sequence ID if the response is ours
245 if (pHeader->sequenceID == S.slave.messaging.sequenceID) {
246 ptp_extract_timestamps(&S.slave.scd.t[T1], pRawMsg->data, 1); // read t1
247 S.slave.scd.cf[T2] = pHeader->correction_ns; // retain correction field
248
249 // initiate the correction
251
252 // log correction field (if enabled)
253 CLILOG(S.logging.corr, "C [Follow_Up]: %09lu\n", pHeader->correction_ns);
254
255 // dispatch FOLLOW_UP_RECVED event
257 }
258
259 // on ID mismatch, just skip the cycle, and expect a new Sync coming
260
261 // switch to next syncState
262 S.slave.messaging.m2sState = SIdle;
263 }
264
265 break;
266 }
267 }
268
269 // ------ (P)DELAY_RESPONSE PROCESSING --------
270
271 // wait for (P)Delay_Resp message
272 if (((mt == PTP_MT_Delay_Resp) && (dm == PTP_DM_E2E)) ||
273 (((mt == PTP_MT_PDelay_Resp) || (mt == PTP_MT_PDelay_Resp_Follow_Up)) && (dm == PTP_DM_P2P))) {
274 if (pHeader->sequenceID == S.slave.messaging.delay_reqSequenceID) { // read clock ID of requester
275 PtpDelay_RespIdentification delay_respID; // identification received in every Delay_Resp packet
276
277 if (mt == PTP_MT_Delay_Resp) { // Delay_Resp processing
278
279 ptp_read_delay_resp_id_data(&delay_respID, pRawMsg->data);
280
281 // if the response was sent to us as a response to our Delay_Req then continue processing
282 if (delay_respID.requestingSourceClockIdentity == S.hwoptions.clockIdentity &&
284
285 ptp_extract_timestamps(&S.slave.scd.t[T4], pRawMsg->data, 1); // store t4
286 S.slave.scd.cf[T4] = pHeader->correction_ns; // store correction field
287
288 // compute mean path delay
289 ptp_compute_mean_path_delay_e2e(S.slave.scd.t, S.slave.scd.cf, &S.network.meanPathDelay);
290
291 // store last response ID
292 S.slave.messaging.lastRespondedDelReqId = pHeader->sequenceID;
293
294 // perform correction if operating on syncmatched mode
295 if (S.profile.logDelayReqPeriod == PTP_LOGPER_SYNCMATCHED) {
297 }
298
299 // dispatch DELAY_RESP_RECVED event
301
302 // log correction field (if enabled)
303 CLILOG(S.logging.corr, "C [Del_Resp]: %09lu\n", pHeader->correction_ns);
304 }
305
306 } else if (mt == PTP_MT_PDelay_Resp) { // PDelay_Resp processing
307 TimestampI *pT = &S.slave.scd.t[2]; // skip the first 2 timestamps
308 uint64_t *cf = &S.slave.scd.cf[2]; // skip the first 2 correction fields
309
310 pT[T4] = pRawMsg->ts; // save t4 (P2P)
311 cf[T2] = pHeader->correction_ns; // save correction field of the PDelay_Resp
312
313 // if the responder is a one-step clock, then...
314 if (!pHeader->flags.PTP_TWO_STEP) {
315 // no t2 and t3 will be involved with the calculations
316 pT[T3] = pT[T2] = zeroTs;
317 ptp_commence_p2p_correction(pHeader->sequenceID); // commence correction
318 } else {
319 ptp_extract_timestamps(&(pT[T2]), pRawMsg->data, 1); // retrieve t2 (P2P)
320 }
321
322 // dispatch PDELAY_RESP_RECVED event
324
325 // log correction field (if enabled)
326 CLILOG(S.logging.corr, "C [PDel_Resp]: %09lu\n", pHeader->correction_ns);
327
328 } else if (mt == PTP_MT_PDelay_Resp_Follow_Up) { // PDelay_Resp_Follow_Up processing
329 ptp_read_delay_resp_id_data(&delay_respID, pRawMsg->data);
330
331 // if sent to us as a response to our Delay_Req then continue processing
332 if (delay_respID.requestingSourceClockIdentity == S.hwoptions.clockIdentity &&
334
335 TimestampI *pT = &S.slave.scd.t[2]; // skip the first 2 timestamps
336 uint64_t *cf = &S.slave.scd.cf[2]; // skip the first 2 correction fields
337
338 ptp_extract_timestamps(&(pT[T3]), pRawMsg->data, 1); // retrieve t3 (P2P)
339 cf[T3] = pHeader->correction_ns; // retain correction field from the PDelay_Resp_Follow_Up
340
341 // commence correction
343
344 // dispatch PDELAY_RESP_FOLLOW_UP_RECVED event
346
347 // log correction field (if enabled)
348 CLILOG(S.logging.corr, "C [PDel_Resp_Follow_Up]: %09lu\n", pHeader->correction_ns);
349 }
350 }
351 }
352 }
353}
354
355// ------------------------
356
358 // initialize coarse threshold
360
361 // reset the slave module
363}
364
366 return;
367}
368
370 // disable slave module
371 S.slave.enabled = false;
372
373 // clear Sync cycle data
374 memset(&S.slave.scd, 0, sizeof(PtpSyncCycleData));
375 S.slave.prevSyncMa = zeroTs;
376
377 // reset messaging state
378 memset(&S.slave.messaging, 0, sizeof(PtpSlaveMessagingState));
379}
380
382 if (!S.slave.enabled) {
383 return;
384 }
385
386 // Delay_Req transmission
387 if (S.profile.logDelayReqPeriod != PTP_LOGPER_SYNCMATCHED) {
388 if (++S.slave.delReqTmr > S.slave.delReqTickPeriod) {
389 S.slave.delReqTmr = 0;
390
391 // check that our last Delay_Req has been responded
392 if (S.profile.logDelayReqPeriod != PTP_LOGPER_SYNCMATCHED) {
393 if (S.slave.messaging.delay_reqSequenceID != S.slave.messaging.lastRespondedDelReqId) {
394 CLILOG(S.logging.info, "(P)Del_Req #%d: no response received!\n", S.slave.messaging.delay_reqSequenceID);
395 PTP_IUEV(PTP_UEV_NETWORK_ERROR); // dispatch network error event
396 }
397
398 // transmit (P)Delay_Req message
400
401 // dispatch (P)DELAY_REQ_SENT message
402 PTP_IUEV((S.profile.delayMechanism == PTP_DM_E2E) ? PTP_UEV_DELAY_REQ_SENT : PTP_UEV_PDELAY_REQ_SENT);
403 }
404 }
405 }
406}
407
409 S.slave.enabled = true;
410
411 if (S.profile.logDelayReqPeriod != PTP_LOGPER_SYNCMATCHED) {
412 S.slave.delReqTickPeriod = ptp_logi2ms(S.profile.logDelayReqPeriod) / PTP_HEARTBEAT_TICKRATE_MS;
413 }
414}
415
417 S.slave.enabled = false;
418}
void ptp_compute_mean_path_delay_p2p(const TimestampI *pTs, const uint64_t *pCf, TimestampI *pMPD)
Definition: common.c:141
void ptp_compute_mean_path_delay_e2e(const TimestampI *pTs, const uint64_t *pCf, TimestampI *pMPD)
Definition: common.c:128
void ptp_send_delay_req_message()
Definition: common.c:39
This module defines messaging functions for both the slave and master modules.
@ PTP_UEV_DELAY_RESP_RECVED
A Delay_Resp had been received (slave)
Definition: event.h:50
@ PTP_UEV_DELAY_REQ_SENT
A Delay_Req had been sent (slave)
Definition: event.h:49
@ PTP_UEV_PDELAY_RESP_FOLLOW_UP_RECVED
A PDelay_Resp_Follow_Up had been received (master/slave)
Definition: event.h:56
@ PTP_UEV_PDELAY_REQ_SENT
A PDelay_Req had been sent (master/slave)
Definition: event.h:53
@ PTP_UEV_PDELAY_RESP_RECVED
A PDelay_Resp had been received (master/slave)
Definition: event.h:54
@ PTP_UEV_SYNC_RECVED
A Sync message has been received (slave)
Definition: event.h:44
@ PTP_UEV_FOLLOW_UP_RECVED
A Follow_Up message has been received (slave)
Definition: event.h:46
@ PTP_UEV_NETWORK_ERROR
Indication of lost messages or the absence of expected responses.
Definition: event.h:66
#define PTP_IUEV(uev)
Definition: event.h:77
#define PTP_SET_CLOCK(s, ns)
#define PTP_SET_ADDEND(addend)
#define CLILOG(en,...)
#define PTP_SERVO_RUN(d, pscd)
uint16_t ptp_logi2ms(int8_t logi)
Definition: format_utils.c:14
This module defines format conversion functions between network and host byte order and conversion fu...
void ptp_extract_timestamps(TimestampI *ts, void *pPayload, uint8_t n)
Definition: msg_utils.c:189
void ptp_read_delay_resp_id_data(PtpDelay_RespIdentification *pDRData, void *pPayload)
Definition: msg_utils.c:214
This module defines functions that deal with actual PTP messages; they can extract or insert headers,...
Core of the PTP implementation. Defines functions for message processing, clock tuning,...
In here reside a multitude of fundamental PTP-related constants and definitions.
#define PTP_HEARTBEAT_TICKRATE_MS
Heartbeat ticking period.
Definition: ptp_defs.h:69
#define PTP_CLOCK_TICK_FREQ_HZ
Rated clock tick frequency.
Definition: ptp_defs.h:62
#define PTP_ADDEND_CORR_PER_PPB_F
Addend/ppb ratio.
Definition: ptp_defs.h:64
#define PTP_COLOR_BYELLOW
Bright yellow.
Definition: ptp_defs.h:150
#define PTP_PORT_ID
PTP port ID on the device.
Definition: ptp_defs.h:73
#define PTP_DEFAULT_COARSE_TRIGGER_NS
Coarse correction kick-in threshold.
Definition: ptp_defs.h:85
#define PTP_COLOR_RESET
Reset colors.
Definition: ptp_defs.h:155
#define T2
#define T3
#define T4
#define T1
This module defines the fundamental PTP message and state machine type, flags, bitfields and the PTP ...
@ PTP_LOGPER_SYNCMATCHED
Messaging occurs whenever a Sync arrives.
Definition: ptp_types.h:281
@ SWaitFollowUp
Waiting for a Follow Up message.
Definition: ptp_types.h:255
@ SIdle
Idle.
Definition: ptp_types.h:254
PtpMessageType
PTP packet type enumeration.
Definition: ptp_types.h:29
@ PTP_MT_Delay_Resp
Delay Response.
Definition: ptp_types.h:35
@ PTP_MT_PDelay_Resp
Peer Delay Response.
Definition: ptp_types.h:33
@ PTP_MT_Sync
Sync.
Definition: ptp_types.h:30
@ PTP_MT_PDelay_Resp_Follow_Up
Peer Delay Response Follow Up.
Definition: ptp_types.h:36
@ PTP_MT_Follow_Up
Follow Up.
Definition: ptp_types.h:34
PtpDelayMechanism
PTP Delay mechanism enumeration.
Definition: ptp_types.h:136
@ PTP_DM_E2E
End-to-End Delay Mechanism.
Definition: ptp_types.h:137
@ PTP_DM_P2P
Peer-to-Peer Delay Mechanism.
Definition: ptp_types.h:138
void ptp_set_coarse_threshold(uint64_t ns)
This module features functions to tweak around the PTP engine's almost every property.
static void ptp_commence_p2p_correction(uint32_t pdelRespSeqId)
Definition: slave.c:187
void ptp_slave_destroy()
Definition: slave.c:365
void ptp_slave_init()
Definition: slave.c:357
#define MIN(a, b)
Definition: slave.c:21
void ptp_slave_process_message(RawPtpMessage *pRawMsg, PtpHeader *pHeader)
Definition: slave.c:200
void ptp_slave_enable()
Definition: slave.c:408
void ptp_slave_tick()
Definition: slave.c:381
void ptp_slave_reset()
Definition: slave.c:369
void ptp_slave_disable()
Definition: slave.c:416
static void ptp_commence_e2e_correction()
Definition: slave.c:158
static void ptp_perform_correction()
Definition: slave.c:38
This module implements the slave clock functionality.
void ptp_collect_stats(int64_t d)
Definition: stats.c:32
This is the statistics module that gathers data of the operating PTP-engine.
Identification carrying Delay_Resp message.
Definition: ptp_types.h:120
uint64_t requestingSourceClockIdentity
Requesting Source Clock Identity.
Definition: ptp_types.h:121
uint16_t requestingSourcePortIdentity
Requesting Source Port Identity.
Definition: ptp_types.h:122
bool PTP_TWO_STEP
Two Step.
Definition: ptp_types.h:59
PTP message header structure.
Definition: ptp_types.h:73
int8_t logMessagePeriod
Definition: ptp_types.h:114
uint8_t messageType
ID.
Definition: ptp_types.h:75
uint16_t sequenceID
Sequence ID.
Definition: ptp_types.h:108
PtpFlags flags
Flags.
Definition: ptp_types.h:92
uint64_t correction_ns
Correction nanoseconds.
Definition: ptp_types.h:95
Data to perform a full synchronization.
PtpSyncCycleData scd
Sync cycle data.
PTP slave messaging state structure.
Definition: ptp_types.h:387
PTP synchronization cycle data.
Raw PTP message structure.
Definition: ptp_types.h:162
TimestampI ts
Timestamp.
Definition: ptp_types.h:163
uint8_t data[(128)]
raw packet data
Definition: ptp_types.h:173
Timestamp (signed)
Definition: timeutils.h:29
int32_t nanosec
nanoseconds
Definition: timeutils.h:31
int64_t sec
seconds
Definition: timeutils.h:30
The entry point of the whole PTP-implementation. Calling reg_task_ptp() initializes the PTP-engine,...
void normTime(TimestampI *t)
Definition: timeutils.c:49
int64_t tsToTick(const TimestampI *ts, uint32_t tps)
Definition: timeutils.c:55
TimestampI * nsToTsI(TimestampI *r, int64_t ns)
Definition: timeutils.c:61
TimestampI * subTime(TimestampI *r, const TimestampI *a, const TimestampI *b)
Definition: timeutils.c:24
bool nonZeroI(const TimestampI *a)
Definition: timeutils.c:67
int64_t nsI(const TimestampI *t)
Definition: timeutils.c:44
This module defines storage classes for timestamps and operations on time values.