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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
#!/usr/bin/env python3
"""FRU EEPROM image for the riser (OCP NIC 3.0 v1.6.0 section 5.10, IPMI Platform Management FRU Information
Storage Definition v1.0): common header, board info area, product info area, multirecord area with the OCP
Base OEM record 0xC0 (Table 66). Writes design/fru.bin (8 KiB, 0xFF padded) and design/fru.txt."""
import os, struct
ROOT = os.path.join(os.path.dirname(__file__), "..")
def tl(s):
b = s.encode("ascii")
assert len(b) < 64
return bytes([0xC0 | len(b)]) + b
def area(body):
body = bytes(body) + b"\xC1"
n = len(body) + 1
pad = (-n) % 8
body += b"\x00" * pad
body = bytes([body[0], (len(body) + 1) // 8]) + body[2:] if False else body
return body
def finish(fixed_head, fields):
raw = bytearray(fixed_head) + b"".join(fields) + b"\xC1"
while (len(raw) + 1) % 8:
raw += b"\x00"
raw[1] = (len(raw) + 1) // 8 # area length in multiples of 8
raw += bytes([(-sum(raw)) & 0xFF])
return bytes(raw)
def build():
MFG = "Adom (open hardware)"
NAME = "OCP3 SFF to 4x OCuLink x4"
PN = "OCP3-OCULINK4-A"
SER = "0000000001"
# board info: version 1, length (patched), language 0 (English), mfg time = minutes since 1996-01-01
import datetime
mins = int((datetime.datetime(2026, 10, 12) - datetime.datetime(1996, 1, 1)).total_seconds() // 60)
board = finish(bytes([1, 0, 0]) + struct.pack("<I", mins)[:3],
[tl(MFG), tl(NAME), tl(SER), tl(PN), tl("fru-v1")])
product = finish(bytes([1, 0, 0]),
[tl(MFG), tl(NAME), tl(PN), tl("A"), tl(SER), tl(""), tl("fru-v1")])
# OCP Base OEM record 0xC0, OEM record version 0x06 (spec 1.6.0)
rec = bytes([
0x7F, 0xA6, 0x00, # OCP IANA 42623, LSB first
0x06, # record version: 1.6.0
0x01, 0x01, # max power main / aux: under 1 W (logic + two LEDs), rounded up
0x01, 0x01, # hot / cold aisle cooling tier with passive cables: tier 1
0x00, # passive cooling (no fan)
0x00, 0x00, # hot aisle airflow with active cables: passive cables only
0x00, 0x00, # cold aisle airflow with active cables: passive cables only
0x00, 0x00, # UART config 1/2 (SFF: 0x00)
0x00, # no USB on the card edge
0x00, # manageability: none
0x04, # FRU write protection: mechanical jumper (JP1)
0x00, # programming mode not supported
0x00, 0x00, # active-cable tiers: passive cables only
0x00, 0x00, # transceiver reference power / temperature: passive cable
0x00, # thermal tier with fan fail: same (no fan)
0x00, # aux power supported ports: none (passive riser)
0x00, 0x00, # hot aisle standby airflow, passive cables
0x00, 0x00, # cold aisle standby airflow, passive cables
0x00, # maximum supported transceiver power
0xFF, # reserved
0x00, # number of physical (SMBus) controllers N = 0: last byte
])
assert len(rec) == 32
hdr = bytes([0xC0, 0x82, len(rec), (-sum(rec)) & 0xFF]) # type 0xC0, format 2 | end of list
hdr += bytes([(-sum(hdr)) & 0xFF])
multi = hdr + rec
# common header: offsets in 8-byte units
off_board = 1
off_product = off_board + len(board) // 8
off_multi = off_product + len(product) // 8
common = bytes([1, 0, 0, off_board, off_product, off_multi, 0])
common += bytes([(-sum(common)) & 0xFF])
img = common + board + product + multi
img += b"\xFF" * (8192 - len(img))
return img, dict(board=board, product=product, multi=multi, common=common)
if __name__ == "__main__":
img, parts = build()
open(os.path.join(ROOT, "design", "fru.bin"), "wb").write(img)
used = sum(len(v) for v in parts.values())
with open(os.path.join(ROOT, "design", "fru.txt"), "w") as fh:
fh.write("FRU EEPROM image for the OCP NIC 3.0 SFF to 4x OCuLink riser (BR24H64, 8 KiB, 2-byte addressing)\n")
fh.write(f"used {used} bytes; common header {parts['common'].hex(' ')}\n")
fh.write(f"board area {len(parts['board'])} B, product area {len(parts['product'])} B, multirecord {len(parts['multi'])} B\n")
fh.write("OCP Base OEM record 0xC0: " + parts["multi"][5:].hex(" ") + "\n")
fh.write("Program (JP1 bridged, card in the slot, Linux host): i2ctransfer with 2-byte addressing at 0x50 + SLOT_ID,\n"
"or ipmitool fru write <id> fru.bin from the BMC. Remove JP1 afterwards to write-protect.\n")
print(f"fru.bin {len(img)} bytes, {used} used")
# self-check: every area checksum sums to zero
for k, v in parts.items():
if k == "multi": # multirecord: 5-byte header sums to zero; data sums to zero with the header's record checksum
ok = sum(v[:5]) % 256 == 0 and (sum(v[5:]) + v[3]) % 256 == 0
else:
ok = sum(v) % 256 == 0
print(k, "checksum ok" if ok else "BAD", len(v))