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>
This commit is contained in:
2026-06-29 20:10:50 -04:00
commit ec82764bef
2462 changed files with 2174303 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
// ============================================================================
// lpddr_dump.c — Ch319 Brick 3 — HPS reads FPGA-private LPDDR4B back THROUGH
// THE HPS BRIDGE (never /dev/mem of the framebuffer itself).
//
// mmaps ONLY the PS2 HPS-bridge register window (the same window ps2_status.sh
// uses), then drives the LPDDR4B read-probe one 32-bit word at a time:
// write LPDDR_RDADDR (0x03C) = byte addr -> sets address + triggers a read
// poll LPDDR_STATUS (0x02C) bit3 (rd_pending) until 0
// read LPDDR_RDATA (0x03C) -> the 32-bit word
//
// Output:
// default : raw little-endian bytes to stdout (pipe to md5sum / save .bin)
// --ppm W H: decode PSMCT16 (RGB5A1) -> binary PPM (P6) on stdout
//
// Disarm the writer first (the FB must be static while dumping):
// busybox devmem 0x40000018 w 0x2
//
// Build on the HPS: gcc -O2 -o lpddr_dump lpddr_dump.c
// Usage:
// sudo ./lpddr_dump 0 8192 > fb.bin ; md5sum fb.bin # acceptance (expect 3b12baff...)
// sudo ./lpddr_dump --ppm 64 64 0 > fb.ppm # screen-dump (64x64 PSMCT16)
// Env: PS2_BRIDGE_BASE (default 0x40000000).
// ============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define OFF_LPDDR_STATUS 0x02C // bit3 = rd_pending
#define OFF_LPDDR_RDPORT 0x03C // write = addr+trigger, read = data
#define MAP_SPAN 0x1000
static volatile uint32_t *g_reg;
static uint32_t rd_word(uint32_t byte_addr) {
long spin = 0;
g_reg[OFF_LPDDR_RDPORT/4] = byte_addr; // set addr + trigger read
while (g_reg[OFF_LPDDR_STATUS/4] & 0x8) { // wait rd_pending -> 0
if (++spin > 100000000L) {
fprintf(stderr, "lpddr_dump: TIMEOUT waiting for read @0x%x\n", byte_addr);
exit(2);
}
}
return g_reg[OFF_LPDDR_RDPORT/4];
}
int main(int argc, char **argv) {
const char *base_env = getenv("PS2_BRIDGE_BASE");
unsigned long bridge_base = base_env ? strtoul(base_env, NULL, 0) : 0x40000000UL;
int ppm = 0, ai = 1, w = 0, h = 0;
if (argc > ai && strcmp(argv[ai], "--ppm") == 0) {
ppm = 1; ai++;
if (argc < ai + 3) { fprintf(stderr, "usage: %s --ppm W H START\n", argv[0]); return 1; }
w = atoi(argv[ai++]); h = atoi(argv[ai++]);
}
if (argc <= ai) { fprintf(stderr, "usage: %s [--ppm W H] START [LEN]\n", argv[0]); return 1; }
uint32_t start = (uint32_t)strtoul(argv[ai++], NULL, 0);
uint32_t len = ppm ? (uint32_t)(w * h * 2) : (argc > ai ? (uint32_t)strtoul(argv[ai], NULL, 0) : 8192);
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) { perror("/dev/mem"); return 1; }
void *map = mmap(NULL, MAP_SPAN, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bridge_base);
if (map == MAP_FAILED) { perror("mmap bridge"); return 1; }
g_reg = (volatile uint32_t *)map;
if (ppm) printf("P6\n%d %d\n255\n", w, h);
// Read in 32-bit words; LEN is byte count (word-aligned up).
for (uint32_t a = 0; a < len; a += 4) {
uint32_t word = rd_word(start + a);
uint8_t b[4] = { word & 0xff, (word >> 8) & 0xff, (word >> 16) & 0xff, (word >> 24) & 0xff };
if (!ppm) {
fwrite(b, 1, 4, stdout);
} else {
// two PSMCT16 (RGB5A1) pixels per word, little-endian halfwords.
for (int p = 0; p < 2; p++) {
uint16_t px = b[p*2] | (b[p*2+1] << 8);
uint8_t r = ((px >> 0) & 0x1f) << 3;
uint8_t g = ((px >> 5) & 0x1f) << 3;
uint8_t bl= ((px >> 10) & 0x1f) << 3;
uint8_t rgb[3] = { r, g, bl };
fwrite(rgb, 1, 3, stdout);
}
}
}
munmap(map, MAP_SPAN);
close(fd);
return 0;
}
@@ -0,0 +1,39 @@
#!/bin/sh
# retroDE_ps2 — Ch336 DEFINITIVE color diagnostic.
#
# 14-prim >FIFO_DEPTH scene: batch0 (tiles 0-7) BLUE, batch1 (tiles 8-13) GREEN.
# GREEN (0,FF,0) shares NO color channel with RED (the suspected fallback) or BLUE (batch0),
# so batch 1's rendered color is unambiguous. Read the HDMI bottom rows and report:
# GREEN bottom -> batch1 color tracks its staged value (the color path is fine).
# RED bottom -> batch1 ignores its staged color and falls back to a constant RED.
# BLUE bottom -> batch1 reuses batch0's color.
# Top half should be BLUE either way. Accumulation (both halves lit) should still hold.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
GREEN="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ffff0000 0000000000000000 0000500000100010 00000000ffff0000 0000000000000030 00005000001000e0 00000000ffff0000 00000000000c0000 0000500000e00010 00000000ffff0000 0000000000000000 0000510000100110 00000000ffff0000 0000000000000030 00005100001001e0 00000000ffff0000 00000000000c0000 0000510000e00110 00000000ffff0000 0000000000000000 0000520000100210 00000000ffff0000 0000000000000030 00005200001002e0 00000000ffff0000 00000000000c0000 0000520000e00210 00000000ffff0000 0000000000000000 0000530000100310 00000000ffff0000 0000000000000030 00005300001003e0 00000000ffff0000 00000000000c0000 0000530000e00310 00000000ffff0000 0000000000000000 0000540001100010 00000000ffff0000 0000000000000030 00005400011000e0 00000000ffff0000 00000000000c0000 0000540001e00010 00000000ffff0000 0000000000000000 0000550001100110 00000000ffff0000 0000000000000030 00005500011001e0 00000000ffff0000 00000000000c0000 0000550001e00110 00000000ffff0000 0000000000000000 0000560001100210 00000000ffff0000 0000000000000030 00005600011002e0 00000000ffff0000 00000000000c0000 0000560001e00210 00000000ffff0000 0000000000000000 0000570001100310 00000000ffff0000 0000000000000030 00005700011003e0 00000000ffff0000 00000000000c0000 0000570001e00310 00000000ff00ff00 0000000000000000 0000580002100010 00000000ff00ff00 0000000000000030 00005800021000e0 00000000ff00ff00 00000000000c0000 0000580002e00010 00000000ff00ff00 0000000000000000 0000590002100110 00000000ff00ff00 0000000000000030 00005900021001e0 00000000ff00ff00 00000000000c0000 0000590002e00110 00000000ff00ff00 0000000000000000 00005a0002100210 00000000ff00ff00 0000000000000030 00005a00021002e0 00000000ff00ff00 00000000000c0000 00005a0002e00210 00000000ff00ff00 0000000000000000 00005b0002100310 00000000ff00ff00 0000000000000030 00005b00021003e0 00000000ff00ff00 00000000000c0000 00005b0002e00310 00000000ff00ff00 0000000000000000 00005c0003100010 00000000ff00ff00 0000000000000030 00005c00031000e0 00000000ff00ff00 00000000000c0000 00005c0003e00010 00000000ff00ff00 0000000000000000 00005d0003100110 00000000ff00ff00 0000000000000030 00005d00031001e0 00000000ff00ff00 00000000000c0000 00005d0003e00110"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready"; return 1
}
echo "=== Ch336 DEFINITIVE: batch0 BLUE (top), batch1 GREEN (bottom) ==="
wait_ready || exit 1
w $OFF_STATUS 0x0; n=0
for word in $GREEN; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "wrote $n words; bridge addr=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || exit 1
w $OFF_GO 0x1
wait_ready || exit 1
echo "records=$(( $(r $OFF_HI) )) (expect 14)"
echo "=== Report HDMI: TOP color (expect BLUE) and BOTTOM color (GREEN=ok / RED=fallback / BLUE=batch0 reuse). ==="
@@ -0,0 +1,42 @@
#!/bin/sh
# retroDE_ps2 — Ch336 DIAGNOSTIC: color-swapped >FIFO_DEPTH accumulation.
#
# Same 14-prim scene as ps2_feeder_accum_test.sh but with the batch colors SWAPPED:
# batch 0 (prims 0-7, tiles 0-7) = BLUE (was RED)
# batch 1 (prims 8-13, tiles 8-13) = RED (was BLUE)
# Localizes the board color bug (the original showed RED top AND RED bottom = batch-0's color
# everywhere). Read the HDMI and report TOP-half color and BOTTOM-rows color:
# * BLUE top + BLUE bottom -> the FIRST batch's color is sticking for the whole scene (per-prim
# color not advancing across FIFO batches).
# * BLUE top + RED bottom -> correct! the bug isn't here (then the original was something else).
# * RED everywhere -> the LAST batch's color is sticking.
# Either way the accumulation (both halves lit) should still hold.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
SWAP="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ffff0000 0000000000000000 0000500000100010 00000000ffff0000 0000000000000030 00005000001000e0 00000000ffff0000 00000000000c0000 0000500000e00010 00000000ffff0000 0000000000000000 0000510000100110 00000000ffff0000 0000000000000030 00005100001001e0 00000000ffff0000 00000000000c0000 0000510000e00110 00000000ffff0000 0000000000000000 0000520000100210 00000000ffff0000 0000000000000030 00005200001002e0 00000000ffff0000 00000000000c0000 0000520000e00210 00000000ffff0000 0000000000000000 0000530000100310 00000000ffff0000 0000000000000030 00005300001003e0 00000000ffff0000 00000000000c0000 0000530000e00310 00000000ffff0000 0000000000000000 0000540001100010 00000000ffff0000 0000000000000030 00005400011000e0 00000000ffff0000 00000000000c0000 0000540001e00010 00000000ffff0000 0000000000000000 0000550001100110 00000000ffff0000 0000000000000030 00005500011001e0 00000000ffff0000 00000000000c0000 0000550001e00110 00000000ffff0000 0000000000000000 0000560001100210 00000000ffff0000 0000000000000030 00005600011002e0 00000000ffff0000 00000000000c0000 0000560001e00210 00000000ffff0000 0000000000000000 0000570001100310 00000000ffff0000 0000000000000030 00005700011003e0 00000000ffff0000 00000000000c0000 0000570001e00310 00000000ff0000ff 0000000000000000 0000580002100010 00000000ff0000ff 0000000000000030 00005800021000e0 00000000ff0000ff 00000000000c0000 0000580002e00010 00000000ff0000ff 0000000000000000 0000590002100110 00000000ff0000ff 0000000000000030 00005900021001e0 00000000ff0000ff 00000000000c0000 0000590002e00110 00000000ff0000ff 0000000000000000 00005a0002100210 00000000ff0000ff 0000000000000030 00005a00021002e0 00000000ff0000ff 00000000000c0000 00005a0002e00210 00000000ff0000ff 0000000000000000 00005b0002100310 00000000ff0000ff 0000000000000030 00005b00021003e0 00000000ff0000ff 00000000000c0000 00005b0002e00310 00000000ff0000ff 0000000000000000 00005c0003100010 00000000ff0000ff 0000000000000030 00005c00031000e0 00000000ff0000ff 00000000000c0000 00005c0003e00010 00000000ff0000ff 0000000000000000 00005d0003100110 00000000ff0000ff 0000000000000030 00005d00031001e0 00000000ff0000ff 00000000000c0000 00005d0003e00110"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready"; return 1
}
echo "=== Ch336 DIAG: color-swapped accum (batch0 BLUE top, batch1 RED bottom) ==="
wait_ready || exit 1
w $OFF_STATUS 0x0; n=0
for word in $SWAP; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "wrote $n words; bridge addr=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || exit 1
w $OFF_GO 0x1
wait_ready || exit 1
echo "records=$(( $(r $OFF_HI) )) (expect 14)"
echo "=== Report HDMI: TOP-half color and BOTTOM-rows color (see header for what each means). ==="
+46
View File
@@ -0,0 +1,46 @@
#!/bin/sh
# retroDE_ps2 — Ch336 >FIFO_DEPTH FRAMEBUFFER ACCUMULATION silicon proof.
#
# A 14-primitive scene (FIFO depth is 8) renders in TWO batches that COMPOSE into one framebuffer
# instead of wiping each other (the pre-Ch336 behavior):
# batch 0 (prims 0-7, tiles 0-7) = RED (clears + full-flushes the framebuffer)
# batch 1 (prims 8-13, tiles 8-13) = BLUE (sparse-flushes only its pixels onto the accumulated FB)
# PROOF: the RED top-half AND the BLUE bottom-rows are simultaneously visible at the end. If batches
# wiped each other, the RED tiles (0-7) would be green. v1 = color accumulation, per-batch Z
# (shapes are tile-separated, so per-batch Z is honest). records = 14.
#
# REQUIRES the Ch336 bitstream (TILE_ACCUM_ENABLE) — a re-fit from Ch335.
# Register map identical to ps2_feeder_test.sh (BASE 0x40000000).
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
ACCUM="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff0000ff 0000000000000000 0000510000100110 00000000ff0000ff 0000000000000030 00005100001001e0 00000000ff0000ff 00000000000c0000 0000510000e00110 00000000ff0000ff 0000000000000000 0000520000100210 00000000ff0000ff 0000000000000030 00005200001002e0 00000000ff0000ff 00000000000c0000 0000520000e00210 00000000ff0000ff 0000000000000000 0000530000100310 00000000ff0000ff 0000000000000030 00005300001003e0 00000000ff0000ff 00000000000c0000 0000530000e00310 00000000ff0000ff 0000000000000000 0000540001100010 00000000ff0000ff 0000000000000030 00005400011000e0 00000000ff0000ff 00000000000c0000 0000540001e00010 00000000ff0000ff 0000000000000000 0000550001100110 00000000ff0000ff 0000000000000030 00005500011001e0 00000000ff0000ff 00000000000c0000 0000550001e00110 00000000ff0000ff 0000000000000000 0000560001100210 00000000ff0000ff 0000000000000030 00005600011002e0 00000000ff0000ff 00000000000c0000 0000560001e00210 00000000ff0000ff 0000000000000000 0000570001100310 00000000ff0000ff 0000000000000030 00005700011003e0 00000000ff0000ff 00000000000c0000 0000570001e00310 00000000ffff0000 0000000000000000 0000580002100010 00000000ffff0000 0000000000000030 00005800021000e0 00000000ffff0000 00000000000c0000 0000580002e00010 00000000ffff0000 0000000000000000 0000590002100110 00000000ffff0000 0000000000000030 00005900021001e0 00000000ffff0000 00000000000c0000 0000590002e00110 00000000ffff0000 0000000000000000 00005a0002100210 00000000ffff0000 0000000000000030 00005a00021002e0 00000000ffff0000 00000000000c0000 00005a0002e00210 00000000ffff0000 0000000000000000 00005b0002100310 00000000ffff0000 0000000000000030 00005b00021003e0 00000000ffff0000 00000000000c0000 00005b0002e00310 00000000ffff0000 0000000000000000 00005c0003100010 00000000ffff0000 0000000000000030 00005c00031000e0 00000000ffff0000 00000000000c0000 00005c0003e00010 00000000ffff0000 0000000000000000 00005d0003100110 00000000ffff0000 0000000000000030 00005d00031001e0 00000000ffff0000 00000000000c0000 00005d0003e00110"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready — is this the Ch336 bitstream?"; return 1
}
echo "=== Ch336 >FIFO_DEPTH framebuffer accumulation — 14-prim scene (2 batches: RED + BLUE) ==="
wait_ready || exit 1
echo "streaming 14-prim scene (>FIFO depth), then GO ..."
w $OFF_STATUS 0x0; n=0
for word in $ACCUM; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "wrote $n words; bridge staging addr now=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || exit 1
w $OFF_GO 0x1
wait_ready || exit 1
rec=$(r $OFF_HI)
echo "records=$(( rec )) (expect 14) fifo_wait_cycles=$(( $(r $OFF_GO) ))"
[ $(( rec )) -eq 14 ] || { echo " !! records != 14"; exit 1; }
echo "=== done — HDMI: RED triangles in the TOP HALF (tiles 0-7, batch 0) AND blue triangles in"
echo " the lower rows (tiles 8-13, batch 1) — BOTH visible = the two FIFO batches accumulated. ==="
+75
View File
@@ -0,0 +1,75 @@
# ps2_feeder — HPS userspace command producer (Ch339)
`tools/ps2_feeder.c` is a native HPS application that encodes structured drawing commands into the
proven GS-feeder staging format and streams them to the FPGA over the existing HPS bridge
(`/dev/mem` + `mmap`), using the **same** register protocol as the `ps2_feeder_*.sh` diagnostic
anchors. The RTL and bridge protocol are unchanged — this is a host-side encoder + streamer that
replaces hand-built `devmem` word lists with structured, validated commands.
## Build (on the HPS / target, native gcc)
```sh
gcc -O2 -o ps2_feeder ps2_feeder.c
```
Portable C (stdint + mmap); builds the same on the board or a host for `--dump`/`--dry-run`.
## Use
```sh
./ps2_feeder --list # built-in named scenes
./ps2_feeder accum # stream one named scene (submit/go/wait)
./ps2_feeder retrigger-a retrigger-b retrigger-a # A -> B -> A, each cleanly retriggered
./ps2_feeder -f scene.txt # stream scenes from a text file
./ps2_feeder --dump accum # print the 256 staging words (no board access)
./ps2_feeder --dry-run -f scene.txt # encode + validate only, no board access
./ps2_feeder --base 0x40000000 accum # override bridge base (default 0x40000000)
```
Built-in scenes reproduce the proven Ch333Ch338 fixtures **byte-for-byte**: `color-tri`,
`native-rect`, `gouraud-tri`, `accum`, `retrigger-a`, `retrigger-b`, `zpersist-near`,
`zpersist-far`, `zpersist-grad`.
Per scene the app prints objective diagnostics: triangle/rect counts, staged words, expanded
primitives, batch estimate, the hardware staged-address / records / wait-cycle readbacks, and
completion. It polls feeder-ready before staging, after staging, and after GO — so it makes no host
timing assumptions and honours the Ch337 whole-scene-drain contract for clean back-to-back scenes.
Lists larger than the FIFO depth are handled by the RTL (Ch336 batching); the host just streams all
words and waits for completion.
## Scene file grammar
One scene per `go` (and a trailing scene at EOF); `#` starts a comment; whitespace-separated:
```
tri x0 y0 x1 y1 x2 y2 z r g b # flat triangle
trig x0 y0 r0 g0 b0 x1 y1 r1 g1 b1 x2 y2 r2 g2 b2 z # gouraud (per-vertex) triangle
tritile T z r g b # flat triangle filling grid tile T (0..15)
rect T z r g b # native rectangle in grid tile T
tex0 TBP TBW TW TH TFX # bind scene texture (Ch341)
tritex x0 y0 u0 v0 x1 y1 u1 v1 x2 y2 u2 v2 z r g b # textured triangle, per-vertex UV (needs tex0)
persp # mark scene PERSPECTIVE (needs tex0)
persptri x0 y0 s0 t0 q0 x1 y1 s1 t1 q1 x2 y2 s2 t2 q2 z r g b # perspective tri, fixed-point ST/Q (Ch342)
sprite x0 y0 x1 y1 u0 v0 u1 v1 r g b # textured + source-over alpha SPRITE (Ch345a; needs tex0)
go # submit accumulated scene; begin next
```
Coordinates are 12-bit screen pixels (0..4095); colors 0..255; `z` is the 32-bit GS Z (GEQUAL test,
higher = nearer). Malformed, out-of-range, and oversized (> 256 staging words) scenes are rejected
cleanly before any board access.
**Ch345a — `sprite` (runtime textured-alpha SPRITE ingestion).** A `sprite` record draws the bound `tex0`
texture (PSMCT32) over the screen rect `(x0,y0)-(x1,y1)` with affine per-corner UV, source-over alpha
blended against the destination. The source alpha is the **texel's** alpha (TCC=1), NOT the `r g b` tint —
the tint MODULATEs the texel color (pass `128 128 128` for identity). Sprite scenes set staging word0[33]
(`sprite_mode`) and are exclusive with tris/rects/perspective (the host fails closed on a mixed scene).
This is the Ch344-proven hardware subset; it is runtime SPRITE ingestion, not authentic-content ingestion.
## Verification
`tools/test_ps2_feeder.sh` compiles the app, proves every named scene's staging output is
byte-equivalent to its golden `bake.py` fixture, and checks that malformed/oversized/out-of-range
input is rejected. Run it on any host with gcc + python3 (no board needed).
`docs/hardware/ps2_feeder_test.sh` (and the other `ps2_feeder_*.sh` scripts) remain the low-level
`devmem` diagnostic anchors.
+52
View File
@@ -0,0 +1,52 @@
#!/bin/sh
# retroDE_ps2 — Ch333 VISUAL PAYLOAD DIVERSITY silicon proof (per-primitive COLOR, runtime-switched).
#
# Proves the runtime feeder controls color, not just geometry. A unity (0x80) texture + TEX0.TFX=
# MODULATE makes the staging RGBAQ the rendered color, so the host picks each primitive's color at
# runtime over the bridge — no rebuild/reset. Three scenes:
# COLOR_TRI : red / green / blue TRIANGLES tiles {0,5,10}
# COLOR_RECT : red / green / blue filled QUADS tiles {0,5,10}
# COLOR_MIX : red tri(0) + green rect(5) + blue tri(10) + yellow rect(15) (shape AND color vary)
# Ends on COLOR_MIX. Sim proof: tb_top_psmct32_feeder_colors_demo (exact per-tile colors).
#
# REQUIRES the Ch333 bitstream (TFX/MODULATE + the unity texture) — a re-fit from Ch331/Ch332.
# Register map identical to ps2_feeder_test.sh (BASE 0x40000000).
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
COLOR_TRI="0000000000000003 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff00ff00 0000000000000000 0000510001100110 00000000ff00ff00 0000000000000030 00005100011001e0 00000000ff00ff00 00000000000c0000 0000510001e00110 00000000ffff0000 0000000000000000 0000520002100210 00000000ffff0000 0000000000000030 00005200021002e0 00000000ffff0000 00000000000c0000 0000520002e00210"
COLOR_RECT="0000000000000006 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff0000ff 0000000000000000 00005100001000e0 00000000ff0000ff 0000000000000030 0000510000e00010 00000000ff0000ff 00000000000c0000 0000510000e000e0 00000000ff00ff00 0000000000000000 0000520001100110 00000000ff00ff00 0000000000000030 00005200011001e0 00000000ff00ff00 00000000000c0000 0000520001e00110 00000000ff00ff00 0000000000000000 00005300011001e0 00000000ff00ff00 0000000000000030 0000530001e00110 00000000ff00ff00 00000000000c0000 0000530001e001e0 00000000ffff0000 0000000000000000 0000540002100210 00000000ffff0000 0000000000000030 00005400021002e0 00000000ffff0000 00000000000c0000 0000540002e00210 00000000ffff0000 0000000000000000 00005500021002e0 00000000ffff0000 0000000000000030 0000550002e00210 00000000ffff0000 00000000000c0000 0000550002e002e0"
COLOR_MIX="0000000000000006 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff00ff00 0000000000000000 0000510001100110 00000000ff00ff00 0000000000000030 00005100011001e0 00000000ff00ff00 00000000000c0000 0000510001e00110 00000000ff00ff00 0000000000000000 00005200011001e0 00000000ff00ff00 0000000000000030 0000520001e00110 00000000ff00ff00 00000000000c0000 0000520001e001e0 00000000ffff0000 0000000000000000 0000530002100210 00000000ffff0000 0000000000000030 00005300021002e0 00000000ffff0000 00000000000c0000 0000530002e00210 00000000ff00ffff 0000000000000000 0000540003100310 00000000ff00ffff 0000000000000030 00005400031003e0 00000000ff00ffff 00000000000c0000 0000540003e00310 00000000ff00ffff 0000000000000000 00005500031003e0 00000000ff00ffff 0000000000000030 0000550003e00310 00000000ff00ffff 00000000000c0000 0000550003e003e0"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready — is this the Ch333 bitstream?"; return 1
}
stage_and_go() { # $1 label $2 words $3 expected-records
label="$1"; words="$2"; exp="$3"
echo "[$label] waiting for feeder ready ..."; wait_ready || return 1
echo "[$label] streaming the list, then GO ..."; w $OFF_STATUS 0x0; n=0
for word in $words; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "[$label] wrote $n words; bridge staging addr now=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || return 1; w $OFF_GO 0x1; wait_ready || return 1
rec=$(r $OFF_HI)
echo "[$label] records=$(( rec )) (expect $exp)"
[ $(( rec )) -eq $exp ] || { echo " !! records != $exp"; return 1; }
echo "[$label] OK — look at HDMI."; sleep 2 2>/dev/null || true
}
echo "=== Ch333 visual payload diversity — COLOR_TRI -> COLOR_RECT -> COLOR_MIX (per-prim color) ==="
stage_and_go "COLOR_TRI (red/green/blue triangles)" "$COLOR_TRI" 3 || exit 1
stage_and_go "COLOR_RECT (red/green/blue quads)" "$COLOR_RECT" 6 || exit 1
stage_and_go "COLOR_MIX (red/green/blue/yellow, shape+color vary)" "$COLOR_MIX" 6 || exit 1
echo "=== done — ENDS ON COLOR_MIX: red triangle (top-left), green square, blue triangle,"
echo " yellow square (bottom-right). Per-primitive color from staging RGBAQ, no rebuild/reset. ==="
+52
View File
@@ -0,0 +1,52 @@
#!/bin/sh
# retroDE_ps2 — Ch335 GOURAUD per-vertex color silicon proof (smooth gradients, runtime-switched).
#
# Distinct per-vertex RGBAQ -> the combined MODULATE path multiplies the texel by the INTERPOLATED
# vertex color, so a primitive shows a smooth gradient. Flat scenes (equal vertex colors) are
# unchanged. Three scenes:
# GOURAUD_TRI : tile0 triangle, v0=red v1=green v2=blue -> RGB gradient (records=1)
# GOURAUD_RECT : tile5 quad (2 tris), corners red/green/blue/white -> gradient quad (records=2)
# GOURAUD_MIX : flat red triangle(0) + RGB gradient triangle(10) (records=2)
# Ends on GOURAUD_MIX. Sim proof: tb_top_psmct32_feeder_gouraud_demo (per-vertex channel dominance).
#
# REQUIRES the Ch335 bitstream (interpolated MODULATE) — a re-fit from Ch334.
# Register map identical to ps2_feeder_test.sh (BASE 0x40000000).
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
GOURAUD_TRI="0000000000000001 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff00ff00 0000000000000030 00005000001000e0 00000000ffff0000 00000000000c0000 0000500000e00010"
GOURAUD_RECT="0000000000000002 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500001100110 00000000ff00ff00 0000000000000030 00005000011001e0 00000000ffff0000 00000000000c0000 0000500001e00110 00000000ff00ff00 0000000000000000 00005100011001e0 00000000ffff0000 0000000000000030 0000510001e00110 00000000ffffffff 00000000000c0000 0000510001e001e0"
GOURAUD_MIX="0000000000000002 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff0000ff 0000000000000000 0000510002100210 00000000ff00ff00 0000000000000030 00005100021002e0 00000000ffff0000 00000000000c0000 0000510002e00210"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready — is this the Ch335 bitstream?"; return 1
}
stage_and_go() { # $1 label $2 words $3 expected-records
label="$1"; words="$2"; exp="$3"
echo "[$label] waiting for feeder ready ..."; wait_ready || return 1
echo "[$label] streaming the list, then GO ..."; w $OFF_STATUS 0x0; n=0
for word in $words; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "[$label] wrote $n words; bridge staging addr now=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || return 1; w $OFF_GO 0x1; wait_ready || return 1
rec=$(r $OFF_HI)
echo "[$label] records=$(( rec )) (expect $exp)"
[ $(( rec )) -eq $exp ] || { echo " !! records != $exp"; return 1; }
echo "[$label] OK — look at HDMI."; sleep 2 2>/dev/null || true
}
echo "=== Ch335 gouraud per-vertex color — GOURAUD_TRI -> GOURAUD_RECT -> GOURAUD_MIX ==="
stage_and_go "GOURAUD_TRI (RGB gradient triangle)" "$GOURAUD_TRI" 1 || exit 1
stage_and_go "GOURAUD_RECT (RGB+white gradient quad)" "$GOURAUD_RECT" 2 || exit 1
stage_and_go "GOURAUD_MIX (flat red tri + gradient tri)" "$GOURAUD_MIX" 2 || exit 1
echo "=== done — ENDS ON GOURAUD_MIX: solid red triangle (top-left) + a smooth red->green->blue"
echo " gradient triangle (center). Smooth per-vertex color from staging RGBAQ, no rebuild/reset. ==="
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
# retroDE_ps2 — Ch334 NATIVE RECTANGLE RECORD silicon proof (host command compression).
#
# A native rectangle is ONE 3-word record (color + 2 corners) that the FEEDER expands into two
# colored triangles — 6x smaller host payload than the explicit 18-word two-triangle form, same
# rendered result. The count word now carries {rect_count[31:16], tri_count[15:0]}. Two scenes:
# NATIVE_RECT : 3 native rects -> red/green/blue filled quads {0,5,10} (records=6, == Ch333 color_rect)
# NATIVE_MIX : red triangle(0) + green/blue/yellow native rects(5/10/15) (records=7)
# Ends on NATIVE_MIX. Sim proof: tb_top_psmct32_feeder_native_demo (matches the explicit version).
#
# REQUIRES the Ch334 bitstream (feeder rect-expansion) — a re-fit from Ch333.
# Register map identical to ps2_feeder_test.sh (BASE 0x40000000).
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
NATIVE_RECT="0000000000030000 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000500000100010 0000500000e000e0 00000000ff00ff00 0000510001100110 0000510001e001e0 00000000ffff0000 0000520002100210 0000520002e002e0"
NATIVE_MIX="0000000000030001 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff00ff00 0000510001100110 0000510001e001e0 00000000ffff0000 0000520002100210 0000520002e002e0 00000000ff00ffff 0000530003100310 0000530003e003e0"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready — is this the Ch334 bitstream?"; return 1
}
stage_and_go() { # $1 label $2 words $3 expected-records
label="$1"; words="$2"; exp="$3"
echo "[$label] waiting for feeder ready ..."; wait_ready || return 1
echo "[$label] streaming the list, then GO ..."; w $OFF_STATUS 0x0; n=0
for word in $words; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "[$label] wrote $n words; bridge staging addr now=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || return 1; w $OFF_GO 0x1; wait_ready || return 1
rec=$(r $OFF_HI)
echo "[$label] records=$(( rec )) (expect $exp)"
[ $(( rec )) -eq $exp ] || { echo " !! records != $exp"; return 1; }
echo "[$label] OK — look at HDMI."; sleep 2 2>/dev/null || true
}
echo "=== Ch334 native rectangle record — NATIVE_RECT -> NATIVE_MIX (host command compression) ==="
stage_and_go "NATIVE_RECT (3 native rects: r/g/b quads, 16 words)" "$NATIVE_RECT" 6 || exit 1
stage_and_go "NATIVE_MIX (red tri + 3 native rects, 25 words)" "$NATIVE_MIX" 7 || exit 1
echo "=== done — ENDS ON NATIVE_MIX: red triangle (top-left) + green/blue/yellow filled squares."
echo " Each rectangle was ONE 3-word record expanded to 2 triangles in the feeder. ==="
@@ -0,0 +1,48 @@
#!/bin/sh
# retroDE_ps2 — Ch337 board acceptance: CLEAN scene-level retrigger for >FIFO_DEPTH scenes.
#
# Streams two distinct 14-prim (>FIFO_DEPTH = 2-batch) scenes and retriggers each on feeder_ready:
# A: tiles 0-13 RED B: tiles 2-15 BLUE
# Sequence A -> B -> A. Each scene's first (full-flush) batch wipes the WHOLE framebuffer, and the
# Ch337 control FSM only reports ready once the WHOLE multi-batch scene has drained — so the host
# can retrigger without racing the last batch. EXPECTED HDMI after the final stage:
# tiles 0-13 RED, tiles 14-15 background, and ZERO blue anywhere (scene B fully gone).
# A premature-ready race (pre-Ch337) would leave BLUE residue from B or a half-drawn frame.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
SCENE_A="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000500000100010 00000000ff0000ff 0000000000000030 00005000001000e0 00000000ff0000ff 00000000000c0000 0000500000e00010 00000000ff0000ff 0000000000000000 0000510000100110 00000000ff0000ff 0000000000000030 00005100001001e0 00000000ff0000ff 00000000000c0000 0000510000e00110 00000000ff0000ff 0000000000000000 0000520000100210 00000000ff0000ff 0000000000000030 00005200001002e0 00000000ff0000ff 00000000000c0000 0000520000e00210 00000000ff0000ff 0000000000000000 0000530000100310 00000000ff0000ff 0000000000000030 00005300001003e0 00000000ff0000ff 00000000000c0000 0000530000e00310 00000000ff0000ff 0000000000000000 0000540001100010 00000000ff0000ff 0000000000000030 00005400011000e0 00000000ff0000ff 00000000000c0000 0000540001e00010 00000000ff0000ff 0000000000000000 0000550001100110 00000000ff0000ff 0000000000000030 00005500011001e0 00000000ff0000ff 00000000000c0000 0000550001e00110 00000000ff0000ff 0000000000000000 0000560001100210 00000000ff0000ff 0000000000000030 00005600011002e0 00000000ff0000ff 00000000000c0000 0000560001e00210 00000000ff0000ff 0000000000000000 0000570001100310 00000000ff0000ff 0000000000000030 00005700011003e0 00000000ff0000ff 00000000000c0000 0000570001e00310 00000000ff0000ff 0000000000000000 0000580002100010 00000000ff0000ff 0000000000000030 00005800021000e0 00000000ff0000ff 00000000000c0000 0000580002e00010 00000000ff0000ff 0000000000000000 0000590002100110 00000000ff0000ff 0000000000000030 00005900021001e0 00000000ff0000ff 00000000000c0000 0000590002e00110 00000000ff0000ff 0000000000000000 00005a0002100210 00000000ff0000ff 0000000000000030 00005a00021002e0 00000000ff0000ff 00000000000c0000 00005a0002e00210 00000000ff0000ff 0000000000000000 00005b0002100310 00000000ff0000ff 0000000000000030 00005b00021003e0 00000000ff0000ff 00000000000c0000 00005b0002e00310 00000000ff0000ff 0000000000000000 00005c0003100010 00000000ff0000ff 0000000000000030 00005c00031000e0 00000000ff0000ff 00000000000c0000 00005c0003e00010 00000000ff0000ff 0000000000000000 00005d0003100110 00000000ff0000ff 0000000000000030 00005d00031001e0 00000000ff0000ff 00000000000c0000 00005d0003e00110"
SCENE_B="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ffff0000 0000000000000000 0000500000100210 00000000ffff0000 0000000000000030 00005000001002e0 00000000ffff0000 00000000000c0000 0000500000e00210 00000000ffff0000 0000000000000000 0000510000100310 00000000ffff0000 0000000000000030 00005100001003e0 00000000ffff0000 00000000000c0000 0000510000e00310 00000000ffff0000 0000000000000000 0000520001100010 00000000ffff0000 0000000000000030 00005200011000e0 00000000ffff0000 00000000000c0000 0000520001e00010 00000000ffff0000 0000000000000000 0000530001100110 00000000ffff0000 0000000000000030 00005300011001e0 00000000ffff0000 00000000000c0000 0000530001e00110 00000000ffff0000 0000000000000000 0000540001100210 00000000ffff0000 0000000000000030 00005400011002e0 00000000ffff0000 00000000000c0000 0000540001e00210 00000000ffff0000 0000000000000000 0000550001100310 00000000ffff0000 0000000000000030 00005500011003e0 00000000ffff0000 00000000000c0000 0000550001e00310 00000000ffff0000 0000000000000000 0000560002100010 00000000ffff0000 0000000000000030 00005600021000e0 00000000ffff0000 00000000000c0000 0000560002e00010 00000000ffff0000 0000000000000000 0000570002100110 00000000ffff0000 0000000000000030 00005700021001e0 00000000ffff0000 00000000000c0000 0000570002e00110 00000000ffff0000 0000000000000000 0000580002100210 00000000ffff0000 0000000000000030 00005800021002e0 00000000ffff0000 00000000000c0000 0000580002e00210 00000000ffff0000 0000000000000000 0000590002100310 00000000ffff0000 0000000000000030 00005900021003e0 00000000ffff0000 00000000000c0000 0000590002e00310 00000000ffff0000 0000000000000000 00005a0003100010 00000000ffff0000 0000000000000030 00005a00031000e0 00000000ffff0000 00000000000c0000 00005a0003e00010 00000000ffff0000 0000000000000000 00005b0003100110 00000000ffff0000 0000000000000030 00005b00031001e0 00000000ffff0000 00000000000c0000 00005b0003e00110 00000000ffff0000 0000000000000000 00005c0003100210 00000000ffff0000 0000000000000030 00005c00031002e0 00000000ffff0000 00000000000c0000 00005c0003e00210 00000000ffff0000 0000000000000000 00005d0003100310 00000000ffff0000 0000000000000030 00005d00031003e0 00000000ffff0000 00000000000c0000 00005d0003e00310"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready"; return 1
}
stage() { # $1=label $2=words
echo "--- stage $1 ---"
wait_ready || exit 1
w $OFF_STATUS 0x0; n=0
for word in $2; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo " wrote $n words; bridge addr=$(( $(r $OFF_LO) ))"
wait_ready || exit 1 # Ch337: ready only after the PRIOR scene fully drained
w $OFF_GO 0x1
wait_ready || exit 1 # ready again only after THIS >8 scene fully drained
echo " records=$(( $(r $OFF_HI) )) (expect 14)"
}
echo "=== Ch337 clean retrigger: A (RED 0-13) -> B (BLUE 2-15) -> A (RED 0-13) ==="
stage "A (RED tiles 0-13)" "$SCENE_A"
stage "B (BLUE tiles 2-15)" "$SCENE_B"
stage "A again (RED tiles 0-13)" "$SCENE_A"
echo "=== Final HDMI must be EXACTLY scene A: RED tiles 0-13, NO blue anywhere (B fully gone). ==="
+65
View File
@@ -0,0 +1,65 @@
#!/bin/sh
# retroDE_ps2 — Ch331 FEEDER EXPRESSIVENESS silicon proof (variable-size multi-tile scenes).
#
# Ch330 proved runtime command ingestion exists (a fixed 4-prim list, repositionable). This proves
# it SCALES: variable-size HPS-staged scenes rendered in ONE pass via the end-of-list flush, with
# no rebuild/reset. Streams three scenes of DIFFERENT sizes across the 4x4 tile grid:
# C1 : 3 prims in tiles {0,5,10} (< the old fixed threshold of 4)
# C2 : 6 prims in tiles {0,3,5,9,12,15} (> 4 — one pass, NOT split across clears)
# C3 : 8 prims in tiles {0,1,2,3,12,13,14,15} (== FIFO_DEPTH, the current max scene size)
# Ends on C3 (top + bottom rows lit) — visibly distinct from the power-up scene.
#
# REQUIRES the Ch331 feeder bitstream: ./scripts/select_de25_profile.sh feeder (then re-fit).
# Register map identical to ps2_feeder_test.sh (BASE 0x40000000):
# 0x0D8 R ready / W staging addr ; 0x0DC W lo ; 0x0E4 W hi(commit+inc)/R records ; 0x0E8 W go/R waits
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
SCENE_C1="0000000000000003 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 0000510001100110 00000000ff000000 0000000000000030 00005100011001e0 00000000ff000000 00000000000c0000 0000510001e00110 00000000ff000000 0000000000000000 0000520002100210 00000000ff000000 0000000000000030 00005200021002e0 00000000ff000000 00000000000c0000 0000520002e00210"
SCENE_C2="0000000000000006 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 0000510000100310 00000000ff000000 0000000000000030 00005100001003e0 00000000ff000000 00000000000c0000 0000510000e00310 00000000ff000000 0000000000000000 0000520001100110 00000000ff000000 0000000000000030 00005200011001e0 00000000ff000000 00000000000c0000 0000520001e00110 00000000ff000000 0000000000000000 0000530002100110 00000000ff000000 0000000000000030 00005300021001e0 00000000ff000000 00000000000c0000 0000530002e00110 00000000ff000000 0000000000000000 0000540003100010 00000000ff000000 0000000000000030 00005400031000e0 00000000ff000000 00000000000c0000 0000540003e00010 00000000ff000000 0000000000000000 0000550003100310 00000000ff000000 0000000000000030 00005500031003e0 00000000ff000000 00000000000c0000 0000550003e00310"
SCENE_C3="0000000000000008 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 0000510000100110 00000000ff000000 0000000000000030 00005100001001e0 00000000ff000000 00000000000c0000 0000510000e00110 00000000ff000000 0000000000000000 0000520000100210 00000000ff000000 0000000000000030 00005200001002e0 00000000ff000000 00000000000c0000 0000520000e00210 00000000ff000000 0000000000000000 0000530000100310 00000000ff000000 0000000000000030 00005300001003e0 00000000ff000000 00000000000c0000 0000530000e00310 00000000ff000000 0000000000000000 0000540003100010 00000000ff000000 0000000000000030 00005400031000e0 00000000ff000000 00000000000c0000 0000540003e00010 00000000ff000000 0000000000000000 0000550003100110 00000000ff000000 0000000000000030 00005500031001e0 00000000ff000000 00000000000c0000 0000550003e00110 00000000ff000000 0000000000000000 0000560003100210 00000000ff000000 0000000000000030 00005600031002e0 00000000ff000000 00000000000c0000 0000560003e00210 00000000ff000000 0000000000000000 0000570003100310 00000000ff000000 0000000000000030 00005700031003e0 00000000ff000000 00000000000c0000 0000570003e00310"
wait_ready() {
i=0
while [ $i -lt 300 ]; do
st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0
i=$(( i + 1 )); sleep 0.01 2>/dev/null || true
done
echo " !! feeder never reported ready (0x0D8 bit0) — is this the feeder bitstream?"; return 1
}
stage_and_go() { # $1 label $2 words $3 expected-records
label="$1"; words="$2"; exp="$3"
echo "[$label] waiting for feeder ready ..."
wait_ready || return 1
echo "[$label] streaming the list, then GO ..."
w $OFF_STATUS 0x0
n=0
for word in $words; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
badr=$(r $OFF_LO)
echo "[$label] wrote $n words; bridge staging addr now=$(( badr )) (expect $n)"
wait_ready || return 1
w $OFF_GO 0x1
wait_ready || return 1
rec=$(r $OFF_HI); wts=$(r $OFF_GO)
echo "[$label] records=$(( rec )) (expect $exp) fifo_wait_cycles=$(( wts ))"
[ $(( rec )) -eq $exp ] || { echo " !! records != $exp — scene not fully emitted"; return 1; }
echo "[$label] OK — look at HDMI."
sleep 2 2>/dev/null || true
}
echo "=== Ch331 feeder expressiveness — variable multi-tile scenes C1(3) -> C2(6) -> C3(8) ==="
stage_and_go "C1 (3 prims: tiles 0/5/10 diagonal)" "$SCENE_C1" 3 || exit 1
stage_and_go "C2 (6 prims: tiles 0/3/5/9/12/15)" "$SCENE_C2" 6 || exit 1
stage_and_go "C3 (8 prims: top+bottom rows 0-3/12-15)" "$SCENE_C3" 8 || exit 1
echo "=== done — ENDS ON C3: triangles in the top row AND bottom row should be lit."
echo " Variable-size scenes (3, 6, 8 prims) each rendered in one pass, no rebuild/reset. ==="
+52
View File
@@ -0,0 +1,52 @@
#!/bin/sh
# retroDE_ps2 — Ch332 SHAPE VOCABULARY silicon proof (triangles + rectangles, runtime-switched).
#
# Proves the feeder is no longer triangle-only smoke: a RECTANGLE (filled quad) is expressed as
# two textured triangles, so the host can command quads on the SAME Ch330/Ch331 path with NO
# rebuild/reset (and NO new bitstream — this runs on the Ch331 feeder RBF). Three scenes:
# TRI : 3 half-tile triangles tiles {0,5,10}
# RECT : 3 filled quads (6 prims) tiles {0,5,10} — same tiles, visibly FULLER
# MIXED : triangles {0,15} + rects {5,10}
# Ends on MIXED. Sim proof (tb_top_psmct32_feeder_shapes_demo): tri tile=91 blue px, rect tile=169
# (full 13x13 quad, no diagonal seam).
#
# Register map identical to ps2_feeder_test.sh (BASE 0x40000000). Needs the Ch331 feeder bitstream.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
SHAPE_TRI="0000000000000003 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 0000510001100110 00000000ff000000 0000000000000030 00005100011001e0 00000000ff000000 00000000000c0000 0000510001e00110 00000000ff000000 0000000000000000 0000520002100210 00000000ff000000 0000000000000030 00005200021002e0 00000000ff000000 00000000000c0000 0000520002e00210"
SHAPE_RECT="0000000000000006 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 00005100001000e0 00000000ff000000 0000000000000030 0000510000e00010 00000000ff000000 00000000000c0000 0000510000e000e0 00000000ff000000 0000000000000000 0000520001100110 00000000ff000000 0000000000000030 00005200011001e0 00000000ff000000 00000000000c0000 0000520001e00110 00000000ff000000 0000000000000000 00005300011001e0 00000000ff000000 0000000000000030 0000530001e00110 00000000ff000000 00000000000c0000 0000530001e001e0 00000000ff000000 0000000000000000 0000540002100210 00000000ff000000 0000000000000030 00005400021002e0 00000000ff000000 00000000000c0000 0000540002e00210 00000000ff000000 0000000000000000 00005500021002e0 00000000ff000000 0000000000000030 0000550002e00210 00000000ff000000 00000000000c0000 0000550002e002e0"
SHAPE_MIXED="0000000000000006 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 0000510001100110 00000000ff000000 0000000000000030 00005100011001e0 00000000ff000000 00000000000c0000 0000510001e00110 00000000ff000000 0000000000000000 00005200011001e0 00000000ff000000 0000000000000030 0000520001e00110 00000000ff000000 00000000000c0000 0000520001e001e0 00000000ff000000 0000000000000000 0000530002100210 00000000ff000000 0000000000000030 00005300021002e0 00000000ff000000 00000000000c0000 0000530002e00210 00000000ff000000 0000000000000000 00005400021002e0 00000000ff000000 0000000000000030 0000540002e00210 00000000ff000000 00000000000c0000 0000540002e002e0 00000000ff000000 0000000000000000 0000550003100310 00000000ff000000 0000000000000030 00005500031003e0 00000000ff000000 00000000000c0000 0000550003e00310"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready — is this the Ch331 feeder bitstream?"; return 1
}
stage_and_go() { # $1 label $2 words $3 expected-records
label="$1"; words="$2"; exp="$3"
echo "[$label] waiting for feeder ready ..."; wait_ready || return 1
echo "[$label] streaming the list, then GO ..."; w $OFF_STATUS 0x0; n=0
for word in $words; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
echo "[$label] wrote $n words; bridge staging addr now=$(( $(r $OFF_LO) )) (expect $n)"
wait_ready || return 1; w $OFF_GO 0x1; wait_ready || return 1
rec=$(r $OFF_HI)
echo "[$label] records=$(( rec )) (expect $exp)"
[ $(( rec )) -eq $exp ] || { echo " !! records != $exp"; return 1; }
echo "[$label] OK — look at HDMI."; sleep 2 2>/dev/null || true
}
echo "=== Ch332 shape vocabulary — TRI(triangles) -> RECT(filled quads) -> MIXED ==="
stage_and_go "TRI (3 triangles: tiles 0/5/10)" "$SHAPE_TRI" 3 || exit 1
stage_and_go "RECT (3 filled quads: tiles 0/5/10)" "$SHAPE_RECT" 6 || exit 1
stage_and_go "MIXED (tri 0/15 + rect 5/10)" "$SHAPE_MIXED" 6 || exit 1
echo "=== done — ENDS ON MIXED: top-left + bottom-right are triangles, the two middle-diagonal"
echo " tiles are FILLED squares. Triangles vs rectangles, runtime-switched, no rebuild/reset. ==="
+89
View File
@@ -0,0 +1,89 @@
#!/bin/sh
# retroDE_ps2 — Ch330 RUNTIME COMMAND-LIST FEEDER silicon proof (HPS-staged primitive lists).
#
# Streams a normalized combined-TAZ triangle list into the feeder's staging RAM over the HPS
# bridge, then pulses GO to retrigger the renderer — no RBF rebuild, no reset. Proves repeatable
# runtime command-list ingestion by cycling list A -> B -> A -> B (ends on B):
# list A : 4 textured tris in tile t0 (top-left) -> blue triangle top-left
# list B : 4 textured tris in tile t15 (bottom-right) -> blue triangle bottom-right
# Ending on B means the final screen (bottom-right) differs from the power-up screen (top-left),
# so the runtime swap is unambiguous rather than netting back to where it started.
# The board powers up drawing list A already (FEEDER_STG_INIT_FILE bitstream-inits the staging
# RAM); this script re-stages it from the HPS to prove the *runtime* path, not just power-up.
#
# REQUIRES the Ch330 feeder bitstream: ./scripts/select_de25_profile.sh feeder (then re-fit).
#
# Register map (bridge BASE 0x40000000):
# 0x0D8 R: bit0 = feeder ready (FSM in C_READY) W: staging word address (set 0 before a list)
# 0x0DC W: staging word LOW 32 bits
# 0x0E4 W: staging word HIGH 32 -> commits {hi,lo} to staging[addr], auto-increments addr
# R: records_emitted (primitives the last list pushed; expect 4)
# 0x0E8 W: bit0 = GO (retrigger) R: fifo_wait_cycles (backpressure stalls)
#
# Acceptance: after each GO, records == 4 and the HDMI image matches the staged list (A/B/A/B).
# Watch the HDMI output change top-left -> bottom-right -> top-left -> bottom-right as lists are staged.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8 # R ready / W staging addr
OFF_LO=0x0DC # W low 32
OFF_HI=0x0E4 # W high 32 (commit+inc) / R records
OFF_GO=0x0E8 # W go / R waits
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
# 43 staging words each (count + FRAME/ALPHA/TEST/ZBUF/TEX0/PRIM + 4 tris x 9 vertex words).
# A = tile t0 (top-left), B = tile t15 (col3,row3 = bottom-right, diagonal opposite of t0) —
# identical lists except the XYZ2 vertex coordinates, so the triangle jumps corner-to-corner.
LIST_A="0000000000000004 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500000100010 00000000ff000000 0000000000000030 00005000001000e0 00000000ff000000 00000000000c0000 0000500000e00010 00000000ff000000 0000000000000000 0000510000100010 00000000ff000000 0000000000000030 00005100001000e0 00000000ff000000 00000000000c0000 0000510000e00010 00000000ff000000 0000000000000000 0000520000100010 00000000ff000000 0000000000000030 00005200001000e0 00000000ff000000 00000000000c0000 0000520000e00010 00000000ff000000 0000000000000000 0000530000100010 00000000ff000000 0000000000000030 00005300001000e0 00000000ff000000 00000000000c0000 0000530000e00010"
LIST_B="0000000000000004 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000888004040 0000000000000053 00000000ff000000 0000000000000000 0000500003100310 00000000ff000000 0000000000000030 00005000031003e0 00000000ff000000 00000000000c0000 0000500003e00310 00000000ff000000 0000000000000000 0000510003100310 00000000ff000000 0000000000000030 00005100031003e0 00000000ff000000 00000000000c0000 0000510003e00310 00000000ff000000 0000000000000000 0000520003100310 00000000ff000000 0000000000000030 00005200031003e0 00000000ff000000 00000000000c0000 0000520003e00310 00000000ff000000 0000000000000000 0000530003100310 00000000ff000000 0000000000000030 00005300031003e0 00000000ff000000 00000000000c0000 0000530003e00310"
wait_ready() { # poll 0x0D8 bit0 until ready, or give up after ~3 s
i=0
while [ $i -lt 300 ]; do
st=$(r $OFF_STATUS)
[ $(( st & 1 )) -eq 1 ] && return 0
i=$(( i + 1 )); sleep 0.01 2>/dev/null || true
done
echo " !! feeder never reported ready (0x0D8 bit0) — is this the feeder bitstream?"
return 1
}
stage_and_go() { # $1 = label, $2 = whitespace-separated 16-hex-digit words
label="$1"; words="$2"
echo "[$label] waiting for feeder ready ..."
wait_ready || return 1
echo "[$label] writing the whole list, then GO ..."
w $OFF_STATUS 0x0 # staging address = 0 (auto-increments per HI write)
n=0
for word in $words; do
lo=$(printf '%s' "$word" | cut -c9-16)
hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo
w $OFF_HI 0x$hi # commit {hi,lo} -> staging[n], addr -> n+1
n=$(( n + 1 ))
done
badr=$(r $OFF_LO) # 0x0DC read = bridge staging address — must equal n (all words committed)
echo "[$label] wrote $n words; bridge staging addr now=$(( badr )) (expect $n)"
[ $(( badr )) -eq $n ] || echo " !! bridge addr != $n — not all commits landed"
wait_ready || return 1 # FSM still C_READY (staging writes don't change state); confirm
w $OFF_GO 0x1 # retrigger
wait_ready || return 1 # render + grid drain -> back to C_READY
rec=$(r $OFF_HI); wts=$(r $OFF_GO)
echo "[$label] staged $n words -> records=$(( rec )) (expect 4) fifo_wait_cycles=$(( wts ))"
[ $(( rec )) -eq 4 ] || { echo " !! records != 4 — list not fully emitted"; return 1; }
echo "[$label] OK — look at HDMI."
sleep 2 2>/dev/null || true
}
echo "=== Ch330 runtime command-list feeder — A -> B -> A -> B (no RBF rebuild, no reset) ==="
stage_and_go "A (t0 top-left)" "$LIST_A" || exit 1
stage_and_go "B (t15 bottom-right)" "$LIST_B" || exit 1
stage_and_go "A (t0 top-left)" "$LIST_A" || exit 1
stage_and_go "B (t15 bottom-right)" "$LIST_B" || exit 1
echo "=== done — ENDS ON B: triangle should now be at the BOTTOM-RIGHT (t15), NOT top-left."
echo " If it's at bottom-right, the runtime swap works end-to-end. If still top-left, tell me the"
echo " 'bridge staging addr now=' lines so I can see whether all 43 words committed. ==="
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
# retroDE_ps2 — Ch338 board acceptance: CROSS-BATCH Z ordering for >FIFO_DEPTH scenes.
#
# A NEAR (RED) and a FAR (BLUE) triangle occupy the SAME tile (tile 5 = the CENTER 16x16 block) but
# are SPLIT across FIFO batches. ZBUF clear=0x4000, TEST=GEQUAL (higher Z = nearer wins). With
# persistent cross-batch Z the NEAR (RED) triangle wins the overlap in BOTH orderings:
# stage 1 NEAR_FIRST (near RED batch0, far BLUE batch1): CENTER block must be RED (far Z-rejected)
# stage 2 FAR_FIRST (far BLUE batch0, near RED batch1): CENTER block must be RED (near wins)
# The CENTER staying RED in BOTH proves identical depth ordering regardless of the batch boundary.
# (Pre-Ch338 per-batch Z would show the CENTER BLUE in stage 1 — the later batch overwriting the
# nearer earlier prim.) Surrounding tiles: stage1 top-left RED / bottom rows BLUE; stage2 reversed.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
OFF_STATUS=0x0D8; OFF_LO=0x0DC; OFF_HI=0x0E4; OFF_GO=0x0E8
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
NEAR_FIRST="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ff0000ff 0000000000000000 0000700001100110 00000000ff0000ff 0000000000000030 00007000011001e0 00000000ff0000ff 00000000000c0000 0000700001e00110 00000000ff0000ff 0000000000000000 0000600000100010 00000000ff0000ff 0000000000000030 00006000001000e0 00000000ff0000ff 00000000000c0000 0000600000e00010 00000000ff0000ff 0000000000000000 0000600000100110 00000000ff0000ff 0000000000000030 00006000001001e0 00000000ff0000ff 00000000000c0000 0000600000e00110 00000000ff0000ff 0000000000000000 0000600000100210 00000000ff0000ff 0000000000000030 00006000001002e0 00000000ff0000ff 00000000000c0000 0000600000e00210 00000000ff0000ff 0000000000000000 0000600000100310 00000000ff0000ff 0000000000000030 00006000001003e0 00000000ff0000ff 00000000000c0000 0000600000e00310 00000000ff0000ff 0000000000000000 0000600001100010 00000000ff0000ff 0000000000000030 00006000011000e0 00000000ff0000ff 00000000000c0000 0000600001e00010 00000000ff0000ff 0000000000000000 0000600001100210 00000000ff0000ff 0000000000000030 00006000011002e0 00000000ff0000ff 00000000000c0000 0000600001e00210 00000000ff0000ff 0000000000000000 0000600001100310 00000000ff0000ff 0000000000000030 00006000011003e0 00000000ff0000ff 00000000000c0000 0000600001e00310 00000000ffff0000 0000000000000000 0000500001100110 00000000ffff0000 0000000000000030 00005000011001e0 00000000ffff0000 00000000000c0000 0000500001e00110 00000000ffff0000 0000000000000000 0000600002100010 00000000ffff0000 0000000000000030 00006000021000e0 00000000ffff0000 00000000000c0000 0000600002e00010 00000000ffff0000 0000000000000000 0000600002100110 00000000ffff0000 0000000000000030 00006000021001e0 00000000ffff0000 00000000000c0000 0000600002e00110 00000000ffff0000 0000000000000000 0000600002100210 00000000ffff0000 0000000000000030 00006000021002e0 00000000ffff0000 00000000000c0000 0000600002e00210 00000000ffff0000 0000000000000000 0000600002100310 00000000ffff0000 0000000000000030 00006000021003e0 00000000ffff0000 00000000000c0000 0000600002e00310 00000000ffff0000 0000000000000000 0000600003100010 00000000ffff0000 0000000000000030 00006000031000e0 00000000ffff0000 00000000000c0000 0000600003e00010"
FAR_FIRST="000000000000000e 0000000000010000 0000000000000044 0000000000050000 0000000000000002 0000000088004060 0000000000000053 00000000ffff0000 0000000000000000 0000500001100110 00000000ffff0000 0000000000000030 00005000011001e0 00000000ffff0000 00000000000c0000 0000500001e00110 00000000ffff0000 0000000000000000 0000600000100010 00000000ffff0000 0000000000000030 00006000001000e0 00000000ffff0000 00000000000c0000 0000600000e00010 00000000ffff0000 0000000000000000 0000600000100110 00000000ffff0000 0000000000000030 00006000001001e0 00000000ffff0000 00000000000c0000 0000600000e00110 00000000ffff0000 0000000000000000 0000600000100210 00000000ffff0000 0000000000000030 00006000001002e0 00000000ffff0000 00000000000c0000 0000600000e00210 00000000ffff0000 0000000000000000 0000600000100310 00000000ffff0000 0000000000000030 00006000001003e0 00000000ffff0000 00000000000c0000 0000600000e00310 00000000ffff0000 0000000000000000 0000600001100010 00000000ffff0000 0000000000000030 00006000011000e0 00000000ffff0000 00000000000c0000 0000600001e00010 00000000ffff0000 0000000000000000 0000600001100210 00000000ffff0000 0000000000000030 00006000011002e0 00000000ffff0000 00000000000c0000 0000600001e00210 00000000ffff0000 0000000000000000 0000600001100310 00000000ffff0000 0000000000000030 00006000011003e0 00000000ffff0000 00000000000c0000 0000600001e00310 00000000ff0000ff 0000000000000000 0000700001100110 00000000ff0000ff 0000000000000030 00007000011001e0 00000000ff0000ff 00000000000c0000 0000700001e00110 00000000ff0000ff 0000000000000000 0000600002100010 00000000ff0000ff 0000000000000030 00006000021000e0 00000000ff0000ff 00000000000c0000 0000600002e00010 00000000ff0000ff 0000000000000000 0000600002100110 00000000ff0000ff 0000000000000030 00006000021001e0 00000000ff0000ff 00000000000c0000 0000600002e00110 00000000ff0000ff 0000000000000000 0000600002100210 00000000ff0000ff 0000000000000030 00006000021002e0 00000000ff0000ff 00000000000c0000 0000600002e00210 00000000ff0000ff 0000000000000000 0000600002100310 00000000ff0000ff 0000000000000030 00006000021003e0 00000000ff0000ff 00000000000c0000 0000600002e00310 00000000ff0000ff 0000000000000000 0000600003100010 00000000ff0000ff 0000000000000030 00006000031000e0 00000000ff0000ff 00000000000c0000 0000600003e00010"
wait_ready() {
i=0
while [ $i -lt 300 ]; do st=$(r $OFF_STATUS); [ $(( st & 1 )) -eq 1 ] && return 0; i=$(( i + 1 )); sleep 0.01 2>/dev/null || true; done
echo " !! feeder never reported ready"; return 1
}
stage() { # $1=label $2=words
echo "--- $1 ---"
wait_ready || exit 1
w $OFF_STATUS 0x0; n=0
for word in $2; do
lo=$(printf '%s' "$word" | cut -c9-16); hi=$(printf '%s' "$word" | cut -c1-8)
w $OFF_LO 0x$lo; w $OFF_HI 0x$hi; n=$(( n + 1 ))
done
wait_ready || exit 1
w $OFF_GO 0x1
wait_ready || exit 1
echo " wrote $n words; records=$(( $(r $OFF_HI) )) (expect 14)"
}
echo "=== Ch338 cross-batch Z: CENTER block must stay RED in BOTH stages ==="
stage "stage 1 NEAR_FIRST (near RED b0, far BLUE b1)" "$NEAR_FIRST"
echo " -> CENTER should be RED now (far blue Z-rejected). Pre-Ch338 it would be BLUE."
stage "stage 2 FAR_FIRST (far BLUE b0, near RED b1)" "$FAR_FIRST"
echo " -> CENTER should be RED now (near wins on submit too)."
echo "=== PASS = CENTER block RED in BOTH stages (identical depth order regardless of batch). ==="
+129
View File
@@ -0,0 +1,129 @@
#!/bin/sh
# retroDE_ps2 LPDDR framebuffer write/readback test — Ch318 operator helper.
#
# Drives the runtime LPDDR test controls in `ps2_hps_bridge` and verifies the
# tile-flush writer reached real LPDDR. ONE bitstream (GS_TILE_PSMCT16FB_DEMO +
# GS_LPDDR_FB); all control is at runtime — no rebuild between stages.
#
# Same style/contract as ps2_status.sh: PS2 HPS-bridge base + busybox devmem
# (busybox avoids the devmem2 "Bus error" quirk on 0x?4-suffixed offsets — and
# LPDDR_BURSTS sits at 0x...34). See rtl/platform/ps2_hps_bridge.sv and
# docs/ch318-lpddr-fb-bringup.md.
#
# Usage (run from HPS Linux after loading the .core.rbf):
# ./ps2_lpddr_test.sh # read-only LPDDR status (safe; no arming)
# ./ps2_lpddr_test.sh --canary # arm canary (1 line), re-render, prove via counters
# ./ps2_lpddr_test.sh --full # arm full frame, re-render, prove via counters
# ./ps2_lpddr_test.sh --disarm # write LPDDR_CTRL = 0x2 (disarmed, canary)
#
# PROOF METHOD = bridge counters, NOT /dev/mem. The Ch318 writer targets the HPS
# LPDDR (f2sdram) at 0x80000000, which is a firmware-RESERVED region: reading it
# with `dd /dev/mem` HARD-CRASHES the fabric (needs a power cycle). So this script
# NEVER touches /dev/mem. It proves the write reached LPDDR by reading LPDDR_BYTES/
# LPDDR_BURSTS/LPDDR_STATUS over the HPS bridge (safe register reads). Byte-level
# CONTENT verification needs the Ch318b bridge-register readback path (ported from
# ao486 lpddr4b_loader.sv) — until that lands, content is not checked here.
#
# ONE-SHOT FIX: the EE bootlet renders once at boot (before you can arm), then
# halts -> BYTES stays 0. So --canary/--full arm FIRST, then pulse the core reset
# (CORE_CTRL[0]) to re-run the bootlet and flush a frame WHILE ARMED.
#
# Defaults are SAFE: the bitstream boots disarmed; this script only writes when
# you pass --canary/--full, and always leaves the writer disarmed on exit.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
MODE="${1:-status}"
# Register offsets (see ps2_hps_bridge.sv).
OFF_CORE_CTRL=0x010 # RW [0]=core reset (pulse 1->0 re-runs the EE bootlet)
OFF_LPDDR_CTRL=0x018 # RW [0]=arm [1]=canary (reset 0x2)
OFF_LPDDR_FB_BASE=0x01C # RW LPDDR byte base (reset 0x80000000)
OFF_LPDDR_STATUS=0x02C # R [0]=idle [1]=bresp_err [2]=fifo_ovf
OFF_LPDDR_BYTES=0x030 # R total bytes written
OFF_LPDDR_BURSTS=0x034 # R total 32-byte bursts
OFF_LPDDR_BRESP_ERRS=0x038 # R count of bursts with non-OKAY response (1=reset-race phantom; 256=all refused)
# Expected byte counts (canary = 1 top line = 32 B; full = 64x64 PSMCT16 = 8 KiB).
EXP_CANARY_BYTES=32
EXP_FULL_BYTES=8192
read_reg() { $DEVMEM "$(printf '0x%08x' $(( BASE + $1 )) )" w; }
write_reg() { $DEVMEM "$(printf '0x%08x' $(( BASE + $1 )) )" w "$2"; }
bit_set() { if [ $(( ($1 >> $2) & 1 )) -eq 1 ]; then echo 1; else echo 0; fi; }
show_status() {
local ctrl base st by bu
ctrl=$(read_reg $OFF_LPDDR_CTRL); base=$(read_reg $OFF_LPDDR_FB_BASE)
st=$(read_reg $OFF_LPDDR_STATUS); by=$(read_reg $OFF_LPDDR_BYTES); bu=$(read_reg $OFF_LPDDR_BURSTS)
printf "LPDDR writer status\n"
printf " LPDDR_CTRL : %s (arm=%d canary=%d)\n" "$ctrl" "$(bit_set $((ctrl)) 0)" "$(bit_set $((ctrl)) 1)"
printf " LPDDR_FB_BASE: %s\n" "$base"
printf " LPDDR_STATUS : %s (idle=%d bresp_err=%d fifo_ovf=%d)\n" \
"$st" "$(bit_set $((st)) 0)" "$(bit_set $((st)) 1)" "$(bit_set $((st)) 2)"
printf " LPDDR_BYTES : %s\n LPDDR_BURSTS : %s\n" "$by" "$bu"
printf " LPDDR_BRESP_ERRS: %s\n" "$(read_reg $OFF_LPDDR_BRESP_ERRS)"
}
err_bits_clear() { # 1 if bresp_err and fifo_ovf both 0
local st=$(( $(read_reg $OFF_LPDDR_STATUS) ))
[ "$(bit_set $st 1)" = "0" ] && [ "$(bit_set $st 2)" = "0" ]
}
# Re-run the EE bootlet so it renders a frame WHILE the writer is armed.
# (The bootlet is one-shot; it renders once at boot, before you can arm.)
rerender_pulse() {
write_reg $OFF_CORE_CTRL 0x1 # assert core reset
sleep 1
write_reg $OFF_CORE_CTRL 0x0 # release -> bootlet re-runs, flushes a frame
sleep 3 # ~2 s DMAC-drain render cadence + margin
}
# Arm, re-render, and prove the write reached LPDDR via the bridge counters.
# $1 = LPDDR_CTRL arm value (0x3 canary / 0x1 full), $2 = expected byte count, $3 = label
prove_via_counters() {
local armval=$1 expbytes=$2 label=$3 by bu
write_reg $OFF_LPDDR_CTRL "$armval" # arm
rerender_pulse # render a frame while armed
by=$(( $(read_reg $OFF_LPDDR_BYTES) ))
bu=$(( $(read_reg $OFF_LPDDR_BURSTS) ))
be=$(( $(read_reg $OFF_LPDDR_BRESP_ERRS) ))
write_reg $OFF_LPDDR_CTRL 0x2 # DISARM
printf "after re-render: LPDDR_BYTES=%d (expect %d) LPDDR_BURSTS=%d BRESP_ERRS=%d\n" "$by" "$expbytes" "$bu" "$be"
if [ "$by" -ge "$expbytes" ] && err_bits_clear; then
printf "%s: PASS (fabric delivered %d B to LPDDR; no AXI/FIFO errors)\n" "$label" "$by"
return 0
else
printf "%s: FAIL (BYTES=%d < %d, or error bits set)\n" "$label" "$by" "$expbytes"
show_status; return 1
fi
}
case "$MODE" in
status)
show_status
;;
--canary)
printf "== LPDDR CANARY (32-byte top-line write, counter proof) ==\n"
printf "defaults: LPDDR_CTRL=%s (expect 0x00000002) LPDDR_FB_BASE=%s (expect 0x80000000)\n" \
"$(read_reg $OFF_LPDDR_CTRL)" "$(read_reg $OFF_LPDDR_FB_BASE)"
prove_via_counters 0x3 "$EXP_CANARY_BYTES" CANARY; exit $?
;;
--full)
printf "== LPDDR FULL FRAME (%d B, counter proof) ==\n" "$EXP_FULL_BYTES"
prove_via_counters 0x1 "$EXP_FULL_BYTES" FULL; exit $?
;;
--disarm)
write_reg $OFF_LPDDR_CTRL 0x2
printf "disarmed (LPDDR_CTRL=0x2)\n"
;;
*)
printf "usage: %s [--canary|--full|--disarm] (no arg = status)\n" "$0"; exit 2
;;
esac
+143
View File
@@ -0,0 +1,143 @@
#!/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
+147
View File
@@ -0,0 +1,147 @@
#!/bin/sh
# retroDE_ps2 OSD path validation — Ch232 hardware bring-up helper.
#
# Writes a 9-character white-on-blue test message ("01234 ABC") into
# the Ch227/Ch229/Ch231 OSD tile RAM at cells (0,0)..(8,0), then
# asserts OSD_CTRL[0]=1 to enable the overlay. Use it to confirm the
# full HPS-to-video OSD path is alive on the DE25-Nano.
#
# The chars 0-9 + space + A,B,C are the glyphs currently populated in
# `osd_overlay_stub.font_rom`. Other ASCII codes will render as solid
# background blocks (correct "missing glyph" fallback) — see the Ch231
# section of the bring-up runbook.
#
# Usage:
# ./ps2_osd_test.sh # write message + enable overlay
# ./ps2_osd_test.sh --off # disable overlay (OSD_CTRL[0]=0)
# ./ps2_osd_test.sh --clear # zero the 9 cells + leave overlay enabled
# ./ps2_osd_test.sh --status # dump OSD_CTRL / tile RAM cells for inspection
#
# Uses `busybox devmem` (matching ps2_status.sh) — sidesteps the
# devmem2 0x?4-offset quirk and reads/writes a single 32-bit word per
# call.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
MODE="${1:-write}"
# Bridge offsets.
OFF_OSD_CTRL=0x100
OFF_TILE_BASE=0x1000
# Compute an absolute address for a relative byte offset.
addr() {
printf "0x%08x" $(( BASE + $1 ))
}
write32() {
# $1 = relative offset, $2 = 32-bit value (hex)
$DEVMEM "$(addr $1)" w "$2"
}
read32() {
# $1 = relative offset; prints "0x%08x"
$DEVMEM "$(addr $1)" w
}
# Cell encoder: 16-bit value = {bg[3:0], fg[3:0], char[7:0]}.
# Default fg=15 (white), bg=1 (blue) for the test message.
cell_val() {
# $1 = char code (decimal), $2 = fg (0..15), $3 = bg (0..15)
printf "0x%04x" $(( ($3 << 12) | ($2 << 8) | $1 ))
}
# Pack two 16-bit cells into a 32-bit word.
# word = {high_cell, low_cell} = (high << 16) | low
# Software writes WORDS to the bridge; each word stores cells
# (col=N, row) in the low half and (col=N+1, row) in the high half
# at byte offset (row * 128 + (N & ~1) * 2).
pack_word() {
# $1 = low 16-bit cell value, $2 = high 16-bit cell value
printf "0x%08x" $(( ($2 << 16) | $1 ))
}
# Write a cell at (col, row). Performs a read-modify-write of the
# underlying 32-bit word so the neighboring cell in the same word
# is preserved.
write_cell() {
# $1 = col, $2 = row, $3 = 16-bit cell value
local col=$1 row=$2 val=$3
local word_byte_off=$(( OFF_TILE_BASE + row * 128 + (col / 2) * 4 ))
local current_word=$($DEVMEM "$(addr $word_byte_off)" w)
# Strip the leading 0x for arithmetic.
local cur=$(( current_word ))
local new
if [ $(( col % 2 )) -eq 0 ]; then
# Low half — preserve high half.
new=$(( (cur & 0xFFFF0000) | (val & 0xFFFF) ))
else
# High half — preserve low half.
new=$(( (cur & 0x0000FFFF) | ((val & 0xFFFF) << 16) ))
fi
$DEVMEM "$(addr $word_byte_off)" w "$(printf '0x%08x' $new)"
}
# Char codes for our test message "01234 ABC".
MSG_CHARS="48 49 50 51 52 32 65 66 67" # '0'..'4' ' ' 'A' 'B' 'C'
FG=15 # white
BG=1 # blue
write_message() {
local col=0
for code in $MSG_CHARS; do
local val=$(cell_val "$code" $FG $BG)
write_cell "$col" 0 "$val"
col=$(( col + 1 ))
done
}
clear_message() {
local col=0
for code in $MSG_CHARS; do
write_cell "$col" 0 0x0000
col=$(( col + 1 ))
done
}
set_osd_enable() {
# $1 = 0 or 1
write32 $OFF_OSD_CTRL "0x0000000$1"
}
dump_status() {
printf "OSD_CTRL @ 0x%03x : %s\n" $OFF_OSD_CTRL "$(read32 $OFF_OSD_CTRL)"
printf "Tile cells (col, row=0) words:\n"
local off=0
while [ $off -lt 20 ]; do
local byte_off=$(( OFF_TILE_BASE + off * 4 ))
printf " word @ 0x%04x : %s (cells %d, %d)\n" \
"$byte_off" "$(read32 $byte_off)" $(( off * 2 )) $(( off * 2 + 1 ))
off=$(( off + 1 ))
done
}
case "$MODE" in
--off)
set_osd_enable 0
echo "OSD disabled."
;;
--clear)
clear_message
set_osd_enable 1
echo "Cleared 9 cells, overlay still enabled."
;;
--status)
dump_status
;;
*)
write_message
set_osd_enable 1
echo 'Wrote "01234 ABC" at cells (0..8, 0), white on blue.'
echo "Overlay enabled (OSD_CTRL[0]=1)."
echo "The text should appear in the top-left of the HDMI image,"
echo "overlaying the Ch171 quadrant test card."
;;
esac
+326
View File
@@ -0,0 +1,326 @@
#!/bin/sh
# retroDE_ps2 status block — Ch219 operator helper.
#
# Reads the retroDE ABI v1.0 + ps2-specific diagnostic registers
# exposed by `ps2_hps_bridge` on the hps2fpga bridge and prints a
# one-screen status block. Run from the HPS Linux on the DE25-Nano
# board after `core_loader.sh load /home/terasic/cores/retroDE_ps2.core.rbf`.
#
# Uses `busybox devmem` rather than `devmem2` to avoid a known
# devmem2 access-size quirk on `0x?4`-suffixed offsets (the older
# devmem2 build throws "Bus error" on ABI_VERSION @ 0x40000004 and
# DMA_DONE_COUNT @ 0x40000024 — busybox devmem reads them cleanly).
#
# Usage:
# ./ps2_status.sh # one-shot snapshot
# ./ps2_status.sh --delta # take two snapshots 500ms apart, show counter deltas
#
# Exits 0 if CORE_ID matches "PS2\0" and CORE_STATUS bit [5]
# (hdmi_i2c_error) is clear, else nonzero. Suitable for automation.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
MODE="${1:-one-shot}"
# Register offsets (matches retroDE ABI v1.0 + ps2 diagnostics; see
# rtl/platform/ps2_hps_bridge.sv and docs/hardware/de25_nano_bringup.md).
OFF_CORE_ID=0x000
OFF_ABI_VERSION=0x004
OFF_CORE_STATUS=0x008
OFF_CORE_CAPS=0x00C
OFF_CORE_CTRL=0x010
OFF_CORE_PULSE=0x014
OFF_FRAME_COUNT=0x020
OFF_DMA_DONE=0x024
OFF_RASTER_OF=0x028
# Ch222 input latches + Ch226 DS2 stub (added to the snapshot in Ch236
# so operators can confirm the input-latch landing on real silicon).
OFF_INPUT_P1=0x040
OFF_INPUT_P2=0x044
OFF_INPUT_P1_RAW=0x048
OFF_DS2_STATUS=0x0F0
OFF_DS2_BUTTONS=0x0F4
# Ch245 — OSD registers (sibling-ABI offsets, now wired to the
# platform osd_overlay + osd_menu_fsm).
OFF_OSD_CTRL=0x100
OFF_OSD_STATUS=0x104
OFF_OSD_TRIGGER=0x108
OFF_OSD_CFG0=0x110
OFF_OSD_CFG1=0x114
read_reg() {
# $1 = offset (hex string with 0x prefix)
# Returns the 32-bit value as 0x%08x via stdout.
local addr=$(printf "0x%08x" $(( BASE + $1 )) )
$DEVMEM "$addr" w
}
bit_set() {
# $1 = value, $2 = bit index → echo "1" if bit set, "0" if not.
local v=$1 b=$2
if [ $(( (v >> b) & 1 )) -eq 1 ]; then echo "1"; else echo "0"; fi
}
print_status() {
local label=$1
local id abi caps status ctrl frame dma raster
id=$(read_reg $OFF_CORE_ID)
abi=$(read_reg $OFF_ABI_VERSION)
caps=$(read_reg $OFF_CORE_CAPS)
status=$(read_reg $OFF_CORE_STATUS)
ctrl=$(read_reg $OFF_CORE_CTRL)
frame=$(read_reg $OFF_FRAME_COUNT)
dma=$(read_reg $OFF_DMA_DONE)
raster=$(read_reg $OFF_RASTER_OF)
printf "retroDE_ps2 core status [%s] @ %s\n" "$label" "$(date)"
printf "================================================\n"
printf " CORE_ID : %s " "$id"
if [ "$id" = "0x50533200" ]; then
printf '"PS2\\0" ✓ ps2 fabric loaded\n'
else
printf " ✗ wrong/no fabric loaded (expected 0x50533200)\n"
fi
printf " ABI_VERSION : %s " "$abi"
if [ "$abi" = "0x00000100" ]; then
printf "(v1.0) ✓ retroDE ABI v1.0\n"
else
printf " ✗ unexpected ABI\n"
fi
# Ch246 — CORE_CAPS now advertises OSD geometry per the sibling-ABI
# bit layout (matches nes_hps_bridge.sv). Decode the fields so
# operators can spot a misadvertisement at a glance.
local caps_cols caps_rows caps_2p caps_analog caps_savestates caps_save
caps_save=$(( caps & 0x1 ))
caps_savestates=$(( (caps >> 1) & 0x1 ))
caps_2p=$(( (caps >> 2) & 0x1 ))
caps_analog=$(( (caps >> 3) & 0x1 ))
caps_cols=$(( (caps >> 8) & 0xff ))
caps_rows=$(( (caps >> 16) & 0x1f ))
printf " CORE_CAPS : %s (osd=%dx%d save=%d ss=%d 2p=%d analog=%d)\n" \
"$caps" "$caps_cols" "$caps_rows" "$caps_save" "$caps_savestates" "$caps_2p" "$caps_analog"
printf "\n"
# CORE_STATUS bit decode
local sv=$((status))
printf " CORE_STATUS : %s\n" "$status"
printf " [0] loaded : %s\n" "$(bit_set $sv 0)"
# Ch251 inverted core_halt semantics: pre-Ch251 the bootlet halted
# after a single paint (lit = healthy); the animated demo now loops
# forever (unlit = healthy, lit = EE stuck on an unexpected SYSCALL).
printf " [1] core_halt : %s " "$(bit_set $sv 1)"
if [ "$(bit_set $sv 1)" = "0" ]; then
printf "✓ Ch251 animated demo running (expected)\n"
else
printf "✗ EE halted — pre-Ch251 sentinel, or animated loop crashed/SYSCALL'd\n"
fi
printf " [2] dma_done_seen : %s\n" "$(bit_set $sv 2)"
printf " [3] frame_seen : %s (lit = PCRTC delivered ≥1 frame)\n" "$(bit_set $sv 3)"
printf " [4] hdmi_init_done : %s (lit = ADV7513 LUT walk complete)\n" "$(bit_set $sv 4)"
printf " [5] hdmi_i2c_error : %s " "$(bit_set $sv 5)"
if [ "$(bit_set $sv 5)" = "0" ]; then printf "✓ no I²C NACKs\n"; else printf "✗ NACK watchdog LATCHED — see runbook triage\n"; fi
printf " [6] raster_overflow : %s " "$(bit_set $sv 6)"
if [ "$(bit_set $sv 6)" = "0" ]; then printf "✓ raster healthy\n"; else printf "✗ raster FIFO overflowed — Ch172 backpressure broken?\n"; fi
printf "\n"
# CORE_CTRL bit decode
local cv=$((ctrl))
printf " CORE_CTRL : %s\n" "$ctrl"
printf " [0] reset : %s (1 = PS2 design held in reset)\n" "$(bit_set $cv 0)"
printf " [1] rom_loaded : %s (ABI-shape latch, no functional effect yet)\n" "$(bit_set $cv 1)"
printf " [2] pause : %s (ABI-shape latch, no functional effect yet)\n" "$(bit_set $cv 2)"
printf "\n"
printf " Counters:\n"
printf " FRAME_COUNT : %s (advances at ~60Hz once PCRTC is alive)\n" "$frame"
printf " DMA_DONE_COUNT : %s\n" "$dma"
printf " RASTER_OVERFLOW_COUNT : %s (should be 0 under Ch172 backpressure)\n" "$raster"
printf "\n"
# Ch222 / Ch226 / Ch235 input-latch readbacks. These prove the
# HPS-to-bridge half of the input path is alive on real silicon.
# PS2-fabric consumption (Ch234 sio2_input_stub via IOP) is sim-
# validated by tb_bridge_iop_pad_input but not wired into the
# synth top yet — so non-zero values here mean "the bridge latch
# landed" rather than "an in-fabric consumer saw it."
local p1 p2 p1raw ds2s ds2b
p1=$(read_reg $OFF_INPUT_P1)
p2=$(read_reg $OFF_INPUT_P2)
p1raw=$(read_reg $OFF_INPUT_P1_RAW)
ds2s=$(read_reg $OFF_DS2_STATUS)
ds2b=$(read_reg $OFF_DS2_BUTTONS)
printf " Input latches (Ch222) + DS2 mirror (Ch226):\n"
printf " INPUT_P1 : %s\n" "$p1"
printf " INPUT_P2 : %s\n" "$p2"
printf " INPUT_P1_RAW : %s\n" "$p1raw"
# Ch248 — DS2_STATUS is now driven by the platform ds2_controller
# (via the bridge's new ds2_connected_i / ds2_error_i input ports).
# Bit layout: [0]=connected, [1]=error, [2]=1 (PS2-local legacy bit).
local ds2_conn ds2_err
ds2_conn=$(( $ds2s & 0x1 ))
ds2_err=$(( ($ds2s >> 1) & 0x1 ))
printf " DS2_STATUS : %s ([0]=connected=%d [1]=error=%d [2]=reserved=1)\n" \
"$ds2s" "$ds2_conn" "$ds2_err"
# DS2_BUTTONS is now the live wired-controller bitmap (Ch248); the
# Ch226 INPUT_P1 mirror was removed because it blocked retrodesd's
# ds2_poll_thread from seeing real button state.
printf " DS2_BUTTONS : %s (live ds2_controller readback — Ch248)\n" "$ds2b"
if [ "$ds2_conn" = "1" ]; then
printf " → controller plugged in; ds2_poll_thread should be writing INPUT_P1/RAW from this.\n"
else
printf " → controller unplugged (DS2_STATUS[0]=0). Plug a DS2 wired pad into\n"
printf " the DE25 GPIO header (CLK/CMD/DATA/ATTN = PIN_H16/Y1/C2/P1) to wake the path.\n"
fi
# Ch245 — platform OSD register surface. Sibling-ABI layout; the
# shared retroDE_splash overlay + menu FSM is wired to these.
local octrl ostatus otrig ocfg0 ocfg1
octrl=$(read_reg $OFF_OSD_CTRL)
ostatus=$(read_reg $OFF_OSD_STATUS)
otrig=$(read_reg $OFF_OSD_TRIGGER)
ocfg0=$(read_reg $OFF_OSD_CFG0)
ocfg1=$(read_reg $OFF_OSD_CFG1)
printf "\n OSD registers (Ch245 platform OSD):\n"
printf " OSD_CTRL : %s ([0]=enable [2]=force_open [3]=force_close,self-clear)\n" "$octrl"
printf " OSD_STATUS : %s ([0]=osd_active [12:8]=cursor_row)\n" "$ostatus"
printf " OSD_TRIGGER : %s ([4]=A [5]=B [6]=down [7]=up [16]=open [12:8]=row)\n" "$otrig"
printf " OSD_CFG0 : %s ([5:0]=cols [12:8]=rows [23:16]=origin_x_chars [31:24]=origin_y_chars)\n" "$ocfg0"
printf " OSD_CFG1 : %s ([4:0]=menu_first_row [12:8]=menu_last_row [23:16]=cursor_attr)\n" "$ocfg1"
# Decode CFG0 fields for human readability.
local cols rows origx origy origx_pix origy_pix
cols=$(( ocfg0 & 0x3f ))
rows=$(( (ocfg0 >> 8 ) & 0x1f ))
origx=$(((ocfg0 >> 16) & 0xff ))
origy=$(((ocfg0 >> 24) & 0xff ))
origx_pix=$(( origx * 16 ))
origy_pix=$(( origy * 16 ))
printf " → cols=%d rows=%d origin=(%d,%d) chars = (%d,%d) px at 2× scale\n" \
"$cols" "$rows" "$origx" "$origy" "$origx_pix" "$origy_pix"
}
print_delta() {
# Ch253 — two snapshots 2 s apart so a Ch251.5 ~1 Hz heartbeat
# toggle is captured reliably regardless of phase (was 500 ms in
# Ch219 when the bootlet was one-shot and counter Δ didn't matter
# for blink rate).
local f1 d1 r1 f2 d2 r2
f1=$(read_reg $OFF_FRAME_COUNT)
d1=$(read_reg $OFF_DMA_DONE)
r1=$(read_reg $OFF_RASTER_OF)
sleep 2
f2=$(read_reg $OFF_FRAME_COUNT)
d2=$(read_reg $OFF_DMA_DONE)
r2=$(read_reg $OFF_RASTER_OF)
local df dd dr
df=$(( f2 - f1 ))
dd=$(( d2 - d1 ))
dr=$(( r2 - r1 ))
printf "\n Counter Δ over 2 s:\n"
printf " FRAME_COUNT : %s → %s Δ=%d (≈ 120 if PCRTC is alive @ 60 Hz)\n" "$f1" "$f2" "$df"
printf " DMA_DONE_COUNT : %s → %s Δ=%d (validated band 02 per 2 s — liveness cue, not a clock)\n" "$d1" "$d2" "$dd"
printf " RASTER_OVERFLOW_COUNT : %s → %s Δ=%d (MUST stay 0 — Ch172 backpressure)\n" "$r1" "$r2" "$dr"
# Ch253 — Ch251+ animated-demo health verdict. Rolls up the
# individual signals into a single PASS/FAIL block so the
# operator can run `ps2_status.sh --delta` and know in <3 s
# whether the field unit is healthy.
local status_final halt_bit i2c_bit raster_bit ds2_conn_bit
status_final=$(read_reg $OFF_CORE_STATUS)
halt_bit=$(bit_set $((status_final)) 1)
i2c_bit=$(bit_set $((status_final)) 5)
raster_bit=$(bit_set $((status_final)) 6)
ds2_conn_bit=$(( $(read_reg $OFF_DS2_STATUS) & 0x1 ))
local verdict_ok=1
printf "\n Ch251+ animated-demo health verdict:\n"
if [ "$df" -ge 100 ] && [ "$df" -le 140 ]; then
printf " [ ✓ ] PCRTC alive (FRAME_COUNT Δ=%d ≈ 120 @ 60 Hz)\n" "$df"
else
printf " [ ✗ ] PCRTC stalled/off-rate (FRAME_COUNT Δ=%d, expected 100140)\n" "$df"
verdict_ok=0
fi
if [ "$raster_bit" = "0" ] && [ "$dr" -eq 0 ]; then
printf " [ ✓ ] Raster healthy (RASTER_OVERFLOW_COUNT stable + bit[6] clear)\n"
else
printf " [ ✗ ] Raster overflowing (Δ=%d / status bit[6]=%s — Ch172 broken?)\n" "$dr" "$raster_bit"
verdict_ok=0
fi
# Ch253 / Ch254 — this counter is a LIVENESS CUE, not a precision
# timer. The bootlet ties DMAC DONE 1:1 to the visible heartbeat
# toggle, so DMA_DONE Δ over 2 s is a faithful proxy for the blink.
# Hardware-validated Δ band is {0, 1, 2}; ~1.2 s fixed per-iter
# overhead (DMAC drain + 17-SPRITE GS rasterize) puts the cadence
# around 0.5 Hz with natural ±0.5 s jitter — see bake.py comment
# block and de25_nano_bringup.md Ch254 for the empirical model.
# Δ=0 is a 2 s phase-miss not a failure; rerun protocol.
if [ "$dd" -ge 1 ] && [ "$dd" -le 2 ]; then
printf " [ ✓ ] DMAC repaint liveness (DMA_DONE Δ=%d over 2 s; bootlet animating)\n" "$dd"
elif [ "$dd" -eq 0 ]; then
printf " [ ? ] DMAC repaint window miss (Δ=0 — likely 2 s phase miss; rerun. Persistent Δ=0 = bootlet stuck)\n"
# Don't fail the verdict on a single Δ=0; rerun is the right
# protocol. Persistent stuck-state shows up as the operator
# running --delta twice in a row and getting Δ=0 both times.
else
printf " [ ? ] DMAC repaint off-rate (Δ=%d, validated band is 02 over 2 s — investigate)\n" "$dd"
verdict_ok=0
fi
if [ "$halt_bit" = "0" ]; then
printf " [ ✓ ] EE core not halted (CORE_STATUS[1]=0)\n"
else
printf " [ ✗ ] EE halted (CORE_STATUS[1]=1 — animated loop crashed/SYSCALL)\n"
verdict_ok=0
fi
if [ "$i2c_bit" = "0" ]; then
printf " [ ✓ ] HDMI I²C clean (CORE_STATUS[5]=0)\n"
else
printf " [ ✗ ] HDMI I²C NACKed (CORE_STATUS[5]=1 — see runbook triage)\n"
verdict_ok=0
fi
if [ "$ds2_conn_bit" = "1" ]; then
printf " [ ✓ ] DS2 controller plugged (DS2_STATUS[0]=1)\n"
else
printf " [ — ] DS2 controller unplugged (informational — Select+Start menu needs a pad)\n"
fi
if [ "$verdict_ok" -eq 1 ]; then
printf "\n ──> Ch251+ field health: PASS\n"
else
printf "\n ──> Ch251+ field health: FAIL (see flags above)\n"
fi
# Build-time profile reminder. The script cannot read VRAM_ENABLE_READ2
# at runtime (it is baked into the bitstream); the sim-side tripwire
# in vram_bram_stub.sv catches a bad profile during iverilog/verilator
# elaboration, but Quartus does not honour it (the guard sits inside
# `translate_off`). Real synth-side protection is the explicit
# parameter override in de25_nano_psmct32_raster_demo_top.sv.
printf "\n Profile note: hardware build assumes VRAM_ENABLE_READ2=0 (Ch252).\n"
printf " Re-enabling read2 at VRAM_BYTES=512 KiB will blow the\n"
printf " Agilex 5 M20K budget at fit time. See\n"
printf " docs/decisions/0006-vram-roadmap.md for triggers.\n"
}
print_status "snapshot"
if [ "$MODE" = "--delta" ]; then
print_delta
fi
# Exit status: 0 if CORE_ID + clean I²C, nonzero otherwise.
id_ok=$(read_reg $OFF_CORE_ID)
status_check=$(read_reg $OFF_CORE_STATUS)
i2c_err=$(bit_set $((status_check)) 5)
if [ "$id_ok" = "0x50533200" ] && [ "$i2c_err" = "0" ]; then
exit 0
else
exit 1
fi
+145
View File
@@ -0,0 +1,145 @@
#!/bin/sh
# retroDE_ps2 — Ch327b 16x16 MULTI-TILE spill/reload silicon proof (256x256 raster FB in LPDDR, line-buffer scanout).
#
# Renders the 16x16 two-batch scene (P1 near color1 + P2 mid color2, cross-seam), reads the
# 256x256 color framebuffer back out of LPDDR via the HPS read-probe (subsampled), dumps a PPM,
# categorizes the regions. Proves grid spill/reload + per-tile depth survival on hardware.
#
# Acceptance (matches the sim tb_gs_tile_spill_grid8x8_lpddr scaled + tb_gs_lpddr_scanout_lb_psm32_256):
# - overlap (P1 region, x+y<256) keeps color1 (red) across MANY tiles => depth survival
# - region B (264<x+y<312, P2 only) accepts color2 (blue)
# - empty tiles (top rows, bottom-right) stay CLEAR (0x008000 green clear), not painted
# - ovf=0, errs=0; spill/reload counters sane (sim ref: spill_color~16384, reloaded_tiles~121)
#
# REQUIRES the Ch327b bitstream: ./scripts/select_de25_profile.sh tile_spill (then re-fit)
# Writes /tmp/ps2_tile8x8.ppm — scp it off and view to SEE the red/blue/green scene.
set -u
BASE="${PS2_BRIDGE_BASE:-0x40000000}"
DEVMEM="${DEVMEM:-busybox devmem}"
PPM="${PPM:-/tmp/ps2_tile8x8.ppm}"
OFF_CORE_CTRL=0x010
OFF_LPDDR_STATUS=0x02C
OFF_LPDDR_RDADDR=0x03C
OFF_SPILL_COLOR_BEATS=0x080; OFF_SPILL_Z_BEATS=0x084
OFF_RELOAD_COLOR_BEATS=0x088; OFF_RELOAD_Z_BEATS=0x08C
OFF_RELOAD_RD_ERRS=0x090; OFF_SPILL_COLOR_ERRS=0x094; OFF_SPILL_Z_ERRS=0x098; OFF_SPILL_OVF=0x09C
OFF_EV_TP_FLUSH=0x0A0; OFF_EV_TP_ZFLUSH=0x0A4; OFF_EV_TP_RELOAD=0x0A8; OFF_EV_TP_RENDER=0x0AC
OFF_EV_FLUSH_EMIT=0x0B0; OFF_EV_ZFLUSH_EMIT=0x0B4; OFF_EV_RELOAD_START=0x0B8; OFF_EV_RELOAD_READY=0x0BC
OFF_DIAG_CTRL=0x0C0 # [6]=trace_clear
OFF_LPDDR_CTRL=0x018 # [2]=video_src (1=LPDDR scanout, 0=BRAM) [3]=scanout_lb (0=frame-cache)
# color-writer pipeline counters (0xCC/0xF8/0xFC/0xE0) + Ch324 Z-writer counters (0xC4/0xC8/0xD0/0xD4)
OFF_C_EMIT=0x0CC; OFF_C_PUSH=0x0F8; OFF_C_POP=0x0FC; OFF_C_BEATS=0x0E0
OFF_Z_BEATS=0x0C4; OFF_Z_EMIT=0x0C8; OFF_Z_PUSH=0x0D0; OFF_Z_POP=0x0D4
COLOR_SPILL_BASE=0x00400000; Z_SPILL_BASE=0x00500000; ROW_STRIDE=1024 # Ch327b 256 px * 4 B = 1024
FBW_PX=256; FBH_PX=256
STEP="${STEP:-2}" # Ch327b — subsample the readback sweep (256x256=65536 reads is ~15 min at STEP=1)
RED=0xFF0000FF; BLUE=0xFFFF0000; CLEARC=0xFF008000 # color1 / color2 / tile clear (green, opaque)
w() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w "$2" >/dev/null; }
r() { $DEVMEM $(printf "0x%X" $(( BASE + $1 ))) w; }
rdprobe() { # $1 = EMIF byte addr -> 32-bit word
w $OFF_LPDDR_RDADDR "$1"
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
}
echo "=== Ch327b 16x16 multi-tile spill/reload silicon proof (256x256, line-buffer scanout) ==="
# ---- (1) hold core in reset; one render (no preseed — clean-Z bootstrap) ----
echo "[1] holding core in reset, then one render ..."
w $OFF_CORE_CTRL 0x1
scb0=$(( $(r $OFF_SPILL_COLOR_BEATS) )); szb0=$(( $(r $OFF_SPILL_Z_BEATS) ))
rcb0=$(( $(r $OFF_RELOAD_COLOR_BEATS) )); rzb0=$(( $(r $OFF_RELOAD_Z_BEATS) ))
rs0=$(( $(r $OFF_EV_RELOAD_START) ))
w $OFF_DIAG_CTRL 0x40; w $OFF_DIAG_CTRL 0x00 # trace_clear pulse
w $OFF_CORE_CTRL 0x0; sleep 4
# ---- (2) counters ----
dC=$(( $(r $OFF_SPILL_COLOR_BEATS) - scb0 )); dZ=$(( $(r $OFF_SPILL_Z_BEATS) - szb0 ))
dRC=$(( $(r $OFF_RELOAD_COLOR_BEATS) - rcb0 )); dRZ=$(( $(r $OFF_RELOAD_Z_BEATS) - rzb0 ))
dRS=$(( $(r $OFF_EV_RELOAD_START) - rs0 ))
ovf=$(( $(r $OFF_SPILL_OVF) ))
errs=$(( $(r $OFF_SPILL_COLOR_ERRS) + $(r $OFF_SPILL_Z_ERRS) + $(r $OFF_RELOAD_RD_ERRS) ))
printf "[2] spill_color=%d spill_z=%d reload_color=%d reload_z=%d reloaded_tiles=%d ovf=0x%X errs=%d\n" \
"$dC" "$dZ" "$dRC" "$dRZ" "$dRS" "$ovf" "$errs"
printf " FSM evts: TP_FLUSH=%d TP_ZFLUSH=%d TP_RELOAD=%d TP_RENDER=%d (sim ref: spill_color~16384 reloaded_tiles~121)\n" \
"$(( $(r $OFF_EV_TP_FLUSH) ))" "$(( $(r $OFF_EV_TP_ZFLUSH) ))" "$(( $(r $OFF_EV_TP_RELOAD) ))" "$(( $(r $OFF_EV_TP_RENDER) ))"
# pipeline split — compare COLOR vs Z in the SAME run to localize the spill_z over-production.
cemit=$(( $(r $OFF_C_EMIT) )); cpush=$(( $(r $OFF_C_PUSH) )); cpop=$(( $(r $OFF_C_POP) )); cbeat=$(( $(r $OFF_C_BEATS) ))
zemit=$(( $(r $OFF_Z_EMIT) )); zpush=$(( $(r $OFF_Z_PUSH) )); zpop=$(( $(r $OFF_Z_POP) )); zbeat=$(( $(r $OFF_Z_BEATS) ))
printf " PIPELINE color: emit=%d push=%d pop=%d beats=%d\n" "$cemit" "$cpush" "$cpop" "$cbeat"
printf " PIPELINE Z : emit=%d push=%d pop=%d beats=%d (color==Z expected; ovf bit1=Z)\n" "$zemit" "$zpush" "$zpop" "$zbeat"
if [ "$zemit" -gt "$cemit" ]; then echo " -> DIAG(Z): emit>color TP_ZFLUSH/tile-iteration emits too many (upstream)"
elif [ "$zpush" -gt "$cpush" ]; then echo " -> DIAG(Z): push>color PACKER over-produces partial Z beats"
elif [ "$zpop" -gt "$zpush" ] || [ "$zbeat" -gt "$zpush" ]; then echo " -> DIAG(Z): pop/beats>push Z async-FIFO/CDC phantom drain"
else echo " -> Z pipeline matches color"; fi
# ---- (3) sweep the 256x256 color FB (subsampled by STEP) -> PPM + categorize ----
sw=$(( FBW_PX / STEP )); sh=$(( FBH_PX / STEP ))
echo "[3] reading 256x256 color FB from LPDDR (STEP=$STEP -> $((sw*sh)) reads) -> $PPM ..."
printf "P3\n%d %d\n255\n" "$sw" "$sh" > "$PPM"
nred=0; nblue=0; nclear=0; nother=0
ovl_red=0; regB_blue=0; empty_painted=0
y=0
while [ $y -lt $FBH_PX ]; do
x=0
while [ $x -lt $FBW_PX ]; do
a=$(( COLOR_SPILL_BASE + y*ROW_STRIDE + x*4 )); v=$(( $(rdprobe "$(printf "0x%X" $a)") ))
cr=$(( v & 0xFF )); cg=$(( (v>>8) & 0xFF )); cb=$(( (v>>16) & 0xFF ))
printf "%d %d %d\n" "$cr" "$cg" "$cb" >> "$PPM"
if [ $v -eq $(( RED )) ]; then nred=$(( nred+1 ))
elif [ $v -eq $(( BLUE )) ]; then nblue=$(( nblue+1 ))
elif [ $v -eq $(( CLEARC )) ]; then nclear=$(( nclear+1 ))
else nother=$(( nother+1 )); fi
s=$(( x + y ))
# P1 (80,80)-(176,80)-(80,176): overlap red where x+y<240 (inside the P1 hypotenuse x+y=256)
[ $x -ge 80 ] && [ $y -ge 80 ] && [ $s -lt 240 ] && [ $v -eq $(( RED )) ] && ovl_red=$(( ovl_red+1 ))
# P2-only band (between P1 hyp 256 and P2 hyp 320): blue
[ $x -ge 80 ] && [ $y -ge 80 ] && [ $s -gt 264 ] && [ $s -lt 312 ] && [ $v -eq $(( BLUE )) ] && regB_blue=$(( regB_blue+1 ))
# empty top tile-rows (gy<64, above the triangles which start at y=80): must not be painted
[ $y -lt 64 ] && { [ $v -eq $(( RED )) ] || [ $v -eq $(( BLUE )) ]; } && empty_painted=$(( empty_painted+1 ))
x=$(( x+STEP ))
done
y=$(( y+STEP ))
done
printf " color census: red=%d blue=%d clear=%d other=%d (subsampled STEP=%d)\n" "$nred" "$nblue" "$nclear" "$nother" "$STEP"
printf " overlap-red(depth-survived)=%d regionB-blue=%d empty-toprow-painted=%d\n" "$ovl_red" "$regB_blue" "$empty_painted"
printf " sample px: ovl(96,96)=0x%06X (144,96)=0x%06X (96,144)=0x%06X | regB(160,160)=0x%06X | empty(16,16)=0x%06X (224,224)=0x%06X\n" \
"$(( $(rdprobe "$(printf "0x%X" $((COLOR_SPILL_BASE+96*ROW_STRIDE+96*4)))") & 0xFFFFFF ))" \
"$(( $(rdprobe "$(printf "0x%X" $((COLOR_SPILL_BASE+96*ROW_STRIDE+144*4)))") & 0xFFFFFF ))" \
"$(( $(rdprobe "$(printf "0x%X" $((COLOR_SPILL_BASE+144*ROW_STRIDE+96*4)))") & 0xFFFFFF ))" \
"$(( $(rdprobe "$(printf "0x%X" $((COLOR_SPILL_BASE+160*ROW_STRIDE+160*4)))") & 0xFFFFFF ))" \
"$(( $(rdprobe "$(printf "0x%X" $((COLOR_SPILL_BASE+16*ROW_STRIDE+16*4)))") & 0xFFFFFF ))" \
"$(( $(rdprobe "$(printf "0x%X" $((COLOR_SPILL_BASE+224*ROW_STRIDE+224*4)))") & 0xFFFFFF ))"
# ---- (4) Ch327a — HDMI is the LPDDR FB via the SCALABLE LINE-BUFFER scanout ----
echo "[4] HDMI source = LPDDR framebuffer via LINE-BUFFER scanout (Ch327a: O(width) BRAM, not the"
echo " O(w*h) frame-cache). FB_LPDDR_ONLY external FB, ~120 KiB reclaimed. On-screen == $PPM."
# scanout error/underflow: LPDDR_STATUS bit [5] = scan_rd_err (line-buffer underflow OR rresp err).
scan_st=$(( $(r $OFF_LPDDR_STATUS) ))
scan_err=$(( (scan_st >> 5) & 1 ))
printf " LINE-BUFFER scanout: LPDDR_STATUS=0x%X scan_err/underflow(bit5)=%d (MUST be 0)\n" "$scan_st" "$scan_err"
if [ "$scan_err" -ne 0 ]; then echo " !! FAIL: line-buffer underflow/read-error — scanout missed its real-time refill deadline"; fi
# ---- diagnosis ----
# PASS is judged on the LPDDR FRAMEBUFFER CONTENT (the real proof — whichever render
# populated it, including the boot auto-render), not the per-render spill DELTA. The
# delta is supplementary: if the measured render this pass re-fired it should be
# color==z==1024, but a 0 delta just means the FB was already correct (e.g. the script
# raced the boot auto-render) — the content + LPDDR scanout still prove correctness.
echo "=== DIAGNOSIS ==="
if [ $ovl_red -ge 3 ] && [ $regB_blue -ge 3 ] && [ $empty_painted -eq 0 ] && [ $ovf -eq 0 ] && [ $errs -eq 0 ]; then
echo "PASS: grid spill/reload + LPDDR scanout works. color1 SURVIVED in the overlap across"
echo " multiple tiles (depth survival via per-tile reload), region B took color2, empty tiles"
echo " stayed clear, no overflow/errors. HDMI [4] now reads this FB from LPDDR scanout."
if [ $(( dC )) -eq 0 ]; then
echo " NOTE: this pass's spill DELTA was 0 — the FB was already rendered (boot auto-render);"
echo " the content above is the proof. Re-run for a fresh spill_color==spill_z==1024 snapshot."
fi
else
echo "PARTIAL/FAIL: ovl_red=$ovl_red regB_blue=$regB_blue empty_painted=$empty_painted ovf=0x$ovf errs=$errs"
echo " expected ovl_red>=3, regB_blue>=3, empty_painted=0, ovf=0, errs=0. Inspect $PPM + the [2] counters."
fi