// 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; 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