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>
62 lines
3.4 KiB
Markdown
62 lines
3.4 KiB
Markdown
# 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)
|
||
1. Confirm defaults: read `LPDDR_CTRL` (expect 0x2), `LPDDR_FB_BASE` (expect 0x8000_0000).
|
||
2. Baseline: `sudo dd if=/dev/mem bs=1 skip=2147483648 count=32 2>/dev/null | hexdump -C`
|
||
3. Arm in canary mode: write `LPDDR_CTRL = 0x3` (arm=1, canary=1).
|
||
4. 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).
|
||
5. Optional: read `LPDDR_BURSTS` (advancing) + `LPDDR_STATUS` (bit1/bit2 = 0).
|
||
PASS = bytes changed baseline → the `00 82` pattern (fabric reached LPDDR at the
|
||
expected physical address). Then **disarm: write LPDDR_CTRL = 0x2**.
|
||
|
||
## Full-frame test (8 KiB)
|
||
1. Arm full: write `LPDDR_CTRL = 0x1` (arm=1, canary=0).
|
||
2. `sudo dd if=/dev/mem bs=4096 skip=524288 count=2 2>/dev/null | md5sum`
|
||
Expect **`3b12baffc00bb6419fa66272c75b2cc7`** (the exact sim image).
|
||
3. Confirm `LPDDR_STATUS` bits 1,2 = 0 (no bresp/FIFO errors). Disarm when done (0x2).
|
||
|
||
## Notes
|
||
- `0x80000000` = 2147483648 bytes; `skip=524288` blocks × 4096 = same address.
|
||
- Never read/write `0x82000000–0xBFFFFFFF` (live Linux RAM).
|
||
- If a hardened kernel blocks `/dev/mem` to the reserved region, use the same
|
||
`devmem`/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.
|