Files
retroDE_ps2/rtl/gif_gs/gs_swizzle_psmct32_stub.sv
thejayman77 ec82764bef 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>
2026-06-29 20:10:50 -04:00

149 lines
6.2 KiB
Systemverilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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