SNAA365 June 2024 LMK5B33216
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;
}