#!/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