aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/timing_map.py
blob: 6fd22b2b6c5dde5bf8c49d9cde357e61446913b6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""Map cmd 0x0d's accepted parameter space using the timing oracle.

Status is uniformly 0 across the whole accepted range, so timing is the only
signal. Round-robin over all cases per round so clock drift affects every
case equally and cannot manufacture structure.
"""
import ctypes, time, statistics
from tinygrad import Device
from tinygrad.runtime.autogen.am import am

d = Device["AMD"]; adev = d.iface.dev_impl; psp = adev.psp
SZ = ctypes.sizeof(am.struct_psp_gfx_cmd_resp); C = 28

def timed(cid, words):
    adev.vram.view(psp.cmd_paddr, SZ)[:] = b"\x00" * SZ
    c = am.struct_psp_gfx_cmd_resp(cmd_id=cid)
    buf = bytearray(memoryview(c).cast('B'))
    for i, v in words.items():
        buf[C+i*4:C+4+i*4] = int(v & 0xFFFFFFFF).to_bytes(4, 'little')
    cmd = am.struct_psp_gfx_cmd_resp.from_buffer_copy(bytes(buf))
    t0 = time.perf_counter()
    try: psp._ring_submit(cmd)
    except Exception: pass
    return (time.perf_counter() - t0) * 1e3

ROUNDS = 24
cases = [("REJECT", {0: 0xF})]
for idx in range(0x0, 0xF):
    cases.append((f"idx={idx:#03x} w2=1", {0: idx, 1: 0, 2: 1}))
    cases.append((f"idx={idx:#03x} w2=0", {0: idx, 1: 0, 2: 0}))

for _ in range(6): timed(0x7e, {})
samples = {lbl: [] for lbl, _ in cases}
for r in range(ROUNDS):
    for lbl, w in cases:
        samples[lbl].append(timed(0x0d, w))

rej = statistics.median(samples["REJECT"])
print(f"reject baseline = {rej:.3f} ms   (n={ROUNDS} per case)\n")
print(f"{'case':<16s} {'median':>8s} {'delta':>8s} {'sd':>7s}   bar")
rows = []
for lbl, _ in cases:
    if lbl == "REJECT": continue
    m = statistics.median(samples[lbl]); sd = statistics.pstdev(samples[lbl])
    rows.append((lbl, m, m - rej, sd))
for lbl, m, dl, sd in rows:
    bar = "#" * max(0, int(dl / 0.01))
    print(f"{lbl:<16s} {m:>8.3f} {dl:>+8.3f} {sd:>7.3f}   {bar}")

ds = sorted(r[2] for r in rows)
print(f"\ndelta range across accepted space: {ds[0]:+.3f} .. {ds[-1]:+.3f} ms  (spread {ds[-1]-ds[0]:.3f})")
grp = {}
for lbl, m, dl, sd in rows:
    grp.setdefault(round(dl, 2), []).append(lbl)
print("\nclustering by delta (0.01 ms buckets):")
for k in sorted(grp):
    print(f"  {k:+.2f} ms : {len(grp[k]):2d}  {', '.join(grp[k][:8])}{' ...' if len(grp[k])>8 else ''}")
print("\nsOS alive:", psp.is_sos_alive())