# 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= trap=1 trap_pc=0x… trap_instr=0x… mnemonic= halt=0 except=0 done=0 bios_reads= 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`.