← Commit history

Delete design/gen/fru.py

John Lauer ·49e2b119e1 ·7d ago ·parent 3ebf12a
1 file changed −103
design/gen/fru.pydeleted−103
@@ -1,103 +0,0 @@-#!/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))