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.
← Commit history
design/gen/live_plan.pydeleted−117@@ -1,117 +0,0 @@-#!/usr/bin/env python3-"""Split the finished routing into (a) the start board's copper and (b) what AI Flow lands live:-placement moves for every part that is not a fixed interface part, and a routing plan (one entry per-polyline, layer markers for vias) for every maze-routed net and GND fan-out."""-import json, os, math-from collections import defaultdict-ROOT = os.path.join(os.path.dirname(__file__), "..")-R = json.load(open(os.path.join(ROOT, "design", "routes.json")))-LS = json.load(open(os.path.join(ROOT, "design", "routes_ls.json")))-lay = json.load(open(os.path.join(ROOT, "design", "layout.json")))-KL = {"L1": "F.Cu", "L3": "In2.Cu", "L5": "In4.Cu", "L6": "B.Cu"}-FIXED = {"J1", "J2", "J3", "J4", "J5", "H1", "H2"}--# live copper: everything the maze produced (routes_ls minus the fixed copper it inherited) plus GND fan-outs-if "fixed_tracks" in LS:- fixed_T = set(map(tuple, LS["fixed_tracks"])); fixed_V = set(map(tuple, LS["fixed_vias"]))-else: # older routes_ls.json: rebuild the fixed copper exactly as route_all does before routing- import sys; sys.path.insert(0, os.path.dirname(__file__))- import route_all as RA- fixed_T = set(map(tuple, RA.T)); fixed_V = set(map(tuple, RA.V))-# taken from the FINAL routes (routes.json): the finishing pass prunes stubs and may rip and re-route a few nets, so-# the maze's own output is stale for those; GND copper is live only where the maze or the fan-out made it-ls_T = {tuple(t) for t in LS["tracks"]} - fixed_T; ls_V = {tuple(v) for v in LS["vias"]} - fixed_V-fan_T = set(map(tuple, R["fanout_tracks"])); fan_V = set(map(tuple, R["fanout_vias"]))-live_T = [t for t in map(tuple, R["tracks"]) if t not in fixed_T and (t[1] != "GND" or t in ls_T or t in fan_T)]-live_V = [v for v in map(tuple, R["vias"]) if v not in fixed_V and (v[2] != "GND" or v in ls_V or v in fan_V)]-live_Ts, live_Vs = set(live_T), set(live_V)-start_T = [t for t in map(tuple, R["tracks"]) if t not in live_Ts]-start_V = [v for v in map(tuple, R["vias"]) if v not in live_Vs]---def key(x, y):- return (round(x, 3), round(y, 3))---def chains(net, tracks, vias):- """Decompose one net's live copper into polylines; a via joins layers at one point."""- adj = defaultdict(list) # (layer, pt) -> [(edge id, other node)]- edges = []- for (L, n, x1, y1, x2, y2, w) in tracks:- a, b = (L, key(x1, y1)), (L, key(x2, y2))- eid = len(edges); edges.append((a, b, w))- adj[a].append((eid, b)); adj[b].append((eid, a))- viapts = {key(v[0], v[1]) for v in vias}- lone = []- for vp in viapts: # via: connect every layer node at that point- nodes = [nd for nd in list(adj) if nd[1] == vp]- if len(nodes) < 2: # copper on one layer only (the other side is a pad or a pour): a via of its own- lone.append((vp, nodes[0][0] if nodes else "L1"))- for i in range(len(nodes) - 1):- a, b = nodes[i], nodes[i + 1]- eid = len(edges); edges.append((a, b, None))- adj[a].append((eid, b)); adj[b].append((eid, a))- used = set()- out = []- order = sorted(adj, key=lambda nd: len(adj[nd]) % 2 == 0) # odd-degree nodes first- for start in order:- while any(e not in used for e, _ in adj[start]):- path = [start]; cur = start; w = None- while True:- nxt = [(e, o) for e, o in adj[cur] if e not in used]- if not nxt:- break- e, o = nxt[0]- used.add(e)- if edges[e][2] is not None:- w = edges[e][2]- path.append(o); cur = o- out.append((path, w))- entries = []- for path, w in out:- pts = []- layer = path[0][0]- for i, (L, (x, y)) in enumerate(path):- if i == 0:- pts.append([x, y])- elif L != layer:- pts.append({"x": x, "y": y, "layer": KL[L]})- layer = L- elif pts and isinstance(pts[-1], dict) and abs(pts[-1]["x"] - x) < 1e-6 and abs(pts[-1]["y"] - y) < 1e-6:- continue- else:- pts.append([x, y])- # a via at the start of a path: express as a layer marker on the first point- entries.append({"net": net, "width": w or 0.15, "viaSize": 0.4, "viaDrill": 0.2, "layer": KL[path[0][0]], "points": pts})- vsize = {key(v[0], v[1]): (v[3], v[4]) for v in vias}- for (x, y), L in lone:- other = "B.Cu" if KL[L] == "F.Cu" else "F.Cu"- d, h = vsize[(x, y)]- entries.append({"net": net, "width": 0.15, "viaSize": d, "viaDrill": h, "layer": KL[L],- "points": [[x, y], {"x": x, "y": y, "layer": other}]})- return entries---bynet_T, bynet_V = defaultdict(list), defaultdict(list)-for t in live_T:- bynet_T[t[1]].append(t)-for v in live_V:- bynet_V[v[2]].append(v)-plan = []-for net in sorted(set(bynet_T) | set(bynet_V)):- plan += chains(net, bynet_T[net], bynet_V[net])-moves = {}-stage = {}-k = 0-for ref, (x, y, rot, side) in lay["placement"].items():- if ref in FIXED:- continue- moves[ref] = {"x": x, "y": y, "rotation": rot}- stage[ref] = (84.0 + 9.0 * (k % 4), 8.0 + 6.0 * (k // 4), rot, side)- k += 1-json.dump({"nets": plan}, open(os.path.join(ROOT, "aiflow", "route-plan.json"), "w"), indent=0)-json.dump(moves, open(os.path.join(ROOT, "aiflow", "moves.json"), "w"), indent=1)-json.dump({"tracks": start_T, "vias": start_V, "zones": R["zones"], "texts": R.get("texts", []), "stage": stage},- open(os.path.join(ROOT, "design", "routes_start.json"), "w"))-print(f"live: {len(plan)} plan entries ({len(live_T)} tracks, {len(live_V)} vias); moves {len(moves)} parts;"- f" start board copper {len(start_T)} tracks, {len(start_V)} vias")