#!/usr/bin/env python3 """READ-ONLY probe: VRAM size + PSP boot state on a Navi31 (RX 7900 XTX) attached over a USB<->PCIe enclosure (ASM2464PD-class bridge). Purpose: establish what a HEALTHY card reports, as a control. This script ONLY READS registers. No writes, no flash access, no reset, no power cycling. Safe to run on a working card. Only requirement: `tinygrad` must be importable (a checkout on PYTHONPATH, or an installed copy). It does NOT need to be run from the repo root. python3 vram_probe.py Two probes run in separate subprocesses, because the device can only be opened by one process at a time. """ import subprocess, sys # mmRCC_CONFIG_MEMSIZE: fixed RCC register, readable before IP discovery (that is # exactly why tinygrad uses it). Hardware address, not firmware dependent. MEMSIZE_DWORD = 0xde3 # dword index -> byte 0x378C TSU_CMD = 0x582CC # = MP0 C2PMSG_115, the PSP TSU flash/update mailbox # (byte offset in BAR5; Navi31/MP0-13.0.0 specific) # --- probe 1: raw registers. Works even when tinygrad cannot open the card. --- # open_gpu()/MMIO are inlined from tinygrad's extra/amdflash/common.py so this # script depends only on tinygrad itself. RAW = r''' import struct from tinygrad.runtime.support.usb import USB3 from tinygrad.runtime.support.system import USBPCIDevice cands = [] for vid, pid in ((0x3801, 0x0001), (0xADD1, 0x0001)): cands += list(USB3.list_devices(vid, pid)) print(" USB bridges found: %%d" %% len(cands)) if not cands: raise SystemExit(" no bridge found - is the enclosure connected and powered?") gpu = USBPCIDevice("AM", *cands[0]) bar5 = gpu.map_bar(5, fmt='B') rd32 = lambda off: struct.unpack(' VRAM = %%d MB" %% (ms, ms)) print(" TSU mailbox (C2PMSG_115) = %%#010x (bit31 set = task ready)" %% rd32(%d)) print(" VRAM[0:16] = %%s" %% bytes(gpu.map_bar(0)[0:16]).hex()) print() print(" >>> " + ("MEMSIZE READS 0 - memory controller reports no VRAM" if ms == 0 else "MEMSIZE OK: %%d MB" %% ms)) ''' % (MEMSIZE_DWORD, TSU_CMD) # --- probe 2: the normal tinygrad path ------------------------------------- HIGH = r''' from tinygrad import Device, Tensor d = Device["AMD"]; adev = d.iface.dev_impl print(" opened OK") print(" vram_size = %#x (%d MB)" % (adev.vram_size, adev.vram_size >> 20)) print(" PSP version = %#x" % adev.reg("regMP0_SMN_C2PMSG_58").read()) hb = [adev.reg("regMP0_SMN_C2PMSG_81").read() for _ in range(3)] print(" PSP heartbeat x3 = %s -> %s" % ([hex(x) for x in hb], "ADVANCING (alive)" if len(set(hb)) > 1 else "FROZEN")) print(" compute test:", (Tensor([2.0, 4.0]) * 3).numpy().tolist()) ''' def run(title, code): print("\n=== %s ===" % title) try: p = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, timeout=300) except subprocess.TimeoutExpired: print(" FAILED: timed out"); return if (out := (p.stdout or "").strip()): print(out) if p.returncode != 0: import re # pick the last line that looks like "SomeError: message" cand = [l.strip().lstrip("| ").strip() for l in (p.stderr or "").splitlines() if re.search(r"^[|+\s]*\w[\w.]*(Error|Exception)\b.*:", l)] print(" FAILED: %s" % (cand[-1] if cand else "see stderr for details")) run("raw register read (works even if tinygrad can't open the card)", RAW) run("tinygrad Device['AMD']", HIGH) print(""" -------------------------------------------------------------------- Please report back all of: * mmRCC_CONFIG_MEMSIZE value * TSU mailbox (C2PMSG_115) value * whether Device['AMD'] opened, and the vram_size it printed --------------------------------------------------------------------""")