Add GPS + PPS distribution sheet (all rev-A blocks captured)
- New carrier/GPS_PPS.kicad_sch via tools/build_gps.py: J_GPS 5-pin (PPS + UART2 NMEA + 3V3_AUX + GND) per Pinout_BOM section 6 - PPS chain: GPS_PPS_RAW -> PESD3V3L1BA TVS -> 74LVC1G17 Schmitt (+3V3_AUX) -> PPS_CLEAN -> 33R series -> SYNC_OUT (CM5 pin 18 + J2.6), plus J_PPS1 (UWB) / J_PPS2 (scope) 2-pin taps; board is PPS sink only - NMEA on UART2 (GPIO4/GPIO5) per correction C2; debug console keeps UART0 - Converted SYNC_OUT/GPIO4/GPIO5 local labels on CM5_GPIO to global (names unchanged) to reach the new sheet; root sheet-symbol needs no pins - J_GPS pins 1/2 typed output (GPS drives PPS/TX) for driver-aware checks - Verify: ERC 133->133 zero new; analyzer +73/-0 zero regressions; netlist confirms all cross-sheet nets; SPICE 24 pass / 2 benign warn / 0 fail Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,23 +7,28 @@
|
||||
## ▶ RESUME HERE (session handoff, 2026-07-01)
|
||||
|
||||
**State:** branch `port/strip-phase`. Sheets in `carrier/`: root `CM5IO`, `CM5_GPIO`,
|
||||
`CM5_HighSpeed` (reference, stripped), `M2_Ekey` (E-key, done), `Power` (**stages 1–4 done —
|
||||
power sheet complete**). Baseline anchor: `analysis/baseline/carrier_stripped.json`.
|
||||
`CM5_HighSpeed` (reference, stripped), `M2_Ekey`, `Power` (stages 1–4), `GPS_PPS`. **ALL
|
||||
rev-A circuit blocks are now captured.** Baseline anchor: `analysis/baseline/carrier_stripped.json`.
|
||||
|
||||
**Done:** strip phase (microSD/HDMI/MIPI/PCIe-M2); M.2 E-key sheet (PCIe ×1 to CM5); Power
|
||||
stage 1 (LM74700 ideal-diode input protection → `VIN_PROT`); Power stage 2 (LM61460-Q1
|
||||
15→`+5v` 5 A, SPICE 5.02 V); Power stage 3 (LM61460-Q1 15→`+3V3_RF` 4 A, SPICE 3.32 V; EN ←
|
||||
CM5 `PCIE_PWR_EN`/106 + 100k pull-down); Power stage 4 (TPS54202 15→`+3V3_AUX` ~1 A, SPICE
|
||||
3.315 V; UVLO divider start 10.1 V / stop 8.6 V). Current whole-design **ERC = 133** (all
|
||||
pre-existing types; a good edit adds **zero new**).
|
||||
**Done:** strip phase (microSD/HDMI/MIPI/PCIe-M2); M.2 E-key sheet (PCIe ×1); Power stage 1
|
||||
(LM74700 → `VIN_PROT`), stage 2 (LM61460-Q1 → `+5v` 5 A, SPICE 5.02 V), stage 3 (LM61460-Q1 →
|
||||
`+3V3_RF` 4 A, SPICE 3.32 V, EN ← `PCIE_PWR_EN`), stage 4 (TPS54202 → `+3V3_AUX` ~1 A, SPICE
|
||||
3.315 V); **GPS + PPS sheet** (`carrier/GPS_PPS.kicad_sch` via `tools/build_gps.py`: J_GPS
|
||||
5-pin per Pinout_BOM §6, PPS → PESD3V3 TVS → 74LVC1G17 @ +3V3_AUX → `PPS_CLEAN` → Rs 33Ω →
|
||||
`SYNC_OUT` (CM5 pin 18 + J2.6) + J_PPS1/J_PPS2 taps; NMEA on **UART2 GPIO4/GPIO5** per
|
||||
correction C2 — netlist: GPIO4 = Module1.54+J8.7+J_GPS.3, GPIO5 = Module1.34+J8.29+J_GPS.2).
|
||||
The `SYNC_OUT`/`GPIO4`/`GPIO5` local labels on CM5_GPIO were converted to **global** labels
|
||||
(names unchanged, nothing else used them) to reach the new sheet without root plumbing.
|
||||
Current whole-design **ERC = 133** (all pre-existing types; a good edit adds **zero new**).
|
||||
|
||||
**NEXT ACTION → GPS + PPS distribution sheet** (last new block, see step 5 below): GPS
|
||||
connector (PPS + UART + 3V3 + GND, powered from `+3V3_AUX`); PPS → TVS → Schmitt buffer
|
||||
(LVC1G17 @ 3.3 V) → fan-out: CM5 `Ethernet_SYNC_OUT` (J2 pin-6 net, no translator) + 2 header
|
||||
taps. Reuse the build_*.py generator pattern (new `tools/build_gps.py` or extend build_power).
|
||||
UART: decide which CM5 UART pins to use (check `CM5_GPIO` — reserve/label; datasheet is the
|
||||
authority). After that: whole-power-tree budget review, then hygiene pass (title blocks, refdes
|
||||
numbering — note generated refs like `J_E`/`J_PWR` trip kicad-cli's "annotation errors" warning).
|
||||
**NEXT ACTION → design-review + close-out passes** (no new blocks):
|
||||
1. **Power-budget review** of the whole tree (loads vs ratings; the kicad skill's power-tree
|
||||
check) + re-read PORT_STATUS "Confirm vs datasheet" leftovers (stage-1 C_VCAP/FET pick,
|
||||
stage-2/3 L saturation ≥ Ipk, RT freq).
|
||||
2. **Hygiene pass** (checklist in kicad-port skill): refdes numbering (un-numbered generated
|
||||
refs `J_E`/`J_PWR`/`U_BUCK5`… trip kicad-cli "annotation errors" — decide whether to number
|
||||
them), title blocks, netlist-vs-BOM sanity, design notes.
|
||||
3. Then merge `port/strip-phase` → main and start layout prep (DF40 placement copy from ref).
|
||||
|
||||
**How to build/verify (every change):**
|
||||
- Sheets are generated wholesale — edit `tools/build_ekey.py` / `tools/build_power.py`, then
|
||||
@@ -135,8 +140,18 @@ Notes that bit us (don't re-learn the hard way):
|
||||
- **Stage 2 — U_BUCK5 15→`+5v` 5A ✅ DONE.** `LM61460-Q1` (6A, 42V absmax sync; **custom symbol built from the datasheet** → `carrier/CM5IO.kicad_sym` as `CM5IO:LM61460-Q1`, fp `Package_SO:Texas_HTSSOP-14-1EP…ThermalVias`). VREF=1.0V → FB divider R_FBT5 40.2k / R_FBB5 10.0k = **5.02V (SPICE-confirmed vfb=1.0V)**. L5 4.7µH, Cout 2×47µF/16V, Cin 2×10µF/50V, Cboot 100n+Rboot 4.7Ω, RT 31.6k(~400kHz), BIAS→+5v, EN→VIN_PROT, PGOOD 100k→+5v. **`+5v` kept HIERARCHICAL** (pre-existing carrier net; global would collide `same_local_global_label`) — Power sheet exposes a `+5v` sheet pin, tied into the root `+5v` net; netlist-verified it drives Module1 + GPIO + USB. ERC 133→133 (zero new); analyzer +32 added **0 regressions**; SPICE 12/13 pass, 1 benign warn (L5/Cboot mis-detected as LC filter). **Confirm vs datasheet:** RT freq, L sat ≥7A, EP=pad15, optional EN UVLO divider.
|
||||
- **Stage 3 — U_BUCK33R 15→`+3V3_RF` 4A ✅ DONE.** Second `LM61460-Q1` (shared BOM with stage 2: L 4.7µH/7A ripple ~1.3A=33%, Cin 2×10µ/50V, Cout 2×47µ/16V, RT 31.6k ~420kHz, Cboot 100n+Rboot 4.7). FB divider R_FBT33 23.2k / R_FBB33 10.0k = **3.32V (SPICE: vfb 0.994V, inrush settles 3.32V)**. BIAS→`+3V3_RF` (datasheet: valid ≥3.1V, auto-falls back to VIN below). **EN ← CM5 `PCIE_PWR_EN`** (Module1 pin 106: NC swapped for a global label on `CM5_HighSpeed`) + **R_EN33 100k pull-down** (mirrors reference R14 — netlist-verified `Module1.106 + U_BUCK33R.7 + R_EN33.1`) so the Wi-Fi rail is OFF until the CM5 asserts it. **E-key temp `#FLG33RF` PWR_FLAG removed** (build_ekey); the buck's PWR_FLAG on L33.2 is now the sole `+3V3_RF` driver — netlist shows buck + all 4 J_E power pins + pull-ups + decoupling on one net. Verify: ERC 133→133 zero new; analyzer +49 added **0 regressions**; SPICE 17 pass / 2 benign warn (the L/C_BOOT "LC filter" mis-detect, once per buck) / 0 fail. (Note: `kicad-cli` netlist export prints a pre-existing "annotation errors" warning — un-numbered generated refdeses like `J_E`/`J_PWR`; present at fd627f5 too, cosmetic.)
|
||||
- **Stage 4 — U_BUCK33A 15→`+3V3_AUX` ~1A ✅ DONE (power sheet complete).** `TPS54202` (SOT-23-6, sync, fixed 500kHz, VFB=0.596V). **Abs-max gate PASSED but thin:** VIN abs-max **30V** vs SMBJ18A clamp ≤29.2V at full rated 600W surge → 0.8V transient margin (operating max 28V is not the surge number). Custom symbol `CM5IO:TPS54202` (the KiCad lib symbol `extends TPS54302` — no own pins, can't embed standalone) + bare copy in `carrier/CM5IO.kicad_sym`. FB per TI's 3.3V table row: **100k/22.1k + Cff 56pF → 3.29V (SPICE: vfb 0.597V, inrush settles 3.315V)**. L 10µH/2.5A (ΔI~0.52A), Cout 2×22µF/16V, Cboot 100n, Cin 10µF/50V+100n. **EN abs-max is only 7V → NO direct VIN tie** (unlike LM61460): UVLO divider **866k/110k** (solved from Ip=0.7µA/Ih=1.55µA, VEN 1.21/1.19V) → **start 10.13V / stop 8.61V**, EN ~1.9V at 15V in (~3.5V at 29V clamp — safe). FB+EN pins typed `passive` (divider-only nets). Verify: ERC 133→133 zero new; analyzer +65/−0 zero regressions (warn/error set identical to stage 3); SPICE 23 pass / 2 benign warn / 0 fail / 0 skip. **New tooling gap found:** `simulate_subcircuits.py` lowercases refdeses into SPICE names — `C_IN33A` vs stage-3 `C_IN33a` collided ("device already exists") and silently SKIPPED the whole VIN_PROT decoupling group → renamed to `C_IN33Aa`. Rule: **never create refdeses differing only by case.**
|
||||
5. **GPS + PPS distribution**: GPS connector (PPS+UART+3V3+GND); PPS → TVS → Schmitt (LVC1G17) →
|
||||
fan-out: CM5 `Ethernet_SYNC_OUT` (J2 pin-6 net, 3.3V, no translator) + 2 header taps. SPICE/timing check.
|
||||
5. **GPS + PPS distribution ✅ DONE** (`carrier/GPS_PPS.kicad_sch`, generated by `tools/build_gps.py`).
|
||||
Per Pinout_BOM §6 (the authority — it CORRECTS the design doc): J_GPS 5-pin (1=PPS, 2=NMEA_RX←GPS TX,
|
||||
3=NMEA_TX→GPS RX, 4=+3V3_AUX, 5=GND); `GPS_PPS_RAW` → TVS_PPS `PESD3V3L1BA` → U_PPS `74LVC1G17`
|
||||
(SOT-23-5, +3V3_AUX, pin 1 true-NC) → `PPS_CLEAN` → R_PPS 33Ω → `SYNC_OUT` + J_PPS1 (UWB) / J_PPS2
|
||||
(scope), 2-pin taps. **NMEA = UART2 on GPIO4/GPIO5** (correction C2; UART0/GPIO14-15 stays debug).
|
||||
Cross-sheet wiring: converted the 6 local labels (`SYNC_OUT`×2, `GPIO4`×2, `GPIO5`×2 on CM5_GPIO) to
|
||||
**global** labels — net names unchanged so no multi-label (LB-001) findings, ERC-neutral (133→133);
|
||||
GPS sheet uses the same global names, root sheet-symbol has NO pins. J_GPS pins 1/2 re-typed `output`
|
||||
(GPS module drives PPS + TX) so the analyzer's no_driver gate passes — same trick as the E-key socket.
|
||||
**Verify:** ERC 133→133 zero new; analyzer +73/−0 **0 regressions** (only the pre-existing MPN-coverage
|
||||
counter moved 50→57 parts); netlist confirms all 6 cross-sheet nets (SYNC_OUT = Module1.18+J2.6+R_PPS.2);
|
||||
SPICE 24 pass / 2 benign warn / 0 fail / 0 skip.
|
||||
|
||||
## Out-of-repo notes
|
||||
|
||||
|
||||
@@ -841,6 +841,10 @@
|
||||
[
|
||||
"9c7ccf72-dd3e-5ed3-bb43-2939833eb069",
|
||||
"Power"
|
||||
],
|
||||
[
|
||||
"5efffb63-edbb-5986-86d3-84ead412bc65",
|
||||
"GPS_PPS"
|
||||
]
|
||||
],
|
||||
"text_variables": {}
|
||||
|
||||
@@ -3489,6 +3489,16 @@
|
||||
(effects (font (size 1.27 1.27)) (justify left))
|
||||
(uuid "f1109a7e-4bfb-54f4-a32e-e6719bad4ba8")
|
||||
)
|
||||
(sheet
|
||||
(at 205 90)
|
||||
(size 30 20)
|
||||
(stroke (width 0.1524) (type solid) (color 132 0 132 1))
|
||||
(fill (color 255 255 255 0.0000))
|
||||
(uuid "5efffb63-edbb-5986-86d3-84ead412bc65")
|
||||
(property "Sheetname" "GPS_PPS" (at 205 86.18 0) (effects (font (size 1.27 1.27)) (justify left bottom)))
|
||||
(property "Sheetfile" "GPS_PPS.kicad_sch" (at 205 87.45 0) (effects (font (size 1.27 1.27)) (justify left top)))
|
||||
(instances (project "CM5IO" (path "/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55" (page "6"))))
|
||||
)
|
||||
(sheet_instances
|
||||
(path "/"
|
||||
(page "1")
|
||||
|
||||
@@ -13645,7 +13645,8 @@
|
||||
)
|
||||
(uuid "05c1c0ae-f846-4942-b9ca-9f0f8f62492d")
|
||||
)
|
||||
(label "GPIO5"
|
||||
(global_label "GPIO5"
|
||||
(shape bidirectional)
|
||||
(at 24.13 62.23 0)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
@@ -13964,7 +13965,8 @@
|
||||
)
|
||||
(uuid "2e95b89a-d151-4ceb-8ea1-75ed88f1541e")
|
||||
)
|
||||
(label "GPIO4"
|
||||
(global_label "GPIO4"
|
||||
(shape bidirectional)
|
||||
(at 24.13 87.63 0)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
@@ -14316,7 +14318,8 @@
|
||||
)
|
||||
(uuid "6fa8342e-2989-40ca-b0ae-b207f17ca831")
|
||||
)
|
||||
(label "SYNC_OUT"
|
||||
(global_label "SYNC_OUT"
|
||||
(shape bidirectional)
|
||||
(at 54.61 175.26 180)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
@@ -14459,7 +14462,8 @@
|
||||
)
|
||||
(uuid "88ce3174-a8b3-4149-886a-872ed4746e98")
|
||||
)
|
||||
(label "SYNC_OUT"
|
||||
(global_label "SYNC_OUT"
|
||||
(shape bidirectional)
|
||||
(at 24.13 41.91 0)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
@@ -14525,7 +14529,8 @@
|
||||
)
|
||||
(uuid "91fb974e-99de-4e0c-bee5-7a6f88905951")
|
||||
)
|
||||
(label "GPIO5"
|
||||
(global_label "GPIO5"
|
||||
(shape bidirectional)
|
||||
(at 147.32 59.69 180)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
@@ -15053,7 +15058,8 @@
|
||||
)
|
||||
(uuid "f6429ab2-213c-4030-a705-9f073170a98c")
|
||||
)
|
||||
(label "GPIO4"
|
||||
(global_label "GPIO4"
|
||||
(shape bidirectional)
|
||||
(at 147.32 31.75 180)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
|
||||
1453
carrier/GPS_PPS.kicad_sch
Normal file
1453
carrier/GPS_PPS.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
166
tools/build_gps.py
Normal file
166
tools/build_gps.py
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
"""build_gps.py — generate the CM5-carrier GPS + PPS distribution sheet.
|
||||
|
||||
Same connectivity model as build_power.py: every pin gets a coincident named
|
||||
label / GND symbol / no_connect; nets form by name (grid-snapped 1.27mm).
|
||||
|
||||
Frozen spec (CM5_Carrier_Pinout_BOM.md §6, CM5_Carrier_Design.md §5a):
|
||||
J_GPS(5-pin): 1=PPS 2=NMEA_RX(GPS TX -> CM5 GPIO5/UART2 RX)
|
||||
3=NMEA_TX(CM5 GPIO4/UART2 TX -> GPS RX) 4=+3V3_AUX 5=GND
|
||||
GPS_PPS_RAW -> TVS -> 74LVC1G17 Schmitt (3V3) -> PPS_CLEAN
|
||||
-> Rs 33R -> SYNC_OUT (CM5 pin 18, Ethernet_SYNC_OUT, 3.3V bidi, cfg input)
|
||||
-> J_PPS1 (UWB tap) / J_PPS2 (scope tap), 2-pin each
|
||||
Board is always a PPS SINK (GPS-sourced).
|
||||
|
||||
Cross-sheet nets are GLOBAL: +3V3_AUX (Power sheet flag drives it), and
|
||||
GPIO4/GPIO5/SYNC_OUT (their CM5_GPIO local labels were converted to global —
|
||||
net names unchanged; nothing else in the design used those names).
|
||||
J_GPS pins 1/2 are re-typed 'output' (the GPS module drives PPS and TX into
|
||||
the board) so the analyzer's no_driver gate sees real drivers (E-key lesson).
|
||||
"""
|
||||
import re, sys, os, uuid as uuidlib
|
||||
|
||||
ROOT_UUID = "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55"
|
||||
PROJECT = "CM5IO"
|
||||
KSS = "/Applications/KiCad/KiCad.app/Contents/SharedSupport/symbols"
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
CARRIER = os.path.join(HERE, "..", "carrier")
|
||||
|
||||
def U(label): return str(uuidlib.uuid5(uuidlib.NAMESPACE_DNS, "cm5carrier-gps-" + label))
|
||||
SHEET_SYMBOL_UUID = U("sheet-symbol")
|
||||
SHEET_FILE_UUID = U("sheet-file")
|
||||
|
||||
def g(v): return round(round(v / 1.27) * 1.27, 2)
|
||||
|
||||
def paren_end(t, s):
|
||||
d = 0
|
||||
for i in range(s, len(t)):
|
||||
if t[i] == '(': d += 1
|
||||
elif t[i] == ')':
|
||||
d -= 1
|
||||
if d == 0: return i + 1
|
||||
raise ValueError("unbalanced")
|
||||
|
||||
def get_symbol(text, name):
|
||||
m = re.search(r'\(symbol\s+"' + re.escape(name) + r'"', text)
|
||||
return text[m.start():paren_end(text, m.start())] if m else None
|
||||
|
||||
def lib_pins(seg):
|
||||
out = {}
|
||||
for pm in re.finditer(r'\(pin\b', seg):
|
||||
s = pm.start(); ps = seg[s:paren_end(seg, s)]
|
||||
at = re.search(r'\(at\s+([\d.-]+)\s+([\d.-]+)\s+([\d.-]+)\)', ps)
|
||||
nu = re.search(r'\(number\s+"([^"]+)"', ps)
|
||||
if at and nu:
|
||||
out.setdefault(nu.group(1), (float(at.group(1)), float(at.group(2)), float(at.group(3))))
|
||||
return out
|
||||
|
||||
def set_pin_type(seg, number, newtype):
|
||||
for pm in re.finditer(r'\(pin\s+(\w+)\s+(\w+)', seg):
|
||||
s = pm.start(); blk = seg[s:paren_end(seg, s)]
|
||||
if re.search(r'\(number\s+"' + re.escape(number) + r'"', blk):
|
||||
head = re.match(r'\(pin\s+\w+', blk).group(0)
|
||||
return seg[:s] + blk.replace(head, f'(pin {newtype}', 1) + seg[s + len(blk):]
|
||||
return seg
|
||||
|
||||
# ---- lib_symbol sources -----------------------------------------------------------
|
||||
carrier_hs = open(os.path.join(CARRIER, "CM5_HighSpeed.kicad_sch")).read()
|
||||
LIBS = {}
|
||||
def add_from(lib_id, srctext, srcname=None, retype=None):
|
||||
srcname = srcname or lib_id.split(':')[-1]
|
||||
blk = get_symbol(srctext, lib_id) or get_symbol(srctext, srcname)
|
||||
if not blk: sys.exit("missing lib_symbol " + lib_id)
|
||||
if ('"' + lib_id + '"') not in blk[:60]:
|
||||
blk = blk.replace('(symbol "' + srcname + '"', '(symbol "' + lib_id + '"', 1)
|
||||
for num, ty in (retype or {}).items():
|
||||
blk = set_pin_type(blk, num, ty)
|
||||
LIBS[lib_id] = (blk, lib_pins(blk))
|
||||
|
||||
for lid in ["Device:R", "Device:C", "power:GND"]:
|
||||
add_from(lid, carrier_hs)
|
||||
add_from("Device:D_TVS", open(f"{KSS}/Device.kicad_sym").read(), "D_TVS")
|
||||
# GPS module DRIVES pins 1 (PPS) and 2 (its TX) -> 'output' for driver-aware checks
|
||||
add_from("Connector_Generic:Conn_01x05", open(f"{KSS}/Connector_Generic.kicad_sym").read(),
|
||||
"Conn_01x05", retype={'1': 'output', '2': 'output'})
|
||||
add_from("Connector_Generic:Conn_01x02", open(f"{KSS}/Connector_Generic.kicad_sym").read(), "Conn_01x02")
|
||||
add_from("74xGxx:74LVC1G17", open(f"{KSS}/74xGxx.kicad_sym").read(), "74LVC1G17")
|
||||
|
||||
GLOBAL_NETS = {"+3V3_AUX", "GPIO4", "GPIO5", "SYNC_OUT"}
|
||||
HIER_NETS = set()
|
||||
|
||||
# ---- placement engine (same as build_power.py, + ('nc',) pins) --------------------
|
||||
body = []
|
||||
def emit_label(net, x, y, ang=0):
|
||||
body.append(f'\t(label "{net}"\n\t\t(at {x} {y} {ang})\n'
|
||||
f'\t\t(effects (font (size 1.27 1.27)) (justify left))\n\t\t(uuid "{U("l-"+net+f"-{x}-{y}")}")\n\t)\n')
|
||||
def emit_glabel(net, x, y, ang=0):
|
||||
body.append(f'\t(global_label "{net}"\n\t\t(shape input)\n\t\t(at {x} {y} {ang})\n'
|
||||
f'\t\t(effects (font (size 1.27 1.27)) (justify left))\n\t\t(uuid "{U("gl-"+net+f"-{x}-{y}")}")\n\t)\n')
|
||||
def emit_nc(x, y, tag):
|
||||
body.append(f'\t(no_connect (at {x} {y}) (uuid "{U("nc-"+tag)}"))\n')
|
||||
def emit_gnd(x, y, tag):
|
||||
body.append(f'\t(symbol\n\t\t(lib_id "power:GND")\n\t\t(at {x} {y} 0)\n\t\t(unit 1)\n'
|
||||
f'\t\t(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n\t\t(uuid "{U("gnd-"+tag)}")\n'
|
||||
f'\t\t(property "Reference" "#PWR{tag}" (at {x} {y+3} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||
f'\t\t(property "Value" "GND" (at {x} {y+2} 0) (effects (font (size 1.27 1.27))))\n'
|
||||
f'\t\t(property "Footprint" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||
f'\t\t(property "Datasheet" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||
f'\t\t(pin "1" (uuid "{U("gp-"+tag)}"))\n'
|
||||
f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "#PWR{tag}") (unit 1))))\n\t)\n')
|
||||
|
||||
def place(lib_id, ref, value, footprint, ox, oy, pin_nets):
|
||||
ox, oy = g(ox), g(oy)
|
||||
_, pins = LIBS[lib_id]
|
||||
extra = ('\t\t(property "Description" "" (at %g %g 0) (effects (font (size 1.27 1.27)) (hide yes)))\n' % (ox, oy))
|
||||
sym = (f'\t(symbol\n\t\t(lib_id "{lib_id}")\n\t\t(at {ox} {oy} 0)\n\t\t(unit 1)\n'
|
||||
f'\t\t(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n\t\t(uuid "{U("sym-"+ref)}")\n'
|
||||
f'\t\t(property "Reference" "{ref}" (at {ox+2.54} {oy-1} 0) (effects (font (size 1.27 1.27)) (justify left)))\n'
|
||||
f'\t\t(property "Value" "{value}" (at {ox+2.54} {oy+1} 0) (effects (font (size 1.27 1.27)) (justify left)))\n'
|
||||
f'\t\t(property "Footprint" "{footprint}" (at {ox} {oy} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||
f'\t\t(property "Datasheet" "" (at {ox} {oy} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n' + extra)
|
||||
for num in sorted(pins, key=lambda n: int(re.sub(r'\D', '', n) or 0)):
|
||||
sym += f'\t\t(pin "{num}" (uuid "{U("p-"+ref+"-"+num)}"))\n'
|
||||
sym += f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "{ref}") (unit 1))))\n\t)\n'
|
||||
body.append(sym)
|
||||
for num, net in pin_nets.items():
|
||||
lx, ly, _ = pins[num]; px, py = round(ox + lx, 2), round(oy - ly, 2)
|
||||
if net == ('gnd',): emit_gnd(px, py, ref + num)
|
||||
elif net == ('nc',): emit_nc(px, py, ref + num)
|
||||
elif isinstance(net, tuple) and net[0] == 'global': emit_glabel(net[1], px, py)
|
||||
elif net in GLOBAL_NETS: emit_glabel(net, px, py)
|
||||
else: emit_label(net, px, py)
|
||||
return ox, oy
|
||||
|
||||
GND = ('gnd',); NC = ('nc',)
|
||||
|
||||
# ---- the GPS + PPS block ----------------------------------------------------------
|
||||
place("Connector_Generic:Conn_01x05", "J_GPS", "GPS_5pin_PPS_UART", "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical",
|
||||
60, 80, {'1': 'GPS_PPS_RAW', '2': 'GPIO5', '3': 'GPIO4', '4': '+3V3_AUX', '5': GND})
|
||||
place("Device:D_TVS", "TVS_PPS", "PESD3V3L1BA", "Diode_SMD:D_SOD-323", 85, 95, {'1': 'GPS_PPS_RAW', '2': GND})
|
||||
# 74LVC1G17: 1=NC 2=A 3=GND 4=Y 5=VCC (KiCad lib types: input/power_in/output/power_in)
|
||||
place("74xGxx:74LVC1G17", "U_PPS", "74LVC1G17", "Package_TO_SOT_SMD:SOT-23-5",
|
||||
110, 80, {'2': 'GPS_PPS_RAW', '4': 'PPS_CLEAN', '5': '+3V3_AUX', '3': GND, '1': NC})
|
||||
place("Device:C", "C_PPS", "100n", "Capacitor_SMD:C_0402_1005Metric", 100, 95, {'1': '+3V3_AUX', '2': GND})
|
||||
place("Device:C", "C_GPS", "10u", "Capacitor_SMD:C_0603_1608Metric", 70, 95, {'1': '+3V3_AUX', '2': GND})
|
||||
# series R guards the LVC1G17 output against a driver fight (CM5 pin 18 is bidi;
|
||||
# board policy configures it as input — spec Rs 33-100R, pick 33R)
|
||||
place("Device:R", "R_PPS", "33", "Resistor_SMD:R_0402_1005Metric", 140, 80, {'1': 'PPS_CLEAN', '2': 'SYNC_OUT'})
|
||||
place("Connector_Generic:Conn_01x02", "J_PPS1", "PPS_tap_UWB", "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical",
|
||||
170, 75, {'1': 'PPS_CLEAN', '2': GND})
|
||||
place("Connector_Generic:Conn_01x02", "J_PPS2", "PPS_tap_scope", "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical",
|
||||
170, 90, {'1': 'PPS_CLEAN', '2': GND})
|
||||
|
||||
# ---- file scaffold ----------------------------------------------------------------
|
||||
libsyms = "\n".join(blk for blk, _ in LIBS.values())
|
||||
sheet = ('(kicad_sch\n\t(version 20231120)\n\t(generator "eeschema")\n\t(generator_version "8.0")\n'
|
||||
f'\t(uuid "{SHEET_FILE_UUID}")\n\t(paper "A4")\n'
|
||||
'\t(title_block\n\t\t(title "CM5 Carrier - GPS + PPS distribution")\n\t\t(rev "A")\n'
|
||||
'\t\t(comment 1 "GPS conn (PPS+UART2+3V3_AUX) -> TVS -> LVC1G17 -> CM5 SYNC + 2 taps; PPS sink only")\n\t)\n'
|
||||
'\t(lib_symbols\n' + libsyms + '\n\t)\n' + "".join(body) +
|
||||
'\t(sheet_instances\n\t\t(path "/"\n\t\t\t(page "6")\n\t\t)\n\t)\n)\n')
|
||||
assert sheet.count('(') == sheet.count(')'), f"UNBALANCED {sheet.count('(')} vs {sheet.count(')')}"
|
||||
print("sheet-symbol uuid:", SHEET_SYMBOL_UUID)
|
||||
print("parens balanced:", sheet.count('('))
|
||||
out = os.path.join(CARRIER, "GPS_PPS.kicad_sch")
|
||||
if '--apply' in sys.argv: open(out, 'w').write(sheet); print("WROTE " + out)
|
||||
else: print("DRY RUN (--apply to write)")
|
||||
Reference in New Issue
Block a user