# 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`.
