SNAA365 June   2024 LMK5B33216

 

  1.   1
  2.   Abstract
  3.   Trademarks
  4. 1Hardware Architecture
    1. 1.1 Clocking Scheme
    2. 1.2 FPGA Design
  5. 2syn1588® Synchronization Algorithm
    1. 2.1 PTP Time-of-Day Clock Adjustment Algorithm
  6. 3Test Setup
    1. 3.1 FMC Adapter Board
    2. 3.2 Compliance Test Setup
    3. 3.3 Compliance Test of Telecom Profile G.8275.1 - Full Timing Support
      1. 3.3.1 Transfer Characteristic
      2. 3.3.2 Absolute Time Error
      3. 3.3.3 Lock Time
    4. 3.4 Compliance Test of Telecom Profile G.8275.2 - Partial Timing Support
    5. 3.5 Compliance Test of Telecom Profile G.8262.1 - SyncE Transient
  7. 4PTP System Application
  8. 5Additional Development
  9. 6Conclusion
  10. 7References

PTP Time-of-Day Clock Adjustment Algorithm

When the PTP Stack is initialized in cold start mode, all of the hardware registers (both of the network synchronizer and the PTP Time-of-Day (ToD) hardware clock) must be uninitialized. After updating the PTP hardware clock registers with the respective startup value, the complete register set of the network synchronizer is written to initialize the device. The startup register initialization values can be generated in hex-file format using the TICS Pro software.

The PTP hardware Time-of-Day clock in steady state is adjusted solely by varying the network timing PTP reference input frequency. The PTP clock servo loop modifies the output frequency by programming the internal DCO in the LMK5XXXXXS1. The corresponding example code section of the syn1588® PTP Stack software is shown below. In the following example code, two functions have been implemented: getDrift and setDrift. The getDrift function reads the current value of the frequency adjustment from the network synchronizer while the setDrift function updates the frequency adjustment with a user-provided value.

The raw input data extracted from the time stamps is reformatted and scaled to ns/s using a scale factor derived from the TICS Pro software. The time information containing the PTP event messages is propagated through prefilters and sent to the phase interpolation (PI) servo which calculates a new frequency adjustment value. The new frequency value is passed to the function setDrift, which reads the current frequency offset from the network synchronizer, calculates a new drift value, rescales and reformats the drift accordingly, and updates the DPLL with both the new DCO adjustment value and whether to increment or decrement the DCO frequency.

static constexpr double magic_factor = 247390116249 / 100000.0;

ptp::scalednanoseconds ptp::ti::Osc::getDrift() const 
{
  auto values = readRegs(ptp::ti::Register::DPLL2_FB_NUM_STAT_4, 4);
  std::uint64_t value = 0;
  value |= values[0];
  value |= static_cast<std::uint64_t>(values[1]) << 8;
  value |= static_cast<std::uint64_t>(values[2]) << 16;
  value |= static_cast<std::uint64_t>(values[3]) << 24;

  // if MSB is 1 handle two's complement
  if (value > (1ull << 39)) value = -((value ^ 0xffffffffff) + 1);
  
  auto drift = std::chrono::duration<double>(value / magic_factor);
  return std::chrono::duration_cast<ptp::scalednanoseconds>(drift);
}

void ptp::ti::Osc::setDrift(ptp::scalednanoseconds drift)
{
    // Check for absolute limits
  if(drift > std::chrono::microseconds(400)) drift = std::chrono::microseconds(400);
  if(drift < std::chrono::microseconds(-400)) drift = std::chrono::microseconds(-400);

  ptp::scalednanoseconds drift_diff = drift - getDrift();
  if(drift_diff == std::chrono::seconds(0)) {return;}    // nothing to do

  // check for limit per change
  if(drift_diff > std::chrono::nanoseconds(40000)) drift_diff = std::chrono::nanoseconds(40000);
  if(drift_diff < std::chrono::nanoseconds(-40000)) drift_diff = std::chrono::nanoseconds(-40000);
  
  auto driff_f = std::chrono::duration_cast<std::chrono::duration<double, std::nano>>(drift_diff).count();

  auto value = static_cast<std::uint64_t>(magic_factor * std::abs(driff_f));
  auto values = std::vector<std::uint8_t>(5, 0x00);
  std::copy(reinterpret_cast<std::uint8_t *>(&value),reinterpret_cast<std::uint8_t *>(&value)+5, values.rbegin());

  writeRegs(ptp::ti::Register::DPLL2_FBFDEV_BY4, values);

  // if we have a positive drift, the PLL has to be slowed down 1 is decrement, 0 is increment speed
  std::uint32_t direction = drift_diff.count() > 0 ? 1: 0;
  writeReg(ptp::ti::Register::DPLL2_FBFDEVUPDATE, direction);

  m_currentDrift = m_currentDrift + drift_diff;
}