#!/usr/bin/env python3 """retroDE_ps2 — Ch357: verify the RASTER's emitted per-fragment Z (persp_z5) end-to-end. Replays the captured raster fragment stream {epoch, x, y, persp_z, color} (from tb_top_psmct32_sh3_zsched -> zsched_frags.txt) through the SAME clamp16 persistent-Z model the RTL ROP (gs_lpddr_zc_emit) implements, and compares the resulting per-pixel OWNER + reject-vs-paint-order to the software oracle (gs_sh3_z_oracle emits sh3_zsched_zowner.mem / _reject.mem). If persp_z5 is the authentic screen-linear Z (right value, right pixel), the replay reproduces the oracle: covered=21692, reject=3971 for the primary [8634,12757,145742]. A wrong Z value or a pipeline-stage misalignment shows up as a reject/owner mismatch — so this is the decisive check on the new gs_stub RTL, independent of the (separately unit-proven) ROP. Usage: verify_persp_z.py [frags.txt] [--tag zsched] [--fbpxw 256] """ import sys, os HERE=os.path.dirname(os.path.abspath(__file__)); ROOT=os.path.normpath(os.path.join(HERE,"..")) DATA=os.path.join(ROOT,"sim","data","top_psmct32_raster_demo") def clamp16(z): z=int(z); return 0xFFFF if z>0xFFFF else (0 if z<0 else z) def main(argv): a=argv[1:] frags = a[0] if a and not a[0].startswith("--") else os.path.join(ROOT,"sim","traces","rtl","zsched_frags.txt") tag = a[a.index("--tag")+1] if "--tag" in a else "zsched" if not os.path.exists(frags): sys.exit(f"[perspZ] no fragment capture at {frags} (run make tb_top_psmct32_sh3_zsched)") # load the oracle owner map: [31]cov [30]reject [27:24]z_owner(+1) [23:8]zq16 [3:0]paint_owner(+1) owner_fn=os.path.join(DATA,f"sh3_{tag}_zowner.mem") if not os.path.exists(owner_fn): sys.exit(f"[perspZ] no oracle map {owner_fn} (run gs_sh3_z_oracle.py --draw-list ... --emit)") orc=[int(l,16)&0xFFFFFFFF for l in open(owner_fn) if l.strip() and not l.startswith("//")] NPX=len(orc) # infer FB width: prefer param, else from the oracle geometry note isn't stored — require --fbpxw or default 256 FBPXW = int(a[a.index("--fbpxw")+1]) if "--fbpxw" in a else 256 # replay the captured fragments (in emit order == dump/paint order across epochs) through persistent clamp16 GEQUAL zbuf=[-1]*NPX; z_owner=[-1]*NPX; paint_owner=[-1]*NPX; cov=[0]*NPX nfr=0; oob=0 for ln in open(frags): p=ln.split() if len(p)<5: continue ep=int(p[0]); x=int(p[1]); y=int(p[2]); z=int(p[3]) o=y*FBPXW+x if o<0 or o>=NPX: oob+=1; continue nfr+=1; cov[o]=1 zq=clamp16(z) paint_owner[o]=ep if zq>=zbuf[o]: zbuf[o]=zq; z_owner[o]=ep # GEQUAL, persistent across epochs, ZMSK=0 # compare to the oracle covered=sum(cov); reject=sum(1 for o in range(NPX) if cov[o] and z_owner[o]!=paint_owner[o]) # oracle covered/reject orc_cov=sum(1 for w in orc if w>>31); orc_rej=sum(1 for w in orc if (w>>30)&1) # per-pixel owner agreement (where both cover) owner_mismatch=0; both=0 for o in range(NPX): if cov[o] and (orc[o]>>31): both+=1 orc_owner=((orc[o]>>24)&0xF)-1 if z_owner[o]!=orc_owner: owner_mismatch+=1 print(f"[perspZ] replayed {nfr} captured fragments (oob={oob}); FBPXW={FBPXW}, NPX={NPX}") print(f"[perspZ] REPLAY : covered={covered} reject-vs-paint={reject}") print(f"[perspZ] ORACLE : covered={orc_cov} reject={orc_rej}") agree = 100.0*(both-owner_mismatch)/max(both,1) rej_ratio = reject/max(orc_rej,1) print(f"[perspZ] per-pixel z-owner agreement (both-covered {both}): {agree:.2f}% ({owner_mismatch} mismatch)") print(f"[perspZ] reject ratio replay/oracle = {rej_ratio:.3f} (1.0 = persp_z5 authentic; ~0 or >>1 = broken/misaligned)") # tolerate RTL-vs-float edge/coverage + fixed-point-Z noise; catch a GROSS persp_z5 error (wrong stage/value). ok = (agree >= 95.0) and (0.80 <= rej_ratio <= 1.20) if ok: print(f"[perspZ] PASS — the raster's persp_z5 reproduces the clamp16 oracle owner/reject within edge tolerance. " f"persp_z5 is the authentic screen-linear Z, correctly aligned to the emit.") return 0 print(f"[perspZ] FAIL — persp_z5 diverges from the oracle (wrong Z value or pipeline-stage misalignment). Numbers above.") return 1 if __name__=="__main__": raise SystemExit(main(sys.argv))