ZHCAE48 June   2024 LMK5B33216

 

  1.   1
  2.   摘要
  3.   商标
  4. 1硬件架构
    1. 1.1 时钟方案
    2. 1.2 FPGA 设计
  5. 2syn1588® 同步算法
    1. 2.1 PTP 计时时钟调整算法
  6. 3测试设置
    1. 3.1 FMC 适配器板
    2. 3.2 合规性测试设置
    3. 3.3 电信规范 G.8275.1 合规性测试 - 全时序支持
      1. 3.3.1 传输特性
      2. 3.3.2 绝对时间误差
      3. 3.3.3 锁定时间
    4. 3.4 电信规范 G.8275.2 合规性测试 - 部分时序支持
    5. 3.5 电信规范 G.8262.1 合规性测试 - SyncE 瞬态
  7. 4PTP 系统应用
  8. 5其他开发
  9. 6结语
  10. 7参考资料

PTP 计时时钟调整算法

当 PTP 协议栈在冷启动模式 下初始化时,所有硬件寄存器(网络同步器和 PTP 计时 (ToD) 硬件时钟)都必须保持未初始化状态。使用相应的启动值更新 PTP 硬件时钟寄存器后,将写入网络同步器的完整寄存器集以初始化器件。可以使用 TICS Pro 软件,以十六进制文件格式生成启动寄存器初始化值。

仅通过改变网络时序 PTP 基准输入频率,即可调节处于稳定状态的 PTP 硬件计时时钟。PTP 时钟伺服环路通过在 LMK5XXXXXS1 中对内部 DCO 进行编程来修改输出频率。syn1588® PTP 协议栈软件的相应示例代码段如下所示。在下面的示例代码中,实现了两个函数:getDriftsetDriftgetDrift 函数从网络同步器读取频率调整的当前值,而 setDrift 函数使用用户提供的值更新频率调整。

从时间戳中提取的原始输入数据会重新进行格式化,并使用源自 TICS Pro 软件的比例因子调整为 ns/s。包含 PTP 事件报文的时间信息通过预滤波器传播,并发送至相位内插 (PI) 伺服器,由其计算新的频率调整值。新的频率值传递给函数 setDrift,后者从网络同步器读取当前频率偏移,计算新的漂移值,相应地重新调整漂移的大小和格式,并使用新的 DCO 调整值以及是递增还是递减 DCO 频率来更新 DPLL。

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;
}