PTP (IEEE 1588) 정밀 시각 동기화

PTP(Precision Time Protocol, IEEE 1588)는 이더넷 네트워크 내에서 나노초 수준의 시각 동기화를 달성하기 위한 표준 프로토콜입니다. TSN(Time-Sensitive Networking), 산업 자동화, 5G 프론트홀, 방송 미디어, 금융 거래 시스템에 핵심으로 쓰입니다. 리눅스 커널의 PTP 서브시스템(drivers/ptp/)은 NIC의 하드웨어 클럭(PHC)을 ptp_clock으로 노출하고, ptp4l/phc2sys 유저스페이스 툴이 클럭 계층 구성·보정·동기화를 담당합니다. 이 문서는 PTP 프로토콜·유저스페이스 도구·PHC 노출 API를 중심으로 다루며, NIC 드라이버의 하드웨어 타임스탬프 구현(SIOCSHWTSTAMP, SO_TIMESTAMPING, ndo_*)은 하드웨어 타임스탬핑 문서를 참조합니다.

전제 조건: 이더넷과 ktime / Clock 문서를 먼저 읽으세요. PTP는 네트워크와 하드웨어 타이밍의 교차점입니다.
일상 비유: PTP는 정밀 전자 시계의 라디오 시각 동기화와 같습니다. 표준 시각 신호를 방송하는 Grandmaster(방송국)가 있고, 수신기들이 전파 지연 시간을 측정·보정하여 1초 이내가 아닌 나노초 이내로 시각을 맞춥니다.

핵심 요약

  • Grandmaster Clock — GPS나 원자 시계로부터 정확한 시각을 제공하는 최상위 클럭입니다.
  • Boundary Clock (BC) — 업스트림 포트는 슬레이브로 동기화하고, 다운스트림 포트로 시각을 재분배합니다. 네트워크 스위치에 구현됩니다.
  • Ordinary Clock (OC) — 단일 포트로 GM에 동기화되는 일반 호스트입니다.
  • PHC (PTP Hardware Clock) — NIC에 내장된 하드웨어 클럭 카운터로, TX/RX 타임스탬프를 나노초 해상도로 기록합니다.
  • ptp4l — PTP 프로토콜 상태 기계를 구현하는 유저스페이스 데몬입니다. PHC를 GM에 동기화합니다.
  • phc2sys — PHC와 시스템 클럭(CLOCK_REALTIME) 간 오프셋을 보정하는 데몬입니다.
  • gPTP (802.1AS) — IEEE 1588을 TSN 브리지에 맞게 프로파일링한 버전으로, 자동차(AUTOSAR) 및 오디오/비디오 브리징에 사용됩니다.
  • BMCA (Best Master Clock Algorithm) — 네트워크에서 가장 좋은 클럭 소스를 자동으로 선출하는 알고리즘입니다.

단계별 이해

  1. 하드웨어 요구사항 확인 — NIC가 하드웨어 타임스탬핑을 지원하는지(ethtool -T eth0) 확인합니다. 지원하지 않으면 소프트웨어 타임스탬프로 마이크로초 수준만 가능합니다.
  2. 클럭 계층 설계 — 네트워크 토폴로지에서 GM, BC, OC의 위치를 결정합니다.
  3. ptp4l로 PHC 동기화 — ptp4l -i eth0 -m으로 PHC를 GM에 동기화합니다.
  4. phc2sys로 시스템 클럭 동기화 — phc2sys -s eth0 -c CLOCK_REALTIME -m으로 CLOCK_REALTIME을 PHC에 동기화합니다.
  5. 정밀도 측정 — ptp4l의 master offset 로그로 나노초 오프셋과 주파수 보정량을 모니터링합니다.

IEEE 1588 프로토콜 개요

IEEE 1588은 2002년 제정(v1), 2008년 개정(v2), 2019년 확장(v2.1)된 네트워크 시각 동기화 표준입니다. 핵심 메커니즘은 지연 측정 + 오프셋 보정입니다.

동기화 메커니즘 (Delay Request-Response)

Grandmaster (GM) Slave (OC) ① Sync t1 ● ● t2 ② Follow_Up (t1 정밀값 포함, 2-step) ③ Delay_Req t3 ● ● t4 ④ Delay_Resp (t4 전달) 오프셋 = ((t2−t1) − (t4−t3)) / 2 지연 = ((t2−t1) + (t4−t3)) / 2

하드웨어 타임스탬핑을 사용하면 t1~t4를 NIC 레벨에서 기록하므로 소프트웨어 지터(OS 스케줄링 지연)가 제거됩니다. 일반 NTP의 마이크로초 정확도 대비 수십 나노초 정확도가 가능합니다.

클럭 유형과 계층

Grandmaster Clock GPS / 원자 시계 기반 (stratum 0) Boundary Clock 1 upstream slave + downstream master Boundary Clock 2 upstream slave + downstream master OC (Slave) End Device OC (Slave) End Device OC (Slave) End Device OC (Slave) End Device
클럭 유형설명포트 수
Grandmaster Clock (GM)GPS/원자 시계 기반 최상위 시각 소스1 (master)
Ordinary Clock (OC)단일 PTP 포트, 슬레이브 또는 마스터로 동작1
Boundary Clock (BC)업스트림 슬레이브 + 다운스트림 마스터(들)2+
Transparent Clock (TC)체류 시간(residence time)을 보정하는 스위치다수
End-to-End TC (E2ETC)전체 경로 지연 측정 지원 TC다수

리눅스 PTP 서브시스템

리눅스 커널의 PTP 서브시스템(drivers/ptp/ptp_clock.c)은 NIC 드라이버가 PHC(PTP Hardware Clock)를 /dev/ptp0와 같은 문자 디바이스로 노출하는 표준 인터페이스를 제공합니다.

ptp_clock_ops 구조체

/* include/linux/ptp_clock_kernel.h */
struct ptp_clock_ops {
    /* 필수 */
    int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);
    int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
    int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
    int (*settime64)(struct ptp_clock_info *ptp,
                     const struct timespec64 *ts);

    /* 선택 — 하드웨어 타임스탬프 큐 */
    int (*getcrosststamp)(struct ptp_clock_info *ptp,
                          struct system_device_crosststamp *cts);

    /* Pulse Per Second / 외부 타임스탬프 / 주기적 출력 */
    int (*enable)(struct ptp_clock_info *ptp,
                  struct ptp_clock_request *request, int on);
};

ptp_clock_info 선언

static struct ptp_clock_info my_nic_ptp_info = {
    .owner      = THIS_MODULE,
    .name       = "my_nic PHC",
    .max_adj    = 500000,   /* 최대 주파수 보정량 (ppb) */
    .n_ext_ts   = 0,        /* 외부 타임스탬프 입력 수 */
    .n_per_out  = 0,        /* 주기적 출력 수 */
    .n_pins     = 0,        /* 재구성 가능한 핀 수 */
    .pps        = 1,        /* PPS (Pulse Per Second) 지원 */
    .adjfine    = my_nic_ptp_adjfine,
    .adjtime    = my_nic_ptp_adjtime,
    .gettime64  = my_nic_ptp_gettime,
    .settime64  = my_nic_ptp_settime,
    .enable     = my_nic_ptp_enable,
    .getcrosststamp = my_nic_ptp_getcrosststamp,
};

핵심 ops 구현

static int my_nic_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
    struct my_nic_priv *priv = container_of(ptp, struct my_nic_priv, ptp_info);
    s64 adj;
    u32 diff, incval;

    /* scaled_ppm은 65536 * ppm 단위 */
    adj = (s64)scaled_ppm * MY_NIC_CLK_PERIOD_NS;
    adj = div_s64(adj, 65536);

    incval = MY_NIC_CLK_BASE_INCVAL + (s32)adj;
    iowrite32(incval, priv->base + MY_NIC_REG_TIMADJ);
    return 0;
}

static int my_nic_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
    struct my_nic_priv *priv = container_of(ptp, struct my_nic_priv, ptp_info);
    unsigned long flags;

    spin_lock_irqsave(&priv->ptp_lock, flags);
    iowrite32((u32)(delta >> 32), priv->base + MY_NIC_REG_TIMEADJ_H);
    iowrite32((u32)(delta & 0xFFFFFFFF), priv->base + MY_NIC_REG_TIMEADJ_L);
    iowrite32(MY_NIC_CMD_ADJTIME, priv->base + MY_NIC_REG_CMD);
    spin_unlock_irqrestore(&priv->ptp_lock, flags);
    return 0;
}

static int my_nic_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
    struct my_nic_priv *priv = container_of(ptp, struct my_nic_priv, ptp_info);
    unsigned long flags;
    u64 ns;

    spin_lock_irqsave(&priv->ptp_lock, flags);
    /* 래치 명령으로 현재 시각 캡처 */
    iowrite32(MY_NIC_CMD_LATCH, priv->base + MY_NIC_REG_CMD);
    ns  = (u64)ioread32(priv->base + MY_NIC_REG_TIME_H) << 32;
    ns |= ioread32(priv->base + MY_NIC_REG_TIME_L);
    spin_unlock_irqrestore(&priv->ptp_lock, flags);

    *ts = ns_to_timespec64(ns);
    return 0;
}

PHC 등록과 해제

/* probe에서 */
priv->ptp_clock = ptp_clock_register(&priv->ptp_info, &pdev->dev);
if (IS_ERR(priv->ptp_clock)) {
    dev_err(&pdev->dev, "ptp_clock_register failed\n");
    priv->ptp_clock = NULL;
}

/* remove에서 */
if (priv->ptp_clock)
    ptp_clock_unregister(priv->ptp_clock);

linuxptp 유저스페이스 도구

ptp4l — PTP 프로토콜 데몬

ptp4l은 PTP 프로토콜 상태 기계(BMCA, 메시지 교환)를 구현하고 PHC를 GM에 동기화합니다.

# 기본 실행 (하드웨어 타임스탬프, eth0)
ptp4l -i eth0 -m

# 설정 파일 기반 (권장)
ptp4l -f /etc/linuxptp/ptp4l.conf -m

# 소프트웨어 타임스탬프 (HW 미지원 시)
ptp4l -i eth0 -m -S

# Slave only 모드 (GM이 될 수 없는 장치)
ptp4l -i eth0 -m -s
# /etc/linuxptp/ptp4l.conf
[global]
tx_timestamp_timeout  10
logMinDelayReqInterval -6
logSyncInterval        -4
domainNumber           0
slaveOnly              1
time_stamping          hardware
transportSpecific      0x0

[eth0]

phc2sys — PHC↔시스템 클럭 동기화

# PHC → CLOCK_REALTIME 동기화
phc2sys -s eth0 -c CLOCK_REALTIME -m -O 0

# 또는 PHC 인덱스로 직접 지정
phc2sys -s /dev/ptp0 -c CLOCK_REALTIME -m

# TAI 시각 오프셋 설정 (GPS 기반 GM에서 TAI↔UTC 차이)
phc2sys -s eth0 -c CLOCK_REALTIME -m -O -37

# 시스템 클럭 → PHC (반대 방향, Grandmaster 역할)
phc2sys -s CLOCK_REALTIME -c eth0 -m

pmc — PTP Management Client

# 포트 상태 확인
pmc -u -b 0 'GET PORT_DATA_SET'

# 현재 클럭 정보
pmc -u -b 0 'GET CURRENT_DATA_SET'

# GM 정보
pmc -u -b 0 'GET PARENT_DATA_SET'

# 시각 프로퍼티 (UTC 오프셋, 윤초 등)
pmc -u -b 0 'GET TIME_PROPERTIES_DATA_SET'

GPS Grandmaster 설정 (ts2phc)

ts2phc는 GPS PPS 신호를 사용하여 PHC를 GPS 시각에 동기화하고 Grandmaster 역할을 수행합니다.

# GPS PPS를 /dev/ptp0에 동기화
ts2phc -f /etc/linuxptp/ts2phc.conf -m

# ts2phc + ptp4l 동시 운용 (GM 구성)
ts2phc -f /etc/ts2phc.conf -m &
ptp4l -f /etc/ptp4l-master.conf -m &
# /etc/linuxptp/ts2phc.conf
[global]
use_syslog      1
verbose         1
time_stamp_mode=L

[eth0]
ts2phc.master   1   # 이 포트가 GM

[/dev/ptp_pps0]
ts2phc.master   0   # GPS PPS 입력

PTP 프로파일 변종

프로파일표준주요 용도정밀도
Default ProfileIEEE 1588-2019일반 네트워크~100ns
gPTP (802.1AS)IEEE 802.1AS-2020TSN, 자동차, AV 브리징<1μs
ITU-T G.8275.1ITU-T G.8275통신 네트워크 (5G 프론트홀)±30ns
ITU-T G.8275.2ITU-T G.8275부분 타임스탬프 네트워크±1.5μs
SMPTE ST 2059-2SMPTE방송 미디어방송 동기 수준

커널 내 PTP 클럭 API

커널 드라이버에서 PHC 시각을 직접 읽거나 비교할 때 사용합니다.

#include <linux/ptp_clock_kernel.h>

/* PHC 인덱스로 ptp_clock 획득 */
struct ptp_clock *ptp_clock;
ptp_clock = ptp_clock_get(phc_index);

/* 시스템 클럭과 PHC의 교차 타임스탬프 */
struct system_device_crosststamp cts;
ret = get_device_system_crosststamp(ptp_getcrosststamp, ptp_info,
                                    NULL, &cts);
/* cts.device: PHC 타임스탬프, cts.sys_realtime: 동시 CLOCK_REALTIME */

동기화 품질 검증

PTP 동기화 품질은 주로 master offset과 path delay의 두 지표로 평가합니다. ptp4l의 로그에서 master offset은 로컬 PHC와 Grandmaster 사이의 시각 차이(나노초)를 나타내고, freq는 발진기 주파수 보정량(ppb)을 의미합니다. 안정적인 하드웨어 타임스탬핑 환경에서는 offset이 수십~수백 나노초 범위를 유지합니다. 수 마이크로초를 초과하거나 진동이 크면 네트워크 체류 시간 변동(지터) 또는 PHC 발진기 품질 문제를 의심합니다.

# ptp4l 실시간 로그에서 오프셋 모니터링
# master offset: GM과의 오프셋(ns), s2: 2step sync
# freq: 주파수 보정량(ppb), path delay: 경로 지연(ns)
# ptp4l[12345.678]: master offset        -42 s2 freq   -1543 path delay    2312

# PHC 시각 직접 확인
phc_ctl /dev/ptp0 get

# PHC와 CLOCK_REALTIME 오프셋
phc_ctl /dev/ptp0 cmp

# 나노초 오프셋 히스토그램 (long-term 통계)
ts2phc -m 2>&1 | grep offset | awk '{print $NF}' | sort -n | uniq -c

TSN과 PTP 통합

IEEE 802.1Qbv(Time-Aware Shaper), 802.1Qbu(Frame Preemption) 등 TSN 표준은 PTP로 정의된 공통 시각을 사용하여 패킷 전송 창(transmission window)을 열고 닫습니다.

# TSN TAS (Time-Aware Shaper) 설정 예 (tc taprio)
tc qdisc replace dev eth0 parent root handle 100 taprio \
   num_tc 4 \
   map 0 1 2 3 0 0 0 0 \
   queues 1@0 1@1 1@2 1@3 \
   base-time 1000000000 \
   sched-entry S 0x8 200000 \   # 큐 3 개방 200μs
   sched-entry S 0x4 300000 \   # 큐 2 개방 300μs
   sched-entry S 0xF 500000 \   # 모든 큐 개방 500μs
   clockid CLOCK_TAI            # TAI 기반 스케줄

One-Step 타임스탬핑

2-step PTP는 Sync 후 Follow_Up을 별도 패킷으로 전송하여 t1을 전달합니다. One-step 타임스탬핑은 NIC 하드웨어가 Sync 패킷이 전선에 나가는 순간 패킷 내부에 직접 타임스탬프를 기록하므로 Follow_Up이 필요 없습니다. 패킷 수와 지터가 절반으로 줄어듭니다.

항목2-StepOne-Step
Sync 패킷 이후Follow_Up 별도 전송Follow_Up 불필요
t1 기록 위치Follow_Up 페이로드Sync 패킷 originTimestamp 필드 (NIC가 직접 기록)
패킷 수많음적음
하드웨어 요구사항TX 타임스탬프 읽기TX 패킷 내용 수정 능력 (egress edit)
Linux 지원표준 HW TSHWTSTAMP_TX_ONESTEP_SYNC
/* One-step TX 타임스탬프 모드 설정 */
struct hwtstamp_config cfg = {
    .tx_type   = HWTSTAMP_TX_ONESTEP_SYNC,  /* one-step */
    .rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT,
};

struct ifreq ifr;
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
ifr.ifr_data = (char *)&cfg;
ioctl(sock, SIOCSHWTSTAMP, &ifr);
ptp4l 설정: /etc/ptp4l.conf에서 tx_timestamp_timeout 1을 설정하고 NIC가 one-step을 지원하는 경우 one_step_sync 1을 활성화합니다.

드라이버가 Sync 패킷의 correctionField를 직접 수정하는 구현(egress edit)과 TX/RX 타임스탬프 전체 경로는 하드웨어 타임스탬핑 문서의 커널 구현 파트를 참조합니다.

IEEE 1588-2019 (v2.1) 주요 개선

2019년 발표된 v2.1(IEEE 1588-2019)은 v2(2008) 대비 아래 기능을 추가했습니다.

기능설명
CMLDS (Common Mean Link Delay Service)여러 PTP 인스턴스가 동일 링크의 지연 측정값을 공유
Enhanced Accuracy TLV클럭 정확도를 서브-나노초 단위로 전달
Port State Decision (PSDN)경로 선택 알고리즘 개선
Alternate Master결함 내성을 위한 다중 마스터 동시 추적
Signaling TLV 확장로그 주기·임계값 동적 협상
# ptp4l에서 v2.1 CMLDS 활성화 (linuxptp 3.x+)
# /etc/ptp4l.conf
[global]
dataset_comparison      ieee1588  # v2.1 데이터셋 비교 알고리즘
G.8275.defaultDS.localPriority 128
boundary_clock_jbod     1         # Boundary Clock JBOD 모드
cmlds                   1         # CMLDS 활성화
관련 페이지: 하드웨어 타임스탬핑 — NIC 레벨 타임스탬핑 구현 상세, 이더넷 — 네트워크 드라이버 기초, ktime / Clock — 커널 시각 서브시스템

문제 해결

증상원인해결
오프셋이 수백 μs 이상소프트웨어 타임스탬핑 사용ethtool -T eth0으로 HW 타임스탬핑 확인
ptp4l LISTENING 상태 고착PTP 패킷이 방화벽에 차단UDP 319/320 포트 허용
phc2sys 오프셋 발산TAI/UTC 오프셋 불일치-O 옵션으로 TAI-UTC 차이(37초) 설정
TX 타임스탬프 타임아웃드라이버 TX TS 지원 불완전tx_timestamp_timeout 값 증가
gPTP 동기화 실패802.1AS 요구 사항 미충족transportSpecific 0x1, peer delay 활성화