// retroDE_ps2 — tb_bridge_iop_pad_input (Ch235)
// ============================================================================
// First end-to-end integration TB for the HPS → bridge → IOP-fabric
// input path. Wires `ps2_hps_bridge` (Ch222 INPUT_P1/P2 store) into
// `iop_memory_map_stub` (Ch234 sio2_input_stub region) across two
// distinct clocks so the bridge-clk → IOP-clk CDC is exercised end-to-end:
//
//   HPS AXI write @ 0x040 (INPUT_P1) →
//     ps2_hps_bridge.input_p1_q latch (bclk @ 100 MHz) →
//       ps2_hps_bridge.input_p1_o output →
//         iop_memory_map_stub.input_p1 →
//           u_sio2_input.input_p1 →
//             2-FF sync into iclk @ 33 MHz →
//               PAD_P1_STATE readable at IOP phys 0x1F80_8500
//
// Verifies:
//   §1. Reset: IOP-side PAD_P1/P2 = 0xFFFF, PAD_STATUS = 1.
//   §2. AXI write INPUT_P1 single bit → after CDC latency, IOP read
//       shows the corresponding Sony bit cleared.
//   §3. AXI write INPUT_P2 with a different pattern → P1 unchanged,
//       P2 reflects new state (independence).
//   §4. Multi-bit AXI write → IOP sees the matching combo word.
//   §5. AXI clear → IOP reads back to 0xFFFF.
//   §6. Bridge reset cycle → IOP sees 0xFFFF (Ch222 latches clear),
//       even if iclk reset stays asserted.
// ============================================================================

`timescale 1ns/1ps

module tb_bridge_iop_pad_input;

    // ------------------------------------------------------------------
    // Two clocks — bridge on bclk @ 100 MHz, IOP on iclk @ 33 MHz.
    // Different periods deliberately exercise the bclk → iclk CDC.
    // ------------------------------------------------------------------
    logic bclk;
    logic iclk;
    initial bclk = 1'b0;
    initial iclk = 1'b0;
    always #5  bclk = ~bclk;    // 100 MHz
    always #15 iclk = ~iclk;    // 33 MHz

    logic breset_n;
    logic ireset_n;

    // ------------------------------------------------------------------
    // Bridge status inputs (tied LOW — we don't exercise CORE_STATUS).
    // ------------------------------------------------------------------
    logic core_halt;
    logic dma_done_seen;
    logic frame_seen;
    logic hdmi_init_done;
    logic hdmi_i2c_error;
    logic raster_overflow;
    logic frame_toggle;
    logic dma_done_toggle;

    // ------------------------------------------------------------------
    // Bridge AXI slave (TB drives master).
    // ------------------------------------------------------------------
    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;

    // ------------------------------------------------------------------
    // Bridge outputs we ignore (Ch176/Ch229/Ch230).
    // ------------------------------------------------------------------
    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;

    // ------------------------------------------------------------------
    // The new Ch235 broadcast — bridge → IOP map.
    // ------------------------------------------------------------------
    wire [31:0] bridge_input_p1;
    wire [31:0] bridge_input_p2;

    // ------------------------------------------------------------------
    // ps2_hps_bridge instance.
    // ------------------------------------------------------------------
    // 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)
    // Ch442 — scanout A+B diagnostic inputs; tied off (.* matches by name).
    logic        scan_diag_uf_i = 1'b0, scan_diag_rderr_nz_i = 1'b0, scan_diag_valid_i = 1'b0;
    logic [29:0] scan_diag_first_i = 30'd0;
    logic [6:0]  scan_diag_stat_i = 7'd0;
    logic [3:0]  scan_diag_pmax_i = 4'd0;
    // Pre-existing clut_stage/commit bridge outputs (fog-baseline ba74bbd); nets so .* binds.
    wire [7:0]  clut_stage_waddr_o; wire [31:0] clut_stage_wdata_o; wire clut_stage_we_o;
    wire        clut_commit_tgl_o;  wire [31:0] clut_expected_crc_o;
    logic       clut_busy_i = 1'b0, clut_done_tgl_i = 1'b0; logic [31:0] clut_crc_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 (),
        .*  // AXI signals
    );

    // ------------------------------------------------------------------
    // IOP map signals (TB drives the CPU-side read; all other ports
    // tied off since we only care about the pad path).
    // ------------------------------------------------------------------
    logic        iop_rd_en;
    logic [31:0] iop_rd_addr;
    wire  [31:0] iop_rd_data;
    wire         iop_rd_valid;
    logic [7:0]  iop_master_id;

    // Trace outputs (ignored).
    wire         tr_valid;
    wire [3:0]   tr_subsys;
    wire [3:0]   tr_event;
    wire [63:0]  tr_arg0, tr_arg1, tr_arg2, tr_arg3;
    wire [31:0]  tr_flags;

    iop_memory_map_stub u_map (
        .clk(iclk), .rst_n(ireset_n),

        // CPU side — TB drives reads only.
        .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(iop_master_id),

        // Bridge-side write port — unused by this TB.
        .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-side read port — unused.
        .dma_rd_en(1'b0), .dma_rd_addr(32'd0),
        .dma_master_id(8'd0),
        .dma_rd_data(), .dma_rd_valid(),

        // RAM port — tied off; CPU reads of RAM aren't exercised.
        .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 port — tied off.
        .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 port — tied off.
        .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 port — tied off.
        .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(),

        // Ch235 — bridge → IOP wiring (the whole point of this TB).
        .input_p1(bridge_input_p1),
        .input_p2(bridge_input_p2),

        // BIOS port — tied off.
        .bios_rd_en(), .bios_rd_addr(),
        .bios_rd_data(32'd0), .bios_rd_valid(1'b0),

        // Trace outputs.
        .ev_valid(tr_valid), .ev_subsys(tr_subsys), .ev_event(tr_event),
        .ev_arg0(tr_arg0), .ev_arg1(tr_arg1),
        .ev_arg2(tr_arg2), .ev_arg3(tr_arg3),
        .ev_flags(tr_flags)
    );

    int errors;

    task automatic check_eq(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_ps2_hps_bridge.sv).
    // ------------------------------------------------------------------
    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

    // ------------------------------------------------------------------
    // IOP read helper. Follows the existing tb_iop_memory_map_stub
    // pattern: drive on @(negedge iclk) with BLOCKING assigns so the
    // address is stable through the next posedge that registers
    // rd_pending. Sample iop_rd_data on the following negedge — by
    // then the map's read response (and the stub's pad_rd_data) have
    // both been registered, and iop_rd_valid is HIGH for one cycle.
    // ------------------------------------------------------------------
    task automatic iop_read32(input logic [31:0] addr,
                              output logic [31:0] data);
        @(negedge iclk);
        iop_rd_en   = 1'b1;
        iop_rd_addr = addr;
        @(negedge iclk);
        iop_rd_en   = 1'b0;
        iop_rd_addr = 32'd0;
        data = iop_rd_data;
        if (iop_rd_valid !== 1'b1) begin
            $error("[iop_read32] iop_rd_valid not high at sample (addr=0x%08x)", addr);
            errors = errors + 1;
        end
    endtask

    // ------------------------------------------------------------------
    // Sony pad-word expected-value helper (mirrors sio2_input_stub).
    // ------------------------------------------------------------------
    function automatic logic [15:0] expected_sony(input logic [31:0] joy);
        logic [7:0] b3, b4;
        b3 = ~{joy[1], joy[2], joy[0], joy[3], joy[4], joy[15], joy[14], joy[5]};
        b4 = ~{joy[8], joy[7], joy[9], joy[6], joy[11], joy[10], joy[13], joy[12]};
        expected_sony = {b4, b3};
    endfunction

    // ------------------------------------------------------------------
    // Wait long enough for an AXI-written value to propagate through
    // the bridge latch + the 2-FF sync inside sio2_input_stub.
    // Conservative — 20 iclk cycles is many sync windows.
    // ------------------------------------------------------------------
    task automatic settle_cdc();
        repeat (20) @(posedge iclk);
    endtask

    logic [31:0] rd;

    localparam logic [31:0] PAD_P1_ADDR = 32'h1F80_8500;
    localparam logic [31:0] PAD_P2_ADDR = 32'h1F80_8504;
    localparam logic [31:0] PAD_ST_ADDR = 32'h1F80_8508;

    initial begin
        errors          = 0;
        breset_n        = 1'b0;
        ireset_n        = 1'b0;

        // AXI defaults.
        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;

        // Bridge status (all LOW — we don't exercise CORE_STATUS).
        core_halt       = 1'b0;
        dma_done_seen   = 1'b0;
        frame_seen      = 1'b0;
        hdmi_init_done  = 1'b0;
        hdmi_i2c_error  = 1'b0;
        raster_overflow = 1'b0;
        frame_toggle    = 1'b0;
        dma_done_toggle = 1'b0;

        // IOP-side defaults.
        iop_rd_en     = 1'b0;
        iop_rd_addr   = 32'd0;
        iop_master_id = 8'd0;

        repeat (4) @(posedge bclk);
        @(posedge iclk);
        breset_n = 1'b1;
        ireset_n = 1'b1;
        // Settle both domains.
        repeat (8) @(posedge bclk);
        repeat (8) @(posedge iclk);

        // ----------------------------------------------------------
        // §1. Reset state — bridge latches at 0 → Sony word 0xFFFF.
        //     PAD_STATUS = 1.
        // ----------------------------------------------------------
        iop_read32(PAD_P1_ADDR, rd); check_eq("rst_P1", rd, 32'h0000_FFFF);
        iop_read32(PAD_P2_ADDR, rd); check_eq("rst_P2", rd, 32'h0000_FFFF);
        iop_read32(PAD_ST_ADDR, rd); check_eq("rst_ST", rd, 32'h0000_0001);

        // ----------------------------------------------------------
        // §2. AXI write JOY_RIGHT (bit 0) into INPUT_P1 @ 0x040.
        //     After CDC settle, IOP-side P1 has byte3.5 (RIGHT)
        //     cleared.
        // ----------------------------------------------------------
        axi_write32(38'h040, 32'h0000_0001);
        settle_cdc();
        iop_read32(PAD_P1_ADDR, rd);
        check_eq("P1_RIGHT_only",
                 rd, {16'd0, expected_sony(32'h0000_0001)});
        // P2 unchanged.
        iop_read32(PAD_P2_ADDR, rd);
        check_eq("P2_unaffected_by_P1_write", rd, 32'h0000_FFFF);

        // ----------------------------------------------------------
        // §3. AXI write a different pattern into INPUT_P2 @ 0x044
        //     (JOY_DOWN | JOY_A = bits 2 | 9). P1 retains its
        //     previous state.
        // ----------------------------------------------------------
        axi_write32(38'h044, 32'h0000_0204);
        settle_cdc();
        iop_read32(PAD_P2_ADDR, rd);
        check_eq("P2_DOWN_A",
                 rd, {16'd0, expected_sony(32'h0000_0204)});
        iop_read32(PAD_P1_ADDR, rd);
        check_eq("P1_persist_after_P2_write",
                 rd, {16'd0, expected_sony(32'h0000_0001)});

        // ----------------------------------------------------------
        // §4. Multi-bit combo. All D-pad + START + SELECT pressed
        //     on P1 (bits 0..5).
        // ----------------------------------------------------------
        axi_write32(38'h040, 32'h0000_003F);
        settle_cdc();
        iop_read32(PAD_P1_ADDR, rd);
        check_eq("P1_dpad_start_sel_combo",
                 rd, {16'd0, expected_sony(32'h0000_003F)});

        // ----------------------------------------------------------
        // §5. AXI clear INPUT_P1 → IOP reads back to 0xFFFF.
        //     INPUT_P2 unchanged.
        // ----------------------------------------------------------
        axi_write32(38'h040, 32'h0000_0000);
        settle_cdc();
        iop_read32(PAD_P1_ADDR, rd);
        check_eq("P1_cleared", rd, 32'h0000_FFFF);
        iop_read32(PAD_P2_ADDR, rd);
        check_eq("P2_persist_after_P1_clear",
                 rd, {16'd0, expected_sony(32'h0000_0204)});

        // ----------------------------------------------------------
        // §6. Bridge reset cycle — Ch222 latches clear; IOP sees
        //     0xFFFF for both pads regardless of any prior state.
        // ----------------------------------------------------------
        @(posedge bclk); breset_n = 1'b0;
        // Hold reset long enough to cycle through the sync chain.
        repeat (8) @(posedge bclk);
        repeat (8) @(posedge iclk);
        @(posedge bclk); breset_n = 1'b1;
        // Need more iclk settle: bridge clears at bclk, sio2_input
        // sync takes 2 iclk to propagate the new value.
        repeat (8) @(posedge bclk);
        settle_cdc();
        iop_read32(PAD_P1_ADDR, rd); check_eq("rst_clr_P1", rd, 32'h0000_FFFF);
        iop_read32(PAD_P2_ADDR, rd); check_eq("rst_clr_P2", rd, 32'h0000_FFFF);
        iop_read32(PAD_ST_ADDR, rd); check_eq("rst_clr_ST", rd, 32'h0000_0001);

        // ----------------------------------------------------------
        // Done.
        // ----------------------------------------------------------
        $display("[tb_bridge_iop_pad_input] errors=%0d", errors);
        if (errors == 0) $display("[tb_bridge_iop_pad_input] PASS");
        else             $display("[tb_bridge_iop_pad_input] FAIL");
        $finish;
    end

    initial begin
        #5_000_000;
        $error("[tb_bridge_iop_pad_input] TIMEOUT");
        $finish;
    end

endmodule : tb_bridge_iop_pad_input
