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>
3.4 KiB
Ch318 — LPDDR framebuffer write/readback: board bring-up
ONE bitstream. All test controls are runtime, via HPS bridge registers — no rebuild to go disabled → canary → full. Defaults are safe: arm OFF, canary ON, base 0x80000000. The booted core writes nothing to LPDDR until the HPS arms it.
Runnable script
docs/hardware/ps2_lpddr_test.sh (same style as ps2_status.sh; bridge base defaults to
0x40000000, busybox devmem):
./ps2_lpddr_test.sh # read-only LPDDR status (safe)
./ps2_lpddr_test.sh --canary # arm canary, verify 32 B vs expected, PASS/FAIL, auto-disarm
./ps2_lpddr_test.sh --full # arm full frame, hash 8 KiB vs expected md5, PASS/FAIL, auto-disarm
./ps2_lpddr_test.sh --disarm # force disarm (LPDDR_CTRL=0x2)
The manual register/dd reference below is what the script automates.
Build
QSF (already set): GS_TILE_PSMCT16FB_DEMO=1 + GS_LPDDR_FB=1 (plus the usual
GS_RMW_DEMO). Build/load the .rbf once. That's the only build.
HPS bridge register map (new in Ch318)
Offsets are relative to the PS2 HPS-bridge base — the same base retrodesd already
uses to reach CORE_ID/OSD_CTRL/INPUT_P1 on this core (the HPS2FPGA bridge window).
32-bit accesses.
| Offset | Name | R/W | Meaning |
|---|---|---|---|
| 0x018 | LPDDR_CTRL | RW | bit0 = arm (1 = permit AXI writes), bit1 = canary (1 = write only the 32-byte top line). Reset = 0x2 (disarmed, canary). |
| 0x01C | LPDDR_FB_BASE | RW | LPDDR byte base address. Reset = 0x8000_0000. |
| 0x02C | LPDDR_STATUS | R | bit0 = idle, bit1 = bresp error seen, bit2 = FIFO overflow seen. |
| 0x030 | LPDDR_BYTES | R | total bytes written. |
| 0x034 | LPDDR_BURSTS | R | total 32-byte bursts issued. |
The framebuffer itself is read from physical LPDDR 0x8000_0000 (the f2sdram AXI
address is the HPS physical address — the qsys slave maps a flat 4 GiB), which is the
reserved region from /proc/iomem (below Linux System RAM at 0x82000000 — safe).
Canary test (32-byte write, deterministic)
- Confirm defaults: read
LPDDR_CTRL(expect 0x2),LPDDR_FB_BASE(expect 0x8000_0000). - Baseline:
sudo dd if=/dev/mem bs=1 skip=2147483648 count=32 2>/dev/null | hexdump -C - Arm in canary mode: write
LPDDR_CTRL = 0x3(arm=1, canary=1). - Re-read the 32 bytes (same
dd). Expect the top scanline (PSMCT16 green = 0x8200):00 82 00 82 00 82 00 82 00 82 00 82 00 82 00 82(×2 lines = 32 bytes). - Optional: read
LPDDR_BURSTS(advancing) +LPDDR_STATUS(bit1/bit2 = 0). PASS = bytes changed baseline → the00 82pattern (fabric reached LPDDR at the expected physical address). Then disarm: write LPDDR_CTRL = 0x2.
Full-frame test (8 KiB)
- Arm full: write
LPDDR_CTRL = 0x1(arm=1, canary=0). sudo dd if=/dev/mem bs=4096 skip=524288 count=2 2>/dev/null | md5sumExpect3b12baffc00bb6419fa66272c75b2cc7(the exact sim image).- Confirm
LPDDR_STATUSbits 1,2 = 0 (no bresp/FIFO errors). Disarm when done (0x2).
Notes
0x80000000= 2147483648 bytes;skip=524288blocks × 4096 = same address.- Never read/write
0x82000000–0xBFFFFFFF(live Linux RAM). - If a hardened kernel blocks
/dev/memto the reserved region, use the samedevmem/mmap path the existing runtime uses; if a readback looks stale, it's CPU caching of that address — read uncached. - Scanout from LPDDR is Ch319 — start only after this write/readback passes.