Files
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression
(272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps,
and all dump-derived textures/traces) is excluded via .gitignore and stays local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:10:50 -04:00

144 lines
6.8 KiB
Bash
Executable File

#!/bin/sh
# retroDE_ps2 — Ch322 LPDDR-backed texture test (HPS-side staging + fill + check).
#
# Stages the 8x8 PSMCT32 "tritex" texture into FPGA-private LPDDR4B through the
# ps2_hps_bridge write-probe, verifies it via the read-probe, fills the on-chip
# prefilled texture cache, checks the fill counters, then re-renders so the GS
# samples the textured triangle with texels sourced FROM LPDDR (through the cache)
# at the existing 1-cycle latency.
#
# Same style/contract as ps2_status.sh / ps2_lpddr_test.sh: busybox devmem
# (avoids the devmem2 "Bus error" quirk on 0x?4-suffixed offsets).
#
# REQUIRES a bitstream built with the Ch322 profile (GS_LPDDR_TEX_DEMO + GS_LPDDR_TEX):
# ./scripts/select_de25_profile.sh lpddr_tex # then re-fit in Quartus
#
# Usage:
# ./ps2_lpddr_tex_test.sh # stage the real quadrant texture, fill, check, render
# ./ps2_lpddr_tex_test.sh --distinct # stage a SWAPPED-quadrant texture: the on-screen
# # triangle then shows the swapped colours, which can
# # ONLY come from LPDDR (the VRAM upload is unchanged)
# # — the definitive cache-is-the-source proof.
#
# Exits 0 iff fill_done=1, beats=64, bytes=2048, rd_errs=0, wr_bresp_errs=0,
# and the read-probe sees the staged texture. Suitable for automation.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
DISTINCT=0
[ "${1:-}" = "--distinct" ] && DISTINCT=1
# --- bridge register offsets (rtl/platform/ps2_hps_bridge.sv, Ch322 map) ---
OFF_CORE_CTRL=0x010 # [0] core reset: pulse 1->0 re-runs the EE bootlet (re-render)
OFF_LPDDR_STATUS=0x02C # [3] rd_pending (read-probe in flight)
OFF_LPDDR_RDADDR=0x03C # W: set read byte addr + trigger; R: 32-bit word
OFF_LPDDR_WRADDR=0x04C # W: LPDDR byte addr (auto-increments +4 per WRDATA write)
OFF_LPDDR_WRDATA=0x050 # W: data word -> single 32-bit LPDDR write + addr+=4
OFF_TEX_FILL_CTRL=0x054 # W[0]: arm cache fill; R: [0]fill_done [1]wr_busy
OFF_TEX_FILL_BEATS=0x058 # R: beats filled (expect 64)
OFF_TEX_FILL_BYTES=0x05C # R: bytes filled (expect 2048)
OFF_TEX_RD_ERRS=0x068 # R: texture-fill non-OKAY read responses (expect 0)
OFF_WR_BRESP_ERRS=0x06C # R: write-probe non-OKAY responses (expect 0)
OFF_TEX_CACHE_HITS=0x078 # R: texel reads served from the LPDDR cache during the render
OFF_TEX_BRAM_HITS=0x07C # R: texel reads served from BRAM (fallback)
# texture geometry (matches the tritex fixture + gs_texture_cache params)
TEX_LPDDR_BASE=0x00200000 # EMIF byte base where the texture is staged (= TEX_LPDDR_BASE RTL)
ROW_STRIDE=256 # TBW=1 -> 64-texel (256-byte) row stride; 8 valid texels/row
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
# tex_demo_texel(u,v): ABGR (A=FF). Quadrants: RED/GREEN/BLUE/YELLOW. --distinct
# swaps top<->bottom rows so the on-screen colours are unmistakably the staged ones.
texel() { # $1=u $2=v -> echoes 0xAABBGGRR
u=$1; v=$2
[ "$DISTINCT" = "1" ] && v=$(( 7 - v )) # vertical flip => obviously-different image
if [ $u -lt 4 ] && [ $v -lt 4 ]; then echo 0xFF0000FF # RED
elif [ $u -ge 4 ] && [ $v -lt 4 ]; then echo 0xFF00FF00 # GREEN
elif [ $u -lt 4 ] && [ $v -ge 4 ]; then echo 0xFFFF0000 # BLUE
else echo 0xFF00FFFF # YELLOW
fi
}
echo "=== Ch322 LPDDR texture test (distinct=$DISTINCT) ==="
echo "Staging 8x8 PSMCT32 texture -> LPDDR @ $TEX_LPDDR_BASE (sparse, ${ROW_STRIDE}B row stride)"
# --- stage: per row v, set WRADDR to the row base, then 8 auto-incrementing words ---
v=0
while [ $v -lt 8 ]; do
row_addr=$(( TEX_LPDDR_BASE + v * ROW_STRIDE ))
w $OFF_LPDDR_WRADDR $(printf "0x%X" $row_addr)
u=0
while [ $u -lt 8 ]; do
w $OFF_LPDDR_WRDATA "$(texel $u $v)"
u=$(( u + 1 ))
done
v=$(( v + 1 ))
done
echo " staged 64 texels (8 rows x 8)."
# --- verify a few texels via the read-probe (EMIF byte addr -> word) ---
rdprobe() { # $1 = EMIF byte addr -> echoes the 32-bit word
w $OFF_LPDDR_RDADDR "$1"
# poll rd_pending (STATUS bit3) low
i=0; while [ $i -lt 1000 ]; do
st=$(r $OFF_LPDDR_STATUS); [ $(( st & 0x8 )) -eq 0 ] && break; i=$(( i + 1 ))
done
r $OFF_LPDDR_RDADDR
}
vfail=0
check_texel() { # $1=u $2=v
addr=$(( TEX_LPDDR_BASE + $2 * ROW_STRIDE + $1 * 4 ))
got=$(rdprobe $(printf "0x%X" $addr)); exp=$(texel $1 $2)
gv=$(( got )); ev=$(( exp ))
if [ $gv -ne $ev ]; then printf " VERIFY FAIL (%d,%d): got 0x%08X exp 0x%08X\n" "$1" "$2" "$gv" "$ev"; vfail=1
else printf " verify (%d,%d) ok = 0x%08X\n" "$1" "$2" "$gv"; fi
}
echo "Read-probe verify (corners):"
check_texel 0 0 # RED (top-left)
check_texel 4 0 # GREEN
check_texel 0 4 # BLUE
check_texel 4 4 # YELLOW
# --- arm the cache fill + check counters ---
echo "Arming texture-cache fill ..."
w $OFF_TEX_FILL_CTRL 0x1
i=0; fd=0
while [ $i -lt 1000 ]; do
st=$(r $OFF_TEX_FILL_CTRL); [ $(( st & 0x1 )) -eq 1 ] && { fd=1; break; }; i=$(( i + 1 ))
done
beats=$(( $(r $OFF_TEX_FILL_BEATS) ))
bytes=$(( $(r $OFF_TEX_FILL_BYTES) ))
rderr=$(( $(r $OFF_TEX_RD_ERRS) ))
wberr=$(( $(r $OFF_WR_BRESP_ERRS) ))
printf " fill_done=%d beats=%d (exp 64) bytes=%d (exp 2048) tex_rd_errs=%d wr_bresp_errs=%d\n" \
"$fd" "$beats" "$bytes" "$rderr" "$wberr"
# --- re-render so the bootlet draws the textured triangle (texels now from LPDDR) ---
echo "Re-rendering (CORE_CTRL pulse) ..."
w $OFF_CORE_CTRL 0x1; sleep 1; w $OFF_CORE_CTRL 0x0; sleep 3
# --- DEFINITIVE camera-free proof: texel-source counters for the render just done ---
# (reset by the core reset above, so they reflect ONLY this render).
chits=$(( $(r $OFF_TEX_CACHE_HITS) ))
bhits=$(( $(r $OFF_TEX_BRAM_HITS) ))
printf "Texel source this render: cache_hits=%d bram_hits=%d\n" "$chits" "$bhits"
echo "Done. The textured triangle should now be on HDMI (texels sourced from LPDDR via the cache)."
[ "$DISTINCT" = "1" ] && echo " (--distinct: colours are vertically swapped => they came from LPDDR, not the VRAM upload.)"
# --- verdict ---
ok=1
[ "$fd" -eq 1 ] || { echo "FAIL: fill_done=0"; ok=0; }
[ "$beats" -eq 64 ] || { echo "FAIL: beats=$beats (exp 64)"; ok=0; }
[ "$bytes" -eq 2048 ] || { echo "FAIL: bytes=$bytes (exp 2048)"; ok=0; }
[ "$rderr" -eq 0 ] || { echo "FAIL: tex_rd_errs=$rderr"; ok=0; }
[ "$wberr" -eq 0 ] || { echo "FAIL: wr_bresp_errs=$wberr"; ok=0; }
[ "$vfail" -eq 0 ] || { echo "FAIL: read-probe verify mismatch"; ok=0; }
# THE acceptance proof for "texture storage external": the render consumed texels
# from the LPDDR cache. cache_hits>0 (and bram_hits=0) proves it without a camera.
[ "$chits" -gt 0 ] || { echo "FAIL: tex_cache_hits=0 — render did NOT consume LPDDR-cached texels"; ok=0; }
if [ "$ok" -eq 1 ]; then echo "=== PASS ==="; exit 0; else echo "=== FAIL ==="; exit 1; fi