aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/probe_i2c_numcmds_timing.py
blob: b3018604d7567858976673add302ab38476a73ba (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
"""Does the SMU clamp NumCmds to MAX_SW_I2C_COMMANDS (24)?

port=1 demonstrably reaches the I2C engine (transaction time scales with work,
vs port=0 which is rejected in ~0.3ms). If the firmware clamps NumCmds to 24,
elapsed time flattens at n=24. If it walks all NumCmds entries, time keeps
scaling past 24 -> the array walk is unbounded -> OOB read of SwI2cCmds[].

SAFETY: READ commands only (READWRITE bit clear) => nothing is written to any
I2C device. NumCmds kept <= 56 so any over-walk stays inside the 116-byte table
buffer that the SMU DMA'd from our own DRAM.
"""
import time, struct, statistics
from tinygrad import Device
import tinygrad.runtime.autogen.am.smu_13_0_0 as smu
d=Device["AMD"]; adev=d.iface.dev_impl
TP=adev.smu.driver_table_paddr; SIZE=116
MSG=adev.mmMP1_SMN_C2PMSG_66; ARG=adev.mmMP1_SMN_C2PMSG_82; RESP=adev.mmMP1_SMN_C2PMSG_90
N={0:'BUSY',1:'OK',0xFF:'FAILED',0xFE:'UNKNOWN',0xFD:'BAD_PREREQ',0xFC:'REJ_BUSY',0xFB:'REJ_PREREQ'}
def send(msg,param=0,tmo=5.0):
    RESP.write(0); ARG.write(param&0xffffffff); MSG.write(msg)
    t0=time.monotonic()
    while time.monotonic()-t0<tmo:
        r=RESP.read()
        if r!=0: return N.get(r,hex(r)),(time.monotonic()-t0)*1000
    return 'TIMEOUT',(time.monotonic()-t0)*1000
def wr(b):
    v=adev.vram.view(TP,len(b),'I')
    for i in range(len(b)//4): v[i]=struct.unpack_from('<I',b,4*i)[0]
STOP=smu.CMDCONFIG_STOP_MASK; RESTART=smu.CMDCONFIG_RESTART_MASK
def build(n, port=1, speed=2, slave=0x50):
    b=bytearray(SIZE)
    b[0]=port; b[1]=speed; b[2]=slave; b[3]=n&0xFF
    for i in range(24):
        b[4+2*i]=0x00; b[5+2*i]=0          # READ, no stop
    b[5]|=RESTART
    # deliberately DO NOT set STOP on the last in-bounds entry, so a firmware that
    # keeps walking has to run off the end to find one.
    for off in range(52,SIZE): b[off]=0xCC  # 0xCC config => STOP|RESTART bits set
    return bytes(b)
print("=== NumCmds vs elapsed time (port=1, READ cmds) ===")
print("  n     resp        median_ms   samples")
res={}
for n in (1,2,4,8,16,24,28,32,40,48,56):
    ts=[]; rr=None
    for _ in range(7):
        wr(build(n))
        r,ms=send(smu.PPSMC_MSG_TransferTableDram2Smu,9)
        rr=r; ts.append(ms)
    res[n]=statistics.median(ts)
    print("  %-5d %-10s %8.2f    %s"%(n,rr,res[n]," ".join("%.1f"%x for x in sorted(ts))))
print()
base=res[24]
print("median(n=24) = %.2f ms"%base)
for n in (28,32,40,48,56):
    print("  n=%-3d  %.2f ms   ratio vs n=24: %.2fx"%(n,res[n],res[n]/base if base else 0))
print()
print("MEMSIZE:",hex(adev.rreg(0xde3)))