Add M.2 E-key (Wi-Fi) sheet; wire PCIe x1 to freed CM5 lanes
First add-phase block. New carrier/M2_Ekey.kicad_sch (generated by tools/build_ekey.py) places J_E = Connector:Bus_M.2_Socket_E (fp Connector_PCBEdge:M.2_2230-xx-E) for an AsiaRF AW7915 / MT7915 Wi-Fi card. Reuses the single CM5 PCIe x1 Gen2 lane freed by the PCIe-M2 drop (design confirmed E-key-only, no M-key SSD). - PCIe link-0 wired to the 9 freed CM5 pins via global labels (TX/RX/CLK +/-, nRST, nCLKREQ, nWAKE) - netlist-confirmed J_E<->Module1 - +3V3_RF (pins 2/4/72/74) + 10uF||2x0.1uF decoupling + temp PWR_FLAG - W_DISABLE1/2 10k pull-ups; 11 GND; ~41 NC (USB/BT, SDIO, UART, etc.) - RX NOT AC-coupled on carrier (card supplies TX coupling per M.2 spec); corrected Pinout_BOM, dropped C_PCIE - CM5_HighSpeed: replaced the 9 PCIe-pin NC markers with global labels (106/PCIE_PWR_EN stays NC, reserved for the +3V3_RF buck EN) - root + .kicad_pro: registered the M2_Ekey sub-sheet - tools/build_ekey.py: new generator (place connector + auto-NC unused pins + by-name labels; grid-snap; cross-connector ERC pin retyping) Verify: ERC 133->133 (zero new violations); analyzer diff +7 added, 0 regressions; netlist confirms all 9 PCIe nets span J_E and Module1. Open (power phase): wire PCIE_PWR_EN->buck EN, drop temp PWR_FLAG once the +3V3_RF buck drives the rail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -94,7 +94,7 @@ A+E key edge. Populate PCIe + control; leave USB pins NC.
|
|||||||
| CLKREQ0# | PCIE_CLKREQ# | CM5 PCIe CLKREQ |
|
| CLKREQ0# | PCIE_CLKREQ# | CM5 PCIe CLKREQ |
|
||||||
| REFCLK+ / REFCLK− | PCIE_REFCLK_P/N | CM5 PCIe refclk (100 MHz) |
|
| REFCLK+ / REFCLK− | PCIE_REFCLK_P/N | CM5 PCIe refclk (100 MHz) |
|
||||||
| PETp0/PETn0 | PCIE_TX_P/N | CM5 PCIe TX (AC-coupled on CM5) |
|
| PETp0/PETn0 | PCIE_TX_P/N | CM5 PCIe TX (AC-coupled on CM5) |
|
||||||
| PERp0/PERn0 | PCIE_RX_P/N | CM5 PCIe RX (AC-couple caps on carrier) |
|
| PERp0/PERn0 | PCIE_RX_P/N | CM5 PCIe RX (**direct** — card supplies its own TX coupling per M.2; no carrier caps) |
|
||||||
| W_DISABLE1# (RF_KILL) | WIFI_DISABLE# | pull-up + optional GPIO |
|
| W_DISABLE1# (RF_KILL) | WIFI_DISABLE# | pull-up + optional GPIO |
|
||||||
| 3.3 V pins | +3V3_RF | from 15→3.3 V 4 A RF buck (direct) |
|
| 3.3 V pins | +3V3_RF | from 15→3.3 V 4 A RF buck (direct) |
|
||||||
| GND | GND | |
|
| GND | GND | |
|
||||||
@@ -221,7 +221,7 @@ PoE pins of the magjack: leave **unloaded / NC** (no PoE in this design).
|
|||||||
| 6 | J_ETH | 1 | RJ45 1:1 GbE magjack | Pulse JXD0-0001NL / Bel V890-1AX | 2–4 |
|
| 6 | J_ETH | 1 | RJ45 1:1 GbE magjack | Pulse JXD0-0001NL / Bel V890-1AX | 2–4 |
|
||||||
| 7 | ESD_ETH | 1 | Ethernet ESD array | TI TPD4E1U06 / Bourns | 0.3–0.6 |
|
| 7 | ESD_ETH | 1 | Ethernet ESD array | TI TPD4E1U06 / Bourns | 0.3–0.6 |
|
||||||
| 8 | J_E | 1 | M.2 E-key (A+E) 2230 conn + standoff | Amphenol/Attend 119A-92A00 | 1.2–2 |
|
| 8 | J_E | 1 | M.2 E-key (A+E) 2230 conn + standoff | Amphenol/Attend 119A-92A00 | 1.2–2 |
|
||||||
| 9 | C_PCIE | 4 | PCIe RX AC-couple 0.1 µF | GRM 0402 100 nF | 0.05 |
|
| ~~9~~ | ~~C_PCIE~~ | 0 | **REMOVED** — PCIe RX coupling lives on the M.2 card's TX (standard M.2); CM5 couples its own TX. No carrier caps. | — |
|
||||||
| 10 | J_USB1/2 | 2 | USB-A USB3.0 right-angle | Amphenol UE27AC54100 | 0.8–1.4 ea |
|
| 10 | J_USB1/2 | 2 | USB-A USB3.0 right-angle | Amphenol UE27AC54100 | 0.8–1.4 ea |
|
||||||
| 11 | J_UPROG | 1 | USB-C 16-pin (USB2 + CC) | GCT USB4085 | 0.5–1 |
|
| 11 | J_UPROG | 1 | USB-C 16-pin (USB2 + CC) | GCT USB4085 | 0.5–1 |
|
||||||
| 12 | ESD_USB | 3 | USB ESD array | ST USBLC6-2SC6 | 0.15 ea |
|
| 12 | ESD_USB | 3 | USB ESD array | ST USBLC6-2SC6 | 0.15 ea |
|
||||||
@@ -267,7 +267,7 @@ DF40 pair (line 1) = ~45–60% of BOM. Everything else commodity.
|
|||||||
|
|
||||||
1. Import CM5 KiCad symbol from `refs/CM5IO.kicad_sym` — pin map is now resolved in `CM5_Carrier_PinMap.md` (all 200 pins).
|
1. Import CM5 KiCad symbol from `refs/CM5IO.kicad_sym` — pin map is now resolved in `CM5_Carrier_PinMap.md` (all 200 pins).
|
||||||
2. **SYNC (pin 18) is 3.3 V** — drive directly from the Schmitt via a series R; **no level translator** (corrected).
|
2. **SYNC (pin 18) is 3.3 V** — drive directly from the Schmitt via a series R; **no level translator** (corrected).
|
||||||
3. AC-couple PCIe **RX** on carrier (TX coupled on CM5).
|
3. PCIe **RX is NOT coupled on the carrier** — caps live on the M.2 card's TX (standard M.2); CM5 couples its own TX. (Corrected: earlier text/BOM said carrier-side; double-coupling would break the link.)
|
||||||
4. CC 5.1 kΩ pulldowns on USB-C; nRPIBOOT jumper logic.
|
4. CC 5.1 kΩ pulldowns on USB-C; nRPIBOOT jumper logic.
|
||||||
5. Magjack PoE pins NC; Bob-Smith + ESD on cable side.
|
5. Magjack PoE pins NC; Bob-Smith + ESD on cable side.
|
||||||
6. Both 15→3.3 buck inductors/thermals sized for their rails (RF 4 A, AUX 1 A); all bucks rated ≥20 V Vin, low-duty-capable.
|
6. Both 15→3.3 buck inductors/thermals sized for their rails (RF 4 A, AUX 1 A); all bucks rated ≥20 V Vin, low-duty-capable.
|
||||||
|
|||||||
@@ -84,10 +84,22 @@ Notes that bit us (don't re-learn the hard way):
|
|||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
1. ~~Drop the PCIe-M2 sheet entirely~~ **DONE** (see strip table). Confirmed E-key-only (no M-key SSD).
|
1. ~~Drop the PCIe-M2 sheet entirely~~ **DONE** (see strip table). Confirmed E-key-only (no M-key SSD).
|
||||||
2. **Re-baseline**: capture a new analyzer baseline of `carrier/` so the add phase diffs against the
|
2. ~~Re-baseline~~ **DONE** — `analysis/baseline/carrier_stripped.json` is the add-phase diff anchor.
|
||||||
stripped design, not the original reference. (`analyze_schematic.py carrier/CM5IO.kicad_sch -o analysis/baseline/carrier_stripped.json`)
|
3. **M.2 E-key sheet** ✅ **DONE** (`carrier/M2_Ekey.kicad_sch`, generated by `tools/build_ekey.py`).
|
||||||
3. **Add phase**: 15V→5V buck front-end (reverse-protection + TVS), M.2 E-key (PCIe Gen2 ×1 +
|
J_E = KiCad `Connector:Bus_M.2_Socket_E`, fp `Connector_PCBEdge:M.2_2230-xx-E`. PCIe link-0 wired to the
|
||||||
CLKREQ/PERST/RF_KILL + 3.3V), GPS connector, PPS distribution (GPS PPS → TVS → Schmitt buffer →
|
9 freed CM5 pins via **global labels** (PCIE_TX/RX/CLK ±, nRST, nCLKREQ, nWAKE — netlist-confirmed J_E↔Module1);
|
||||||
|
`+3V3_RF` (4 pins) + 10µF∥2×0.1µF decoupling + temp PWR_FLAG; 11 GND; W_DISABLE1/2 10k pull-ups to +3V3_RF;
|
||||||
|
~41 NC (USB/BT, PCM/I2S, SDIO, UART, COEX, I2C, link-1). RX **not** coupled on carrier (card-side per M.2; BOM C_PCIE removed).
|
||||||
|
**Verify:** ERC 133→133 (zero new); analyzer diff +7 added, **0 regressions**.
|
||||||
|
Connector-symbol pin types remodeled for cross-connector ERC: lines CM5 drives → `passive`; lines the card
|
||||||
|
drives into CM5 inputs (RX pair + CLKREQ#/WAKE#) → `output`.
|
||||||
|
**Open items (review/next):** (a) CM5 `PCIE_PWR_EN` (pin 106) still NC — wire to the +3V3_RF buck EN in the
|
||||||
|
power sheet; (b) the temp PWR_FLAG on +3V3_RF must be REMOVED once the buck drives it (else dual-driver ERC);
|
||||||
|
(c) optionally route `WIFI_DISABLE#` to a CM5 GPIO for SW radio control; (d) analyzer CG-AUD 5:1 sig/gnd on J_E
|
||||||
|
is inherent to the M.2 connector (INFO, not fixable). New tool: `tools/build_ekey.py` (place connector + auto-NC + labels).
|
||||||
|
4. **Remaining add phase**: 15V→5V buck front-end (reverse-protection + TVS); the two 3.3V rails
|
||||||
|
(`+3V3_RF` for the E-key — wire its buck EN to CM5 `PCIE_PWR_EN`/106 and drop the temp PWR_FLAG —
|
||||||
|
and `+3V3_AUX` for GPS/IO); GPS connector; PPS distribution (GPS PPS → TVS → Schmitt buffer →
|
||||||
fan-out: CM5 `Ethernet_SYNC_OUT` + 2 header taps). SPICE-validate the buck divider + PPS network.
|
fan-out: CM5 `Ethernet_SYNC_OUT` + 2 header taps). SPICE-validate the buck divider + PPS network.
|
||||||
|
|
||||||
## Out-of-repo notes
|
## Out-of-repo notes
|
||||||
|
|||||||
@@ -833,7 +833,11 @@
|
|||||||
[
|
[
|
||||||
"00000000-0000-0000-0000-00005cff706a",
|
"00000000-0000-0000-0000-00005cff706a",
|
||||||
"CM5_GPIO ( Ethernet, GPIO, SDCARD)"
|
"CM5_GPIO ( Ethernet, GPIO, SDCARD)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"c57f5f03-e2c5-5c1b-ba90-8072a2a088a0",
|
||||||
|
"M2_Ekey"
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"text_variables": {}
|
"text_variables": {}
|
||||||
}
|
}
|
||||||
@@ -3459,6 +3459,16 @@
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
(sheet
|
||||||
|
(at 205 34.29)
|
||||||
|
(size 30 20)
|
||||||
|
(stroke (width 0.1524) (type solid) (color 132 0 132 1))
|
||||||
|
(fill (color 255 255 255 0.0000))
|
||||||
|
(uuid "c57f5f03-e2c5-5c1b-ba90-8072a2a088a0")
|
||||||
|
(property "Sheetname" "M2_Ekey" (at 205 30.48 0) (effects (font (size 1.27 1.27)) (justify left bottom)))
|
||||||
|
(property "Sheetfile" "M2_Ekey.kicad_sch" (at 205 31.75 0) (effects (font (size 1.27 1.27)) (justify left top)))
|
||||||
|
(instances (project "CM5IO" (path "/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55" (page "4"))))
|
||||||
|
)
|
||||||
(sheet_instances
|
(sheet_instances
|
||||||
(path "/"
|
(path "/"
|
||||||
(page "1")
|
(page "1")
|
||||||
|
|||||||
@@ -12684,44 +12684,62 @@
|
|||||||
(at 38.1 144.78)
|
(at 38.1 144.78)
|
||||||
(uuid "3e94757c-62b4-5b7d-9eb9-0bce1d18da64")
|
(uuid "3e94757c-62b4-5b7d-9eb9-0bce1d18da64")
|
||||||
)
|
)
|
||||||
(no_connect
|
|
||||||
(at 38.1 25.4)
|
|
||||||
(uuid "aab94d9f-4d88-55fd-8963-e421713879b9")
|
|
||||||
)
|
|
||||||
(no_connect
|
|
||||||
(at 38.1 27.94)
|
|
||||||
(uuid "bf767c9b-22d7-5638-8a4e-bb8fee0ab324")
|
|
||||||
)
|
|
||||||
(no_connect
|
(no_connect
|
||||||
(at 38.1 30.48)
|
(at 38.1 30.48)
|
||||||
(uuid "22043a85-7370-5a2b-9195-7034c3f95e65")
|
(uuid "22043a85-7370-5a2b-9195-7034c3f95e65")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_nCLKREQ"
|
||||||
(at 99.06 35.56)
|
(shape input)
|
||||||
(uuid "cb39ce49-dc2e-52cf-9ae5-2fad794b9c0c")
|
(at 38.1 25.4 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "2d6bcc7f-bf6a-5023-b9a8-f27291d2d3ae")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_nWAKE"
|
||||||
(at 38.1 35.56)
|
(shape input)
|
||||||
(uuid "2c45bf1f-14d2-5a34-839d-dece52143315")
|
(at 38.1 27.94 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "d8b40857-196b-5180-bd8c-a975b3ad79da")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_nRST"
|
||||||
(at 38.1 38.1)
|
(shape input)
|
||||||
(uuid "03029999-8098-5e97-add5-bc4fb95371cc")
|
(at 99.06 35.56 0)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify left))
|
||||||
|
(uuid "2274596d-a153-5b0c-816c-fd4b3c161cd2")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_CLK_P"
|
||||||
(at 38.1 43.18)
|
(shape input)
|
||||||
(uuid "59658b5e-4bf4-566d-b811-b0bf504a95d9")
|
(at 38.1 35.56 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "e43761cd-21a8-5c71-9bd0-14fa2388618a")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_CLK_N"
|
||||||
(at 38.1 45.72)
|
(shape input)
|
||||||
(uuid "a4407bb3-9030-51e1-88f0-738765ce6334")
|
(at 38.1 38.1 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "cee20a9f-240d-5034-a7d9-e0ea94c6b19e")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_RX_P"
|
||||||
(at 38.1 50.8)
|
(shape input)
|
||||||
(uuid "15995150-d574-51ad-b24d-4ddbdb8a19b0")
|
(at 38.1 43.18 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "dbc3aa3a-3d74-54a7-8e16-45a47db880db")
|
||||||
)
|
)
|
||||||
(no_connect
|
(global_label "PCIE_RX_N"
|
||||||
(at 38.1 53.34)
|
(shape input)
|
||||||
(uuid "8fe68385-81e4-5057-aaaf-7839e45c385d")
|
(at 38.1 45.72 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "257305aa-6458-525c-be07-f4457a28ba77")
|
||||||
|
)
|
||||||
|
(global_label "PCIE_TX_P"
|
||||||
|
(shape input)
|
||||||
|
(at 38.1 50.8 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "ac99503c-1d9e-5ff9-a810-1a99c646684d")
|
||||||
|
)
|
||||||
|
(global_label "PCIE_TX_N"
|
||||||
|
(shape input)
|
||||||
|
(at 38.1 53.34 180)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
|
(uuid "1273b5e1-2d9d-5d45-bc25-040dc4ddc6f1")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
2319
carrier/M2_Ekey.kicad_sch
Normal file
2319
carrier/M2_Ekey.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
250
tools/build_ekey.py
Normal file
250
tools/build_ekey.py
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""build_ekey.py — generate the M.2 E-key (Wi-Fi) sub-sheet for the CM5 carrier.
|
||||||
|
|
||||||
|
The inverse of retire_block.py: places a library connector + passives, attaches a
|
||||||
|
named label at every USED pin connection point, and a (no_connect) at every unused
|
||||||
|
pin. Connectivity is by KiCad's by-name net model (a label coincident with a pin
|
||||||
|
endpoint joins that pin to the net) — no wire routing, which keeps generation exact.
|
||||||
|
|
||||||
|
Locked net map (see CM5_Carrier_Pinout_BOM.md §4, design confirmed E-key-only):
|
||||||
|
PCIe link-0 -> the 9 freed CM5 pins ; +3V3_RF (4 pins) ; GND (11) ; W_DISABLE x2 pull-ups.
|
||||||
|
RX is NOT AC-coupled on the carrier (caps live on the card's TX, per M.2 spec).
|
||||||
|
|
||||||
|
Emits carrier/M2_Ekey.kicad_sch. Pin connection point: abs=(px+lx, py-ly) for a
|
||||||
|
rot0/no-mirror placement (the transform validated in retire_block.py).
|
||||||
|
"""
|
||||||
|
import re, sys, os, uuid as uuidlib
|
||||||
|
|
||||||
|
ROOT_UUID = "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55"
|
||||||
|
PROJECT = "CM5IO"
|
||||||
|
SHEET_SYMBOL_UUID = "00000000-0000-0000-0000-00005 e000ekey".replace(" ", "") # placeholder, overwritten below
|
||||||
|
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-ekey-" + label))
|
||||||
|
|
||||||
|
# deterministic sheet-symbol uuid (used in instance paths + the root sheet block)
|
||||||
|
SHEET_SYMBOL_UUID = U("sheet-symbol")
|
||||||
|
SHEET_FILE_UUID = U("sheet-file")
|
||||||
|
|
||||||
|
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)
|
||||||
|
if not m: return None
|
||||||
|
s = m.start(); return text[s:paren_end(text, s)]
|
||||||
|
|
||||||
|
def lib_pins(seg):
|
||||||
|
"""[(number, lx, ly, angle)] for a lib_symbol block (descends into _x_1 units)."""
|
||||||
|
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.append((nu.group(1), float(at.group(1)), float(at.group(2)), float(at.group(3))))
|
||||||
|
return out
|
||||||
|
|
||||||
|
# ---- gather lib_symbol definitions -------------------------------------------------
|
||||||
|
carrier_hs = open(os.path.join(CARRIER, "CM5_HighSpeed.kicad_sch")).read()
|
||||||
|
conn_lib = open(os.path.join(KSS, "Connector.kicad_sym")).read()
|
||||||
|
power_lib = open(os.path.join(KSS, "power.kicad_sym")).read()
|
||||||
|
|
||||||
|
# reuse R / C / GND verbatim from the carrier (guaranteed-compatible embeds)
|
||||||
|
DEV_R = get_symbol(carrier_hs, "Device:R")
|
||||||
|
DEV_C = get_symbol(carrier_hs, "Device:C")
|
||||||
|
PWR_GND = get_symbol(carrier_hs, "power:GND")
|
||||||
|
# socket + PWR_FLAG from KiCad libs, renamed to their full lib_id
|
||||||
|
sock = get_symbol(conn_lib, "Bus_M.2_Socket_E")
|
||||||
|
sock = sock.replace('(symbol "Bus_M.2_Socket_E"', '(symbol "Connector:Bus_M.2_Socket_E"', 1)
|
||||||
|
|
||||||
|
def set_pin_type(seg, number, newtype):
|
||||||
|
"""Rewrite the electrical type of the lib pin whose (number "N") matches.
|
||||||
|
The socket is typed from the host's view; for ERC across the connector we model
|
||||||
|
the lines CM5 drives as 'passive' and the RX pair (card drives -> CM5) as 'output'."""
|
||||||
|
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
|
||||||
|
# Lines CM5 drives (TX 35/37, REFCLK 47/49, PERST 52): socket side passive.
|
||||||
|
for n in ['35', '37', '47', '49', '52']:
|
||||||
|
sock = set_pin_type(sock, n, 'passive')
|
||||||
|
# Lines the CARD drives into CM5 inputs -> model socket side as output:
|
||||||
|
# RX pair (41/43) + CLKREQ# (53) + WAKE# (55) are all CM5 inputs (open-drain for
|
||||||
|
# CLKREQ/WAKE; the card pulls them). 'output' gives each net a driver for ERC.
|
||||||
|
for n in ['41', '43', '53', '55']:
|
||||||
|
sock = set_pin_type(sock, n, 'output')
|
||||||
|
pflag = get_symbol(power_lib, "PWR_FLAG")
|
||||||
|
pflag = pflag.replace('(symbol "PWR_FLAG"', '(symbol "power:PWR_FLAG"', 1)
|
||||||
|
for nm, blk in [("Device:R", DEV_R), ("Device:C", DEV_C), ("power:GND", PWR_GND),
|
||||||
|
("Connector:Bus_M.2_Socket_E", sock), ("power:PWR_FLAG", pflag)]:
|
||||||
|
if not blk: sys.exit("missing lib_symbol: " + nm)
|
||||||
|
|
||||||
|
SOCK_PINS = {n: (lx, ly, a) for n, lx, ly, a in lib_pins(sock)} # 67 pins
|
||||||
|
|
||||||
|
# ---- the net map -------------------------------------------------------------------
|
||||||
|
# pin -> ('hier', net) cross-sheet PCIe ; ('gnd',) ; ('v33',) ; ('loc', net) local
|
||||||
|
HIER = {
|
||||||
|
'35': 'PCIE_TX_P', '37': 'PCIE_TX_N', '41': 'PCIE_RX_P', '43': 'PCIE_RX_N',
|
||||||
|
'47': 'PCIE_CLK_P', '49': 'PCIE_CLK_N', '52': 'PCIE_nRST', '53': 'PCIE_nCLKREQ',
|
||||||
|
'55': 'PCIE_nWAKE',
|
||||||
|
}
|
||||||
|
V33 = ['2', '4', '72', '74']
|
||||||
|
GND = ['1', '7', '18', '33', '39', '45', '51', '57', '63', '69', '75']
|
||||||
|
LOCAL = {'56': 'WIFI_DISABLE#', '54': 'WIFI_DISABLE2#'}
|
||||||
|
used = set(HIER) | set(V33) | set(GND) | set(LOCAL)
|
||||||
|
NC = sorted((p for p in SOCK_PINS if p not in used), key=lambda x: int(x))
|
||||||
|
|
||||||
|
assert len(used) + len(NC) == len(SOCK_PINS), (len(used), len(NC), len(SOCK_PINS))
|
||||||
|
|
||||||
|
# ---- placement ---------------------------------------------------------------------
|
||||||
|
def g(v): # snap to the 1.27mm (50mil) schematic grid so labels bind to pins
|
||||||
|
return round(round(v / 1.27) * 1.27, 2)
|
||||||
|
PX, PY = g(170.0), g(105.0)
|
||||||
|
def pin_abs(num):
|
||||||
|
lx, ly, a = SOCK_PINS[num]
|
||||||
|
return (round(PX + lx, 2), round(PY - ly, 2), a)
|
||||||
|
|
||||||
|
def lbl_angle(num):
|
||||||
|
lx, ly, a = SOCK_PINS[num]
|
||||||
|
if abs(lx) >= abs(ly): return 0 if lx > 0 else 180
|
||||||
|
return 90 if ly > 0 else 270
|
||||||
|
|
||||||
|
def hier_label(name, x, y, ang, shape="bidirectional"):
|
||||||
|
return (f'\t(hierarchical_label "{name}"\n\t\t(shape {shape})\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("hl-"+name+"-"+str(x)+"-"+str(y))}")\n\t)\n')
|
||||||
|
|
||||||
|
def glob_label(name, x, y, ang):
|
||||||
|
return (f'\t(global_label "{name}"\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-"+name+"-"+str(x)+"-"+str(y))}")\n\t)\n')
|
||||||
|
|
||||||
|
def loc_label(name, x, y, ang):
|
||||||
|
return (f'\t(label "{name}"\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("ll-"+name+"-"+str(x)+"-"+str(y))}")\n\t)\n')
|
||||||
|
|
||||||
|
def no_connect(x, y, tag):
|
||||||
|
return f'\t(no_connect (at {x} {y}) (uuid "{U("nc-"+tag)}"))\n'
|
||||||
|
|
||||||
|
def gnd_sym(x, y, tag):
|
||||||
|
return (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'
|
||||||
|
f'\t\t(uuid "{U("gnd-"+tag)}")\n'
|
||||||
|
f'\t\t(property "Reference" "#PWR{tag}" (at {x} {y+3.0} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||||
|
f'\t\t(property "Value" "GND" (at {x} {y+2.0} 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("gndpin-"+tag)}"))\n'
|
||||||
|
f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "#PWR{tag}") (unit 1))))\n\t)\n')
|
||||||
|
|
||||||
|
def pwr_flag(x, y, tag):
|
||||||
|
return (f'\t(symbol\n\t\t(lib_id "power:PWR_FLAG")\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'
|
||||||
|
f'\t\t(uuid "{U("pflag-"+tag)}")\n'
|
||||||
|
f'\t\t(property "Reference" "#FLG{tag}" (at {x} {y-3.0} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||||
|
f'\t\t(property "Value" "PWR_FLAG" (at {x} {y-2.0} 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("pflagpin-"+tag)}"))\n'
|
||||||
|
f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "#FLG{tag}") (unit 1))))\n\t)\n')
|
||||||
|
|
||||||
|
def passive(lib_id, ref, value, footprint, x, y, tag):
|
||||||
|
return (f'\t(symbol\n\t\t(lib_id "{lib_id}")\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'
|
||||||
|
f'\t\t(uuid "{U("sym-"+ref)}")\n'
|
||||||
|
f'\t\t(property "Reference" "{ref}" (at {x+2.54} {y-1.0} 0) (effects (font (size 1.27 1.27)) (justify left)))\n'
|
||||||
|
f'\t\t(property "Value" "{value}" (at {x+2.54} {y+1.0} 0) (effects (font (size 1.27 1.27)) (justify left)))\n'
|
||||||
|
f'\t\t(property "Footprint" "{footprint}" (at {x} {y} 90) (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("p1-"+ref)}"))\n'
|
||||||
|
f'\t\t(pin "2" (uuid "{U("p2-"+ref)}"))\n'
|
||||||
|
f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "{ref}") (unit 1))))\n\t)\n')
|
||||||
|
|
||||||
|
def socket_symbol():
|
||||||
|
# the placed J_E (multi-unit symbol uses unit 1; both graphic units share lib def)
|
||||||
|
props = ('\t\t(property "Reference" "J_E" (at 170 53 0) (effects (font (size 1.27 1.27))))\n'
|
||||||
|
'\t\t(property "Value" "M.2_Ekey_WiFi" (at 170 55 0) (effects (font (size 1.27 1.27))))\n'
|
||||||
|
'\t\t(property "Footprint" "Connector_PCBEdge:M.2_2230-xx-E" (at 170 105 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||||
|
'\t\t(property "Datasheet" "" (at 170 105 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
|
||||||
|
'\t\t(property "Description" "M.2 A+E key socket, Wi-Fi (AsiaRF AW7915/MT7915)" (at 170 105 0) (effects (font (size 1.27 1.27)) (hide yes)))\n')
|
||||||
|
pins = "".join(f'\t\t(pin "{n}" (uuid "{U("jepin-"+n)}"))\n' for n in sorted(SOCK_PINS, key=int))
|
||||||
|
return (f'\t(symbol\n\t\t(lib_id "Connector:Bus_M.2_Socket_E")\n\t\t(at {PX} {PY} 0)\n\t\t(unit 1)\n'
|
||||||
|
f'\t\t(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n'
|
||||||
|
f'\t\t(uuid "{U("J_E")}")\n' + props + pins +
|
||||||
|
f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "J_E") (unit 1))))\n\t)\n')
|
||||||
|
|
||||||
|
# ---- assemble the sheet ------------------------------------------------------------
|
||||||
|
body = []
|
||||||
|
body.append(socket_symbol())
|
||||||
|
|
||||||
|
# connector pin labels / NC / GND
|
||||||
|
for num, net in HIER.items():
|
||||||
|
# global labels: connect to the matching CM5-side labels across sheets without
|
||||||
|
# parent sheet-pin plumbing (the 9 freed CM5 PCIe pins get the same global names).
|
||||||
|
x, y, _ = pin_abs(num); body.append(glob_label(net, x, y, lbl_angle(num)))
|
||||||
|
for num in V33:
|
||||||
|
x, y, _ = pin_abs(num); body.append(glob_label("+3V3_RF", x, y, lbl_angle(num)))
|
||||||
|
for num, net in LOCAL.items():
|
||||||
|
x, y, _ = pin_abs(num); body.append(loc_label(net, x, y, lbl_angle(num)))
|
||||||
|
for num in GND:
|
||||||
|
x, y, _ = pin_abs(num); body.append(gnd_sym(x, y, num))
|
||||||
|
for num in NC:
|
||||||
|
x, y, _ = pin_abs(num); body.append(no_connect(x, y, num))
|
||||||
|
|
||||||
|
# pull-up resistors: pin1(top) -> signal label ; pin2(bottom) -> +3V3_RF
|
||||||
|
def place_rc(lib_id, ref, value, fp, x, y, top_label_fn, top_name, bot_name):
|
||||||
|
body.append(passive(lib_id, ref, value, fp, x, y, ref))
|
||||||
|
body.append(top_label_fn(top_name, x, round(y - 3.81, 2), 90)) # pin1
|
||||||
|
body.append(glob_label(bot_name, x, round(y + 3.81, 2), 270)) # pin2
|
||||||
|
|
||||||
|
place_rc("Device:R", "R_E1", "10k", "Resistor_SMD:R_0402_1005Metric", g(215.0), g(80.0), loc_label, "WIFI_DISABLE#", "+3V3_RF")
|
||||||
|
place_rc("Device:R", "R_E2", "10k", "Resistor_SMD:R_0402_1005Metric", g(225.0), g(80.0), loc_label, "WIFI_DISABLE2#", "+3V3_RF")
|
||||||
|
|
||||||
|
# decoupling caps: pin1 -> +3V3_RF ; pin2 -> GND
|
||||||
|
c_e1_pin1 = None
|
||||||
|
for i, (ref, val) in enumerate([("C_E1", "10u"), ("C_E2", "100n"), ("C_E3", "100n")]):
|
||||||
|
cx = g(215.0 + i * 7.62); cy = g(110.0)
|
||||||
|
body.append(passive("Device:C", ref, val, "Capacitor_SMD:C_0402_1005Metric", cx, cy, ref))
|
||||||
|
p1 = (cx, round(cy - 3.81, 2))
|
||||||
|
body.append(glob_label("+3V3_RF", p1[0], p1[1], 90)) # pin1
|
||||||
|
body.append(gnd_sym(cx, round(cy + 3.81, 2), ref + "g")) # pin2 -> GND symbol
|
||||||
|
if ref == "C_E1": c_e1_pin1 = p1
|
||||||
|
|
||||||
|
# one PWR_FLAG to drive +3V3_RF until the power-sheet buck exists.
|
||||||
|
# Co-locate its pin directly on an existing +3V3_RF node (C_E1 pin1) so it binds.
|
||||||
|
# REMOVE this flag once the power sheet's buck drives +3V3_RF (else dual-driver ERC).
|
||||||
|
body.append(pwr_flag(c_e1_pin1[0], c_e1_pin1[1], "33RF"))
|
||||||
|
|
||||||
|
# ---- file scaffold -----------------------------------------------------------------
|
||||||
|
libsyms = "\n".join([DEV_R, DEV_C, PWR_GND, pflag, sock])
|
||||||
|
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 - M.2 E-key (Wi-Fi)")\n\t\t(rev "A")\n'
|
||||||
|
'\t\t(comment 1 "AW7915 / MT7915, PCIe x1 Gen2, USB NC")\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 "4")\n\t\t)\n\t)\n'
|
||||||
|
')\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
assert sheet.count('(') == sheet.count(')'), f"UNBALANCED ({sheet.count('(')} vs {sheet.count(')')})"
|
||||||
|
out = os.path.join(CARRIER, "M2_Ekey.kicad_sch")
|
||||||
|
print(f"pins: {len(SOCK_PINS)} used: {len(used)} (hier {len(HIER)}, v33 {len(V33)}, gnd {len(GND)}, local {len(LOCAL)}) NC: {len(NC)}")
|
||||||
|
print(f"sheet-symbol uuid: {SHEET_SYMBOL_UUID}")
|
||||||
|
print(f"parens balanced: {sheet.count('(')}")
|
||||||
|
if '--apply' in sys.argv:
|
||||||
|
open(out, 'w').write(sheet); print("WROTE " + out)
|
||||||
|
else:
|
||||||
|
print("DRY RUN (pass --apply to write " + out + ")")
|
||||||
Reference in New Issue
Block a user