// retroDE_ps2 — ps2_sh3_tex_upload (Ch352) // // Uploads the reconstructed 512x512 PSMT8 SH3 texture (65536 32-bit words) into FPGA-private LPDDR4B via the // PS2 HPS-bridge write-probe, verifies the readback, arms the texture-cache fill, and retriggers the feeder. // One command — mmap'd register pokes (NOT 65536 devmem process spawns). Same bridge protocol as ps2_feeder.c // and docs/hardware/ps2_lpddr_tex_test.sh (Ch322), just scaled to the full 256 KiB texture. // // Build on the board: gcc -O2 -o ps2_sh3_tex_upload ps2_sh3_tex_upload.c // Run (after fit+boot): sudo ./ps2_sh3_tex_upload sh3_real_tex_lpddr.mem // (copy sh3_real_tex_lpddr.mem from sim/data/top_psmct32_raster_demo/ to the board alongside the binary.) // // Sequence: (1) write WRADDR=0x200000, stream 65536 words to WRDATA; (2) read each back via the read-probe and // confirm sum32/xor32 match the file; (3) arm cache fill (0x054), poll fill_done, check beats/bytes/rd_errs; // (4) pulse the feeder retrigger (0x0E8) so the scene re-renders with the now-warm cache. #include #include #include #include #include #include #include #include #define OFF_LPDDR_STATUS 0x02C // R: [3] rd_pending (read-probe in flight) #define OFF_LPDDR_RDADDR 0x03C // W: set read byte addr + trigger ; R: 32-bit word #define OFF_LPDDR_WRADDR 0x04C // W: set LPDDR byte addr (auto-increments +4 per WRDATA write) #define OFF_LPDDR_WRDATA 0x050 // W: data word -> single 32-bit LPDDR write + addr += 4 #define OFF_TEX_FILL_CTRL 0x054 // W[0]: arm cache fill ; R: [0]fill_done [1]wr_busy #define OFF_LPDDR_WR_ERRS 0x06C // R: write-probe non-OKAY (BRESP) responses (expect 0) #define WR_PENDING_BIT 0x4 // 0x054 bit2 — Ch352 STABLE write-done flag (poll instead of transient wr_busy) #define RD_PENDING_BIT 0x8 // 0x02C bit3 #define OFF_TEX_FILL_BEATS 0x058 // R: beats filled (expect TEX_BYTES/32 = 8192) #define OFF_TEX_FILL_BYTES 0x05C // R: bytes filled (expect 262144) #define OFF_TEX_RD_ERRS 0x068 // R: fill non-OKAY read responses (expect 0) #define OFF_TEX_FILL_CRC 0x070 // R: sum32 of EVERY word the cache wrote into tex_mem (must == file sum32) #define OFF_FEEDER_GO 0x0E8 // W[0]: trigger/retrigger the feeder #define N_WORDS 65536 // 512*512 PSMT8 / 4 #define TEX_BYTES 262144 #define N_BEATS 8192 // TEX_BYTES / 32 typedef struct { volatile uint8_t *base; int dry; } bridge_t; static void wr32(bridge_t *b, int off, uint32_t v){ if(!b->dry) *(volatile uint32_t*)(b->base+off)=v; } static uint32_t rd32(bridge_t *b, int off){ return b->dry?0:*(volatile uint32_t*)(b->base+off); } int main(int argc, char **argv){ unsigned long base = 0x40000000UL; // PS2 HPS-bridge base (override --base or PS2_BRIDGE_BASE) unsigned long lpddr_base = 0x00200000; // EMIF byte base where the texture is staged (= TEX_LPDDR_BASE RTL) const char *texfile = "sh3_real_tex_lpddr.mem"; int dry=0, do_fill=1, do_retrig=1; char *env = getenv("PS2_BRIDGE_BASE"); if (env) base = strtoul(env,NULL,0); for (int i=1;i LPDDR 0x%lx (bridge base 0x%lx%s)\n", n, texfile, sum, xr, lpddr_base, base, dry?", DRY-RUN":""); // ---- open the bridge ---- bridge_t br = {0,dry}; int fd=-1; void *map=NULL; if (!dry){ fd=open("/dev/mem", O_RDWR|O_SYNC); if (fd<0){ fprintf(stderr,"error: open /dev/mem (run as root?): %s\n", strerror(errno)); return 1; } map=mmap(NULL,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(off_t)base); if (map==MAP_FAILED){ fprintf(stderr,"error: mmap 0x%lx: %s\n", base, strerror(errno)); close(fd); return 1; } br.base=(volatile uint8_t*)map; } // ---- (1) upload: set WRADDR then stream WRDATA. CRITICAL: poll wr_busy (0x054 bit1) clear after each word // so the write-probe actually COMMITS before the next write — otherwise the fast mmap writes outrun the // CDC/AXI commit and get DROPPED (the bug: most words read back as 0). The Ch322 devmem script got away with // no poll only because devmem process-spawns are slow. ---- wr32(&br, OFF_LPDDR_WRADDR, (uint32_t)lpddr_base); for (int i=0;i tex_mem %s\n", fcrc, sum, (fcrc==sum) ? "INTEGRITY OK" : "CORRUPT"); if (!done || beats!=N_BEATS || bytes!=TEX_BYTES || errs!=0) fprintf(stderr,"WARN: cache fill stats off — texels may be wrong; check EMIF cal + LPDDR base.\n"); if (fcrc!=sum) fprintf(stderr,"WARN: cache fill_crc mismatch — tex_mem corrupt on board (NOT a divider/sampler issue).\n"); } // ---- (4) retrigger the feeder so the scene re-renders with the warm cache ---- if (do_retrig && !dry){ wr32(&br, OFF_FEEDER_GO, 0x1); printf("[ps2_sh3_tex_upload] feeder retriggered.\n"); } if (!dry){ munmap(map,0x1000); close(fd); } printf("[ps2_sh3_tex_upload] DONE — check HDMI vs the crop reference (recon/sh3_real_ref.png).\n"); return 0; }