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

277 lines
17 KiB
Markdown

# Simulation Area
Testbenches, traces, vectors, and golden-reference comparison artifacts.
This directory exists early on purpose — `retroDE_ps2` should grow with
validation infrastructure from the beginning rather than bolting it on
later.
## Running
Driver is a plain Makefile. From this directory:
```
make lint # verilator --lint-only over all RTL (strict)
make tb_ee_fetch # build + run Milestone B chain via iverilog
make tb_gs # build + run gs_stub TB
make tb_intc # build + run intc_stub TB (incl. ack+inject collision guard)
make tb_platform_video # build + run Milestone A integration (gs_stub → platform_video)
make tb_bgcolor_via_dma # build + run Milestone A+ (DMAC → GIF → GS → platform)
make tb_sif_mailbox # build + run sif_mailbox_stub unit TB
make tb_sif_command_echo # build + run EE↔peer command-echo integration TB
make tb_sif_command_echo_rearm # build + run re-armable two-command TB
make tb_sif_negative_path # build + run stall/malformed-lifecycle TB
make tb_sif_dma_smoke # build + run first SIF DMA receive test (ch5)
make tb_sif_dma_mid_stall # build + run mid-transfer backpressure test
make tb_sif_dma_overflow # build + run full-stop capacity backpressure test
make tb_sif_combined_ctrl_data # build + run first combined ctrl+data SIF handshake
make tb_iop_ram # build + run first IOP-side primitive (iop_ram_stub)
make tb_iop_memory_map # build + run IOP memory map decode + routing
make tb_iop_fetch_through_map # build + run IOP fetch → map → RAM integration
make tb_sif_iop_bridge_smoke # build + run SIF-to-IOP-RAM bridge write test
make tb_sif_iop_bridge_exec # build + run execution-visible bridge test
make tb_iop_to_sif_via_map # build + run IOP-reaches-SIF-via-real-map
make tb_iop_dmac_via_map # build + run IOP DMAC ch9 through the real map
make tb_sif_ee_landing_via_dmac # build + run first reverse-direction SIF DMA
make tb_sif_iop_driven_combined # build + run IOP-driven combined ctrl+data handshake
make tb_ee_dmac_intc # build + run first EE DMAC completion → INTC flow
make tb_iop_dmac_intc # build + run IOP DMAC ch9 → IOP INTC through real map
make tb_iop_self_driven # build + run first self-driven IOP transaction (RAM-backed)
make tb_iop_autonomous_two_xfers # build + run first autonomous two-transaction script (BNE loop)
make tb_iop_core_basic # build + run first real MIPS-decode IOP core program
make tb_iop_core_interrupts # build + run first exception-driven IOP program (COP0 + RAM ISR)
make tb_iop_core_bootstrap # build + run first BIOS-sourced bootstrap (Phase 1, no IRQs)
make tb_iop_core_bootstrap_intc # build + run BIOS-sourced interrupt flow (Phase 2)
make tb_iop_core_bios_smoke # build + run strict-mode BIOS smoke (halts on first unsupported opcode)
make tb_iop_core_bios_smoke BIOS=/path/to/bios.hex # same TB, real BIOS image
make tb_ee_core_basic # build + run first EE-side real-decode program
make tb_ee_core_memops # build + run EE CPU→RAM SW/LW round-trip
make tb_ee_core_dmac # build + run EE core programming DMAC ch2 via SW
make tb_ee_core_dmac_poll # build + run EE core polling CHCR + DONE_COUNT
make tb_ee_core_dmac_intc # build + run EE DMAC → INTC → exception → ISR → RFE
make tb_ee_core_bios_smoke # build + run EE strict-mode BIOS smoke (halts on first unsupported opcode)
make tb_ee_core_bios_smoke BIOS=/path/to/bios.hex # same TB, real BIOS image
make tb_ee_core_slti # build + run SLTI/SLTIU coverage (first real-BIOS-driven growth)
make tb_ee_core_addi # build + run ADDI coverage
make tb_ee_core_andi # build + run ANDI coverage
make tb_ee_core_rtype_logic # build + run R-type AND/OR/XOR/NOR coverage
make tb_ee_core_sb # build + run SB byte-store coverage
make tb_ee_core_lb # build + run LB sign-extended byte-load coverage
make tb_ee_core_jal # build + run JAL jump-and-link coverage
make tb_ee_core_rtype_addu # build + run R-type ADDU/SUBU coverage
make tb_ee_core_slt # build + run R-type SLT/SLTU coverage
make tb_ee_core_lh # build + run LH sign-extended halfword-load coverage
make tb_ee_core_lhu # build + run LHU zero-extended halfword-load coverage
make tb_ee_core_shift # build + run SLL/SRL/SRA shift-family coverage
make tb_ee_core_sh # build + run SH halfword-store coverage
make tb_ee_core_jalr # build + run JALR register-indirect call coverage
make tb_ee_core_add_sub # build + run R-type ADD/SUB coverage (overflow-trap deferred)
make tb_ee_core_cop0_count # build + run COP0 Count (reg 9) coverage
make run # all fifty-three TBs in sequence
make all_checks # lint + run (RTL + benches; the default gate)
make full_checks # all_checks + test_compare (widest regression)
# Golden-reference harness (Milestone B track) — see sim/golden/
make golden_nop # generate the NOP-sled golden trace
make compare_ee_fetch # diff RTL ee_fetch.trace against NOP-sled golden
make test_compare # exercise all three documented exit-code paths
make traces # list trace files under sim/traces/rtl/
make clean # wipe build/ and traces/
```
**`make all_checks` is the quality gate.** `make lint` alone does not compile
the testbenches — it only runs Verilator over RTL. Treat "lint green" as
necessary-but-not-sufficient.
**Toolchain used locally:**
- `iverilog` (Icarus Verilog 12+) for running testbenches. Event-driven,
handles `@(posedge)` / `@(negedge)` / `force` cleanly.
- `verilator` (5.x, built with `--timing` support) for strict lint.
The split is deliberate: Verilator's lint is strict and fast; iverilog's
event-driven scheduler is the right fit for stimulus-heavy testbenches
with timing-control tasks.
## Expected output
```
[tb_ee_fetch_stub] ifetch_count=32 unmapped_count=1 errors=0
[tb_ee_fetch_stub] PASS
[tb_gs_stub] bgcolor_events=2 mode_events=1 errors=0
[tb_gs_stub] PASS
[tb_intc_stub] source_assertion=3 register_write=2 ack=4 errors=0
[tb_intc_stub] PASS
[tb_platform_video_stub] frames=4 active_pixels=150 hsync_pulses=34 vsync_pulses=4 errors=0
[tb_platform_video_stub] PASS
[tb_bgcolor_via_dma] mem_writes=2 mem_reads_dmac=2 map_reads_dmac=2 dma_cfg=3 dma_start=1 dma_beat=2 dma_done=1 giftag=2 gs_bgcolor=2 frames=3 post_frames=2 last0=0 last1=1 bg=(ff,00,00) errors=0
[tb_bgcolor_via_dma] PASS
[tb_sif_mailbox_stub] ee_writes=4 iop_writes=3 ee_reads=3 iop_reads=4 errors=0
[tb_sif_mailbox_stub] PASS
[tb_sif_command_echo] peer_done=1 wait_cycles=8 ee_side_writes=2 iop_side_writes=2 errors=0
[tb_sif_command_echo] PASS
[tb_sif_command_echo_rearm] ack_count=2 ee_writes=6 iop_writes=4 captured={...} errors=0
[tb_sif_command_echo_rearm] PASS
[tb_sif_negative_path] phase_a_ack=0 phase_b_ack=1 phase_c_ack=2 errors=0
[tb_sif_negative_path] PASS
[tb_sif_dma_smoke] stall_rx=0 stall_done=0 rx_count=4 dma_beats=4 dma_done=1 sif_writes=4 last_seen=1 errors=0
[tb_sif_dma_smoke] PASS
[tb_sif_dma_mid_stall] stalled_beats=2 stalled_done=0 final_beats=4 rx=4 beat2_src=0x320 last=1 errors=0
[tb_sif_dma_mid_stall] PASS
[tb_sif_dma_overflow] settle_beats=2 settle_rx=2 full=1 done=0 beat_idx=2 errors=0
[tb_sif_dma_overflow] PASS
[tb_sif_combined_ctrl_data] pre_dma_acks=0 ack_count=1 dma_done=1 peer_writes=2 last_seen_cy=74 smcom_wr_cy=79 errors=0
[tb_sif_combined_ctrl_data] PASS
[tb_iop_ram_stub] iop_reads=5 iop_writes=3 iop_region_hits=8 errors=0
[tb_iop_ram_stub] PASS
[tb_iop_memory_map_stub] map_reads_ram=4 map_writes_ram=2 map_unmapped=2 master_mismatches=0 errors=0
[tb_iop_memory_map_stub] PASS
[tb_iop_fetch_through_map] ifetches=22 map_routed=22 map_iop_cpu_tagged=22 errors=0
[tb_iop_fetch_through_map] PASS
[tb_sif_iop_bridge_smoke] dma_done=1 bridge_writes_map=8 bridge_writes_ram=8 errors=0
[tb_sif_iop_bridge_smoke] PASS
[tb_sif_iop_bridge_exec] dma_done=1 bridge_writes=8 ifetches=16 iop_cpu_reads=16 mismatches=0 errors=0 last_bw_cy=27 first_fetch_cy=48
[tb_sif_iop_bridge_exec] PASS
[tb_iop_to_sif_via_map] map_sif=4 map_ram=2 map_unmapped=1 mbx_iop_side=4 errors=0
[tb_iop_to_sif_via_map] PASS
[tb_iop_dmac_via_map] map_dmac=9 map_ram_dma=8 cfg=6 start=2 beat=8 done=2 stall_cy=9 errors=0
[tb_iop_dmac_via_map] PASS
[tb_sif_ee_landing_via_dmac] start=1 beat=16 done=1 ee_map_writes=4 iop_map_dma_reads=16 stall_cy=20 stall_beats=6 errors=0
[tb_sif_ee_landing_via_dmac] PASS
[tb_sif_iop_driven_combined] start=1 beat=16 done=1 ee_map_writes=4 pre_dma_ack=0 ack=1 errors=0
[tb_sif_iop_driven_combined] PASS
[tb_ee_dmac_intc] done=2 asserts=2 acks=2 errors=0
[tb_ee_dmac_intc] PASS
[tb_iop_dmac_intc] done=2 asserts=2 acks=2 map_intc_events=9 errors=0
[tb_iop_dmac_intc] PASS
[tb_iop_self_driven] stall_pc=4 ops=10 wait_irq_exit=1 halt=1 done=1 asserts=1 acks=1 errors=0
[tb_iop_self_driven] PASS
[tb_iop_autonomous_two_xfers] ops=16 wait_irq_exit=2 bne_taken=1 halt=1 done=2 asserts=2 acks=2 errors=0
[tb_iop_autonomous_two_xfers] PASS
[tb_iop_core_basic] retired=38 branches_taken=3 delay_slots=3 halt=1 done=2 asserts=2 acks=2 errors=0
[tb_iop_core_basic] PASS
[tb_iop_core_interrupts] retired=49 branches_taken=4 delay_slots=4 except=2 rfe=2 halt=1 done=2 asserts=2 acks=2 intc_stat_polls=0 errors=0
[tb_iop_core_interrupts] PASS
[tb_iop_core_bootstrap] retired=19 taken=2 delay=2 halt=1 done=1 bios_reads=19 reset_reads=1 errors=0
[tb_iop_core_bootstrap] PASS
[tb_iop_core_bootstrap_intc] retired=51 taken=5 delay=5 except=2 rfe=2 halt=1 done=2 acks=2 bios_reads=35 reset_reads=1 intc_stat_polls=0 errors=0
[tb_iop_core_bootstrap_intc] PASS
[tb_iop_core_bios_smoke] retired=11 trap=1 trap_pc=0xbfc00120 trap_instr=0x00432824 halt=0 except=0 done=0 bios_reads=11 hist(lui,ori,addiu,sw,lw,j,other)=(1,4,1,2,0,1,1) errors=0
[tb_iop_core_bios_smoke] PASS
[tb_ee_core_basic] retired=9 taken=2 delay=2 halt=1 except=0 trap=0 bios_reads=9 errors=0
[tb_ee_core_basic] PASS
```
Trace files land in `sim/traces/rtl/`:
- `ee_fetch.trace` — EE fetch-side events (one RESET + one IFETCH per fetch)
- `ee_mem.trace` — memory-map request-time events (READ / UNMAPPED)
- `ee_bios.trace` — BIOS ROM completion-time events
- `gs.trace` — BGCOLOR / MODE register writes (tb_gs_stub)
- `intc.trace` — IRQ state transitions (includes the collision regression trace)
- `milestone_a_gs.trace` — GS events during Milestone A integration TB
- `milestone_a_plat.trace` — platform frame-complete events during Milestone A TB
- `bgcolor_via_dma_ram.trace` — EE RAM write + delivery-time reads (Wave 2.5+)
- `bgcolor_via_dma_map.trace` — map-layer request-routing events (Wave 2.7)
- `bgcolor_via_dma_dmac.trace` — DMAC CFG/START/BEAT/DONE events
- `bgcolor_via_dma_gif.trace` — GIF tag events
- `bgcolor_via_dma_gs.trace` — GS BGCOLOR write events
- `bgcolor_via_dma_plat.trace` — platform frames during transfer
- `sif_mailbox.trace` — SIF mailbox/flag reads and writes with side_id (EE vs. IOP)
- `sif_command_echo.trace` — EE↔peer command-echo exchange on the mailbox
- `sif_command_echo_rearm.trace` — two-command re-armable exchange with explicit clear
- `sif_negative_path.trace` — stall / malformed-lifecycle / recovery sequence
- `sif_dma_ram.trace` — RAM traffic during SIF DMA smoke test
- `sif_dma_map.trace` — memory-map routing for DMAC ch5 fetches
- `sif_dma_dmac.trace` — DMAC ch5 CFG/START/BEAT/DONE
- `sif_dma_sif.trace` — SIF-side DMA receive events
- `sif_dma_mid_stall_*.trace` — mid-transfer stall variant traces (ram/map/dmac/sif)
- `iop_dmac_via_map.trace` — IOP map routing for DMAC ch9 accesses (region=IOP_DMAC)
- `iop_dmac_reg.trace` — IOP DMAC ch9 CFG/START/BEAT/DONE events
- `ee_landing_iop_map.trace` — IOP map routing during reverse-direction transfer (CPU programming + DMA source reads)
- `ee_landing_ee_map.trace` — EE map routing for bridge writes into EE RAM (master_id=5)
- `ee_landing_dmac.trace` — IOP DMAC ch9 CFG/START/BEAT/DONE during full-chain transfer
- `iop_driven_iop_map.trace` — IOP map routing during IOP-driven combined handshake (CPU ops + DMA reads)
- `iop_driven_ee_map.trace` — EE map bridge-write events during the combined handshake
- `iop_driven_dmac.trace` — IOP DMAC ch9 events during the combined handshake
- `iop_driven_mailbox.trace` — mailbox reads/writes tagged by side (IOP doorbell + EE-side combiner ack)
- `ee_dmac_intc_*.trace` — EE DMAC → INTC completion-visibility flow (DMAC + INTC events)
- `iop_intc_*.trace` — IOP DMAC ch9 → IOP INTC flow via real map (iop_map/dmac/intc)
- `iop_self_driven_exec.trace` — scripted IOP executor op timeline (one event per op)
- `iop_self_driven_map.trace` — IOP map routing during the self-driven flow
- `iop_self_driven_dmac.trace` — IOP DMAC ch9 events during the self-driven flow
- `iop_self_driven_intc.trace` — IOP INTC events (assertion + ack) during the self-driven flow
- `iop_two_xfers_*.trace` — RAM-backed BNE-looping two-transaction flow (exec/map/dmac/intc)
- `iop_core_basic_*.trace` — first real MIPS-decode run (core/map/dmac/intc); one IFETCH event per retired instruction with delay-slot and branch-taken flags
- `iop_core_intr_*.trace` — exception-driven run with COP0 + RAM-resident ISR; core trace flags include exception-taken (bit 5) and RFE (bit 6) retires
- `iop_boot_*.trace` — first BIOS-sourced IOP bootstrap; map trace shows BIOS region (6) reads starting at 0xBFC0_0000
- `iop_boot_intc_*.trace` — BIOS-sourced bootstrap that enables interrupts and vectors to a RAM-resident ISR (full reset-source + exception-entry story in one timeline)
- `iop_bios_smoke_*.trace` — strict-mode BIOS smoke; core retire trace flags bit 7 on the halt cycle; replace TB's synthetic BIOS preload with a real dump to drive targeted core growth
- `ee_core_basic_*.trace` — first EE-side real-decode run; core (SUBSYS_EE) + map streams; mirror of the IOP `tb_iop_core_basic` story on the EE execution path
## Contents
- `Makefile` — build/run/lint driver.
- `build/` — iverilog compile artifacts (gitignore candidate).
- `tb/` — testbenches, one subdirectory per subsystem.
- `traces/` — captured traces. `traces/rtl/` is simulation output;
`traces/golden/` is reserved for emulator-side traces once
the golden-reference harness lands.
- `vectors/` — deterministic stimulus inputs.
- `golden/` — scripts + notes for emulator comparison, plus
`bin_to_hex.py` for the real-BIOS iteration loop
(see `sim/golden/README.md`).
## Real-BIOS iteration
The `tb_iop_core_bios_smoke` TB doubles as the iteration harness for
growing `iop_core_stub`'s instruction surface against a real Sony BIOS
dump. Per `third_party/LICENSING.md` no BIOS image is shipped with the
repository; the user supplies their own.
The loop is:
1. **Convert** your `.bin` dump to the text hex format iverilog's
`$readmemh` expects (one 32-bit little-endian word per line, no 0x
prefix, no commas):
```
python3 sim/golden/bin_to_hex.py path/to/ps2_bios.bin \
-o /tmp/ps2_bios.hex
```
The utility rejects inputs larger than the stub's 4 MiB window and
pads shorter inputs with NOPs.
2. **Run** the smoke TB with the `BIOS=` Makefile variable. The
resolved absolute path is forwarded to vvp as `+BIOS=…` and the TB
skips its synthetic bootstrap in favour of `$readmemh` into the
BIOS memory:
```
make -C sim tb_iop_core_bios_smoke BIOS=/tmp/ps2_bios.hex
```
The TB runs with `STRICT_UNSUPPORTED=1`, halts on the first opcode
`iop_core_stub` doesn't implement, and prints a single report line:
```
[tb_iop_core_bios_smoke] retired=<N> trap=1 trap_pc=0x…
trap_instr=0x… mnemonic=<op> halt=0 except=0 done=0
bios_reads=<N> hist(lui,ori,addiu,sw,lw,j,other)=(…) errors=0
```
3. **Add** the smallest useful opcode family around that `mnemonic`
(e.g. if it's `and`, add AND/OR/XOR/NOR together because they share
SPECIAL-R-type decode cheaply).
4. **Rerun** step 2. The next trap is the next opcode to add.
Running the target without `BIOS=` preserves the CI-deterministic
synthetic path (the committed `make run` gate). `make full_checks`
continues to exercise only the synthetic variant.
## Notes for later waves
- Warnings about `$error` / `$fdisplay` in `always_ff` are expected —
iverilog flags them as "cannot be synthesized" which is correct for
simulation-only constructs. Ignore.
- Trace format and cycle-counting conventions are documented in
`docs/decisions/0000-trace-format.md` and `rtl/debug/README.md`.
- Trace files are git-ignored candidates; regenerate via `make run`.