// 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) ; $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 ; marker C // slot 10 SW $6, 0($5) // slot 11 J LOOP ; restart // slot 12 NOP ; delay slot // slot 13 ORI $6, $0, 0x00AA ; marker A // slot 14 SW $6, 0($5) // slot 15 J LOOP // slot 16 NOP // slot 17 ORI $6, $0, 0x00BB ; 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