// 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;
    logic        frame_drained_i  = 1'b0;   // Ch353 — new bridge diagnostic input; tied off (.* matches by name)
    logic        clear_done_i     = 1'b0;   // Ch357 — persistent-Z preclear ack; tied off (.* matches by name)
    logic [31:0] frag_drops_i     = 32'd0;  // Ch357 — persistent-Z drop count; tied off (.* matches by name)
    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
