ec82764bef
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>
144 lines
6.8 KiB
Bash
Executable File
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
|