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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
#!/usr/bin/env python3
"""The design as data: components, footprints, MPNs and every pin's net. Writes design/netlist.json,
the single source the schematic and the board are both generated from."""
import json, os, sys
sys.path.insert(0, os.path.dirname(__file__))
from footprints import ocp_pinout
HERE = os.path.dirname(__file__)
OUT = os.path.join(HERE, "..", "design", "netlist.json")
STD = "${KICAD10_3DMODEL_DIR}"
comps = []
nets = {}
def C(ref, value, fp, mpn, mfr, mouser, block, desc, model=None, sym=None):
comps.append(dict(ref=ref, value=value, fp=fp, mpn=mpn, mfr=mfr, src=f"Mouser {mouser}", block=block,
desc=desc, model=model, sym=sym))
def N(net, *pins):
nets.setdefault(net, [])
for p in pins:
if p not in nets[net]:
nets[net].append(p)
# ---------------------------------------------------------------- the card edge (J1)
C("J1", "OCP_NIC3_SFF_4C+", "Riser:OCP_NIC3_SFF_4CPlus_CardEdge", "", "PCB gold fingers", "-", "host",
"OCP NIC 3.0 SFF 4C+ card edge (PCB feature, no part)")
edge = ocp_pinout()
# pins on this passive card that stay unconnected (spec: leave as no connect)
NC = {"MAIN_PWR_EN0", "FLEXIO_0_0", "FLEXIO_0_1", "FLEXIO_0_2", "FLEXIO_0_3", "FLEXIO_0_4", "FLEXIO_0_5",
"RBT0_ARB_IN", "RBT0_ARB_OUT", "RBT0_CLK_IN", "BIF0#", "BIF1#", "BIF2#", "SMRST0#", "PRSNTB0#", "PRSNTB1#",
"RFU1", "RFU2", "USB_DAT0n", "USB_DAT0p", "PWRBRK0#"}
RENAME = {"+3.3V_EDGE": "+3V3_EDGE"}
for pin, net in edge.items():
if net in NC:
continue
N(RENAME.get(net, net), f"J1.{pin}")
# ---------------------------------------------------------------- OCuLink ports J2..J5 (SFF-9402 Table 6-1, root side)
OCU = {"A2": "GND", "A3": "PER{}p0", "A4": "PER{}n0", "A5": "GND", "A6": "PER{}p1", "A7": "PER{}n1", "A8": "GND",
"A11": "GND", "A12": "REFCLKp{}", "A13": "REFCLKn{}", "A14": "GND", "A15": "PER{}p2", "A16": "PER{}n2",
"A17": "GND", "A18": "PER{}p3", "A19": "PER{}n3", "A20": "GND",
"B2": "GND", "B3": "PET{}p0", "B4": "PET{}n0", "B5": "GND", "B6": "PET{}p1", "B7": "PET{}n1", "B8": "GND",
"B11": "GND", "B12": "PERST{}", "B13": "CPRSNT{}", "B14": "GND", "B15": "PET{}p2", "B16": "PET{}n2",
"B17": "GND", "B18": "PET{}p3", "B19": "PET{}n3", "B20": "GND", "SH": "GND"}
# A1/A21/B1/B21 = power pins defined NO WIRE in SFF-9402; A9 BP_TYPE, A10 CWAKE#, B9/B10 2-wire: not used by a passive root
for k in range(4):
ref = f"J{2+k}"
C(ref, "G14A42111C12HR", "Riser:Amphenol_G14A42111C12HR_OCuLink_4X_RA", "G14A42111C12HR",
"Amphenol Commercial Products", "523-G14A42111C12HR", f"port{k}",
f"OCuLink 4i port {k}: PCIe lanes {4*k}-{4*k+3}, REFCLK{k}, PERST{k}#")
for pin, tmpl in OCU.items():
if tmpl == "GND":
N("GND", f"{ref}.{pin}")
continue
if tmpl.startswith("PER{}") or tmpl.startswith("PET{}"):
# host-side lane names map straight through: port k lane i = host lane 4k+i
kind = tmpl[:3]
pol = tmpl[5]
i = int(tmpl[6])
host = 4 * k + i
N(f"{kind}{pol}{host}", f"{ref}.{pin}")
elif tmpl.startswith("REFCLK"):
N(tmpl.format(k), f"{ref}.{pin}")
elif tmpl == "PERST{}":
N(f"PORT{k}_PERST#", f"{ref}.{pin}")
elif tmpl == "CPRSNT{}":
N(f"PORT{k}_CPRSNT#", f"{ref}.{pin}")
# ---------------------------------------------------------------- scan chain SR0 (U1, SN74LV165A)
C("U1", "SN74LV165APWR", "Package_SO:TSSOP-16_4.4x5mm_P0.65mm", "SN74LV165APWRE4", "Texas Instruments",
"595-SN74LV165APWRE4", "scan", "OCP scan chain SCAN_DATA_IN shift register 0 (OCP 4.5.6, Table 40)",
f"{STD}/Package_SO.3dshapes/TSSOP-16_4.4x5mm_P0.65mm.step")
N("SCAN0_LD#", "U1.1"); N("SCAN0_CLK", "U1.2")
N("+3V3_EDGE", "U1.3") # E? no: E = bit4 WAKE_N, fixed below
nets["+3V3_EDGE"].remove("U1.3")
N("WAKE0#", "U1.3") # E = bit 0.4 WAKE_N mirrored from the card edge
N("+3V3_EDGE", "U1.4", "U1.5") # F,G = bit 0.5 TEMP_WARN_N=1, 0.6 TEMP_CRIT_N=1
N("GND", "U1.6") # H = bit 0.7 FAN_ON_AUX=0
N("GND", "U1.8"); N("SCAN0_DATA_IN", "U1.9"); N("SR0_SER", "U1.10")
N("+3V3_EDGE", "U1.11", "U1.12") # A,B = PRSNTB0#,PRSNTB1# = 1 (local straps, OCP Table 40 allows)
N("GND", "U1.13", "U1.14") # C,D = PRSNTB2#,PRSNTB3# = 0 (card is 0b0011)
N("GND", "U1.15"); N("+3V3_EDGE", "U1.16")
# U1.7 /QH unused (no connect)
C("R3", "1k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-071KL", "YAGEO", "603-RC0402FR-071KL", "scan",
"SCAN_CLK pull-up to +3.3V_EDGE (OCP 4.5.6.1: 1 k on the NIC)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("SCAN0_CLK", "R3.1"); N("+3V3_EDGE", "R3.2")
C("R4", "10k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-0710KL", "YAGEO", "603-RC0402FR-0710KL", "scan",
"SCAN_LD# pull-up 10 k (OCP 4.5.6.4)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("SCAN0_LD#", "R4.1"); N("+3V3_EDGE", "R4.2")
C("R5", "10k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-0710KL", "YAGEO", "603-RC0402FR-0710KL", "scan",
"SCAN_DATA_OUT pull-down 10 k (OCP 4.5.6.2)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("SCAN0_DATA_OUT", "R5.1", "J1.OCP_B5"); N("GND", "R5.2")
C("R6", "1k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-071KL", "YAGEO", "603-RC0402FR-071KL", "scan",
"SER of the last shift register pulled up 1 k (OCP 4.5.6.5)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("SR0_SER", "R6.1"); N("+3V3_EDGE", "R6.2")
C("R7", "100k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-07100KL", "YAGEO", "603-RC0402FR-07100KL", "scan",
"weak pull-up on WAKE0# so scan bit 0.4 never floats (baseboard has 10 k)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("WAKE0#", "R7.1"); N("+3V3_EDGE", "R7.2")
# ---------------------------------------------------------------- PRSNT straps: 4 x4 card, PRSNTB[3:0]# = 0b0011
C("R1", "100", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-07100RL", "YAGEO", "603-RC0402FR-07100RL", "host",
"PRSNTA0# to PRSNTB2# strap, series 0-200 ohm (OCP 4.5.2.2)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("PRSNTA0#", "R1.1"); N("PRSNTB2#", "R1.2")
C("R2", "100", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-07100RL", "YAGEO", "603-RC0402FR-07100RL", "host",
"PRSNTA0# to PRSNTB3# strap, series 0-200 ohm (OCP 4.5.2.2)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("PRSNTA0#", "R2.1"); N("PRSNTB3#", "R2.2")
# ---------------------------------------------------------------- FRU EEPROM (U2)
C("U2", "BR24H64FJ-5ACE2", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm", "BR24H64FJ-5ACE2", "ROHM Semiconductor",
"755-BR24H64FJ-5ACE2", "fru", "FRU EEPROM 64 Kbit, double-byte addressing, address 0xA0 + SLOT_ID (OCP 5.10)",
f"{STD}/Package_SO.3dshapes/SOIC-8_3.9x4.9mm_P1.27mm.step")
N("GND", "U2.1", "U2.4"); N("SLOT_ID0", "U2.2"); N("SLOT_ID1", "U2.3")
N("SMDAT0", "U2.5"); N("SMCLK0", "U2.6"); N("FRU_WP", "U2.7"); N("+3V3_EDGE", "U2.8")
C("R8", "10k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-0710KL", "YAGEO", "603-RC0402FR-0710KL", "fru",
"FRU write protect pull-up: protected unless JP1 is bridged (OCP 5.10.2)", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("FRU_WP", "R8.1"); N("+3V3_EDGE", "R8.2")
C("JP1", "FRU_WRITE", "Jumper:SolderJumper-2_P1.3mm_Open_RoundedPad1.0x1.5mm", "", "PCB solder jumper", "-", "fru",
"Bridge to allow writing the FRU EEPROM")
N("FRU_WP", "JP1.1"); N("GND", "JP1.2")
# ---------------------------------------------------------------- NIC_PWR_GOOD (U3)
C("U3", "TPS3808G33", "Package_TO_SOT_SMD:SOT-23-6", "TPS3808G33DBVTG4", "Texas Instruments", "595-TPS3808G33DBVTG4",
"power", "NIC_PWR_GOOD: +3.3V_EDGE within tolerance AND AUX_PWR_EN high; 20 ms delay with CT open (OCP 4.5.7.6, TAPL 50 ms)",
f"{STD}/Package_TO_SOT_SMD.3dshapes/SOT-23-6.step")
N("NIC_PWR_GOOD0", "U3.1"); N("GND", "U3.2"); N("AUX_PWR_EN0", "U3.3"); N("+3V3_EDGE", "U3.5", "U3.6")
# U3.4 CT open = 20 ms
C("R9", "10k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-0710KL", "YAGEO", "603-RC0402FR-0710KL", "power",
"NIC_PWR_GOOD open-drain pull-up", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("NIC_PWR_GOOD0", "R9.1"); N("+3V3_EDGE", "R9.2")
# ---------------------------------------------------------------- PERST# buffers (U4) and per-port terminations
C("U4", "SN74LVC125APWR", "Package_SO:TSSOP-14_4.4x5mm_P0.65mm", "SN74LVC125APWR", "Texas Instruments",
"595-SN74LVC125APWR", "perst", "PERST0..3# buffers to the OCuLink ports; Ioff keeps the outputs off when the slot is unpowered",
f"{STD}/Package_SO.3dshapes/TSSOP-14_4.4x5mm_P0.65mm.step")
N("GND", "U4.1", "U4.4", "U4.10", "U4.13", "U4.7"); N("+3V3_EDGE", "U4.14")
for k, (a, y) in enumerate([("2", "3"), ("5", "6"), ("9", "8"), ("12", "11")]):
N(f"PERST{k}#", f"U4.{a}"); N(f"PERST{k}_BUF", f"U4.{y}")
rs = f"R{11+k}"; rp = f"R{15+k}"
C(rs, "33", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-0733RL", "YAGEO", "603-RC0402FR-0733RL", "perst",
f"PERST{k}# series termination at the buffer", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N(f"PERST{k}_BUF", f"{rs}.1"); N(f"PORT{k}_PERST#", f"{rs}.2")
C(rp, "100k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-07100KL", "YAGEO", "603-RC0402FR-07100KL", "perst",
f"port {k} PERST# held asserted while the slot is unpowered", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N(f"PORT{k}_PERST#", f"{rp}.1"); N("GND", f"{rp}.2")
# ---------------------------------------------------------------- ESD at each port (sideband pins leave the chassis)
for k in range(4):
ref = f"U{5+k}"
C(ref, "TPD4E05U06", "Package_SON:USON-10_2.5x1.0mm_P0.5mm", "TPD4E05U06QDQARQ1", "Texas Instruments",
"595-PD4E05U06QDQARQ1", f"port{k}", f"ESD clamps on port {k} PERST# and CPRSNT#",
f"{STD}/Package_SON.3dshapes/USON-10_2.5x1.0mm_P0.5mm.step")
N(f"PORT{k}_PERST#", f"{ref}.1"); N(f"PORT{k}_CPRSNT#", f"{ref}.2"); N("GND", f"{ref}.3", f"{ref}.8")
# channels D2+ / D2- (pins 4, 5) unused: tie to GND so they are not floating antennas
N("GND", f"{ref}.4", f"{ref}.5")
# ---------------------------------------------------------------- power indication, decoupling, test points
C("D1", "GREEN", "LED_SMD:LED_0603_1608Metric", "IN-S63BTG", "Inolux", "743-IN-S63BTG", "power",
"+3.3V_EDGE present (slot aux power)", f"{STD}/LED_SMD.3dshapes/LED_0603_1608Metric.step")
C("R23", "1k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-071KL", "YAGEO", "603-RC0402FR-071KL", "power",
"D1 current, (3.3-2.0)/1k = 1.3 mA", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("+3V3_EDGE", "R23.1"); N("LED3V3_A", "R23.2", "D1.2"); N("GND", "D1.1")
C("D2", "GREEN", "LED_SMD:LED_0603_1608Metric", "IN-S63BTG", "Inolux", "743-IN-S63BTG", "power",
"+12V_EDGE present (slot main power)", f"{STD}/LED_SMD.3dshapes/LED_0603_1608Metric.step")
C("R24", "4.7k", "Riser:R_0402_1005Metric_NoSilk", "RC0402FR-074K7L", "YAGEO", "603-RC0402FR-074K7L", "power",
"D2 current, (12-2.0)/4.7k = 2.1 mA, 21 mW", f"{STD}/Resistor_SMD.3dshapes/R_0402_1005Metric.step")
N("+12V_EDGE", "R24.1"); N("LED12V_A", "R24.2", "D2.2"); N("GND", "D2.1")
for i, (ref, near) in enumerate([("C1", "U1"), ("C2", "U2"), ("C3", "U3"), ("C4", "U4")]):
C(ref, "100n", "Riser:C_0402_1005Metric_NoSilk", "CL05B104KO5NNNC", "Samsung Electro-Mechanics", "187-CL05B104KO5NNNC", "power",
f"{near} decoupling", f"{STD}/Capacitor_SMD.3dshapes/C_0402_1005Metric.step")
N("+3V3_EDGE", f"{ref}.1"); N("GND", f"{ref}.2")
C("C5", "10u", "Riser:C_0603_1608Metric_NoSilk", "CL10A106KQ8NNNC", "Samsung Electro-Mechanics", "187-CL10A106KQ8NNNC",
"power", "+3.3V_EDGE bulk", f"{STD}/Capacitor_SMD.3dshapes/C_0603_1608Metric.step")
N("+3V3_EDGE", "C5.1"); N("GND", "C5.2")
for ref, net, val in [("TP1", "+3V3_EDGE", "+3V3"), ("TP2", "GND", "GND"), ("TP3", "SMCLK0", "SMCLK"),
("TP4", "SMDAT0", "SMDAT"), ("TP5", "+12V_EDGE", "+12V")]:
C(ref, val, "TestPoint:TestPoint_Pad_D1.0mm", "", "PCB test pad", "-", "test", f"test/programming pad {val}")
N(net, f"{ref}.1")
# probe spots for the bench test plan (design/tests.json): 0.5 mm pads placed on each net's routed top-layer trace
for ref, net, val in [("TP6", "NIC_PWR_GOOD0", "PWRGD"), ("TP7", "PORT0_PERST#", "PERST0"), ("TP8", "PORT1_PERST#", "PERST1"),
("TP9", "PORT2_PERST#", "PERST2"), ("TP10", "PORT3_PERST#", "PERST3"), ("TP11", "SCAN0_CLK", "SCLK"),
("TP12", "SCAN0_DATA_OUT", "SDOUT")]:
C(ref, val, "Riser:TestPoint_Pad_D0.5mm", "", "PCB test pad", "-", "test", f"probe spot {net}")
N(net, f"{ref}.1")
for ref in ("H1", "H2"):
C(ref, "Faceplate_GND", "Riser:OCP_SFF_Faceplate_Ground_3.2mm", "", "PCB feature", "-", "mech",
"OCP SFF faceplate mounting and grounding hole")
N("GND", f"{ref}.1")
# every IC carries its laser-etched MPN (adom-aiflow-etch, kicad/3d/etch; plain KiCad models in kicad/3d/src)
for c in comps:
if c["ref"].startswith("U"):
c["model"] = "${KIPRJMOD}/3d/etch/" + c["fp"].split(":")[1] + "_" + c["mpn"] + ".step"
if __name__ == "__main__":
os.makedirs(os.path.dirname(OUT), exist_ok=True)
# sanity: every pin once
seen = {}
for net, pins in nets.items():
for p in pins:
if p in seen:
sys.exit(f"pin {p} on {seen[p]} and {net}")
seen[p] = net
json.dump({"design": "ocp-oculink-riser", "components": comps, "nets": nets}, open(OUT, "w"), indent=1)
print(f"{len(comps)} components, {len(nets)} nets, {len(seen)} connected pins -> {OUT}")