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>
260 lines
16 KiB
Systemverilog
260 lines
16 KiB
Systemverilog
// retroDE_ps2 — gs_swizzle_psmt8_stub (Ch131)
|
||
//
|
||
// Pure-combinational PSMT8 page/block/column swizzle: maps a
|
||
// pixel coordinate (x, y) within a framebuffer at (FBP, FBW) to
|
||
// its physical VRAM byte address using the real PS2 GS PSMT8
|
||
// layout. Mirrors Ch119's `gs_swizzle_psmct32_stub` and Ch125's
|
||
// `gs_swizzle_psmct16_stub` shape, but with PSMT8's wider page
|
||
// (128 px vs 64 px), 8-cols × 4-rows page block grid, and the
|
||
// 16×16 within-block column table.
|
||
//
|
||
// THIS MODULE IS NOT YET WIRED INTO gs_pcrtc_stub /
|
||
// gif_image_xfer_stub / gs_stub. Future chapters will wire it
|
||
// behind a `PSMT8_SWIZZLE`-style parameter gate, mirroring the
|
||
// PSMCT32 (Ch120/121/122) and PSMCT16 (Ch126/127/128) progressions.
|
||
// Default-off keeps the legacy linear PSMT8 TBs (Ch96, Ch97,
|
||
// Ch103, Ch105, Ch107, Ch117) on the linear path.
|
||
//
|
||
// SOURCE-TABLE PROVENANCE (per Codex's Ch125/Ch131 guidance):
|
||
// blockTable8 — pcsx2/GS/GSTables.cpp lines 53–59, master
|
||
// HEAD commit 3000e113e2b3a76357c08dfa80d3c747f40e2706
|
||
// (file blob SHA 3581209b8217378f473f9de22a9dbc8c45ca49b6).
|
||
// 4 rows × 8 cols, indexed [block_y][block_x].
|
||
// columnTable8 — pcsx2/GS/GSTables.cpp lines 111–145, same
|
||
// commit. 16 rows × 16 cols, indexed [yb][xb],
|
||
// values are byte-within-block (0..255).
|
||
// Cross-check — GSLocalMemory.h line 551 BlockNumber8 +
|
||
// pxOffset template at GSTables.cpp lines 247–258
|
||
// (blockSize=256, pageSize=8192, pageWidth=128).
|
||
// PSMT8 has pageShiftX=7, pageShiftY=6,
|
||
// blockShiftX=4, blockShiftY=4,
|
||
// m_bwPg = bw >> (pageShiftX - 6) = bw >> 1
|
||
// (so FBW must be even for PSMT8 — PCSX2 asserts
|
||
// `(bw & 1) == 0` at GSLocalMemory.h:553).
|
||
// PCSX2's `bp` is in 256-byte block-pointer
|
||
// units; in our FBP (2048-byte) units,
|
||
// bp = FBP * 8, so bp*256 = FBP*2048.
|
||
//
|
||
// NOTE on PCSX2 license: the PCSX2 project is GPL-3.0+. This
|
||
// stub re-expresses the same PSMT8 swizzle math in SystemVerilog
|
||
// as a hardware contract — the values in the blockTable8 /
|
||
// columnTable8 case statements come from PCSX2 source and
|
||
// represent the PS2 hardware layout itself (not PCSX2-original
|
||
// creative content). The retroDE_ps2 project authors should
|
||
// consider whether this provenance affects licensing for
|
||
// downstream consumers; from an engineering correctness
|
||
// standpoint, locking against the canonical source is the only
|
||
// way to be byte-accurate to real PS2 VRAM.
|
||
//
|
||
// Real PS2 PSMT8 layout:
|
||
// - VRAM is 4 MiB total, organized in 8 KiB pages.
|
||
// - Each page is 128×64 PSMT8 pixels (= 128*64*1 = 8192 bytes).
|
||
// 2× as many pixels per page as PSMCT16 (which has 64×64 px)
|
||
// and 4× as many as PSMCT32 (64×32 px) because each PSMT8
|
||
// pixel is only 1 byte vs CT16's 2 vs CT32's 4.
|
||
// - Each page is divided into a 8×4 grid of blocks (8 cols of
|
||
// blocks across, 4 rows down). Each block is 16×16 PSMT8
|
||
// pixels (= 16*16*1 = 256 bytes). 8×4 = 32 blocks/page.
|
||
// - Block ordering within a page follows blockTable8.
|
||
// - Within a block, byte placement follows columnTable8: a
|
||
// 16×16 → 256-entry permutation that organizes 4 internal
|
||
// columns (4 wide each) × 4 internal row-groups (4 tall each)
|
||
// with intra-group y-pair interleaving.
|
||
//
|
||
// Address formula (FBP in 2048-byte units; FBW in 64-pixel
|
||
// units; addr in bytes; FBW must be even):
|
||
// page_x = x / 128
|
||
// page_y = y / 64
|
||
// bw_pg = FBW / 2 // pages per row
|
||
// page_index = page_y * bw_pg + page_x
|
||
// page_base = FBP*2048 + page_index*8192
|
||
//
|
||
// block_x_in_page = (x % 128) / 16 // 0..7
|
||
// block_y_in_page = (y % 64) / 16 // 0..3
|
||
// block_idx = blockTable8[block_y_in_page][block_x_in_page]
|
||
// block_base = page_base + block_idx*256
|
||
//
|
||
// xb = x % 16
|
||
// yb = y % 16
|
||
// byte_idx = columnTable8[yb][xb] // 0..255
|
||
// addr = block_base + byte_idx
|
||
|
||
`timescale 1ns/1ps
|
||
|
||
module gs_swizzle_psmt8_stub
|
||
(
|
||
input logic [8:0] fbp, // FBP — frame base, in 2048-byte units
|
||
input logic [5:0] fbw, // FBW — frame width, in 64-pixel units (must be even)
|
||
input logic [11:0] x,
|
||
input logic [11:0] y,
|
||
output logic [31:0] addr
|
||
);
|
||
|
||
// --------------------------------------------------------------
|
||
// blockTable8 (verbatim from pcsx2/GS/GSTables.cpp lines 53–59).
|
||
// Indexed [block_y_in_page (0..3)][block_x_in_page (0..7)].
|
||
// by=0: { 0, 1, 4, 5,16,17,20,21}
|
||
// by=1: { 2, 3, 6, 7,18,19,22,23}
|
||
// by=2: { 8, 9,12,13,24,25,28,29}
|
||
// by=3: {10,11,14,15,26,27,30,31}
|
||
// --------------------------------------------------------------
|
||
function automatic logic [4:0] swizzle_psmt8(
|
||
input logic [1:0] by,
|
||
input logic [2:0] bx);
|
||
case ({by, bx})
|
||
// by=0
|
||
5'd0: return 5'd0; 5'd1: return 5'd1; 5'd2: return 5'd4; 5'd3: return 5'd5;
|
||
5'd4: return 5'd16; 5'd5: return 5'd17; 5'd6: return 5'd20; 5'd7: return 5'd21;
|
||
// by=1
|
||
5'd8: return 5'd2; 5'd9: return 5'd3; 5'd10: return 5'd6; 5'd11: return 5'd7;
|
||
5'd12: return 5'd18; 5'd13: return 5'd19; 5'd14: return 5'd22; 5'd15: return 5'd23;
|
||
// by=2
|
||
5'd16: return 5'd8; 5'd17: return 5'd9; 5'd18: return 5'd12; 5'd19: return 5'd13;
|
||
5'd20: return 5'd24; 5'd21: return 5'd25; 5'd22: return 5'd28; 5'd23: return 5'd29;
|
||
// by=3
|
||
5'd24: return 5'd10; 5'd25: return 5'd11; 5'd26: return 5'd14; 5'd27: return 5'd15;
|
||
5'd28: return 5'd26; 5'd29: return 5'd27; 5'd30: return 5'd30; default: return 5'd31;
|
||
endcase
|
||
endfunction
|
||
|
||
// --------------------------------------------------------------
|
||
// columnTable8 (verbatim from pcsx2/GS/GSTables.cpp lines 111–145).
|
||
// Indexed [yb (0..15)][xb (0..15)] → byte-within-block 0..255.
|
||
// yb=0: 0 4 16 20 32 36 48 52 2 6 18 22 34 38 50 54
|
||
// yb=1: 8 12 24 28 40 44 56 60 10 14 26 30 42 46 58 62
|
||
// yb=2: 33 37 49 53 1 5 17 21 35 39 51 55 3 7 19 23
|
||
// yb=3: 41 45 57 61 9 13 25 29 43 47 59 63 11 15 27 31
|
||
// yb=4: 96 100 112 116 64 68 80 84 98 102 114 118 66 70 82 86
|
||
// yb=5: 104 108 120 124 72 76 88 92 106 110 122 126 74 78 90 94
|
||
// yb=6: 65 69 81 85 97 101 113 117 67 71 83 87 99 103 115 119
|
||
// yb=7: 73 77 89 93 105 109 121 125 75 79 91 95 107 111 123 127
|
||
// yb=8: 128 132 144 148 160 164 176 180 130 134 146 150 162 166 178 182
|
||
// yb=9: 136 140 152 156 168 172 184 188 138 142 154 158 170 174 186 190
|
||
// yb=10: 161 165 177 181 129 133 145 149 163 167 179 183 131 135 147 151
|
||
// yb=11: 169 173 185 189 137 141 153 157 171 175 187 191 139 143 155 159
|
||
// yb=12: 224 228 240 244 192 196 208 212 226 230 242 246 194 198 210 214
|
||
// yb=13: 232 236 248 252 200 204 216 220 234 238 250 254 202 206 218 222
|
||
// yb=14: 193 197 209 213 225 229 241 245 195 199 211 215 227 231 243 247
|
||
// yb=15: 201 205 217 221 233 237 249 253 203 207 219 223 235 239 251 255
|
||
// --------------------------------------------------------------
|
||
function automatic logic [7:0] col_idx_psmt8(
|
||
input logic [3:0] yb,
|
||
input logic [3:0] xb);
|
||
case ({yb, xb})
|
||
// yb=0
|
||
8'd0: return 8'd0; 8'd1: return 8'd4; 8'd2: return 8'd16; 8'd3: return 8'd20;
|
||
8'd4: return 8'd32; 8'd5: return 8'd36; 8'd6: return 8'd48; 8'd7: return 8'd52;
|
||
8'd8: return 8'd2; 8'd9: return 8'd6; 8'd10: return 8'd18; 8'd11: return 8'd22;
|
||
8'd12: return 8'd34; 8'd13: return 8'd38; 8'd14: return 8'd50; 8'd15: return 8'd54;
|
||
// yb=1
|
||
8'd16: return 8'd8; 8'd17: return 8'd12; 8'd18: return 8'd24; 8'd19: return 8'd28;
|
||
8'd20: return 8'd40; 8'd21: return 8'd44; 8'd22: return 8'd56; 8'd23: return 8'd60;
|
||
8'd24: return 8'd10; 8'd25: return 8'd14; 8'd26: return 8'd26; 8'd27: return 8'd30;
|
||
8'd28: return 8'd42; 8'd29: return 8'd46; 8'd30: return 8'd58; 8'd31: return 8'd62;
|
||
// yb=2
|
||
8'd32: return 8'd33; 8'd33: return 8'd37; 8'd34: return 8'd49; 8'd35: return 8'd53;
|
||
8'd36: return 8'd1; 8'd37: return 8'd5; 8'd38: return 8'd17; 8'd39: return 8'd21;
|
||
8'd40: return 8'd35; 8'd41: return 8'd39; 8'd42: return 8'd51; 8'd43: return 8'd55;
|
||
8'd44: return 8'd3; 8'd45: return 8'd7; 8'd46: return 8'd19; 8'd47: return 8'd23;
|
||
// yb=3
|
||
8'd48: return 8'd41; 8'd49: return 8'd45; 8'd50: return 8'd57; 8'd51: return 8'd61;
|
||
8'd52: return 8'd9; 8'd53: return 8'd13; 8'd54: return 8'd25; 8'd55: return 8'd29;
|
||
8'd56: return 8'd43; 8'd57: return 8'd47; 8'd58: return 8'd59; 8'd59: return 8'd63;
|
||
8'd60: return 8'd11; 8'd61: return 8'd15; 8'd62: return 8'd27; 8'd63: return 8'd31;
|
||
// yb=4
|
||
8'd64: return 8'd96; 8'd65: return 8'd100; 8'd66: return 8'd112; 8'd67: return 8'd116;
|
||
8'd68: return 8'd64; 8'd69: return 8'd68; 8'd70: return 8'd80; 8'd71: return 8'd84;
|
||
8'd72: return 8'd98; 8'd73: return 8'd102; 8'd74: return 8'd114; 8'd75: return 8'd118;
|
||
8'd76: return 8'd66; 8'd77: return 8'd70; 8'd78: return 8'd82; 8'd79: return 8'd86;
|
||
// yb=5
|
||
8'd80: return 8'd104; 8'd81: return 8'd108; 8'd82: return 8'd120; 8'd83: return 8'd124;
|
||
8'd84: return 8'd72; 8'd85: return 8'd76; 8'd86: return 8'd88; 8'd87: return 8'd92;
|
||
8'd88: return 8'd106; 8'd89: return 8'd110; 8'd90: return 8'd122; 8'd91: return 8'd126;
|
||
8'd92: return 8'd74; 8'd93: return 8'd78; 8'd94: return 8'd90; 8'd95: return 8'd94;
|
||
// yb=6
|
||
8'd96: return 8'd65; 8'd97: return 8'd69; 8'd98: return 8'd81; 8'd99: return 8'd85;
|
||
8'd100: return 8'd97; 8'd101: return 8'd101; 8'd102: return 8'd113; 8'd103: return 8'd117;
|
||
8'd104: return 8'd67; 8'd105: return 8'd71; 8'd106: return 8'd83; 8'd107: return 8'd87;
|
||
8'd108: return 8'd99; 8'd109: return 8'd103; 8'd110: return 8'd115; 8'd111: return 8'd119;
|
||
// yb=7
|
||
8'd112: return 8'd73; 8'd113: return 8'd77; 8'd114: return 8'd89; 8'd115: return 8'd93;
|
||
8'd116: return 8'd105; 8'd117: return 8'd109; 8'd118: return 8'd121; 8'd119: return 8'd125;
|
||
8'd120: return 8'd75; 8'd121: return 8'd79; 8'd122: return 8'd91; 8'd123: return 8'd95;
|
||
8'd124: return 8'd107; 8'd125: return 8'd111; 8'd126: return 8'd123; 8'd127: return 8'd127;
|
||
// yb=8
|
||
8'd128: return 8'd128; 8'd129: return 8'd132; 8'd130: return 8'd144; 8'd131: return 8'd148;
|
||
8'd132: return 8'd160; 8'd133: return 8'd164; 8'd134: return 8'd176; 8'd135: return 8'd180;
|
||
8'd136: return 8'd130; 8'd137: return 8'd134; 8'd138: return 8'd146; 8'd139: return 8'd150;
|
||
8'd140: return 8'd162; 8'd141: return 8'd166; 8'd142: return 8'd178; 8'd143: return 8'd182;
|
||
// yb=9
|
||
8'd144: return 8'd136; 8'd145: return 8'd140; 8'd146: return 8'd152; 8'd147: return 8'd156;
|
||
8'd148: return 8'd168; 8'd149: return 8'd172; 8'd150: return 8'd184; 8'd151: return 8'd188;
|
||
8'd152: return 8'd138; 8'd153: return 8'd142; 8'd154: return 8'd154; 8'd155: return 8'd158;
|
||
8'd156: return 8'd170; 8'd157: return 8'd174; 8'd158: return 8'd186; 8'd159: return 8'd190;
|
||
// yb=10
|
||
8'd160: return 8'd161; 8'd161: return 8'd165; 8'd162: return 8'd177; 8'd163: return 8'd181;
|
||
8'd164: return 8'd129; 8'd165: return 8'd133; 8'd166: return 8'd145; 8'd167: return 8'd149;
|
||
8'd168: return 8'd163; 8'd169: return 8'd167; 8'd170: return 8'd179; 8'd171: return 8'd183;
|
||
8'd172: return 8'd131; 8'd173: return 8'd135; 8'd174: return 8'd147; 8'd175: return 8'd151;
|
||
// yb=11
|
||
8'd176: return 8'd169; 8'd177: return 8'd173; 8'd178: return 8'd185; 8'd179: return 8'd189;
|
||
8'd180: return 8'd137; 8'd181: return 8'd141; 8'd182: return 8'd153; 8'd183: return 8'd157;
|
||
8'd184: return 8'd171; 8'd185: return 8'd175; 8'd186: return 8'd187; 8'd187: return 8'd191;
|
||
8'd188: return 8'd139; 8'd189: return 8'd143; 8'd190: return 8'd155; 8'd191: return 8'd159;
|
||
// yb=12
|
||
8'd192: return 8'd224; 8'd193: return 8'd228; 8'd194: return 8'd240; 8'd195: return 8'd244;
|
||
8'd196: return 8'd192; 8'd197: return 8'd196; 8'd198: return 8'd208; 8'd199: return 8'd212;
|
||
8'd200: return 8'd226; 8'd201: return 8'd230; 8'd202: return 8'd242; 8'd203: return 8'd246;
|
||
8'd204: return 8'd194; 8'd205: return 8'd198; 8'd206: return 8'd210; 8'd207: return 8'd214;
|
||
// yb=13
|
||
8'd208: return 8'd232; 8'd209: return 8'd236; 8'd210: return 8'd248; 8'd211: return 8'd252;
|
||
8'd212: return 8'd200; 8'd213: return 8'd204; 8'd214: return 8'd216; 8'd215: return 8'd220;
|
||
8'd216: return 8'd234; 8'd217: return 8'd238; 8'd218: return 8'd250; 8'd219: return 8'd254;
|
||
8'd220: return 8'd202; 8'd221: return 8'd206; 8'd222: return 8'd218; 8'd223: return 8'd222;
|
||
// yb=14
|
||
8'd224: return 8'd193; 8'd225: return 8'd197; 8'd226: return 8'd209; 8'd227: return 8'd213;
|
||
8'd228: return 8'd225; 8'd229: return 8'd229; 8'd230: return 8'd241; 8'd231: return 8'd245;
|
||
8'd232: return 8'd195; 8'd233: return 8'd199; 8'd234: return 8'd211; 8'd235: return 8'd215;
|
||
8'd236: return 8'd227; 8'd237: return 8'd231; 8'd238: return 8'd243; 8'd239: return 8'd247;
|
||
// yb=15
|
||
8'd240: return 8'd201; 8'd241: return 8'd205; 8'd242: return 8'd217; 8'd243: return 8'd221;
|
||
8'd244: return 8'd233; 8'd245: return 8'd237; 8'd246: return 8'd249; 8'd247: return 8'd253;
|
||
8'd248: return 8'd203; 8'd249: return 8'd207; 8'd250: return 8'd219; 8'd251: return 8'd223;
|
||
8'd252: return 8'd235; 8'd253: return 8'd239; 8'd254: return 8'd251; default: return 8'd255;
|
||
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 [3:0] xb;
|
||
logic [3:0] yb;
|
||
logic [5:0] bw_pg;
|
||
|
||
assign page_x = x >> 7; // x / 128
|
||
assign page_y = y >> 6; // y / 64
|
||
assign by = y[5:4]; // (y % 64) / 16
|
||
assign bx = x[6:4]; // (x % 128) / 16
|
||
assign xb = x[3:0]; // x % 16
|
||
assign yb = y[3:0]; // y % 16
|
||
assign bw_pg = fbw >> 1; // FBW / 2 (FBW must be even)
|
||
|
||
logic [4:0] block_idx;
|
||
assign block_idx = swizzle_psmt8(by, bx);
|
||
|
||
logic [7:0] byte_idx;
|
||
assign byte_idx = col_idx_psmt8(yb, xb);
|
||
|
||
logic [31:0] page_base;
|
||
logic [31:0] block_base;
|
||
logic [31:0] page_index;
|
||
assign page_index = ({20'd0, page_y} * {26'd0, bw_pg}) + {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 addr = block_base + {24'd0, byte_idx}; // + byte_idx (1 byte/pixel)
|
||
|
||
endmodule : gs_swizzle_psmt8_stub
|