diff --git a/docs/MODEL_REBUILD.md b/docs/MODEL_REBUILD.md index dffe95adb..e810d3a8b 100644 --- a/docs/MODEL_REBUILD.md +++ b/docs/MODEL_REBUILD.md @@ -88,6 +88,12 @@ For a model that cannot run on the device GPU, compile with the USB AMD GPU atta ./models --lebowski --gpu ``` +The ASM2464PD bridge must run the current tinygrad custom firmware from +https://github.com/tinygrad/asm2464pd-firmware. Its USB product string starts +with `custom`; the legacy `USB 3.2 PCIe TinyEnclosure` patch is not compatible +with comma's current external-GPU runtime. Firmware flashing is a separate, +explicit hardware setup step and StarPilot never performs it automatically. + The dynamic flag (`--lebowski` above) sets the output and manifest model ID; when only one source model is staged, its ONNX filename does not need to match that ID. Input format and behavior version are inferred. `--external-gpu` diff --git a/tinygrad_repo/test/unit/test_usb_gpu_firmware.py b/tinygrad_repo/test/unit/test_usb_gpu_firmware.py new file mode 100644 index 000000000..09353b8bf --- /dev/null +++ b/tinygrad_repo/test/unit/test_usb_gpu_firmware.py @@ -0,0 +1,37 @@ +import pytest + +from tinygrad.runtime.support import system + + +class FakeUSB: + def __init__(self, product: str, is_custom: bool): + self.product, self.is_custom = product, is_custom + + +def patch_usb_dependencies(monkeypatch, usb): + calls = [] + monkeypatch.setattr(system.System, "flock_acquire", lambda _: object()) + monkeypatch.setattr(system, "USB3", lambda *args, **kwargs: calls.append((args, kwargs)) or usb) + return calls + + +def test_usb_gpu_rejects_legacy_bridge_firmware(monkeypatch): + calls = patch_usb_dependencies(monkeypatch, FakeUSB("USB 3.2 PCIe TinyEnclosure", False)) + + with pytest.raises(RuntimeError, match="unsupported legacy USB GPU firmware"): + system.USBPCIDevice("AM", object(), "usb:3-6") + + assert calls[0][1] == {"use_bot": True} + + +def test_usb_gpu_accepts_custom_bridge_firmware(monkeypatch): + usb = FakeUSB("custom ASM2464PD", True) + calls = patch_usb_dependencies(monkeypatch, usb) + controller = object() + monkeypatch.setattr(system, "CustomASM24Controller", lambda candidate: controller if candidate is usb else None) + monkeypatch.setattr(system.System, "pci_setup_usb_bars", lambda *args, **kwargs: {2: (0, 1)}) + + device = system.USBPCIDevice("AM", object(), "usb:3-6") + + assert calls[0][1] == {"use_bot": True} + assert device.usb is controller diff --git a/tinygrad_repo/tinygrad/runtime/support/system.py b/tinygrad_repo/tinygrad/runtime/support/system.py index bb47c77d5..34031e965 100644 --- a/tinygrad_repo/tinygrad/runtime/support/system.py +++ b/tinygrad_repo/tinygrad/runtime/support/system.py @@ -220,9 +220,11 @@ class USBPCIDevice(PCIDevice): def __init__(self, devpref:str, dev, pcibus): self.pcibus, self.peer_group = pcibus, f"USBPCIDevice_{pcibus}" self.lock_fd = System.flock_acquire(f"{devpref.lower()}_{pcibus.lower()}.lock") - usb = USB3(dev, 0x81, 0x83, 0x02, 0x04) + usb = USB3(dev, 0x81, 0x83, 0x02, 0x04, use_bot=True) if DEBUG >= 1: print(f"am {self.pcibus}: product string: {usb.product!r}") - self.usb: CustomASM24Controller | ASM24Controller = CustomASM24Controller(usb) if usb.is_custom else ASM24Controller(usb) + if not usb.is_custom: + raise RuntimeError(f"unsupported legacy USB GPU firmware ({usb.product!r}); flash the current tinygrad ASM2464PD custom firmware") + self.usb: CustomASM24Controller = CustomASM24Controller(usb) self._bar_info = System.pci_setup_usb_bars(self.usb, gpu_bus=4, mem_base=0x10000000, pref_mem_base=(32 << 30)) self.sram = BumpAllocator(size=0x80000, wrap=False) # asm24 controller sram diff --git a/tinygrad_repo/tinygrad/runtime/support/usb.py b/tinygrad_repo/tinygrad/runtime/support/usb.py index 1ff0d936d..54562b7a5 100644 --- a/tinygrad_repo/tinygrad/runtime/support/usb.py +++ b/tinygrad_repo/tinygrad/runtime/support/usb.py @@ -5,10 +5,6 @@ from tinygrad.helpers import DEBUG, DEV, to_mv, round_up, OSX, getenv, ceildiv from tinygrad.runtime.support.hcq import MMIOInterface from tinygrad.runtime.support import c -CUSTOM_ASM24_PRODUCT_PREFIXES = ("custom", "USB 3.2 PCIe TinyEnclosure") - -def is_custom_asm24_product(product:str) -> bool: return product.startswith(CUSTOM_ASM24_PRODUCT_PREFIXES) - def alloc_cbuffer(sz:int) -> tuple[ctypes.Array, memoryview]: return (buf:=(ctypes.c_ubyte * sz)()), to_mv(ctypes.addressof(buf), sz) def checked(fn, msg=None): @functools.wraps(fn) @@ -67,7 +63,7 @@ class USB3: checked(libusb.libusb_get_device_descriptor)(libusb.libusb_get_device(self.handle), ctypes.byref(_desc)) _ret = checked(libusb.libusb_get_string_descriptor_ascii)(self.handle, _desc.iProduct, _buf, 256) self.product = bytes(_buf[:_ret]).decode("ascii", errors="replace") - self.is_custom = is_custom_asm24_product(self.product) + self.is_custom = self.product.startswith("custom") if self.is_custom: self.use_bot = use_bot = True # Detach kernel driver if needed