Files
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
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>
2026-06-29 20:10:50 -04:00

8.2 KiB

rtl/iop

IOP subsystem. Matches docs/contracts/iop.md.

Current contents

  • iop_ram_stub.sv — 32-bit IOP RAM primitive. Default 16 KiB, parameterizable. Read + write ports with byte-enable granularity, one-cycle read latency, caller-provided master_id for trace attribution. Emits trace events under SUBSYS_IOP.
  • iop_memory_map_stub.sv — IOP-side address decode. CPU-side port uses kseg0/kseg1 stripping (phys = iop_addr[28:0]). Second write-master port for DMA bridges (bridge_wr_*), physical addressing. Regions now decoded:
    • IOP RAM (phys 0x00000000-0x001FFFFF) → iop_ram_stub
    • SIF registers (phys 0x1D000000 block) → SIF register shell (sif_mailbox_stub IOP side) via sif_rd_* / sif_wr_* ports
    • IOP DMAC channel 9 (phys 0x1F801520-0x1F80152F) → IOP DMAC register shell via iop_dmac_rd_* / iop_dmac_wr_* ports
    • IOP INTC (phys 0x1F801070-0x1F80107F) → intc_stub (IOP-side instance) via iop_intc_rd_* / iop_intc_wr_* ports
    • Shared BIOS ROM (phys 0x1FC00000-0x1FFFFFFF, 4 MiB) → bios_rom_stub via bios_rd_* port. kseg1 aliasing makes 0xBFC0_0000 reset fetches land here transparently. BIOS is read-only; writes to this window trace as UNMAPPED.
    • everything else → UNMAPPED with deterministic 0xDEADBEEF Future regions (other DMAC channels, IOP timers, SPU2) reserved in comments. Arbitration between CPU and bridge writes on RAM path: CPU wins on same-cycle collision. SIF, DMAC, INTC, and BIOS are separate ports and don't contend with RAM.
  • iop_fetch_stub.sv — minimal sequential 32-bit fetcher. Mirrors ee_fetch_stub in shape: PC-incrementing, no decode, no branches, no exceptions. Default RESET_VECTOR is in IOP RAM (0x00000000), NOT in BIOS space — explicitly non-BIOS boot. Emits IOP RESET once and IOP IFETCH per response. First execution-visible IOP traffic in the project; fetches route through iop_memory_map_stub.
  • iop_core_stub.svreal instruction-decoding IOP core with minimal COP0, asynchronous interrupt exception entry, and the architectural MIPS reset vector. Tiny MIPS R3000 subset, multi-cycle FSM, speaks the same map / DMAC / INTC protocol as every previous engine. Default PC_RESET = 0xBFC0_0000 (kseg1 into the shared BIOS window; override with a parameter for RAM-only tests). Supported opcodes: LUI, ORI, ADDIU, LW, SW, BEQ, BNE, J, JR (SPECIAL func 0x08), NOP (any other SPECIAL func / unknown opcode), SYSCALL (SPECIAL func 0x0C, halts), MFC0 / MTC0 / RFE (COP0 opcode 0x10). 32-entry register file with $0 hardwired. COP0 subset: Status (IE/KU triple stack + IM), Cause (ExcCode + IP reflecting cpu_irq), EPC. Exception entry is sampled at clean instruction-retire boundaries: if a delay slot is outstanding, the exception defers until the delay slot resolves. On entry: push IE/KU stack, ExcCode=0, save EPC=next_pc, PC←EXC_VECTOR (parameter). Branch delay slot honoured from day one; taken-branch and delay-slot retires are both flagged in the trace. Strict mode: STRICT_UNSUPPORTED parameter (default 0). When set, unsupported opcodes halt the core and latch the offending pc/instr word into trap_o / trap_pc_o / trap_instr_o instead of silently retiring as NOPs. The canonical NOP (instr == 32'h0, SLL $0,$0,0) is always treated as a real NOP. Retire trace flag bit 7 marks strict-trap retires. Used by the BIOS smoke TB; other benches leave it off for backwards compatibility. Deferred: BD bit in Cause, nested interrupts, syscall/break exception dispatch, R-type ALU/shifts/HI-LO.
  • iop_exec_stub.svRAM-backed IOP execution primitive (bridge module). Not a MIPS core, not an ISA decoder. A tiny FSM sequencer that fetches its micro-ops from IOP RAM through the real iop_memory_map_stub CPU-side port — the same way a future instruction-fetching CPU will. The control program is no longer RTL-resident; it lives as data in RAM that someone (a TB, eventually a BIOS loader) preloads before pulsing go_i. Five opcodes: HALT, WRITE(addr, data), READ(addr), WAIT_IRQ, BNE(target_pc, expected) — branch if the last READ's result does not equal expected, enabling real loops. Op layout in RAM: 16 bytes per op (pc<<4 addressing). Word 0 is the opcode (low 4 bits), word 1 is addr or branch target, word 2 is data or expected value, word 3 is reserved. SCRIPT_BASE is a parameter (default 0x0000_0400). Takes cpu_irq from the IOP INTC; WAIT_IRQ genuinely blocks until a real interrupt asserts. One trace event per op completion with flag bits marking WAIT_IRQ exit (bit 1), HALT entry (bit 2), and BNE taken (bit 3). When a real MIPS decode primitive eventually arrives, it replaces this module while keeping the same map / DMA / INTC hookup verbatim.
  • iop_dmac_reg_stub.sv — IOP DMAC for one SIF-facing channel (CHANNEL=9, PATH_ID=9, MASTER_ID=4). Register surface (low-byte offsets): MADR @ 0x00, BCR @ 0x04, CHCR @ 0x08, DONE_COUNT @ 0x0C (read-only monotonic counter); start bit is CHCR[0]. Real data path: on start, DMAC latches MADR/BCR, then steps through IDLE → FETCH_WAIT → ACTIVE_SEND → DONE per beat, sourcing 32-bit words from IOP RAM through the map's dma_rd_* port (src_addr stepping by 4 per beat). Endpoint is a word-granularity ready/valid/last stream with ep_ready back-pressure — no false completion under stall. Emits DMA_CFG on register writes, DMA_START on arm, DMA_BEAT per accepted beat (with src_addr + remaining count), DMA_DONE on the final beat. done_count_o is a monotonic visible counter. irq_completion_o is a one-cycle pulse on S_DONE — wired into the IOP INTC as source bit 0 so software can observe channel completion. Only reachable through the real IOP map at 0x1F80_1520.

Explicit non-goals (current step)

  • Full MIPS R3000 ISA coverage (the core is still a narrow subset; strict-mode halts on the first unsupported opcode so the BIOS tells us what to grow next)
  • Full 2 MiB RAM sizing (stub defaults stay small for sim speed; the map window is 2 MiB and truncates at the connection to the smaller stub)
  • IOP I/O beyond the currently decoded regions (DMAC ch9 / INTC / BIOS); SPU2, timers, and other peripherals are not wired yet
  • IOP DMAC channels other than ch9 (SIF0 IOP→EE)
  • Real Sony BIOS execution (the smoke TB's synthetic bootstrap is the current committed content; swapping in a user-supplied dump is a drop-in exercise that will reveal the next missing opcode)

Scope boundary

This directory owns IOP CPU execution, IOP-local RAM/I/O decode, IOP interrupt intake, IOP DMAC channels, and BIOS-side IOP boot sequencing behavior (per docs/contracts/iop.md).

The IOP side now runs a MIPS R3000 subset from an architecturally correct BIOS reset vector, with precise interrupt exception entry and a RAM-resident ISR. The project has crossed five architectural seams:

  1. TB-orchestrated → fabric-orchestrated (scripted exec stub)
  2. RTL-resident → RAM-resident control (exec stub reads ops from RAM)
  3. Micro-op bridge → real ISA decode (iop_core_stub)
  4. Polled completion → asynchronous exception-driven control flow (COP0 + cpu_irq)
  5. TB-preloaded RAM as reset source → BIOS ROM at 0xBFC0_0000 (shared BIOS wired through the IOP map; hand-assembled bootstraps prove the seam before any real Sony BIOS is attempted)

Each seam preserved every prior module — only where code comes from evolved.

Planned next increments

These are possibilities, not commitments — order will be decided per the next architectural question:

  • BIOS-driven core growth: point tb_iop_core_bios_smoke at a user-supplied BIOS dump (swap the TB's synthetic preload for $readmemh into u_bios.mem), observe the first unsupported opcode, add it to iop_core_stub, repeat. Expected near-term additions: ANDI, ADDU/SUBU, SLL/SRL/SRA, JAL, SLT(U). Do not add speculatively; let the BIOS trace drive the order.
  • Core exception growth as the BIOS path demands it: BD bit in Cause, nested interrupts, syscall/break exception dispatch.
  • Other IOP DMAC channels (CDVD / SPU2 / DEV9 / SIF1-2 / SIO2).
  • IOP map expansion: remaining IOP I/O (0x1F800000), SPU2 (0x1F900000).