ec82764bef
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
3.8 KiB
C
94 lines
3.8 KiB
C
// ============================================================================
|
|
// 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;
|
|
}
|