Concept for TimeSlave ====================== .. contents:: Table of Contents :depth: 3 :local: TimeSlave concept ------------------ Use Cases ~~~~~~~~~ TimeSlave is a standalone gPTP (IEEE 802.1AS) slave endpoint process that implements the low-level time synchronization protocol for the Eclipse SCORE time system. It is deployed as a separate process from the TimeDaemon to isolate real-time network I/O from the higher-level time validation and distribution logic. More precisely we can specify the following use cases for the TimeSlave: 1. Receiving gPTP Sync/FollowUp messages from a Time Master on the Ethernet network 2. Measuring peer delay via the IEEE 802.1AS PDelayReq/PDelayResp exchange 3. Optionally adjusting the PTP Hardware Clock (PHC) on the NIC 4. Publishing the resulting ``PtpTimeInfo`` to shared memory for consumption by the TimeDaemon The raw architectural diagram is represented below. .. raw:: html
.. uml:: _assets/timeslave_deployment.puml :alt: Raw architectural diagram .. raw:: html
Components decomposition ~~~~~~~~~~~~~~~~~~~~~~~~~ The design consists of several sw components: 1. `TimeSlave Application <#timeslave-application-sw-component>`_ 2. `GptpEngine <#gptpengine-sw-component>`_ 3. `FrameCodec <#framecodec-sw-component>`_ 4. `MessageParser <#messageparser-sw-component>`_ 5. `SyncStateMachine <#syncstatemachine-sw-component>`_ 6. `PeerDelayMeasurer <#peerdelaymeasurer-sw-component>`_ 7. `PhcAdjuster <#phcadjuster-sw-component>`_ 8. `libTSClient <#libtsclient-sw-component>`_ Class view ~~~~~~~~~~ Main classes and components are presented on this diagram: .. raw:: html
.. uml:: _assets/timeslave_class.puml :alt: Class View :width: 100% :align: center .. raw:: html
Data and control flow ~~~~~~~~~~~~~~~~~~~~~ The Data and Control flow are presented in the following diagram: .. raw:: html
.. uml:: _assets/timeslave_data_flow.puml :alt: Data and Control flow View .. raw:: html
On this view you could see several "workers" scopes: 1. RxThread scope 2. PdelayThread scope 3. Main thread (periodic publish) scope Each control flow is implemented with the dedicated thread and is independent from another ones. Control flows ^^^^^^^^^^^^^ RxThread scope '''''''''''''' This control flow is responsible for the: 1. receive raw gPTP Ethernet frames with hardware timestamps from the NIC via raw sockets 2. decode and parse the PTP messages (Sync, FollowUp, PdelayResp, PdelayRespFollowUp) 3. correlate Sync/FollowUp pairs and compute clock offset and neighborRateRatio 4. update the shared ``PtpTimeInfo`` snapshot under mutex protection PdelayThread scope ''''''''''''''''''' This control flow is responsible for the: 1. periodically transmit PDelayReq frames and capture hardware transmit timestamps 2. coordinate with the RxThread to receive PDelayResp and PDelayRespFollowUp messages 3. compute the peer delay using the IEEE 802.1AS formula: ``path_delay = ((t2 - t1) + (t4 - t3c)) / 2`` Main thread (periodic publish) scope '''''''''''''''''''''''''''''''''''''' This control flow is responsible for the: 1. periodically call ``GptpEngine::ReadPTPSnapshot()`` to get the latest time measurement 2. enrich the snapshot with the local clock timestamp from ``HighPrecisionLocalSteadyClock`` 3. publish to shared memory via ``GptpIpcPublisher::Publish()`` Data types or events ^^^^^^^^^^^^^^^^^^^^ There are several data types, which components are communicating to each other: PTPMessage '''''''''' ``PTPMessage`` is a union-based container for decoded gPTP messages including the hardware receive timestamp. It is produced by ``MessageParser`` and consumed by ``SyncStateMachine`` and ``PeerDelayMeasurer``. SyncResult '''''''''' ``SyncResult`` is produced by ``SyncStateMachine::OnFollowUp()`` and contains the computed master timestamp, clock offset, Sync/FollowUp data, and time jump flags (forward/backward). PDelayResult '''''''''''' ``PDelayResult`` is produced by ``PeerDelayMeasurer`` and contains the computed path delay in nanoseconds and a validity flag. PtpTimeInfo '''''''''''' ``PtpTimeInfo`` is the aggregated snapshot that combines PTP status flags, Sync/FollowUp data, peer delay data, and a local clock reference. This is the data published to shared memory for the TimeDaemon. SW Components decomposition ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TimeSlave Application SW component ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``TimeSlave Application`` component is the main entry point for the TimeSlave process. It extends ``score::mw::lifecycle::Application`` and is responsible for orchestrating the overall lifecycle of the GptpEngine and the IPC publisher. Component requirements '''''''''''''''''''''' The ``TimeSlave Application`` has the following requirements: - The ``TimeSlave Application`` shall implement the ``Initialize()`` method to create the ``GptpEngine`` with configured options, initialize the ``GptpIpcPublisher`` (creates the shared memory segment), and prepare the ``HighPrecisionLocalSteadyClock`` for local time reference - The ``TimeSlave Application`` shall implement the ``Run()`` method to start the GptpEngine, enter a periodic publish loop, and monitor the ``stop_token`` for graceful shutdown - The ``TimeSlave Application`` shall implement the ``Deinitialize()`` method to stop the GptpEngine threads and destroy the shared memory segment - The ``TimeSlave Application`` shall periodically read the latest ``PtpTimeInfo`` snapshot, enrich it with the local clock timestamp, and publish it via ``GptpIpcPublisher`` GptpEngine SW component ^^^^^^^^^^^^^^^^^^^^^^^^ The ``GptpEngine`` component is the core gPTP protocol engine. It manages two background threads (RxThread and PdelayThread) for network I/O and peer delay measurement, and exposes a thread-safe ``ReadPTPSnapshot()`` method for the main thread to read the latest time measurement. Component requirements '''''''''''''''''''''' The ``GptpEngine`` has the following requirements: - The ``GptpEngine`` shall manage an RxThread for receiving and parsing gPTP frames from raw Ethernet sockets - The ``GptpEngine`` shall manage a PdelayThread for periodic peer delay measurement - The ``GptpEngine`` shall provide a thread-safe ``ReadPTPSnapshot()`` method that returns the latest ``PtpTimeInfo`` - The ``GptpEngine`` shall support configurable parameters via ``GptpEngineOptions`` (interface name, PDelay interval, sync timeout, time jump thresholds, PHC configuration) - The ``GptpEngine`` shall support exchangeability of the raw socket implementation for different platforms (Linux, QNX) Class view '''''''''' The Class Diagram is presented below: .. raw:: html
.. uml:: _assets/gptp_engine_class.puml :alt: Class Diagram .. raw:: html
Threading model ''''''''''''''' The GptpEngine operates with two background threads. The threading model is represented below: .. raw:: html
.. uml:: _assets/gptp_threading.puml :alt: Threading Model .. raw:: html
Concurrency aspects ''''''''''''''''''' The ``GptpEngine`` uses the following synchronization mechanisms: - A ``std::mutex`` protects the ``latest_snapshot_`` field, shared between the RxThread (writer) and the main thread (reader via ``ReadPTPSnapshot()``) - The ``PeerDelayMeasurer`` uses its own ``std::mutex`` to synchronize between the PdelayThread (``SendRequest()``) and the RxThread (``OnResponse()``, ``OnResponseFollowUp()``) - The ``SyncStateMachine`` uses ``std::atomic`` for the timeout flag, which is read from the main thread and written from the RxThread FrameCodec SW component ^^^^^^^^^^^^^^^^^^^^^^^^^ The ``FrameCodec`` component handles raw Ethernet frame encoding and decoding for gPTP communication. Component requirements '''''''''''''''''''''' The ``FrameCodec`` has the following requirements: - The ``FrameCodec`` shall parse incoming Ethernet frames, extracting source/destination MAC addresses, handling 802.1Q VLAN tags, and validating the EtherType (``0x88F7``) - The ``FrameCodec`` shall construct outgoing Ethernet headers for PDelayReq frames using the standard PTP multicast destination MAC (``01:80:C2:00:00:0E``) MessageParser SW component ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``MessageParser`` component parses the PTP wire format (IEEE 1588-v2) from raw payload bytes. Component requirements '''''''''''''''''''''' The ``MessageParser`` has the following requirements: - The ``MessageParser`` shall validate the PTP header (version, domain, message length) - The ``MessageParser`` shall decode all relevant message types: Sync, FollowUp, PdelayReq, PdelayResp, PdelayRespFollowUp - The ``MessageParser`` shall use packed wire structures (``__attribute__((packed))``) for direct memory mapping of PTP messages SyncStateMachine SW component ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``SyncStateMachine`` component implements the two-step Sync/FollowUp correlation logic. It correlates incoming Sync and FollowUp messages by sequence ID, computes the clock offset and neighbor rate ratio, and detects time jumps. Component requirements '''''''''''''''''''''' The ``SyncStateMachine`` has the following requirements: - The ``SyncStateMachine`` shall store Sync messages and correlate them with subsequent FollowUp messages by sequence ID - The ``SyncStateMachine`` shall compute the clock offset: ``offset_ns = master_time - slave_receive_time - path_delay`` - The ``SyncStateMachine`` shall compute the ``neighborRateRatio`` from successive Sync intervals (master vs. slave clock progression) - The ``SyncStateMachine`` shall detect forward and backward time jumps against configurable thresholds - The ``SyncStateMachine`` shall provide thread-safe timeout detection via ``std::atomic``, set when no Sync is received within the configured timeout PeerDelayMeasurer SW component ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``PeerDelayMeasurer`` component implements the IEEE 802.1AS two-step peer delay measurement protocol. It manages the four timestamps (``t1``, ``t2``, ``t3c``, ``t4``) across two threads. Component requirements '''''''''''''''''''''' The ``PeerDelayMeasurer`` has the following requirements: - The ``PeerDelayMeasurer`` shall transmit PDelayReq frames and capture the hardware transmit timestamp (``t1``) - The ``PeerDelayMeasurer`` shall receive PDelayResp (providing ``t2``, ``t4``) and PDelayRespFollowUp (providing ``t3c``) messages - The ``PeerDelayMeasurer`` shall compute the peer delay using the IEEE 802.1AS formula: ``path_delay = ((t2 - t1) + (t4 - t3c)) / 2`` - The ``PeerDelayMeasurer`` shall provide thread-safe access to the ``PDelayResult`` via a mutex, as ``SendRequest()`` runs on the PdelayThread while response handlers are called from the RxThread PhcAdjuster SW component ^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``PhcAdjuster`` component synchronizes the PTP Hardware Clock (PHC) on the NIC. It applies step corrections for large offsets and frequency slew for smooth convergence of small offsets. Component requirements '''''''''''''''''''''' The ``PhcAdjuster`` has the following requirements: - The ``PhcAdjuster`` shall apply an immediate time step correction for offsets exceeding ``step_threshold_ns`` - The ``PhcAdjuster`` shall apply frequency slew (in ppb) for offsets below the step threshold - The ``PhcAdjuster`` shall support platform-specific implementations: ``clock_adjtime()`` on Linux, EMAC PTP ioctls on QNX - The ``PhcAdjuster`` shall be configurable via ``PhcConfig`` (device path, step threshold, enable/disable flag) libTSClient SW component ^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``libTSClient`` component is the shared memory IPC library that connects the TimeSlave process to the TimeDaemon process. It provides a lock-free, single-writer/multi-reader communication channel using a seqlock protocol over POSIX shared memory. The component provides two sub components: publisher and receiver to be deployed on the TimeSlave and TimeDaemon sides accordingly. Component requirements '''''''''''''''''''''' The ``libTSClient`` has the following requirements: - The ``libTSClient`` shall define a shared memory layout (``GptpIpcRegion``) with a magic number for validation, an atomic seqlock counter, and a ``PtpTimeInfo`` data payload - The ``libTSClient`` shall align the shared memory region to 64 bytes (cache line size) to prevent false sharing - The ``libTSClient`` shall provide a ``GptpIpcPublisher`` component that creates and manages the POSIX shared memory segment and writes ``PtpTimeInfo`` using the seqlock protocol - The ``libTSClient`` shall provide a ``GptpIpcReceiver`` component that opens the shared memory segment read-only and reads ``PtpTimeInfo`` with up to 20 seqlock retries - The ``libTSClient`` shall use the POSIX shared memory name ``/gptp_ptp_info`` by default Class view '''''''''' The Class Diagram is presented below: .. raw:: html
.. uml:: _assets/ipc_channel.puml :alt: Class Diagram .. raw:: html
Publish new data '''''''''''''''' When ``TimeSlave Application`` has a new ``PtpTimeInfo`` snapshot, it publishes to the shared memory via the seqlock protocol: 1. Increment ``seq`` (becomes odd — signals write in progress) 2. ``memcpy`` the data 3. Increment ``seq`` (becomes even — signals write complete) Receive data '''''''''''' From TimeDaemon side, the receiver reads from the shared memory using the seqlock protocol with bounded retry: 1. Read ``seq`` (must be even, otherwise retry) 2. ``memcpy`` the data 3. Read ``seq`` again (must match step 1, otherwise retry — torn read detected) 4. Return ``std::optional`` (empty if all 20 retries exhausted) The seqlock protocol workflow is presented in the following sequence diagram: .. raw:: html
.. uml:: _assets/ipc_sequence.puml :alt: Seqlock Protocol .. raw:: html
Platform support ~~~~~~~~~~~~~~~~~ TimeSlave supports two target platforms with platform-specific implementations selected at compile time via Bazel ``select()``: .. list-table:: Platform Implementations :header-rows: 1 :widths: 25 35 40 * - Component - Linux - QNX * - Raw Socket - ``AF_PACKET`` with ``SO_TIMESTAMPING`` - QNX raw-socket shim * - Network Identity - ``ioctl(SIOCGIFHWADDR)`` - QNX-specific MAC resolution * - PHC Adjuster - ``clock_adjtime()`` - EMAC PTP ioctls * - HighPrecisionLocalSteadyClock - ``std::chrono`` system clock - QTIME clock API The ``IRawSocket`` and ``INetworkIdentity`` interfaces provide the abstraction boundary. Platform-specific source files are organized under ``score/TimeSlave/code/gptp/platform/linux/`` and ``score/TimeSlave/code/gptp/platform/qnx/``. Instrumentation ~~~~~~~~~~~~~~~~ ProbeManager ^^^^^^^^^^^^ The ``ProbeManager`` is a singleton that records probe events at key processing points in the gPTP engine. Probe points include: - ``RxPacketReceived`` — Raw frame received from socket - ``SyncFrameParsed`` — Sync message successfully parsed - ``FollowUpProcessed`` — Offset computed from Sync/FollowUp pair - ``OffsetComputed`` — Final offset value available - ``PdelayReqSent`` — PDelayReq frame transmitted - ``PdelayCompleted`` — Peer delay measurement completed - ``PhcAdjusted`` — PHC adjustment applied The ``GPTP_PROBE()`` macro provides zero-overhead when probing is disabled. Recorder ^^^^^^^^^ Thread-safe CSV file writer that persists probe events and other diagnostic data. Each ``RecordEntry`` contains a timestamp, event type, offset, peer delay, sequence ID, and status flags. Variability ~~~~~~~~~~~ Configuration ^^^^^^^^^^^^^ The ``GptpEngineOptions`` struct provides all configurable parameters for the gPTP engine: .. list-table:: GptpEngine Configuration :header-rows: 1 :widths: 30 15 55 * - Parameter - Type - Description * - ``interface_name`` - string - Network interface for gPTP frames (e.g., ``eth0``) * - ``pdelay_interval_ms`` - uint32_t - Interval between PDelayReq transmissions * - ``sync_timeout_ms`` - uint32_t - Timeout for Sync message reception before declaring timeout state * - ``time_jump_forward_ns`` - int64_t - Threshold for forward time jump detection * - ``time_jump_backward_ns`` - int64_t - Threshold for backward time jump detection * - ``phc_config`` - PhcConfig - PHC device path, step threshold, and enable flag The ``PhcConfig`` struct additionally contains: .. list-table:: PhcAdjuster Configuration :header-rows: 1 :widths: 30 15 55 * - Parameter - Type - Description * - ``enabled`` - bool - Enable or disable PHC adjustment * - ``device_path`` - string - Path to the PHC device (e.g., ``/dev/ptp0``) * - ``step_threshold_ns`` - int64_t - Offset threshold above which a step correction is applied instead of frequency slew .. toctree:: :maxdepth: 2 :hidden: gptp_engine/index libTSClient/index