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>
172 lines
7.0 KiB
Markdown
172 lines
7.0 KiB
Markdown
# Ch287 closeout — EE DMAC global control stub; qbert advances by 5 to channel-4 base
|
|
|
|
**Status:** Closed. **Verdict from re-running qbert.elf:**
|
|
`elf_first_unmapped_mmio (ea=0x1000C000 pc=0x001123CC)`. qbert
|
|
advanced 27,907 → **27,912 retires (+5)** — a small but meaningful
|
|
step: the D_STAT poll completed (read returned 0 → "no pending
|
|
DMAC interrupts" → poll exits) and qbert moved on to the next
|
|
DMAC-touch in its init sweep, the per-channel base of DMAC
|
|
channel 4 (toIPU).
|
|
|
|
## What landed
|
|
|
|
Per Codex's narrow framing ("not a silent region-wide accept;
|
|
implement at least D_CTRL and D_STAT"), Ch287 delivers the EE DMAC
|
|
global control/status surface as a dedicated stub:
|
|
|
|
### New module — `rtl/dmac/ee_dmac_ctrl_stub.sv`
|
|
|
|
Hosts six registers in the 0x1000_E000-0x1000_E0FF window:
|
|
|
|
| Offset | Reg | Semantics |
|
|
|--------|---------|-----------|
|
|
| 0x00 | D_CTRL | Latch (write last, read back). Reset = 0. |
|
|
| 0x10 | D_STAT | Low half (CIS) is **W1C** on writes (a 1 clears that bit); high half (CIM) is unconditional write. Reset = 0 (no pending interrupts). |
|
|
| 0x20 | D_PCR | Latch. |
|
|
| 0x30 | D_SQWC | Latch. |
|
|
| 0x40 | D_RBSR | Latch. |
|
|
| 0x50 | D_RBOR | Latch. |
|
|
| others | — | Reads return 0; writes traced + dropped. |
|
|
|
|
Standard `reg_wr_en / reg_offset / reg_wr_data / reg_rd_en /
|
|
reg_rd_data / reg_rd_valid + trace_pkg::*` port interface (mirrors
|
|
`dmac_reg_stub` and `intc_stub`).
|
|
|
|
### Memory-map integration — `rtl/memory/ee_memory_map_stub.sv`
|
|
|
|
- New `REGION_EE_DMAC_CTRL = 64'd13` localparam.
|
|
- New `EE_DMAC_CTRL_BASE = 29'h1000_E000` localparam.
|
|
- New `ee_rd_is_dmac_ctrl` / `ee_wr_is_dmac_ctrl` predicates
|
|
(`phys[28:12] == EE_DMAC_CTRL_BASE[28:12]`).
|
|
- **Internal instantiation** of `ee_dmac_ctrl_stub` inside
|
|
`ee_memory_map_stub` so the 88 existing TBs don't need new port
|
|
routing. Precedent: the `useg_shadow_mem` backing also lives
|
|
inside the map.
|
|
- Response mux arm for `ee_rd_was_dmac_ctrl`.
|
|
- Read+write trace branches emit `EV_READ`/`EV_WRITE` with
|
|
`arg3=REGION_EE_DMAC_CTRL` (instead of `EV_UNMAPPED`).
|
|
|
|
This last point matters — the first qbert rerun after wiring the
|
|
stub *still* reported `elf_first_unmapped_mmio` because the trace
|
|
branches weren't updated to recognize the new region. The runner
|
|
watches for the `EV_UNMAPPED` event; until the trace arm is
|
|
added, even a fully-routed region still surfaces as "unmapped" to
|
|
the verdict. Easy mistake to make twice; the trace-emission update
|
|
is mandatory for every new region.
|
|
|
|
## TB — `tb_ee_dmac_ctrl_stub.sv`
|
|
|
|
Direct DUT instantiation (no memory map intermediate; matches the
|
|
isolated-stub TB pattern used by `tb_ee_biu_mmio` / `tb_intc_stub`).
|
|
18 named assertions covering:
|
|
|
|
1. **Reset-init**: all six named offsets read 0.
|
|
2. **D_CTRL latch round-trip**.
|
|
3. **D_STAT W1C semantics**: hierarchically poke d_stat to known
|
|
values, then issue W1C writes and verify the low half clears
|
|
selectively while the high half (CIM) is unconditionally
|
|
written.
|
|
4. **D_PCR / D_SQWC / D_RBSR / D_RBOR latch round-trips**.
|
|
5. **Distinct-register independence** (D_CTRL untouched by D_PCR
|
|
writes).
|
|
6. **Unknown offset** (0x80): reads return 0; writes don't damage
|
|
anything; the next valid read still works.
|
|
|
|
Result: `errors=0 PASS` (18/18 sub-checks).
|
|
|
|
The W1C assertion is the key correctness check — if a future ELF
|
|
needs the bit-set side (via a real DMAC channel completion), the
|
|
W1C semantics here must be preserved. The negative-half test
|
|
(CIM = high 16 bits, unconditional write) ensures we don't
|
|
accidentally W1C the mask.
|
|
|
|
## Makefile + regression
|
|
|
|
- `tb_ee_dmac_ctrl_stub` target.
|
|
- `rtl/dmac/ee_dmac_ctrl_stub.sv` added to RTL_SRCS.
|
|
- TB added to both PHONY list and `run:` master list.
|
|
- Regression: 173 → **174**.
|
|
|
|
## qbert progression
|
|
|
|
| Chapter | Blocker | retire_count |
|
|
|---|---|---|
|
|
| Post-Ch286 (EI) | unmapped 0x1000E010 (D_STAT) at 0x001123A8 | 27,907 |
|
|
| **Post-Ch287 (DMAC ctrl stub)** | **unmapped 0x1000C000 (DMAC ch4 toIPU) at 0x001123CC** | **27,912** |
|
|
|
|
+5 retires. The D_STAT poll completed (one read returning 0 = "no
|
|
pending interrupts" → branch exits) and qbert progressed
|
|
immediately to the next DMAC register touch in its init sweep. The
|
|
new blocker EA 0x1000C000 is the channel-4 (toIPU) base. The
|
|
hot_pc 0x00112364 (count=29 / 256) suggests a loop iterating
|
|
across all DMAC channels — clearing or zeroing their per-channel
|
|
registers.
|
|
|
|
## Ch288 framing
|
|
|
|
`0x1000C000` = `D4 toIPU` per-channel base. The PS2 DMAC has 10
|
|
channels:
|
|
|
|
| Ch | Base | Endpoint | Modeled? |
|
|
|----|------------|-----------|----------|
|
|
| 0 | 0x10008000 | VIF0 | No |
|
|
| 1 | 0x10009000 | VIF1 | No |
|
|
| 2 | 0x1000A000 | GIF | **Yes** (`dmac_reg_stub` CHANNEL=2) |
|
|
| 3 | 0x1000B000 | IPU_FROM | No |
|
|
| 4 | 0x1000C000 | IPU_TO | No ← Ch288 blocker |
|
|
| 5 | 0x1000D000 | SIF0 | No |
|
|
| 8 | 0x1000F000 | SIF1 | No |
|
|
| 9 | 0x1000F400 | SPR_FROM | No |
|
|
| — | 0x1000F800 | SPR_TO | No |
|
|
|
|
qbert is touching the per-channel surfaces. The simplest path:
|
|
extend `dmac_reg_stub` (which is already channel-agnostic, has a
|
|
CHANNEL parameter) to instantiate stubs for the missing channels
|
|
inside `ee_memory_map_stub` — **OR** introduce a single
|
|
"unused-channel" stub that just latches CHCR/MADR/QWC/TADR for the
|
|
clear-loop pattern and doesn't try to do any real transfer.
|
|
|
|
The right call (for Codex to weigh):
|
|
- (a) Multi-instance `dmac_reg_stub` with CHANNEL=0/1/3/4/5 in
|
|
the map. Heavier; each instance includes the full transfer FSM,
|
|
but for unused channels the FSM never starts.
|
|
- (b) Lightweight `ee_dmac_unused_channel_stub.sv` per-channel
|
|
with just the 4 latched registers (CHCR/MADR/QWC/TADR) and no
|
|
FSM. Cheaper.
|
|
- (c) Widen `dmac_reg_stub` to host *all* channels in one module
|
|
(channel-multiplexed register file).
|
|
|
|
I lean (b) for the next chapter — qbert's init sweep wants the
|
|
register surface, not the transfer machinery. A real-transfer
|
|
channel like GIF (ch2) keeps its full dmac_reg_stub; everyone else
|
|
gets a minimal latched-register stub.
|
|
|
|
## Files changed
|
|
|
|
- `rtl/dmac/ee_dmac_ctrl_stub.sv` — new module (~150 LOC).
|
|
- `rtl/memory/ee_memory_map_stub.sv` — localparam, predicates,
|
|
internal instantiation, response mux arm, trace branches.
|
|
- `sim/tb/dmac/tb_ee_dmac_ctrl_stub.sv` — new focused TB.
|
|
- `sim/Makefile` — RTL_SRCS entry, new tb target, both regression
|
|
lists.
|
|
|
|
## Pattern review (17 chapters)
|
|
|
|
| Ch | Blocker | Edits | Pattern |
|
|
|-----|--------------|-------|---------|
|
|
| 271..286 | opcodes | various | opcode-era |
|
|
| 286 | EI (last opcode chapter) | 3 | NEW narrow exact-32 decode |
|
|
| **287** | **DMAC ctrl MMIO** | **~30** | **NEW MMIO stub + map routing** |
|
|
|
|
First MMIO chapter. The chapter cost is higher than recent
|
|
opcode chapters because adding a new memory region requires
|
|
touching multiple coordinated points in the map (predicate,
|
|
internal instance, mux arm, two trace branches, RTL_SRCS,
|
|
PHONY+run lists). One missing piece (the trace branches) cost a
|
|
diagnostic re-run.
|
|
|
|
## Regression
|
|
|
|
**174/174 PASS** (was 173/173 in Ch286; +1 for the new
|
|
tb_ee_dmac_ctrl_stub).
|