Skip to content
HN On Hacker News ↗

Stratum 1 PTP Grandmaster: CM4 + SR1723U10 (Part 1)

▲ 44 points 11 comments by malcolmfrazier 1w ago HN discussion ↗

Pangram verdict · v3.3

We believe this text is mainly AI, with some human-written content.

88 %

AI likelihood · overall

AI
2% human-written 98% AI-generated
SEGMENTS · HUMAN 0 of 1
SEGMENTS · AI 1 of 1
WORD COUNT 1,380
PEAK AI % 89% · §1
Analyzed
Aug 13
backend: pangram/v3.3
Segments scanned
1 windows
avg 1380 words each
Distribution
2 / 98%
human / AI fraction
Verdict
AI
Pangram v3.3

Article text · 1,380 words · 1 segments analyzed

Human AI-generated
§1 AI · 89%

I work with industrial infrastructure that requires nanosecond-accurate time synchronization. The commercial GPS-disciplined PTP grandmaster clocks that solve this problem run from several thousand dollars on the low end, and considerably more with options and support. I had CM4s and CM4 IO boards already sitting in the lab. A TimeHAT with an OCP M.2 GNSS module would have been the cleaner path at around $400, but that is steep when the goal is learning how PTP actually works, not deploying production infrastructure. This build came in at around $103 in new parts.This post documents what I built, what broke, what I had to fix, and the results I measured.What is PTP and why does it matterTime precision is a spectrum:UnitWhat it isExample Uses1 second (s)The tick of a clockHuman scheduling, cron jobs1 millisecond (ms)1 second split into 1,000 piecesWeb browsing, video streaming, log timestamps1 microsecond (µs)1 second split into 1,000,000 piecesHigh-frequency trading, audio/video sync, distributed databases1 nanosecond (ns)1 second split into 1,000,000,000 piecesAerospace instrumentation, industrial automation, scientific data acquisition, this buildNTP is good enough for web servers, databases, authentication systems, and anything where you need to know when something happened but don’t need to coordinate hardware events across machines. Over a LAN with a good reference, NTP can reach low microseconds. Over the WAN it typically lands in the low milliseconds. The ceiling is the network path jitter between client and server, every variable delay adds noise to the offset estimate.PTP (Precision Time Protocol, IEEE 1588) handles the nanosecond tier. With hardware timestamping at the Ethernet PHY layer, PTP can synchronize clocks to within tens of nanoseconds on a local network. The grandmaster clock is the root time source. It takes GPS-disciplined time and distributes it to clients via the PTP protocol.A nanosecond is the time it takes light to travel about 30 centimeters (roughly 1 foot). After this build, the clocks on my homelab nodes were off from GPS truth by the time it takes light to travel across a room.HardwareComponentNotesCostRaspberry Pi CM4 (8GB/32GB eMMC)Already ownedN/ACM4 IO BoardAlready ownedN/ASR1723U10 GPS moduleu-blox M10 based~$15JEFA Tech U.FL to SMA pigtailFor antenna connection~$8Bingfu GPS antennaActive, SMA~$12MOOKEERF SMA cablesVarious lengths~$10Waveshare CM4 IO Board case1U aluminum~$25ELEGOO Dupont jumper wire kitFor wiring~$8M2.5 standoff kitMounting hardware~$812V 2A power supplyFor IO board~$12CR2032 batteryFor onboard RTC~$5Total (new parts)~$103The CM4 and IO board were already on hand.This build vs the TimeHAT path:This BuildTimeHAT PathComputeCM4 + IO Board (owned)Raspberry Pi 5 (~$80)GPS integrationSR1723U10 + wiring (~$23)TimeHAT + OCP M.2 GNSS module (~$395)AntennaBingfu + pigtail (~$20)Same antenna works (~$20)OscillatorStandard crystalTCXO (temperature compensated)New spend~$103~$475+The oscillator difference matters. The TimeHAT includes a TCXO, which compensates for temperature-induced frequency drift. During GPS holdover, a TCXO holds time significantly more accurately than a standard crystal because its frequency stays stable as the board heats up or cools down. The CM4 uses a standard crystal with no temperature compensation. This build held 15ms of drift over 10 hours of holdover, which was acceptable for this use case. In a production environment where holdover accuracy is critical, the TCXO is worth the cost difference.The TimeHAT also uses an Intel i226 NIC rather than relying on the CM4’s onboard BCM54210PE, uses proper SMA connectors, and requires no jumper wires. If you are starting from zero hardware, that path is easier. If you have a CM4 already and want to understand what is happening at the PHY level, this build gets you there for a fraction of the cost.The key hardware fact: The CM4 uses the BCM54210PE Ethernet PHY which has full IEEE 1588v2 hardware timestamping support. This is what makes sub-microsecond PTP possible on the CM4. The BCM54210PE timestamps packets right at the wire inside the PHY, before the packet touches the kernel network stack. This eliminates the jitter that comes from kernel scheduling and interrupt handling.Clients:Turing Pi 2 cluster board (RTL8370MB-CG+ switch)RK1 compute module (Rockchip RK3588, Ubuntu 22.04)CM4 (Ubuntu 24.04), the k8s1 nodeThe Turing Pi 2 has two external RJ45 ports. Both ports are bridged into the same switch fabric as the four node slots. The grandmaster is plugged into ge1 (the second RJ45 port). The RK1 and CM4 client nodes are on the internal node slots. All five are on the same RTL8370MB-CG+ switch fabric, which means PTP packets between the grandmaster and clients pass through a single switch hop with no routing involved.Why Ubuntu 24.04The CM4 was originally running Ubuntu 22.04 with kernel 5.15.0-1093-raspi. The BCM54210PE PTP support was added to the Raspberry Pi kernel tree in 2022 and subsequently upstreamed to mainline Linux, but Ubuntu 22.04’s raspi kernel did not include it. The result was no PHC device and no hardware timestamping:$ ethtool -T eth0 Time stamping parameters for eth0: Capabilities: software-transmit software-receive software-system-clock PTP Hardware Clock: none $ ls /sys/class/ptp/ (empty) Without /dev/ptp0, SatPulse has no hardware clock to discipline. There is no path from GPS to network time.There was also a secondary issue: Ubuntu 22.04 had console=ttyAMA0,115200 in cmdline.txt, attaching the kernel console to the same UART the GPS module uses. This caused continuous input overruns in dmesg and garbage output from the GPS port.Upgrading to Ubuntu 24.04 with kernel 6.8.0-raspi fixed both problems:$ ethtool -T eth0 Time stamping parameters for eth0: Capabilities: hardware-transmit hardware-receive hardware-raw-clock PTP Hardware Clock: 0 $ ls /sys/class/ptp/ ptp0 WiringThe SR1723U10 connects to the CM4 IO Board via five Dupont wires. Four carry power and serial UART. The fifth carries the PPS signal to the J2 header.SR1723U10 pinColorCM4 IO BoardVCCRed40-pin pin 1 (3.3V)GNDBrown40-pin pin 6TXOrange40-pin pin 10 (RXD0)RXYellow40-pin pin 8 (TXD0)PPSGreenJ2 header pin 9 (SYNC_OUT)The J2 header is the small 10-pin header on the CM4 IO Board near the Ethernet jack. Pin 9 is labeled SYNC_OUT and connects to the BCM54210PE PHY’s external timestamp input. This is how the GPS pulse-per-second signal gets into the hardware clock.OS setupAdd to /boot/firmware/config.txt:dtoverlay=disable-bt enable_uart=1 dtoverlay=i2c-rtc,pcf85063a,i2c_csi_dsi The first two lines disable Bluetooth and connect /dev/ttyAMA0 to the GPIO header so the GPS module can communicate with the CM4. The third line enables the onboard PCF85063A real-time clock on the correct I2C bus for the CM4 IO Board.Remove the serial console from /boot/firmware/cmdline.txt:# Remove this portion from cmdline.txt: console=ttyAMA0,115200 Disable the Bluetooth service. The dtoverlay=disable-bt overlay disconnects Bluetooth from the UART hardware, but the hciuart service will still try to initialize and may interfere:sudo systemctl disable hciuart After reboot verify the GPS module is outputting NMEA at 38400 baud:(stty 38400 -echo -icrnl; cat) </dev/ttyAMA0 | head -10 You should see lines starting with $GNRMC, $GNGGA, and similar. The SR1723U10 defaults to 38400 baud, not the 9600 that many guides assume.Software stackThe stack is SatPulse + ptp4l + chrony. SatPulse is a daemon written by jclark that ties the GPS module, the PHC, ptp4l, and chrony together. It handles GPS configuration via UBX protocol, reads PPS timestamps from the PHC, disciplines the PHC frequency, and feeds samples to chrony via a SOCK refclock.Install SatPulse from the GitHub releases page (arm64 .deb):wget https://github.com/jclark/satpulse/releases/download/v0.3.0/satpulse_0.3.0_arm64.deb sudo dpkg -i satpulse_0.3.0_arm64.deb Configure SatPulse at /etc/satpulse.toml:[phc] interface = "eth0" [serial] speed = 38400 [gps] config = true [ntp] sock.path = "/var/run/chrony.satpulse.sock" [ptp] ptp4l.udsAddress = "/var/run/ptp4l" Install linuxptp and configure ptp4l as grandmaster at /etc/linuxptp/ptp4l.conf:[global] masterOnly 1 tx_timestamp_timeout 100 ptp_minor_version 0 [eth0] Copy the SatPulse ptp4l service file:sudo cp /usr/share/doc/satpulse/ptp4l.service /etc/systemd/system/ptp4l.service sudo systemctl daemon-reload sudo systemctl enable --now ptp4l Configure chrony at /etc/chrony/conf.d/satpulse.conf:refclock SOCK /var/run/chrony.satpulse.sock poll 2 filter 4 refid GNSS Add an allow directive to /etc/chrony/chrony.conf so clients can use the grandmaster as an NTP server:allow 192.168.0.0/24 log tracking measurements statistics Restart chrony:sudo systemctl restart chrony Enable and start SatPulse:sudo systemctl enable --now satpulse@ttyAMA0 Bugs and gotchasThis is where it got interesting.The BCM54210PE SYNC_OUT pin bugEvery time the system boots, the BCM54210PE driver initializes the SYNC_OUT pin as output (1 0) instead of input (0 1). SatPulse tries to reconfigure it but fails silently. The result is no PTP hardware clock external timestamps being received and a completely non-functional grandmaster.Diagnose it:cat /sys/class/ptp/ptp0/pins/SYNC_OUT # "1 0" means the pin is stuck in output mode sudo satpulsetool sdp -i --pin 0 eth0 # "no timestamps received" means the PHC is not getting PPS Fix it by forcing the pin into the correct state before SatPulse starts. Create /etc/systemd/system/satpulse@.service.d/override.conf:[Unit] After=chrony.service ptp4l.service Requires=chrony.service [Service] ExecStartPre=/bin/sh -c 'sleep 5 && echo 1 0 > /sys/class/ptp/ptp0/pins/SYNC_OUT && sleep 1 && echo 0 1 > /sys/class/ptp/ptp0/pins/SYNC_OUT'