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