Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
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>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
// retroDE_ps2 — ee_gs_priv_bridge_stub (Ch111)
|
||||
//
|
||||
// Bridges 32-bit EE-MMIO writes targeting the GS privileged-
|
||||
// register window at 0x1200_0000 into the 64-bit gs_stub.reg_wr_*
|
||||
// port. Real PS2 driver code reaches PMODE / DISPFB1 / DISPLAY1
|
||||
// (etc.) via 64-bit MIPS `sd` instructions; the EE microarch
|
||||
// breaks each `sd` into a pair of 32-bit `sw` operations to the
|
||||
// low+high halves of the 8-byte register slot. This bridge does
|
||||
// the inverse — it watches the 32-bit EE write stream, latches a
|
||||
// 64-bit shadow per 8-byte slot, and fires a gs_stub.reg_wr_*
|
||||
// pulse on EVERY half-write with the running 64-bit shadow value.
|
||||
//
|
||||
// Scope:
|
||||
// - One shared 64-bit shadow + an offset[15:3] tag identifying
|
||||
// the currently-tracked 8-byte slot. Sequential writes to the
|
||||
// SAME slot accumulate (low first, then high → final shadow
|
||||
// has both halves correct on the second fire). Switching
|
||||
// slots resets the shadow to zero so partial-half writes to
|
||||
// a fresh slot don't carry stale data from a different reg.
|
||||
// - Each EE half-write fires a gs_stub.reg_wr_* pulse with the
|
||||
// 8-byte-aligned offset (`{ee_wr_addr[15:3], 3'b000}`) and
|
||||
// the FULL 64-bit shadow. Single-half writes (e.g. PMODE
|
||||
// where only the low byte matters) work because the high
|
||||
// half stays zero and gs_stub's latch sees the right value.
|
||||
// - 32-bit EE write width (matches ee_memory_map_stub's
|
||||
// ee_wr_*-port surface). **Full-word writes only**:
|
||||
// `ee_wr_be` MUST be 4'b1111 on every accepted write. Byte-
|
||||
// lane merging into the 64-bit shadow is intentionally NOT
|
||||
// modelled here — control-plane GS registers (PMODE/
|
||||
// DISPFB1/DISPLAY1/etc.) are always written as full 32-bit
|
||||
// halves of an `sd`, and constraining the contract keeps the
|
||||
// shadow + commit logic small. A simulation-time `$error`
|
||||
// fires if a non-full be is presented; a future chapter can
|
||||
// widen the bridge to per-byte merge if/when a real driver
|
||||
// pattern needs sub-word writes here.
|
||||
//
|
||||
// Wiring contract (TB-level for Ch111):
|
||||
// ee_wr_en ← TB EE-MMIO write strobe at 0x12000000+offset
|
||||
// ee_wr_addr ← 16-bit offset within the GS priv window (= EE
|
||||
// phys addr [15:0]; the upper EE-window decode
|
||||
// lives in the test bench / memory map)
|
||||
// ee_wr_data ← 32-bit EE data (one of two halves of a 64-bit
|
||||
// GS register)
|
||||
// ee_wr_be ← 4-bit per-byte enable (typically 4'b1111)
|
||||
//
|
||||
// The bridge does NOT participate in EE reads. The gs_stub
|
||||
// privileged-register port is write-only at this scope, matching
|
||||
// the limited read coverage of the GS priv block in the rest of
|
||||
// the design.
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module ee_gs_priv_bridge_stub
|
||||
(
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
|
||||
// EE-MMIO write port (32-bit data, 16-bit offset within
|
||||
// 0x1200_0000 window).
|
||||
input logic ee_wr_en,
|
||||
input logic [15:0] ee_wr_addr,
|
||||
input logic [31:0] ee_wr_data,
|
||||
input logic [3:0] ee_wr_be,
|
||||
|
||||
// gs_stub privileged-register port (16-bit offset, 64-bit data).
|
||||
output logic gs_reg_wr_en,
|
||||
output logic [15:0] gs_reg_wr_addr,
|
||||
output logic [63:0] gs_reg_wr_data
|
||||
);
|
||||
|
||||
// Shared 64-bit shadow + the 13-bit offset[15:3] tag of the
|
||||
// currently-tracked slot. Resets to zero on rst_n or on a
|
||||
// switch to a different slot.
|
||||
logic [63:0] shadow;
|
||||
logic [12:0] shadow_tag;
|
||||
logic shadow_valid;
|
||||
|
||||
logic [12:0] cur_tag;
|
||||
logic cur_is_high;
|
||||
logic [63:0] new_shadow;
|
||||
|
||||
assign cur_tag = ee_wr_addr[15:3];
|
||||
assign cur_is_high = ee_wr_addr[2];
|
||||
|
||||
always_comb begin
|
||||
logic [63:0] base_shadow;
|
||||
// If the EE write hits the same 8-byte slot we're already
|
||||
// tracking, merge into the existing shadow. Otherwise start
|
||||
// a fresh shadow at zero (the un-touched half stays 0 — that's
|
||||
// safe for the demo where we always write the half that
|
||||
// matters first; high-only writes are not used in this TB
|
||||
// family).
|
||||
base_shadow = (shadow_valid && shadow_tag == cur_tag) ? shadow
|
||||
: 64'd0;
|
||||
if (cur_is_high)
|
||||
new_shadow = {ee_wr_data, base_shadow[31:0]};
|
||||
else
|
||||
new_shadow = {base_shadow[63:32], ee_wr_data};
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (!rst_n) begin
|
||||
shadow <= 64'd0;
|
||||
shadow_tag <= 13'd0;
|
||||
shadow_valid <= 1'b0;
|
||||
gs_reg_wr_en <= 1'b0;
|
||||
gs_reg_wr_addr <= 16'd0;
|
||||
gs_reg_wr_data <= 64'd0;
|
||||
end else begin
|
||||
gs_reg_wr_en <= 1'b0;
|
||||
if (ee_wr_en) begin
|
||||
// Contract: full-word writes only. Sub-word
|
||||
// (per-byte) merging into the 64-bit shadow is
|
||||
// out of scope at Ch111. Catch contract violations
|
||||
// loudly so a future driver pattern that needs
|
||||
// byte-lane writes is forced to widen the bridge.
|
||||
if (ee_wr_be !== 4'b1111) begin
|
||||
$error("ee_gs_priv_bridge_stub: ee_wr_be=%b — only 4'b1111 supported (full-word writes); offset=0x%04x data=0x%08h",
|
||||
ee_wr_be, ee_wr_addr, ee_wr_data);
|
||||
end
|
||||
shadow <= new_shadow;
|
||||
shadow_tag <= cur_tag;
|
||||
shadow_valid <= 1'b1;
|
||||
gs_reg_wr_en <= 1'b1;
|
||||
gs_reg_wr_addr <= {cur_tag, 3'b000};
|
||||
gs_reg_wr_data <= new_shadow;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule : ee_gs_priv_bridge_stub
|
||||
Reference in New Issue
Block a user