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>
223 lines
8.9 KiB
Systemverilog
223 lines
8.9 KiB
Systemverilog
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (c) 2025-2026 retroDE contributors
|
|
// ============================================================================
|
|
// ps2_hps_bridge_null — minimal AXI4 slave for the PS2 core's Ch170 shell
|
|
// ============================================================================
|
|
//
|
|
// Purpose: present an AXI4 slave endpoint to the HPS hps2fpga bridge that
|
|
// (a) does proper AXI handshake so HPS transactions can't stall the bus,
|
|
// and (b) exposes a minimal "core identity" register window at 0x000-0x00F
|
|
// so retrodesd / probing utilities can read back who loaded.
|
|
//
|
|
// This is the Ch170 placeholder — when a real ps2_hps_bridge.sv lands (with
|
|
// HPS-driven core_reset, status mirrors, ROM staging, etc.), it should keep
|
|
// the same AXI4 port signature so the top-wrapper instantiation doesn't
|
|
// need to change.
|
|
//
|
|
// AXI4 subset (matches splash_hps_bridge.sv):
|
|
// - 128-bit data bus with byte-lane selection via {awaddr[3:2] / araddr[3:2]}
|
|
// - Single-beat only (awlen=0, arlen=0)
|
|
// - 4-bit ID echo
|
|
// - 38-bit address
|
|
//
|
|
// Identity register map (ABI v1.0 — read-only):
|
|
// 0x000 CORE_ID = 32'h70533200 ("pS2\0" — placeholder, refine later)
|
|
// 0x004 ABI_VERSION = 32'h00000100 (v1.0)
|
|
// 0x008 CORE_STATUS = 32'h00000001 (bit 0 = loaded)
|
|
// 0x00C CORE_CAPS = 32'h00000000 (no caps advertised)
|
|
//
|
|
// Everything else: reads return 0, writes ACK'd and discarded.
|
|
// ============================================================================
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module ps2_hps_bridge_null (
|
|
input logic clk, // qsys clk_100_clk domain
|
|
input logic reset_n,
|
|
input logic h2f_reset, // HPS-driven fabric reset (active high) — unused; reserved
|
|
|
|
// AXI4 slave — write address channel
|
|
input logic [3:0] s_axi_awid,
|
|
input logic [37:0] s_axi_awaddr,
|
|
input logic [7:0] s_axi_awlen,
|
|
input logic [2:0] s_axi_awsize,
|
|
input logic [1:0] s_axi_awburst,
|
|
input logic s_axi_awlock,
|
|
input logic [3:0] s_axi_awcache,
|
|
input logic [2:0] s_axi_awprot,
|
|
input logic s_axi_awvalid,
|
|
output logic s_axi_awready,
|
|
|
|
// AXI4 slave — write data channel
|
|
input logic [127:0] s_axi_wdata,
|
|
input logic [15:0] s_axi_wstrb,
|
|
input logic s_axi_wlast,
|
|
input logic s_axi_wvalid,
|
|
output logic s_axi_wready,
|
|
|
|
// AXI4 slave — write response channel
|
|
output logic [3:0] s_axi_bid,
|
|
output logic [1:0] s_axi_bresp,
|
|
output logic s_axi_bvalid,
|
|
input logic s_axi_bready,
|
|
|
|
// AXI4 slave — read address channel
|
|
input logic [3:0] s_axi_arid,
|
|
input logic [37:0] s_axi_araddr,
|
|
input logic [7:0] s_axi_arlen,
|
|
input logic [2:0] s_axi_arsize,
|
|
input logic [1:0] s_axi_arburst,
|
|
input logic s_axi_arlock,
|
|
input logic [3:0] s_axi_arcache,
|
|
input logic [2:0] s_axi_arprot,
|
|
input logic s_axi_arvalid,
|
|
output logic s_axi_arready,
|
|
|
|
// AXI4 slave — read data channel
|
|
output logic [3:0] s_axi_rid,
|
|
output logic [127:0] s_axi_rdata,
|
|
output logic [1:0] s_axi_rresp,
|
|
output logic s_axi_rlast,
|
|
output logic s_axi_rvalid,
|
|
input logic s_axi_rready
|
|
);
|
|
|
|
// ----------------------------------------------------------------
|
|
// Identity register window (Ch170 ABI v1.0).
|
|
// ----------------------------------------------------------------
|
|
localparam logic [31:0] CORE_ID = 32'h70533200;
|
|
localparam logic [31:0] ABI_VERSION = 32'h00000100;
|
|
localparam logic [31:0] CORE_STATUS = 32'h00000001;
|
|
localparam logic [31:0] CORE_CAPS = 32'h00000000;
|
|
|
|
function automatic logic [31:0] identity_lookup(input logic [37:0] addr);
|
|
// Identity registers live in the first 16 bytes of the bridge map.
|
|
// Anything else returns 0. addr[3:2] picks one of four 32-bit slots.
|
|
if (addr[37:4] != '0)
|
|
return 32'd0;
|
|
case (addr[3:2])
|
|
2'b00: identity_lookup = CORE_ID;
|
|
2'b01: identity_lookup = ABI_VERSION;
|
|
2'b10: identity_lookup = CORE_STATUS;
|
|
default: identity_lookup = CORE_CAPS;
|
|
endcase
|
|
endfunction
|
|
|
|
// ----------------------------------------------------------------
|
|
// Write FSM. Single-beat: accept awvalid + wvalid together, hold
|
|
// them ready for one cycle each, then emit bvalid. Stays in the
|
|
// BRESP state until bready, so multi-cycle bready timing from
|
|
// qsys still completes cleanly.
|
|
// ----------------------------------------------------------------
|
|
typedef enum logic [1:0] { W_IDLE, W_DATA, W_RESP } w_state_t;
|
|
w_state_t w_state;
|
|
logic [3:0] aw_id_q;
|
|
|
|
always_ff @(posedge clk or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
w_state <= W_IDLE;
|
|
aw_id_q <= '0;
|
|
s_axi_bvalid <= 1'b0;
|
|
end else begin
|
|
case (w_state)
|
|
W_IDLE: begin
|
|
s_axi_bvalid <= 1'b0;
|
|
if (s_axi_awvalid && s_axi_awready) begin
|
|
aw_id_q <= s_axi_awid;
|
|
w_state <= W_DATA;
|
|
end
|
|
end
|
|
W_DATA: begin
|
|
if (s_axi_wvalid && s_axi_wready) begin
|
|
s_axi_bvalid <= 1'b1;
|
|
w_state <= W_RESP;
|
|
end
|
|
end
|
|
W_RESP: begin
|
|
if (s_axi_bready) begin
|
|
s_axi_bvalid <= 1'b0;
|
|
w_state <= W_IDLE;
|
|
end
|
|
end
|
|
default: w_state <= W_IDLE;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
assign s_axi_awready = (w_state == W_IDLE);
|
|
assign s_axi_wready = (w_state == W_DATA);
|
|
assign s_axi_bid = aw_id_q;
|
|
assign s_axi_bresp = 2'b00; // OKAY
|
|
|
|
// ----------------------------------------------------------------
|
|
// Read FSM. Same shape — accept arvalid, drive rdata + rvalid,
|
|
// hold until rready.
|
|
// ----------------------------------------------------------------
|
|
typedef enum logic [0:0] { R_IDLE, R_RESP } r_state_t;
|
|
r_state_t r_state;
|
|
logic [3:0] ar_id_q;
|
|
logic [37:0] ar_addr_q;
|
|
logic [127:0] rdata_q;
|
|
|
|
always_ff @(posedge clk or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
r_state <= R_IDLE;
|
|
ar_id_q <= '0;
|
|
ar_addr_q <= '0;
|
|
rdata_q <= '0;
|
|
s_axi_rvalid <= 1'b0;
|
|
end else begin
|
|
case (r_state)
|
|
R_IDLE: begin
|
|
s_axi_rvalid <= 1'b0;
|
|
if (s_axi_arvalid && s_axi_arready) begin
|
|
ar_id_q <= s_axi_arid;
|
|
ar_addr_q <= s_axi_araddr;
|
|
// Replicate the 32-bit identity word into the
|
|
// matching 32-bit lane of the 128-bit response,
|
|
// mirroring splash_hps_bridge's lane semantics.
|
|
case (s_axi_araddr[3:2])
|
|
2'b00: rdata_q <= {96'd0, identity_lookup(s_axi_araddr)};
|
|
2'b01: rdata_q <= {64'd0, identity_lookup(s_axi_araddr), 32'd0};
|
|
2'b10: rdata_q <= {32'd0, identity_lookup(s_axi_araddr), 64'd0};
|
|
default: rdata_q <= {identity_lookup(s_axi_araddr), 96'd0};
|
|
endcase
|
|
s_axi_rvalid <= 1'b1;
|
|
r_state <= R_RESP;
|
|
end
|
|
end
|
|
R_RESP: begin
|
|
if (s_axi_rready) begin
|
|
s_axi_rvalid <= 1'b0;
|
|
r_state <= R_IDLE;
|
|
end
|
|
end
|
|
default: r_state <= R_IDLE;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
assign s_axi_arready = (r_state == R_IDLE);
|
|
assign s_axi_rid = ar_id_q;
|
|
assign s_axi_rdata = rdata_q;
|
|
assign s_axi_rresp = 2'b00; // OKAY
|
|
assign s_axi_rlast = 1'b1; // single-beat
|
|
|
|
// ----------------------------------------------------------------
|
|
// Tie off the AXI4 fields we don't consume so Quartus doesn't
|
|
// emit lint warnings: awlen/awsize/awburst/awlock/awcache/awprot,
|
|
// wstrb/wlast, arlen/arsize/arburst/arlock/arcache/arprot, h2f_reset.
|
|
// ----------------------------------------------------------------
|
|
// verilator lint_off UNUSED
|
|
wire _unused_ok = &{ 1'b0,
|
|
s_axi_awlen, s_axi_awsize, s_axi_awburst,
|
|
s_axi_awlock, s_axi_awcache, s_axi_awprot,
|
|
s_axi_wdata, s_axi_wstrb, s_axi_wlast,
|
|
s_axi_arlen, s_axi_arsize, s_axi_arburst,
|
|
s_axi_arlock, s_axi_arcache, s_axi_arprot,
|
|
h2f_reset,
|
|
1'b0 };
|
|
// verilator lint_on UNUSED
|
|
|
|
endmodule : ps2_hps_bridge_null
|