Reader sent Geerling's CM4 PTP write-up, which shows two CM4s syncing to 10-15ns.
Our headline overclaimed ('impossible on a Pi 4') and our mechanism was wrong (we
blamed the bcmgenet MAC for not exposing a PHC).
The real story is better: the CM4 (BCM54210PE) and the 4B (BCM54213PE) report the
IDENTICAL PHY ID 0x600d84a2, so the kernel cannot tell them apart by asking the
chip. bcm-phy-ptp.c disambiguates by MDIO bus address instead — addr 0 = CM4,
proceed; addr 1 = 4B, return NULL. Ours is addr 1.
It is not a kernel config gap and not our RT patch: CONFIG_BCM_NET_PHYPTP=y and
CONFIG_NETWORK_PHY_TIMESTAMPING=y are both set. The driver loads, looks at the
board, and declines.
Correction is visible on the page rather than quietly patched. Credit to Geerling.
119 lines
4.3 KiB
JavaScript
119 lines
4.3 KiB
JavaScript
// The Cuckoo Escapement — Starlight, diátaxis-shaped.
|
|
//
|
|
// This site is a FIELD REPORT, not a tutorial. The build guides already exist
|
|
// (geerlingguy/time-pi, josh-blake/pixie) and they're good. What doesn't exist
|
|
// is a record of everything they get wrong on a Pi 4, with numbers. So the IA is
|
|
// deliberately inverted from the usual docs site: heavy on Explanation and
|
|
// Reference, and there is no Tutorial section at all.
|
|
//
|
|
// Telemetry + devToolbar off per project convention. The HMR block is required
|
|
// when the dev server runs behind Caddy (TLS-terminating proxy) — without an
|
|
// explicit host/protocol/clientPort, Vite's WebSocket drops every ~10s.
|
|
//
|
|
// Site URL comes from DOMAIN so one image serves both escapement.warehack.ing (prod)
|
|
// and escapement.l.warehack.ing (local dev).
|
|
|
|
import mdx from "@astrojs/mdx";
|
|
import sitemap from "@astrojs/sitemap";
|
|
import starlight from "@astrojs/starlight";
|
|
import { defineConfig } from "astro/config";
|
|
import remarkGfm from "remark-gfm";
|
|
import starlightLinksValidator from "starlight-links-validator";
|
|
|
|
const domain = process.env.DOMAIN ?? "escapement.warehack.ing";
|
|
|
|
export default defineConfig({
|
|
site: `https://${domain}`,
|
|
telemetry: false,
|
|
devToolbar: { enabled: false },
|
|
|
|
vite: {
|
|
server: {
|
|
host: "0.0.0.0",
|
|
hmr: { host: domain, protocol: "wss", clientPort: 443 },
|
|
},
|
|
},
|
|
|
|
integrations: [
|
|
starlight({
|
|
title: "The Cuckoo Escapement",
|
|
description:
|
|
"What the Raspberry Pi time-server guides get wrong, and the numbers to prove it. " +
|
|
"GPS Stratum 1 on a Pi 4: PREEMPT_RT makes PPS jitter worse, the PPS interrupt " +
|
|
"cannot be pinned, PTP needs a CM4 not a 4B, and your dashboard is taxing your clock.",
|
|
|
|
// The mark IS the word "cuckoo" — a rebus. replacesTitle stops Starlight
|
|
// rendering the title text beside it (which would read "…escapement The
|
|
// Cuckoo Escapement"). The SVG's aria-label carries the full name.
|
|
//
|
|
// Two variants, because Starlight renders the logo as an <img> and CSS
|
|
// cannot reach inside it. The single cream-on-black logo was invisible in
|
|
// the light theme — cream wordmark on a cream page.
|
|
logo: {
|
|
light: "./src/assets/logo-light.svg",
|
|
dark: "./src/assets/logo.svg",
|
|
replacesTitle: true,
|
|
},
|
|
favicon: "/favicon.svg",
|
|
customCss: ["./src/styles/brass.css"],
|
|
|
|
social: [
|
|
{
|
|
icon: "seti:git",
|
|
label: "Source",
|
|
href: "https://git.supported.systems/warehack.ing/cuckoo-escapement",
|
|
},
|
|
],
|
|
|
|
// Diátaxis, but weighted for a field report. Explanation leads, because the
|
|
// whole point is WHY the received wisdom is wrong. There is no Tutorial —
|
|
// that would be the one thing the world does not need another of.
|
|
// NB: Starlight >=0.39 removed the inline {label, autogenerate} shorthand;
|
|
// it must be nested as {label, items: [{autogenerate}]}.
|
|
sidebar: [
|
|
{
|
|
label: "Start here",
|
|
items: [
|
|
{ label: "What this is (and isn't)", slug: "index" },
|
|
{ label: "The findings, in brief", slug: "findings" },
|
|
],
|
|
},
|
|
{
|
|
label: "Explanation",
|
|
items: [{ autogenerate: { directory: "explanation" } }],
|
|
},
|
|
{
|
|
label: "Reference",
|
|
items: [{ autogenerate: { directory: "reference" } }],
|
|
},
|
|
{
|
|
label: "How-to",
|
|
items: [{ autogenerate: { directory: "how-to" } }],
|
|
},
|
|
],
|
|
|
|
components: {
|
|
// Appends the "A Supported Systems Joint" badge under Starlight's default
|
|
// footer, without rewriting the component.
|
|
Footer: "./src/components/Footer.astro",
|
|
},
|
|
|
|
plugins: [
|
|
starlightLinksValidator({
|
|
// Broken internal links fail the build rather than shipping silently.
|
|
errorOnRelativeLinks: false,
|
|
}),
|
|
],
|
|
|
|
pagination: true,
|
|
lastUpdated: true,
|
|
}),
|
|
// GFM tables do NOT render in .mdx without this, even though they work fine
|
|
// in plain .md — Astro's remark-gfm doesn't reach the MDX pipeline here. The
|
|
// failure is silent and ugly: the table falls through as a paragraph of raw
|
|
// pipe characters. It shipped that way on the landing page. Don't remove.
|
|
mdx({ remarkPlugins: [remarkGfm] }),
|
|
sitemap(),
|
|
],
|
|
});
|