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,148 @@
|
||||
// retroDE_ps2 — gs_swizzle_psmct32_stub (Ch119)
|
||||
//
|
||||
// Pure-combinational PSMCT32 page/block swizzle: maps a pixel
|
||||
// coordinate (x, y) within a framebuffer at (FBP, FBW) to its
|
||||
// physical VRAM byte address using the real PS2 GS layout.
|
||||
//
|
||||
// THIS MODULE DOES NOT YET REPLACE THE LINEAR ADDRESSING in
|
||||
// gs_stub / gs_pcrtc_stub / gif_image_xfer_stub. It is the
|
||||
// math primitive that future chapters will wire into the
|
||||
// existing address paths to swap "linear FBW*64*y + x*4"
|
||||
// for the real GS swizzled addressing. Ch119 establishes the
|
||||
// math, locks it against the canonical PCSX2 PSMCT32 block
|
||||
// table with a focused TB, and leaves integration to follow-on
|
||||
// chapters so the existing 109 TBs stay on the linear path.
|
||||
//
|
||||
// Real PS2 PSMCT32 layout (per PCSX2 GS source):
|
||||
// - VRAM is 4 MiB total, organized in 8 KiB pages.
|
||||
// - Each page is 64×32 PSMCT32 pixels (= 64×32×4 = 8192 bytes).
|
||||
// - Each page is divided into a 4×8 grid of blocks (4 rows of
|
||||
// blocks, 8 cols of blocks per row), each block 8×8 pixels
|
||||
// (= 256 bytes). 4×8 = 32 blocks/page.
|
||||
// - Block ordering within a page is NOT row-major; it follows
|
||||
// the PSMCT32 swizzle table below (a Z-order-like permutation
|
||||
// of (block_x, block_y) that PCSX2's GSLocalMemoryFunctions.cpp
|
||||
// defines verbatim).
|
||||
// - Within a block, PSMCT32 is row-major: pixel (xb, yb) maps
|
||||
// to byte_offset_in_block = yb*32 + xb*4 (no further swizzle
|
||||
// for PSMCT32 — other PSMs have intra-block reorderings).
|
||||
//
|
||||
// Address formula (linear in pages, swizzled in blocks within a
|
||||
// page, linear within a block):
|
||||
// pages_per_fbrow = FBW // FBW is in 64-px units; PSMCT32 page is 64 px wide
|
||||
// page_x = x / 64
|
||||
// page_y = y / 32
|
||||
// page_index = page_y * pages_per_fbrow + page_x
|
||||
// page_base = FBP*2048 + page_index*8192
|
||||
//
|
||||
// block_x_in_page = (x % 64) / 8 // 0..7
|
||||
// block_y_in_page = (y % 32) / 8 // 0..3
|
||||
// block_idx = SWIZZLE[block_y_in_page][block_x_in_page]
|
||||
// block_base = page_base + block_idx*256
|
||||
//
|
||||
// xb = x % 8
|
||||
// yb = y % 8
|
||||
// byte_in_block = yb*32 + xb*4
|
||||
//
|
||||
// addr = block_base + byte_in_block
|
||||
//
|
||||
// FBP is a 9-bit field at 2048-byte granularity, so FBP*2048
|
||||
// can land at any 2048-byte boundary in VRAM — including mid-
|
||||
// page boundaries (FBP[1:0] != 0). The math here treats FBP*2048
|
||||
// as the literal byte base, with the swizzled page/block/pixel
|
||||
// offset added on top, which matches real-PS2 behavior. Page-
|
||||
// aligned FBP (FBP[1:0]==0) is the common case in our demo, but
|
||||
// the address formula is bit-correct for any 2048-byte-aligned
|
||||
// FBP, and the focused TB exercises non-page-aligned FBP=1,2,3
|
||||
// to lock that.
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module gs_swizzle_psmct32_stub
|
||||
(
|
||||
// Framebuffer config (matches FRAME_1 register fields).
|
||||
input logic [8:0] fbp, // FBP — frame base, in 2048-byte units
|
||||
input logic [5:0] fbw, // FBW — frame width, in 64-pixel units
|
||||
// Pixel coordinate within the framebuffer.
|
||||
input logic [11:0] x,
|
||||
input logic [11:0] y,
|
||||
// Resulting VRAM byte address.
|
||||
output logic [31:0] addr
|
||||
);
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Block swizzle table for PSMCT32 (PCSX2 GSLocalMemoryFunctions.cpp,
|
||||
// psmt32 block order). Indexed [block_y_in_page][block_x_in_page]
|
||||
// with block_x ∈ 0..7 and block_y ∈ 0..3; value is the linear
|
||||
// block index within the page (0..31).
|
||||
// --------------------------------------------------------------
|
||||
function automatic logic [4:0] swizzle_psmct32(
|
||||
input logic [1:0] by, // block_y_in_page (0..3)
|
||||
input logic [2:0] bx); // block_x_in_page (0..7)
|
||||
case ({by, bx})
|
||||
5'h00: return 5'd0; // (0,0)
|
||||
5'h01: return 5'd1; // (0,1)
|
||||
5'h02: return 5'd4; // (0,2)
|
||||
5'h03: return 5'd5; // (0,3)
|
||||
5'h04: return 5'd16; // (0,4)
|
||||
5'h05: return 5'd17; // (0,5)
|
||||
5'h06: return 5'd20; // (0,6)
|
||||
5'h07: return 5'd21; // (0,7)
|
||||
5'h08: return 5'd2; // (1,0)
|
||||
5'h09: return 5'd3; // (1,1)
|
||||
5'h0A: return 5'd6; // (1,2)
|
||||
5'h0B: return 5'd7; // (1,3)
|
||||
5'h0C: return 5'd18; // (1,4)
|
||||
5'h0D: return 5'd19; // (1,5)
|
||||
5'h0E: return 5'd22; // (1,6)
|
||||
5'h0F: return 5'd23; // (1,7)
|
||||
5'h10: return 5'd8; // (2,0)
|
||||
5'h11: return 5'd9; // (2,1)
|
||||
5'h12: return 5'd12; // (2,2)
|
||||
5'h13: return 5'd13; // (2,3)
|
||||
5'h14: return 5'd24; // (2,4)
|
||||
5'h15: return 5'd25; // (2,5)
|
||||
5'h16: return 5'd28; // (2,6)
|
||||
5'h17: return 5'd29; // (2,7)
|
||||
5'h18: return 5'd10; // (3,0)
|
||||
5'h19: return 5'd11; // (3,1)
|
||||
5'h1A: return 5'd14; // (3,2)
|
||||
5'h1B: return 5'd15; // (3,3)
|
||||
5'h1C: return 5'd26; // (3,4)
|
||||
5'h1D: return 5'd27; // (3,5)
|
||||
5'h1E: return 5'd30; // (3,6)
|
||||
default: return 5'd31; // (3,7)
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
// Decompose pixel coord into page / block / pixel-in-block.
|
||||
logic [11:0] page_x;
|
||||
logic [11:0] page_y;
|
||||
logic [1:0] by;
|
||||
logic [2:0] bx;
|
||||
logic [2:0] xb;
|
||||
logic [2:0] yb;
|
||||
|
||||
assign page_x = x >> 6; // x / 64
|
||||
assign page_y = y >> 5; // y / 32
|
||||
assign by = y[4:3]; // (y % 32) / 8
|
||||
assign bx = x[5:3]; // (x % 64) / 8
|
||||
assign xb = x[2:0]; // x % 8
|
||||
assign yb = y[2:0]; // y % 8
|
||||
|
||||
logic [4:0] block_idx;
|
||||
assign block_idx = swizzle_psmct32(by, bx);
|
||||
|
||||
// Address composition.
|
||||
logic [31:0] page_base;
|
||||
logic [31:0] block_base;
|
||||
logic [31:0] byte_in_block;
|
||||
logic [31:0] page_index;
|
||||
assign page_index = ({20'd0, page_y} * {26'd0, fbw}) + {20'd0, page_x};
|
||||
assign page_base = ({23'd0, fbp} << 11) + (page_index << 13); // FBP*2048 + page_index*8192
|
||||
assign block_base = page_base + ({27'd0, block_idx} << 8); // + block_idx*256
|
||||
assign byte_in_block = ({29'd0, yb} << 5) + ({29'd0, xb} << 2); // yb*32 + xb*4
|
||||
|
||||
assign addr = block_base + byte_in_block;
|
||||
|
||||
endmodule : gs_swizzle_psmct32_stub
|
||||
Reference in New Issue
Block a user