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