Files
retroDE_ps2/sim/tb/integration/tb_ee_pad_buffer_branch.sv
T
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

742 lines
30 KiB
Systemverilog

// retroDE_ps2 — tb_ee_pad_buffer_branch (Ch240)
// ============================================================================
// First end-to-end TB where EE-side code consumes the fixed-slot pad
// buffer Ch239 established at EE_PAD_BUFFER_BASE. Closes the loop on
// the input arc — HPS writes INPUT_P1, pad bytes flow through the
// bridge/IOP/SIF DMA path, EE software reads the buffer and branches
// to a per-scenario marker write that the TB then samples.
//
// Pipeline (same as Ch239 plus an EE-side consumer):
//
// HPS AXI write @ 0x040 (INPUT_P1, bclk)
// │
// ▼
// ps2_hps_bridge.input_p1_q → input_p1_o (bclk)
// │
// ▼ 2-FF sync (Ch234 sio2_input_stub inside iop_map)
// PAD_P1_STATE @ 0x1F80_8500 (iclk)
// │
// ▼ TB packs 16-byte Sony struct, drives 4 SIF beats
// sif_dma_ee_ram_bridge_stub (Ch239 rewind_i pulsed first)
// │
// ▼ bridge_wr → ee_memory_map_stub → ee_ram_stub
// EE RAM @ EE_PAD_BUFFER_BASE = 0x0008_0000 (16 bytes)
// │
// ▼ ee_core_stub running an LBU/BEQ/SW program from BIOS
// EE RAM @ EE_MARKER_ADDR = 0x0000_1000 (32-bit marker)
// │
// ▼ TB DMAC-side qword read → check marker byte
// PASS / FAIL
//
// **EE program** (BIOS slot layout, RESET starts at slot 0):
//
// slot 0 LUI $1, 0x0008 ; $1 = EE_PAD_BUFFER_BASE (0x00080000)
// slot 1 ORI $5, $0, 0x1000 ; $5 = EE_MARKER_ADDR
// slot 2 LBU $2, 3($1) ; <LOOP> $2 = pad byte3 from EE RAM
// slot 3 ORI $3, $0, 0x00FF ; $3 = "no buttons" sentinel
// slot 4 BEQ $2, $3, MARK_A ; byte3=0xFF → IDLE / no buttons
// slot 5 NOP ; delay slot
// slot 6 ORI $3, $0, 0x00DF ; $3 = "RIGHT only" sentinel
// slot 7 BEQ $2, $3, MARK_B ; byte3=0xDF → RIGHT only
// slot 8 NOP ; delay slot
// slot 9 ORI $6, $0, 0x00CC ; <COMBO> marker C
// slot 10 SW $6, 0($5)
// slot 11 J LOOP ; restart
// slot 12 NOP ; delay slot
// slot 13 ORI $6, $0, 0x00AA ; <MARK_A> marker A
// slot 14 SW $6, 0($5)
// slot 15 J LOOP
// slot 16 NOP
// slot 17 ORI $6, $0, 0x00BB ; <MARK_B> marker B
// slot 18 SW $6, 0($5)
// slot 19 J LOOP
// slot 20 NOP
//
// The program runs continuously. Each loop iteration is a few core
// cycles; the TB sets up a scenario, kicks the pad DMA, waits long
// enough for the EE to re-read the buffer + write the marker, then
// samples the marker byte through the EE map's DMAC-read port.
//
// **Scenarios:**
// §1. INPUT_P1=0x0000_0000 (no buttons) → byte3=0xFF → marker A (0xAA)
// §2. INPUT_P1=0x0000_0001 (JOY_RIGHT only) → byte3=0xDF → marker B (0xBB)
// §3. INPUT_P1=0x0000_0021 (RIGHT + SELECT) → byte3=0xDE → marker C (0xCC)
// §4. INPUT_P1=0x0000_0000 (re-clear) → byte3=0xFF → marker A (0xAA)
// ============================================================================
`timescale 1ns/1ps
module tb_ee_pad_buffer_branch;
localparam int BIOS_BYTES = 4 * 1024 * 1024;
localparam int EE_RAM_BYTES = 1024 * 1024; // 1 MiB
localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES);
// Physical EE-RAM addresses (what the SIF bridge writes and the
// TB DMAC-side reads see). Useg-style — addr[31]=0.
localparam logic [31:0] EE_PAD_BUFFER_BASE = 32'h0008_0000;
localparam logic [31:0] EE_MARKER_ADDR = 32'h0000_1000;
// KSEG0 aliases — the EE CPU MUST use these (not the useg copies)
// so writes route through `ee_wr_is_ram` to the external ee_ram_stub
// instead of the map's internal `useg_shadow_mem`. The TB's DMAC
// read uses the physical (useg) address and reads ee_ram_stub
// directly — same backing store, different access port.
localparam logic [31:0] EE_PAD_BUFFER_KSEG0 = 32'h8008_0000;
localparam logic [31:0] EE_MARKER_KSEG0 = 32'h8000_1000;
localparam logic [31:0] MARK_VAL_A = 32'h0000_00AA;
localparam logic [31:0] MARK_VAL_B = 32'h0000_00BB;
localparam logic [31:0] MARK_VAL_C = 32'h0000_00CC;
// BIOS slots (each = 4 bytes, byte offset in BIOS = slot*4).
// RESET starts at slot 0 (PC = 0xBFC0_0000).
localparam int SLOT_LUI_PADBASE = 0;
localparam int SLOT_LUI_MARKERBASE = 1;
localparam int SLOT_ORI_MARKERBASE = 2;
localparam int SLOT_LBU_BYTE3 = 3; // LOOP
localparam int SLOT_ORI_FF = 4;
localparam int SLOT_BEQ_A = 5;
localparam int SLOT_BEQ_A_DS = 6;
localparam int SLOT_ORI_DF = 7;
localparam int SLOT_BEQ_B = 8;
localparam int SLOT_BEQ_B_DS = 9;
localparam int SLOT_ORI_CC = 10;
localparam int SLOT_SW_C = 11;
localparam int SLOT_J_LOOP_FROM_C = 12;
localparam int SLOT_J_LOOP_FROM_C_DS = 13;
localparam int SLOT_ORI_AA = 14;
localparam int SLOT_SW_A = 15;
localparam int SLOT_J_LOOP_FROM_A = 16;
localparam int SLOT_J_LOOP_FROM_A_DS = 17;
localparam int SLOT_ORI_BB = 18;
localparam int SLOT_SW_B = 19;
localparam int SLOT_J_LOOP_FROM_B = 20;
localparam int SLOT_J_LOOP_FROM_B_DS = 21;
// PC of LOOP = BIOS base + slot * 4 = 0xBFC0_0000 + SLOT_LBU_BYTE3 * 4.
localparam logic [31:0] LOOP_PC = 32'hBFC0_0000 + SLOT_LBU_BYTE3 * 4;
localparam logic [5:0] OP_J = 6'h02;
localparam logic [5:0] OP_BEQ = 6'h04;
localparam logic [5:0] OP_ORI = 6'h0D;
localparam logic [5:0] OP_LUI = 6'h0F;
localparam logic [5:0] OP_LBU = 6'h24;
localparam logic [5:0] OP_SW = 6'h2B;
// ------------------------------------------------------------------
// Two clocks: 100 MHz bridge, 33 MHz design (EE/IOP/SIF).
// ------------------------------------------------------------------
logic bclk;
logic clk;
initial bclk = 1'b0;
initial clk = 1'b0;
always #5 bclk = ~bclk;
always #15 clk = ~clk;
logic breset_n;
logic rst_n;
// ------------------------------------------------------------------
// ps2_hps_bridge (HPS-side AXI master driven by TB).
// ------------------------------------------------------------------
logic core_halt = 1'b0;
logic dma_done_seen = 1'b0;
logic frame_seen = 1'b0;
logic hdmi_init_done = 1'b0;
logic hdmi_i2c_error = 1'b0;
logic raster_overflow = 1'b0;
logic frame_toggle = 1'b0;
logic dma_done_toggle = 1'b0;
logic [3:0] s_axi_awid;
logic [37:0] s_axi_awaddr;
logic [7:0] s_axi_awlen;
logic [2:0] s_axi_awsize;
logic [1:0] s_axi_awburst;
logic s_axi_awlock;
logic [3:0] s_axi_awcache;
logic [2:0] s_axi_awprot;
logic s_axi_awvalid;
logic s_axi_awready;
logic [127:0] s_axi_wdata;
logic [15:0] s_axi_wstrb;
logic s_axi_wlast;
logic s_axi_wvalid;
logic s_axi_wready;
logic [3:0] s_axi_bid;
logic [1:0] s_axi_bresp;
logic s_axi_bvalid;
logic s_axi_bready;
logic [3:0] s_axi_arid;
logic [37:0] s_axi_araddr;
logic [7:0] s_axi_arlen;
logic [2:0] s_axi_arsize;
logic [1:0] s_axi_arburst;
logic s_axi_arlock;
logic [3:0] s_axi_arcache;
logic [2:0] s_axi_arprot;
logic s_axi_arvalid;
logic s_axi_arready;
logic [3:0] s_axi_rid;
logic [127:0] s_axi_rdata;
logic [1:0] s_axi_rresp;
logic s_axi_rlast;
logic s_axi_rvalid;
logic s_axi_rready;
logic core_reset_req;
logic tile_wr_toggle;
logic [9:0] tile_wr_index;
logic [31:0] tile_wr_data;
// Ch245 — platform OSD register-surface ports (unused by this
// integration TB; declared so the `.*` wildcard binds them).
wire [31:0] osd_ctrl_o;
wire [31:0] osd_cfg0_o;
wire [31:0] osd_cfg1_o;
logic osd_active_i = 1'b0;
logic [4:0] osd_cursor_row_i = 5'd0;
logic osd_set_trigger_i = 1'b0;
logic osd_back_trigger_i = 1'b0;
logic osd_scroll_down_trigger_i = 1'b0;
logic osd_scroll_up_trigger_i = 1'b0;
logic osd_open_trigger_i = 1'b0;
logic [4:0] osd_trigger_row_i = 5'd0;
// Ch248 DS2 wired-controller inputs (unused by this TB; tied
// unplugged so the bridge's DS2_STATUS reads as such).
logic [31:0] ds2_buttons_i = 32'd0;
logic ds2_connected_i = 1'b0;
logic ds2_error_i = 1'b0;
wire [31:0] bridge_input_p1;
wire [31:0] bridge_input_p2;
// Ch318 — LPDDR test ports (this TB doesn't exercise them; nets exist so the
// bridge's .* connects, status inputs tied safe).
logic lpddr_arm_o, lpddr_canary_o;
logic lpddr_ctrl_commit_o;
logic [31:0] lpddr_fb_base_o;
logic [31:0] lpddr_bytes_i = 32'd0, lpddr_bursts_i = 32'd0, lpddr_bresp_err_i = 32'd0, lpddr_fifo_ovf_i = 32'd0;
logic lpddr_idle_i = 1'b1;
// Ch319 — read-probe nets (tie inputs safe; .* binds them).
logic [31:0] lpddr_rd_addr_o; logic lpddr_rd_pulse_o;
logic [31:0] lpddr_rd_data_i = 32'd0; logic lpddr_rd_done_i = 1'b0;
logic lpddr_video_src_o;
logic lpddr_scanout_lb_o;
logic lpddr_scan_valid_i = 1'b0, lpddr_scan_err_i = 1'b0;
// Ch322 — write-probe + texture-cache fill nets (matched by the .* below).
logic [31:0] lpddr_wr_addr_o, lpddr_wr_data_o; logic lpddr_wr_pulse_o;
logic lpddr_wr_busy_i = 1'b0, lpddr_wr_done_i = 1'b0; logic [31:0] lpddr_wr_bresp_err_i = 32'd0;
logic tex_fill_start_o, tex_fill_done_i = 1'b0;
logic [31:0] tex_fill_beats_i = 32'd0, tex_fill_bytes_i = 32'd0, tex_rd_errs_i = 32'd0, tex_fill_crc_i = 32'd0;
logic [31:0] tex_cache_hits_i = 32'd0, tex_bram_hits_i = 32'd0;
// Ch323 — tile COLOR+Z spill/reload counters (unused here; 0 so the .* connect resolves).
logic [31:0] spill_color_beats_i = 32'd0, spill_z_beats_i = 32'd0;
logic [31:0] reload_color_beats_i = 32'd0, reload_z_beats_i = 32'd0;
logic [31:0] reload_rd_errs_i = 32'd0, spill_color_errs_i = 32'd0, spill_z_errs_i = 32'd0;
logic spill_color_ovf_i = 1'b0, spill_z_ovf_i = 1'b0;
logic [1:0] diag_ctrl_o; // Ch323 diag DIAG_CTRL output
logic [31:0] color_spill_awaddr_i=0, color_spill_wdata0_i=0;
logic [31:0] dbg_c_first_awaddr_i=0, dbg_c_first_wdata0_i=0, dbg_c_first_wstrb_i=0, dbg_c_last_wstrb_i=0, dbg_c_beat_count_i=0;
logic [31:0] dbg_c_emit_count_i=0, dbg_c_push_count_i=0, dbg_c_pop_count_i=0; // Ch323 pipeline-split (.* tie-off)
logic [31:0] dbg_z_beat_count_i=0, dbg_z_emit_count_i=0, dbg_z_push_count_i=0, dbg_z_pop_count_i=0; // Ch324 (.* tie-off)
logic [31:0] dbg_m_first_awaddr_i=0, dbg_m_first_wdata0_i=0, dbg_m_first_wstrb_i=0;
logic [31:0] ev_tp_flush_i=0, ev_tp_zflush_i=0, ev_tp_reload_i=0, ev_tp_render_i=0;
logic [31:0] ev_flush_emit_i=0, ev_zflush_emit_i=0, ev_reload_start_i=0, ev_reload_ready_i=0;
// Ch330 Brick 4 — feeder control ports (.* tie-off; this TB doesn't exercise the feeder).
logic feeder_stg_we_tgl_o, feeder_go_tgl_o;
logic [11:0] feeder_stg_waddr_o;
logic [63:0] feeder_stg_wdata_o;
logic feeder_ready_i = 1'b0;
logic [15:0] feeder_records_i = 16'd0;
logic [31:0] feeder_waits_i = 32'd0;
ps2_hps_bridge u_bridge (
.clk (bclk),
.reset_n (breset_n),
.h2f_reset (1'b0),
.core_halt (core_halt),
.dma_done_seen (dma_done_seen),
.frame_seen (frame_seen),
.hdmi_init_done (hdmi_init_done),
.hdmi_i2c_error (hdmi_i2c_error),
.raster_overflow(raster_overflow),
.frame_toggle (frame_toggle),
.dma_done_toggle(dma_done_toggle),
.core_reset_req (core_reset_req),
.tile_wr_toggle (tile_wr_toggle),
.tile_wr_index (tile_wr_index),
.tile_wr_data (tile_wr_data),
.input_p1_o (bridge_input_p1),
.input_p2_o (bridge_input_p2),
.input_p1_raw_o (),
.*
);
// ------------------------------------------------------------------
// IOP memory map (with sio2_input_stub inside via Ch234).
// TB drives the CPU-side read port for PAD_P1_STATE.
// ------------------------------------------------------------------
logic iop_rd_en;
logic [31:0] iop_rd_addr;
wire [31:0] iop_rd_data;
wire iop_rd_valid;
iop_memory_map_stub u_iop_map (
.clk(clk), .rst_n(rst_n),
.iop_rd_en(iop_rd_en), .iop_rd_addr(iop_rd_addr),
.iop_rd_data(iop_rd_data), .iop_rd_valid(iop_rd_valid),
.iop_wr_en(1'b0), .iop_wr_addr(32'd0),
.iop_wr_data(32'd0), .iop_wr_be(4'd0),
.master_id(8'd0),
.bridge_wr_en(1'b0), .bridge_wr_addr(32'd0),
.bridge_wr_data(32'd0), .bridge_wr_be(4'd0),
.bridge_master_id(8'd0),
.dma_rd_en(1'b0), .dma_rd_addr(32'd0),
.dma_master_id(8'd0),
.dma_rd_data(), .dma_rd_valid(),
.ram_rd_en(), .ram_rd_addr(),
.ram_rd_data(32'd0), .ram_rd_valid(1'b0),
.ram_wr_en(), .ram_wr_addr(),
.ram_wr_data(), .ram_wr_be(),
.ram_master_id(),
.sif_rd_en(), .sif_rd_addr(),
.sif_rd_data(32'd0), .sif_rd_valid(1'b0),
.sif_wr_en(), .sif_wr_addr(), .sif_wr_data(),
.iop_dmac_rd_en(), .iop_dmac_rd_addr(),
.iop_dmac_rd_data(32'd0), .iop_dmac_rd_valid(1'b0),
.iop_dmac_wr_en(), .iop_dmac_wr_addr(), .iop_dmac_wr_data(),
.iop_intc_rd_en(), .iop_intc_rd_addr(),
.iop_intc_rd_data(32'd0), .iop_intc_rd_valid(1'b0),
.iop_intc_wr_en(), .iop_intc_wr_addr(), .iop_intc_wr_data(),
.input_p1(bridge_input_p1),
.input_p2(bridge_input_p2),
.bios_rd_en(), .bios_rd_addr(),
.bios_rd_data(32'd0), .bios_rd_valid(1'b0),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(),
.ev_flags()
);
// ------------------------------------------------------------------
// SIF egress bridge with Ch239 rewind. TB drives 4-beat bursts;
// the bridge accumulates into a qword and writes the EE map's
// bridge_wr_* port.
// ------------------------------------------------------------------
logic in_valid;
logic [31:0] in_data;
logic in_last;
logic rewind_i;
wire in_ready;
wire last_seen;
wire eebr_wr_en;
wire [31:0] eebr_wr_addr;
wire [127:0] eebr_wr_data;
wire [15:0] eebr_wr_be;
wire [7:0] eebr_master_id;
sif_dma_ee_ram_bridge_stub #(
.DEST_BASE_ADDR(EE_PAD_BUFFER_BASE),
.MASTER_ID(8'd5)
) u_ee_bridge (
.clk(clk), .rst_n(rst_n),
.in_valid(in_valid), .in_data(in_data), .in_last(in_last),
.in_ready(in_ready),
.bridge_wr_en(eebr_wr_en), .bridge_wr_addr(eebr_wr_addr),
.bridge_wr_data(eebr_wr_data), .bridge_wr_be(eebr_wr_be),
.bridge_master_id(eebr_master_id),
.last_seen_o(last_seen),
.rewind_i(rewind_i)
);
// ------------------------------------------------------------------
// EE map + EE RAM + BIOS ROM + EE core.
// ------------------------------------------------------------------
logic ee_rd_en;
logic [31:0] ee_rd_addr;
wire [31:0] ee_rd_data;
wire ee_rd_valid;
logic ee_wr_en;
logic [31:0] ee_wr_addr;
logic [31:0] ee_wr_data;
logic [3:0] ee_wr_be;
logic ee_dmac_rd_en;
logic [31:0] ee_dmac_rd_addr;
wire [127:0] ee_dmac_rd_data;
wire ee_dmac_rd_valid;
wire bios_rd_en;
wire [21:0] bios_rd_addr;
wire [31:0] bios_rd_data;
wire bios_rd_valid;
wire ee_ram_rd_en;
wire [24:0] ee_ram_rd_addr;
wire [127:0] ee_ram_rd_data;
wire ee_ram_rd_valid;
wire ee_ram_wr_en;
wire [24:0] ee_ram_wr_addr;
wire [127:0] ee_ram_wr_data;
wire [15:0] ee_ram_wr_be;
wire [7:0] ee_ram_master_id;
ee_memory_map_stub u_ee_map (
.clk(clk), .rst_n(rst_n),
.ee_rd_en(ee_rd_en), .ee_rd_addr(ee_rd_addr),
.ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
.ee_wr_en(ee_wr_en), .ee_wr_addr(ee_wr_addr),
.ee_wr_data(ee_wr_data), .ee_wr_be(ee_wr_be),
.dmac_rd_en(ee_dmac_rd_en), .dmac_rd_addr(ee_dmac_rd_addr),
.dmac_rd_data(ee_dmac_rd_data), .dmac_rd_valid(ee_dmac_rd_valid),
.bridge_wr_en(eebr_wr_en), .bridge_wr_addr(eebr_wr_addr),
.bridge_wr_data(eebr_wr_data), .bridge_wr_be(eebr_wr_be),
.bridge_master_id(eebr_master_id),
.bios_rd_en(bios_rd_en), .bios_rd_addr(bios_rd_addr),
.bios_rd_data(bios_rd_data), .bios_rd_valid(bios_rd_valid),
.ram_rd_en(ee_ram_rd_en), .ram_rd_addr(ee_ram_rd_addr),
.ram_rd_data(ee_ram_rd_data), .ram_rd_valid(ee_ram_rd_valid),
.ram_wr_en(ee_ram_wr_en), .ram_wr_addr(ee_ram_wr_addr),
.ram_wr_data(ee_ram_wr_data), .ram_wr_be(ee_ram_wr_be),
.ram_master_id(ee_ram_master_id),
.ee_dmac_ch2_wr_en(), .ee_dmac_ch2_wr_addr(), .ee_dmac_ch2_wr_data(),
.ee_dmac_ch2_rd_en(), .ee_dmac_ch2_rd_addr(),
.ee_dmac_ch2_rd_data(32'd0), .ee_dmac_ch2_rd_valid(1'b0),
.ee_intc_wr_en(), .ee_intc_wr_addr(), .ee_intc_wr_data(),
.ee_intc_rd_en(), .ee_intc_rd_addr(),
.ee_intc_rd_data(32'd0), .ee_intc_rd_valid(1'b0),
.ee_misc_mmio_wr_en(), .ee_misc_mmio_wr_addr(), .ee_misc_mmio_wr_data(), .ee_misc_mmio_wr_be(),
.ee_misc_mmio_rd_en(), .ee_misc_mmio_rd_addr(),
.ee_misc_mmio_rd_data(32'd0), .ee_misc_mmio_rd_valid(1'b0),
.ee_biu_wr_en(), .ee_biu_wr_addr(), .ee_biu_wr_data(), .ee_biu_wr_be(),
.ee_biu_rd_en(), .ee_biu_rd_addr(),
.ee_biu_rd_data(32'd0), .ee_biu_rd_valid(1'b0),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
);
ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
.clk(clk), .rst_n(rst_n),
.rd_en(ee_ram_rd_en), .rd_addr(ee_ram_rd_addr[EE_RAM_ADDR_W-1:0]),
.rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid),
.wr_en(ee_ram_wr_en), .wr_addr(ee_ram_wr_addr[EE_RAM_ADDR_W-1:0]),
.wr_data(ee_ram_wr_data), .wr_be(ee_ram_wr_be),
.master_id(ee_ram_master_id),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
);
bios_rom_stub #(.SIZE_BYTES(BIOS_BYTES)) u_bios (
.clk(clk), .rst_n(rst_n),
.rd_en(bios_rd_en), .rd_addr(bios_rd_addr),
.rd_data(bios_rd_data), .rd_valid(bios_rd_valid),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
);
logic core_go;
wire core_halt_o;
wire [31:0] core_pc;
wire core_trap_o;
wire [31:0] core_trap_pc;
wire [31:0] core_trap_instr;
ee_core_stub #(
.STRICT_UNSUPPORTED(1'b1)
) u_ee_core (
.clk(clk), .rst_n(rst_n),
.go_i(core_go),
.map_rd_en(ee_rd_en), .map_rd_addr(ee_rd_addr),
.map_rd_data(ee_rd_data), .map_rd_valid(ee_rd_valid),
.map_wr_en(ee_wr_en), .map_wr_addr(ee_wr_addr),
.map_wr_data(ee_wr_data), .map_wr_be(ee_wr_be),
.cpu_irq(1'b0),
.halt_o(core_halt_o), .pc_o(core_pc),
.trap_o(core_trap_o), .trap_pc_o(core_trap_pc),
.trap_instr_o(core_trap_instr),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
);
int errors;
task automatic check_eq32(input string label,
input logic [31:0] got,
input logic [31:0] expected);
if (got !== expected) begin
$error("[%s] got 0x%08x expected 0x%08x", label, got, expected);
errors = errors + 1;
end
endtask
// ------------------------------------------------------------------
// AXI master tasks (mirror tb_pad_state_via_sif_to_ee).
// ------------------------------------------------------------------
task automatic axi_write32(input logic [37:0] addr,
input logic [31:0] data);
@(posedge bclk);
s_axi_awid <= 4'd0;
s_axi_awaddr <= addr;
s_axi_awlen <= 8'd0;
s_axi_awsize <= 3'd2;
s_axi_awburst <= 2'b01;
s_axi_awlock <= 1'b0;
s_axi_awcache <= 4'd0;
s_axi_awprot <= 3'd0;
s_axi_awvalid <= 1'b1;
case (addr[3:2])
2'b00: s_axi_wdata <= {96'd0, data};
2'b01: s_axi_wdata <= {64'd0, data, 32'd0};
2'b10: s_axi_wdata <= {32'd0, data, 64'd0};
default: s_axi_wdata <= {data, 96'd0};
endcase
s_axi_wstrb <= 16'h000F << ({addr[3:2], 2'b00});
s_axi_wlast <= 1'b1;
s_axi_wvalid <= 1'b1;
s_axi_bready <= 1'b1;
wait (s_axi_awready);
@(posedge bclk);
s_axi_awvalid <= 1'b0;
wait (s_axi_wready);
@(posedge bclk);
s_axi_wvalid <= 1'b0;
wait (s_axi_bvalid);
@(posedge bclk);
s_axi_bready <= 1'b0;
endtask
task automatic iop_read32(input logic [31:0] addr,
output logic [31:0] data);
@(negedge clk);
iop_rd_en = 1'b1;
iop_rd_addr = addr;
@(negedge clk);
iop_rd_en = 1'b0;
iop_rd_addr = 32'd0;
data = iop_rd_data;
endtask
task automatic sif_send_beat(input logic [31:0] data, input logic last);
@(negedge clk);
in_valid = 1'b1;
in_data = data;
in_last = last;
while (!in_ready) @(posedge clk);
@(posedge clk);
@(negedge clk);
in_valid = 1'b0;
in_data = 32'd0;
in_last = 1'b0;
endtask
task automatic pulse_rewind();
@(negedge clk);
rewind_i = 1'b1;
@(posedge clk);
@(negedge clk);
rewind_i = 1'b0;
endtask
task automatic ee_read_qword(input logic [31:0] addr,
output logic [127:0] data);
@(negedge clk);
ee_dmac_rd_en = 1'b1;
ee_dmac_rd_addr = addr;
@(negedge clk);
ee_dmac_rd_en = 1'b0;
ee_dmac_rd_addr = 32'd0;
@(posedge clk);
data = ee_dmac_rd_data;
endtask
// ------------------------------------------------------------------
// MIPS instruction encoders + BIOS preload helpers.
// ------------------------------------------------------------------
function automatic logic [31:0] enc_i(input logic [5:0] op,
input int rs, input int rt,
input logic [15:0] imm);
return {op, rs[4:0], rt[4:0], imm};
endfunction
function automatic logic [31:0] enc_j(input logic [31:0] target_addr);
return {OP_J, target_addr[27:2]};
endfunction
function automatic logic [15:0] branch_imm(input int branch_slot,
input int target_slot);
int diff;
diff = target_slot - branch_slot - 1;
return diff[15:0];
endfunction
task automatic bios_word(input int slot, input logic [31:0] word);
u_bios.mem[slot] = word;
endtask
task automatic bios_nop(input int slot); bios_word(slot, 32'd0); endtask
task automatic bios_lui(input int slot, input int rt, input logic [15:0] imm);
bios_word(slot, enc_i(OP_LUI, 0, rt, imm));
endtask
task automatic bios_ori(input int slot, input int rt, input int rs,
input logic [15:0] imm);
bios_word(slot, enc_i(OP_ORI, rs, rt, imm));
endtask
task automatic bios_lbu(input int slot, input int rt, input int rs,
input logic [15:0] offs);
bios_word(slot, enc_i(OP_LBU, rs, rt, offs));
endtask
task automatic bios_sw(input int slot, input int rt, input int rs,
input logic [15:0] offs);
bios_word(slot, enc_i(OP_SW, rs, rt, offs));
endtask
task automatic bios_beq(input int slot, input int rs, input int rt,
input int target_slot);
bios_word(slot, enc_i(OP_BEQ, rs, rt, branch_imm(slot, target_slot)));
endtask
task automatic bios_j(input int slot, input logic [31:0] tgt);
bios_word(slot, enc_j(tgt));
endtask
task automatic preload_bios();
// $1 = EE_PAD_BUFFER_KSEG0 = 0x8008_0000 → LUI $1, 0x8008
bios_lui(SLOT_LUI_PADBASE, 1, 16'h8008);
// $5 = EE_MARKER_KSEG0 = 0x8000_1000 → LUI + ORI
bios_lui(SLOT_LUI_MARKERBASE, 5, 16'h8000);
bios_ori(SLOT_ORI_MARKERBASE, 5, 5, 16'h1000);
// LOOP:
bios_lbu(SLOT_LBU_BYTE3, 2, 1, 16'h0003); // $2 = lbu 3($1)
bios_ori(SLOT_ORI_FF, 3, 0, 16'h00FF); // $3 = 0xFF
bios_beq(SLOT_BEQ_A, 2, 3, SLOT_ORI_AA); // → MARK_A if eq
bios_nop(SLOT_BEQ_A_DS); // delay slot
bios_ori(SLOT_ORI_DF, 3, 0, 16'h00DF); // $3 = 0xDF
bios_beq(SLOT_BEQ_B, 2, 3, SLOT_ORI_BB); // → MARK_B if eq
bios_nop(SLOT_BEQ_B_DS);
// Fall-through → COMBO
bios_ori(SLOT_ORI_CC, 6, 0, 16'h00CC);
bios_sw (SLOT_SW_C, 6, 5, 16'h0000);
bios_j (SLOT_J_LOOP_FROM_C, LOOP_PC);
bios_nop(SLOT_J_LOOP_FROM_C_DS);
// MARK_A:
bios_ori(SLOT_ORI_AA, 6, 0, 16'h00AA);
bios_sw (SLOT_SW_A, 6, 5, 16'h0000);
bios_j (SLOT_J_LOOP_FROM_A, LOOP_PC);
bios_nop(SLOT_J_LOOP_FROM_A_DS);
// MARK_B:
bios_ori(SLOT_ORI_BB, 6, 0, 16'h00BB);
bios_sw (SLOT_SW_B, 6, 5, 16'h0000);
bios_j (SLOT_J_LOOP_FROM_B, LOOP_PC);
bios_nop(SLOT_J_LOOP_FROM_B_DS);
endtask
// ------------------------------------------------------------------
// Drive a Sony pad-state burst for INPUT_P1=joy_pattern. Rewinds
// first so the new packet overwrites the same EE_PAD_BUFFER_BASE
// slot, then drives the 4 beats. The IOP-side PAD_P1_STATE read
// confirms the bridge latch arrived at the IOP map before packing.
// ------------------------------------------------------------------
logic [31:0] pad_word;
logic [127:0] expected_qw;
logic [7:0] b3, b4;
task automatic deliver_pad_packet(input logic [31:0] joy_pattern);
axi_write32(38'h040, joy_pattern);
repeat (20) @(posedge clk); // CDC settle
iop_read32(32'h1F80_8500, pad_word);
b3 = pad_word[7:0];
b4 = pad_word[15:8];
expected_qw = {32'd0,
32'h0000_0080,
{8'h80, 8'h80, 8'h80, b4},
{b3, 8'h5A, 8'h41, 8'h00}};
pulse_rewind();
sif_send_beat(expected_qw[ 31: 0], 1'b0);
sif_send_beat(expected_qw[ 63:32], 1'b0);
sif_send_beat(expected_qw[ 95:64], 1'b0);
sif_send_beat(expected_qw[127:96], 1'b1);
// Give the EE program time to loop at least once and write
// the new marker. The loop is ~10 instructions, and the EE
// core stub takes a few cycles per instruction — 500 clk is
// many loop iterations.
repeat (500) @(posedge clk);
endtask
// ------------------------------------------------------------------
// Read the marker byte from EE RAM and verify it.
// ------------------------------------------------------------------
logic [127:0] got_qw;
logic [31:0] got_marker;
task automatic check_marker(input string label, input logic [31:0] expected);
// Marker is at EE_MARKER_ADDR (= 0x0000_1000), word 0 of the
// qword at 0x0000_1000. EE_MARKER_ADDR is qword-aligned.
ee_read_qword(EE_MARKER_ADDR, got_qw);
got_marker = got_qw[31:0];
check_eq32(label, got_marker, expected);
endtask
initial begin
errors = 0;
breset_n = 1'b0;
rst_n = 1'b0;
core_go = 1'b0;
s_axi_arvalid = 1'b0;
s_axi_awvalid = 1'b0;
s_axi_wvalid = 1'b0;
s_axi_rready = 1'b0;
s_axi_bready = 1'b0;
iop_rd_en = 1'b0;
iop_rd_addr = 32'd0;
in_valid = 1'b0;
in_data = 32'd0;
in_last = 1'b0;
rewind_i = 1'b0;
ee_dmac_rd_en = 1'b0;
ee_dmac_rd_addr = 32'd0;
// Advance past time 0 so the bios_rom_stub's `initial` NOP-sled
// fill has completed BEFORE we overwrite specific slots with
// the test program. (SV LRM doesn't guarantee initial-block
// ordering; the existing tb_ee_core_lbu uses the same pattern.)
repeat (4) @(posedge clk);
preload_bios();
repeat (4) @(posedge bclk);
@(posedge clk);
breset_n = 1'b1;
rst_n = 1'b1;
repeat (4) @(posedge bclk);
repeat (4) @(posedge clk);
// Start the EE core — it begins executing the bootstrap from
// 0xBFC0_0000 immediately.
@(posedge clk);
core_go = 1'b1;
@(posedge clk);
core_go = 1'b0;
// §1. No buttons → byte3=0xFF → marker A.
deliver_pad_packet(32'h0000_0000);
check_marker("no_buttons_marker_A", MARK_VAL_A);
// §2. RIGHT only (bit 0) → byte3=0xDF → marker B.
deliver_pad_packet(32'h0000_0001);
check_marker("right_only_marker_B", MARK_VAL_B);
// §3. RIGHT + SELECT (bits 0,5) → byte3=0xDE → marker C.
deliver_pad_packet(32'h0000_0021);
check_marker("combo_marker_C", MARK_VAL_C);
// §4. Re-clear → marker A again.
deliver_pad_packet(32'h0000_0000);
check_marker("reclear_marker_A", MARK_VAL_A);
$display("[tb_ee_pad_buffer_branch] errors=%0d", errors);
if (errors == 0) $display("[tb_ee_pad_buffer_branch] PASS");
else $display("[tb_ee_pad_buffer_branch] FAIL");
$finish;
end
initial begin
#20_000_000;
$error("[tb_ee_pad_buffer_branch] TIMEOUT");
$finish;
end
endmodule : tb_ee_pad_buffer_branch