board
OCuLink Riser for the Supermicro AIOM Slot
Public Unreviewedby John Lauer
Passive OCP NIC 3.0 SFF card that breaks a Supermicro AIOM slot's PCIe x16 into four OCuLink 4i ports (4 x4, Gen4): 6-layer, 85 ohm, length-matched, DRC-clean KiCad project with Gerbers, BOM and CPL.
main
John Lauer
README: component table per the wiki format, open-source section, no duplicate hero; generators at gen/
39a59ef
7d ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
#!/usr/bin/env python3
"""Render and clearance-check the routing plan (container side, before KiCad)."""
import sys, os, json, math
sys.path.insert(0, os.path.dirname(__file__))
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from shapely.geometry import LineString, Point
from shapely.strtree import STRtree
COL = {"L1": "#d62728", "L3": "#2ca02c", "L5": "#1f77b4", "L6": "#9467bd"}
def shapes(tracks, vias):
"""list of (layerset, net, geom)"""
out = []
for (lay, net, x1, y1, x2, y2, w) in tracks:
out.append(({lay}, net, LineString([(x1, y1), (x2, y2)]).buffer(w / 2, cap_style=1), ("t", lay, x1, y1, x2, y2)))
for (x, y, net, d, h) in vias:
out.append(({"L1", "L3", "L5", "L6"}, net, Point(x, y).buffer(d / 2), ("v", x, y)))
return out
def clearance(items, clr=0.15, extra=()):
geoms = [g for (_, _, g, _) in items]
tree = STRtree(geoms)
bad = []
for i, (ls, net, g, tag) in enumerate(items):
for j in tree.query(g.buffer(clr)):
if j <= i:
continue
ls2, net2, g2, tag2 = items[j]
if net2 == net or not (ls & ls2):
continue
dd = g.distance(g2)
if dd < clr - 1e-6:
bad.append((round(dd, 3), net, net2, sorted(ls & ls2)[0], tag[:4], tag2[:4]))
return bad
def render(tracks, vias, path, outline=None, extra_polys=(), xlim=(0, 76), ylim=(115, 0), layers=("L1", "L3", "L5", "L6"), dpi=110):
fig, ax = plt.subplots(figsize=(8, 12))
if outline:
xs, ys = zip(*outline)
ax.plot(xs, ys, "k-", lw=0.6)
for (lay, net, x1, y1, x2, y2, w) in tracks:
if lay in layers:
ax.plot([x1, x2], [y1, y2], "-", color=COL[lay], lw=max(0.3, w * 3), solid_capstyle="round", alpha=0.85)
for (x, y, net, d, h) in vias:
ax.add_patch(plt.Circle((x, y), d / 2, color="#444", lw=0, alpha=0.8))
for poly, c in extra_polys:
xs, ys = zip(*poly)
ax.fill(xs, ys, color=c, alpha=0.35, lw=0)
ax.set_xlim(*xlim); ax.set_ylim(*ylim); ax.set_aspect("equal")
ax.grid(True, lw=0.2)
fig.savefig(path, dpi=dpi, bbox_inches="tight")
plt.close(fig)
if __name__ == "__main__":
import hs
pairs, lines = hs.all_routes()
T = [t for p in pairs + lines for t in p.tracks]
V = [v for p in pairs + lines for v in p.vias]
bad = clearance(shapes(T, V), 0.15)
print(len(bad), "clearance violations")
for b in bad[:40]:
print(b)
os.makedirs("../design/plots", exist_ok=True)
render(T, V, "../design/plots/hs_all.png")
for k in range(4):
cx = hs.CX[k]
render(T, V, f"../design/plots/hs_port{k}_top.png", xlim=(cx - 9, cx + 9), ylim=(22, 2), dpi=160)
render(T, V, "../design/plots/hs_fan.png", xlim=(15, 75), ylim=(113, 86), dpi=150)
render(T, V, "../design/plots/hs_tune.png", xlim=(10, 76), ylim=(80, 34), dpi=150)
render(T, V, "../design/plots/hs_bus.png", xlim=(5, 76), ylim=(110, 76), dpi=150)