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>
149 lines
6.2 KiB
Systemverilog
149 lines
6.2 KiB
Systemverilog
// 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
|