Files
retroDE_ps2/rtl/gif_gs/gs_swizzle_psmt4_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

340 lines
24 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_psmt4_stub (Ch137)
//
// Pure-combinational PSMT4 page/block/column swizzle: maps a
// pixel coordinate (x, y) within a framebuffer at (FBP, FBW) to
// its physical VRAM byte address AND high/low nibble select
// using the real PS2 GS PSMT4 layout. Mirrors Ch119's PSMCT32 +
// Ch125's PSMCT16 + Ch131's PSMT8 stubs but with PSMT4's wider
// 32-px-wide block, 32×16 within-block nibble layout, and the
// half-byte addressing distinction (each PSMT4 pixel is 4 bits;
// two PSMT4 pixels share a byte). Output `nibble_hi` selects
// which nibble of the byte at `addr` the pixel occupies.
//
// THIS MODULE IS NOT YET WIRED INTO gs_pcrtc_stub /
// gif_image_xfer_stub / gs_stub. Future chapters will wire it
// behind a `PSMT4_SWIZZLE`-style parameter gate, mirroring the
// PSMCT32 (Ch120/121/122), PSMCT16 (Ch126/127/128), and PSMT8
// (Ch132/133/134) progressions. Default-off keeps the legacy
// linear PSMT4 TBs (Ch103, Ch106, Ch118, Ch107 e2e palette path)
// on the linear path. The existing per-bit write_mask 0x0F/0xF0
// nibble RMW from Ch106/Ch118 will still apply on top of the
// swizzled byte address — the swizzle doesn't touch the nibble
// merge logic.
//
// SOURCE-TABLE PROVENANCE (per Codex's Ch125/Ch131/Ch137 guidance):
// blockTable4 — pcsx2/GS/GSTables.cpp lines 6169, master
// HEAD commit 3000e113e2b3a76357c08dfa80d3c747f40e2706
// (file blob SHA 3581209b8217378f473f9de22a9dbc8c45ca49b6).
// 8 rows × 4 cols, indexed [block_y][block_x].
// columnTable4 — pcsx2/GS/GSTables.cpp lines 147213, same
// commit. 16 rows × 32 cols, indexed [yb][xb],
// values are nibble-within-block (0..511).
// Cross-check — GSLocalMemory.h:558 BlockNumber4 + the
// pxOffset template at GSTables.cpp:247258
// (blockSize=512, pageSize=16384, pageWidth=128,
// note `blockSize` here is in NIBBLES; byte-
// grain pageSize = 8192 = 16384 nibbles / 2).
// PSMT4 has pageShiftX=7, pageShiftY=7,
// blockShiftX=5, blockShiftY=4,
// m_bwPg = bw >> (pageShiftX - 6) = bw >> 1
// (so FBW must be even for PSMT4 — PCSX2 asserts
// `(bw & 1) == 0` at GSLocalMemory.h:560).
// 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 PSMT4 swizzle math in SystemVerilog
// as a hardware contract — the values in the blockTable4 /
// columnTable4 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 PSMT4 layout:
// - VRAM is 4 MiB total, organized in 8 KiB pages.
// - Each page is 128×128 PSMT4 pixels (= 128*128/2 = 8192
// bytes — 2 PSMT4 pixels per byte). 4× as many pixels per
// page as PSMT8 (128×64) and same byte stride.
// - Each page is divided into a 4×8 grid of blocks (4 cols of
// blocks across, 8 rows down). Each block is 32×16 PSMT4
// pixels (= 32*16/2 = 256 bytes = 512 nibbles). 4×8 = 32
// blocks/page (same number of blocks as the other PSMs).
// - Block ordering within a page follows blockTable4 (which
// happens to be identical to PSMCT16's blockTable16 — both
// PSMs share the same block grid orientation).
// - Within a block, NIBBLE placement follows columnTable4: a
// 32×16 → 512-entry permutation that organizes the internal
// 8-wide × 4-tall sub-columns and 4-tall row-groups of the
// block.
//
// 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 / 128
// 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) / 32 // 0..3
// block_y_in_page = (y % 128) / 16 // 0..7
// block_idx = blockTable4[block_y_in_page][block_x_in_page]
// block_base = page_base + block_idx*256
//
// xb = x % 32
// yb = y % 16
// nibble_idx = columnTable4[yb][xb] // 0..511
// byte_in_block = nibble_idx >> 1 // 0..255
// addr = block_base + byte_in_block
// nibble_hi = nibble_idx[0] // 0=low nibble, 1=high
`timescale 1ns/1ps
module gs_swizzle_psmt4_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,
output logic nibble_hi
);
// --------------------------------------------------------------
// blockTable4 (verbatim from pcsx2/GS/GSTables.cpp lines 6169).
// Indexed [block_y_in_page (0..7)][block_x_in_page (0..3)].
// by=0: { 0, 2, 8, 10 }
// by=1: { 1, 3, 9, 11 }
// by=2: { 4, 6, 12, 14 }
// by=3: { 5, 7, 13, 15 }
// by=4: { 16, 18, 24, 26 }
// by=5: { 17, 19, 25, 27 }
// by=6: { 20, 22, 28, 30 }
// by=7: { 21, 23, 29, 31 }
// --------------------------------------------------------------
function automatic logic [4:0] swizzle_psmt4(
input logic [2:0] by,
input logic [1:0] bx);
case ({by, bx})
5'd0: return 5'd0; 5'd1: return 5'd2; 5'd2: return 5'd8; 5'd3: return 5'd10;
5'd4: return 5'd1; 5'd5: return 5'd3; 5'd6: return 5'd9; 5'd7: return 5'd11;
5'd8: return 5'd4; 5'd9: return 5'd6; 5'd10: return 5'd12; 5'd11: return 5'd14;
5'd12: return 5'd5; 5'd13: return 5'd7; 5'd14: return 5'd13; 5'd15: return 5'd15;
5'd16: return 5'd16; 5'd17: return 5'd18; 5'd18: return 5'd24; 5'd19: return 5'd26;
5'd20: return 5'd17; 5'd21: return 5'd19; 5'd22: return 5'd25; 5'd23: return 5'd27;
5'd24: return 5'd20; 5'd25: return 5'd22; 5'd26: return 5'd28; 5'd27: return 5'd30;
5'd28: return 5'd21; 5'd29: return 5'd23; 5'd30: return 5'd29; default: return 5'd31;
endcase
endfunction
// --------------------------------------------------------------
// columnTable4 (verbatim from pcsx2/GS/GSTables.cpp lines 147213).
// Indexed [yb (0..15)][xb (0..31)] → nibble-within-block 0..511.
// 512 entries total. Encoded as one large case statement on
// {yb, xb} (4+5 = 9 bits). Comments separate yb-block boundaries.
// --------------------------------------------------------------
function automatic logic [8:0] col_idx_psmt4(
input logic [3:0] yb,
input logic [4:0] xb);
case ({yb, xb})
// yb=0: 0 8 32 40 64 72 96 104 2 10 34 42 66 74 98 106
// 4 12 36 44 68 76 100 108 6 14 38 46 70 78 102 110
9'd0: return 9'd0; 9'd1: return 9'd8; 9'd2: return 9'd32; 9'd3: return 9'd40;
9'd4: return 9'd64; 9'd5: return 9'd72; 9'd6: return 9'd96; 9'd7: return 9'd104;
9'd8: return 9'd2; 9'd9: return 9'd10; 9'd10: return 9'd34; 9'd11: return 9'd42;
9'd12: return 9'd66; 9'd13: return 9'd74; 9'd14: return 9'd98; 9'd15: return 9'd106;
9'd16: return 9'd4; 9'd17: return 9'd12; 9'd18: return 9'd36; 9'd19: return 9'd44;
9'd20: return 9'd68; 9'd21: return 9'd76; 9'd22: return 9'd100; 9'd23: return 9'd108;
9'd24: return 9'd6; 9'd25: return 9'd14; 9'd26: return 9'd38; 9'd27: return 9'd46;
9'd28: return 9'd70; 9'd29: return 9'd78; 9'd30: return 9'd102; 9'd31: return 9'd110;
// yb=1: 16 24 48 56 80 88 112 120 18 26 50 58 82 90 114 122
// 20 28 52 60 84 92 116 124 22 30 54 62 86 94 118 126
9'd32: return 9'd16; 9'd33: return 9'd24; 9'd34: return 9'd48; 9'd35: return 9'd56;
9'd36: return 9'd80; 9'd37: return 9'd88; 9'd38: return 9'd112; 9'd39: return 9'd120;
9'd40: return 9'd18; 9'd41: return 9'd26; 9'd42: return 9'd50; 9'd43: return 9'd58;
9'd44: return 9'd82; 9'd45: return 9'd90; 9'd46: return 9'd114; 9'd47: return 9'd122;
9'd48: return 9'd20; 9'd49: return 9'd28; 9'd50: return 9'd52; 9'd51: return 9'd60;
9'd52: return 9'd84; 9'd53: return 9'd92; 9'd54: return 9'd116; 9'd55: return 9'd124;
9'd56: return 9'd22; 9'd57: return 9'd30; 9'd58: return 9'd54; 9'd59: return 9'd62;
9'd60: return 9'd86; 9'd61: return 9'd94; 9'd62: return 9'd118; 9'd63: return 9'd126;
// yb=2: 65 73 97 105 1 9 33 41 67 75 99 107 3 11 35 43
// 69 77 101 109 5 13 37 45 71 79 103 111 7 15 39 47
9'd64: return 9'd65; 9'd65: return 9'd73; 9'd66: return 9'd97; 9'd67: return 9'd105;
9'd68: return 9'd1; 9'd69: return 9'd9; 9'd70: return 9'd33; 9'd71: return 9'd41;
9'd72: return 9'd67; 9'd73: return 9'd75; 9'd74: return 9'd99; 9'd75: return 9'd107;
9'd76: return 9'd3; 9'd77: return 9'd11; 9'd78: return 9'd35; 9'd79: return 9'd43;
9'd80: return 9'd69; 9'd81: return 9'd77; 9'd82: return 9'd101; 9'd83: return 9'd109;
9'd84: return 9'd5; 9'd85: return 9'd13; 9'd86: return 9'd37; 9'd87: return 9'd45;
9'd88: return 9'd71; 9'd89: return 9'd79; 9'd90: return 9'd103; 9'd91: return 9'd111;
9'd92: return 9'd7; 9'd93: return 9'd15; 9'd94: return 9'd39; 9'd95: return 9'd47;
// yb=3: 81 89 113 121 17 25 49 57 83 91 115 123 19 27 51 59
// 85 93 117 125 21 29 53 61 87 95 119 127 23 31 55 63
9'd96: return 9'd81; 9'd97: return 9'd89; 9'd98: return 9'd113; 9'd99: return 9'd121;
9'd100: return 9'd17; 9'd101: return 9'd25; 9'd102: return 9'd49; 9'd103: return 9'd57;
9'd104: return 9'd83; 9'd105: return 9'd91; 9'd106: return 9'd115; 9'd107: return 9'd123;
9'd108: return 9'd19; 9'd109: return 9'd27; 9'd110: return 9'd51; 9'd111: return 9'd59;
9'd112: return 9'd85; 9'd113: return 9'd93; 9'd114: return 9'd117; 9'd115: return 9'd125;
9'd116: return 9'd21; 9'd117: return 9'd29; 9'd118: return 9'd53; 9'd119: return 9'd61;
9'd120: return 9'd87; 9'd121: return 9'd95; 9'd122: return 9'd119; 9'd123: return 9'd127;
9'd124: return 9'd23; 9'd125: return 9'd31; 9'd126: return 9'd55; 9'd127: return 9'd63;
// yb=4: 192 200 224 232 128 136 160 168 194 202 226 234 130 138 162 170
// 196 204 228 236 132 140 164 172 198 206 230 238 134 142 166 174
9'd128: return 9'd192; 9'd129: return 9'd200; 9'd130: return 9'd224; 9'd131: return 9'd232;
9'd132: return 9'd128; 9'd133: return 9'd136; 9'd134: return 9'd160; 9'd135: return 9'd168;
9'd136: return 9'd194; 9'd137: return 9'd202; 9'd138: return 9'd226; 9'd139: return 9'd234;
9'd140: return 9'd130; 9'd141: return 9'd138; 9'd142: return 9'd162; 9'd143: return 9'd170;
9'd144: return 9'd196; 9'd145: return 9'd204; 9'd146: return 9'd228; 9'd147: return 9'd236;
9'd148: return 9'd132; 9'd149: return 9'd140; 9'd150: return 9'd164; 9'd151: return 9'd172;
9'd152: return 9'd198; 9'd153: return 9'd206; 9'd154: return 9'd230; 9'd155: return 9'd238;
9'd156: return 9'd134; 9'd157: return 9'd142; 9'd158: return 9'd166; 9'd159: return 9'd174;
// yb=5: 208 216 240 248 144 152 176 184 210 218 242 250 146 154 178 186
// 212 220 244 252 148 156 180 188 214 222 246 254 150 158 182 190
9'd160: return 9'd208; 9'd161: return 9'd216; 9'd162: return 9'd240; 9'd163: return 9'd248;
9'd164: return 9'd144; 9'd165: return 9'd152; 9'd166: return 9'd176; 9'd167: return 9'd184;
9'd168: return 9'd210; 9'd169: return 9'd218; 9'd170: return 9'd242; 9'd171: return 9'd250;
9'd172: return 9'd146; 9'd173: return 9'd154; 9'd174: return 9'd178; 9'd175: return 9'd186;
9'd176: return 9'd212; 9'd177: return 9'd220; 9'd178: return 9'd244; 9'd179: return 9'd252;
9'd180: return 9'd148; 9'd181: return 9'd156; 9'd182: return 9'd180; 9'd183: return 9'd188;
9'd184: return 9'd214; 9'd185: return 9'd222; 9'd186: return 9'd246; 9'd187: return 9'd254;
9'd188: return 9'd150; 9'd189: return 9'd158; 9'd190: return 9'd182; 9'd191: return 9'd190;
// yb=6: 129 137 161 169 193 201 225 233 131 139 163 171 195 203 227 235
// 133 141 165 173 197 205 229 237 135 143 167 175 199 207 231 239
9'd192: return 9'd129; 9'd193: return 9'd137; 9'd194: return 9'd161; 9'd195: return 9'd169;
9'd196: return 9'd193; 9'd197: return 9'd201; 9'd198: return 9'd225; 9'd199: return 9'd233;
9'd200: return 9'd131; 9'd201: return 9'd139; 9'd202: return 9'd163; 9'd203: return 9'd171;
9'd204: return 9'd195; 9'd205: return 9'd203; 9'd206: return 9'd227; 9'd207: return 9'd235;
9'd208: return 9'd133; 9'd209: return 9'd141; 9'd210: return 9'd165; 9'd211: return 9'd173;
9'd212: return 9'd197; 9'd213: return 9'd205; 9'd214: return 9'd229; 9'd215: return 9'd237;
9'd216: return 9'd135; 9'd217: return 9'd143; 9'd218: return 9'd167; 9'd219: return 9'd175;
9'd220: return 9'd199; 9'd221: return 9'd207; 9'd222: return 9'd231; 9'd223: return 9'd239;
// yb=7: 145 153 177 185 209 217 241 249 147 155 179 187 211 219 243 251
// 149 157 181 189 213 221 245 253 151 159 183 191 215 223 247 255
9'd224: return 9'd145; 9'd225: return 9'd153; 9'd226: return 9'd177; 9'd227: return 9'd185;
9'd228: return 9'd209; 9'd229: return 9'd217; 9'd230: return 9'd241; 9'd231: return 9'd249;
9'd232: return 9'd147; 9'd233: return 9'd155; 9'd234: return 9'd179; 9'd235: return 9'd187;
9'd236: return 9'd211; 9'd237: return 9'd219; 9'd238: return 9'd243; 9'd239: return 9'd251;
9'd240: return 9'd149; 9'd241: return 9'd157; 9'd242: return 9'd181; 9'd243: return 9'd189;
9'd244: return 9'd213; 9'd245: return 9'd221; 9'd246: return 9'd245; 9'd247: return 9'd253;
9'd248: return 9'd151; 9'd249: return 9'd159; 9'd250: return 9'd183; 9'd251: return 9'd191;
9'd252: return 9'd215; 9'd253: return 9'd223; 9'd254: return 9'd247; 9'd255: return 9'd255;
// yb=8: 256 264 288 296 320 328 352 360 258 266 290 298 322 330 354 362
// 260 268 292 300 324 332 356 364 262 270 294 302 326 334 358 366
9'd256: return 9'd256; 9'd257: return 9'd264; 9'd258: return 9'd288; 9'd259: return 9'd296;
9'd260: return 9'd320; 9'd261: return 9'd328; 9'd262: return 9'd352; 9'd263: return 9'd360;
9'd264: return 9'd258; 9'd265: return 9'd266; 9'd266: return 9'd290; 9'd267: return 9'd298;
9'd268: return 9'd322; 9'd269: return 9'd330; 9'd270: return 9'd354; 9'd271: return 9'd362;
9'd272: return 9'd260; 9'd273: return 9'd268; 9'd274: return 9'd292; 9'd275: return 9'd300;
9'd276: return 9'd324; 9'd277: return 9'd332; 9'd278: return 9'd356; 9'd279: return 9'd364;
9'd280: return 9'd262; 9'd281: return 9'd270; 9'd282: return 9'd294; 9'd283: return 9'd302;
9'd284: return 9'd326; 9'd285: return 9'd334; 9'd286: return 9'd358; 9'd287: return 9'd366;
// yb=9: 272 280 304 312 336 344 368 376 274 282 306 314 338 346 370 378
// 276 284 308 316 340 348 372 380 278 286 310 318 342 350 374 382
9'd288: return 9'd272; 9'd289: return 9'd280; 9'd290: return 9'd304; 9'd291: return 9'd312;
9'd292: return 9'd336; 9'd293: return 9'd344; 9'd294: return 9'd368; 9'd295: return 9'd376;
9'd296: return 9'd274; 9'd297: return 9'd282; 9'd298: return 9'd306; 9'd299: return 9'd314;
9'd300: return 9'd338; 9'd301: return 9'd346; 9'd302: return 9'd370; 9'd303: return 9'd378;
9'd304: return 9'd276; 9'd305: return 9'd284; 9'd306: return 9'd308; 9'd307: return 9'd316;
9'd308: return 9'd340; 9'd309: return 9'd348; 9'd310: return 9'd372; 9'd311: return 9'd380;
9'd312: return 9'd278; 9'd313: return 9'd286; 9'd314: return 9'd310; 9'd315: return 9'd318;
9'd316: return 9'd342; 9'd317: return 9'd350; 9'd318: return 9'd374; 9'd319: return 9'd382;
// yb=10: 321 329 353 361 257 265 289 297 323 331 355 363 259 267 291 299
// 325 333 357 365 261 269 293 301 327 335 359 367 263 271 295 303
9'd320: return 9'd321; 9'd321: return 9'd329; 9'd322: return 9'd353; 9'd323: return 9'd361;
9'd324: return 9'd257; 9'd325: return 9'd265; 9'd326: return 9'd289; 9'd327: return 9'd297;
9'd328: return 9'd323; 9'd329: return 9'd331; 9'd330: return 9'd355; 9'd331: return 9'd363;
9'd332: return 9'd259; 9'd333: return 9'd267; 9'd334: return 9'd291; 9'd335: return 9'd299;
9'd336: return 9'd325; 9'd337: return 9'd333; 9'd338: return 9'd357; 9'd339: return 9'd365;
9'd340: return 9'd261; 9'd341: return 9'd269; 9'd342: return 9'd293; 9'd343: return 9'd301;
9'd344: return 9'd327; 9'd345: return 9'd335; 9'd346: return 9'd359; 9'd347: return 9'd367;
9'd348: return 9'd263; 9'd349: return 9'd271; 9'd350: return 9'd295; 9'd351: return 9'd303;
// yb=11: 337 345 369 377 273 281 305 313 339 347 371 379 275 283 307 315
// 341 349 373 381 277 285 309 317 343 351 375 383 279 287 311 319
9'd352: return 9'd337; 9'd353: return 9'd345; 9'd354: return 9'd369; 9'd355: return 9'd377;
9'd356: return 9'd273; 9'd357: return 9'd281; 9'd358: return 9'd305; 9'd359: return 9'd313;
9'd360: return 9'd339; 9'd361: return 9'd347; 9'd362: return 9'd371; 9'd363: return 9'd379;
9'd364: return 9'd275; 9'd365: return 9'd283; 9'd366: return 9'd307; 9'd367: return 9'd315;
9'd368: return 9'd341; 9'd369: return 9'd349; 9'd370: return 9'd373; 9'd371: return 9'd381;
9'd372: return 9'd277; 9'd373: return 9'd285; 9'd374: return 9'd309; 9'd375: return 9'd317;
9'd376: return 9'd343; 9'd377: return 9'd351; 9'd378: return 9'd375; 9'd379: return 9'd383;
9'd380: return 9'd279; 9'd381: return 9'd287; 9'd382: return 9'd311; 9'd383: return 9'd319;
// yb=12: 448 456 480 488 384 392 416 424 450 458 482 490 386 394 418 426
// 452 460 484 492 388 396 420 428 454 462 486 494 390 398 422 430
9'd384: return 9'd448; 9'd385: return 9'd456; 9'd386: return 9'd480; 9'd387: return 9'd488;
9'd388: return 9'd384; 9'd389: return 9'd392; 9'd390: return 9'd416; 9'd391: return 9'd424;
9'd392: return 9'd450; 9'd393: return 9'd458; 9'd394: return 9'd482; 9'd395: return 9'd490;
9'd396: return 9'd386; 9'd397: return 9'd394; 9'd398: return 9'd418; 9'd399: return 9'd426;
9'd400: return 9'd452; 9'd401: return 9'd460; 9'd402: return 9'd484; 9'd403: return 9'd492;
9'd404: return 9'd388; 9'd405: return 9'd396; 9'd406: return 9'd420; 9'd407: return 9'd428;
9'd408: return 9'd454; 9'd409: return 9'd462; 9'd410: return 9'd486; 9'd411: return 9'd494;
9'd412: return 9'd390; 9'd413: return 9'd398; 9'd414: return 9'd422; 9'd415: return 9'd430;
// yb=13: 464 472 496 504 400 408 432 440 466 474 498 506 402 410 434 442
// 468 476 500 508 404 412 436 444 470 478 502 510 406 414 438 446
9'd416: return 9'd464; 9'd417: return 9'd472; 9'd418: return 9'd496; 9'd419: return 9'd504;
9'd420: return 9'd400; 9'd421: return 9'd408; 9'd422: return 9'd432; 9'd423: return 9'd440;
9'd424: return 9'd466; 9'd425: return 9'd474; 9'd426: return 9'd498; 9'd427: return 9'd506;
9'd428: return 9'd402; 9'd429: return 9'd410; 9'd430: return 9'd434; 9'd431: return 9'd442;
9'd432: return 9'd468; 9'd433: return 9'd476; 9'd434: return 9'd500; 9'd435: return 9'd508;
9'd436: return 9'd404; 9'd437: return 9'd412; 9'd438: return 9'd436; 9'd439: return 9'd444;
9'd440: return 9'd470; 9'd441: return 9'd478; 9'd442: return 9'd502; 9'd443: return 9'd510;
9'd444: return 9'd406; 9'd445: return 9'd414; 9'd446: return 9'd438; 9'd447: return 9'd446;
// yb=14: 385 393 417 425 449 457 481 489 387 395 419 427 451 459 483 491
// 389 397 421 429 453 461 485 493 391 399 423 431 455 463 487 495
9'd448: return 9'd385; 9'd449: return 9'd393; 9'd450: return 9'd417; 9'd451: return 9'd425;
9'd452: return 9'd449; 9'd453: return 9'd457; 9'd454: return 9'd481; 9'd455: return 9'd489;
9'd456: return 9'd387; 9'd457: return 9'd395; 9'd458: return 9'd419; 9'd459: return 9'd427;
9'd460: return 9'd451; 9'd461: return 9'd459; 9'd462: return 9'd483; 9'd463: return 9'd491;
9'd464: return 9'd389; 9'd465: return 9'd397; 9'd466: return 9'd421; 9'd467: return 9'd429;
9'd468: return 9'd453; 9'd469: return 9'd461; 9'd470: return 9'd485; 9'd471: return 9'd493;
9'd472: return 9'd391; 9'd473: return 9'd399; 9'd474: return 9'd423; 9'd475: return 9'd431;
9'd476: return 9'd455; 9'd477: return 9'd463; 9'd478: return 9'd487; 9'd479: return 9'd495;
// yb=15: 401 409 433 441 465 473 497 505 403 411 435 443 467 475 499 507
// 405 413 437 445 469 477 501 509 407 415 439 447 471 479 503 511
9'd480: return 9'd401; 9'd481: return 9'd409; 9'd482: return 9'd433; 9'd483: return 9'd441;
9'd484: return 9'd465; 9'd485: return 9'd473; 9'd486: return 9'd497; 9'd487: return 9'd505;
9'd488: return 9'd403; 9'd489: return 9'd411; 9'd490: return 9'd435; 9'd491: return 9'd443;
9'd492: return 9'd467; 9'd493: return 9'd475; 9'd494: return 9'd499; 9'd495: return 9'd507;
9'd496: return 9'd405; 9'd497: return 9'd413; 9'd498: return 9'd437; 9'd499: return 9'd445;
9'd500: return 9'd469; 9'd501: return 9'd477; 9'd502: return 9'd501; 9'd503: return 9'd509;
9'd504: return 9'd407; 9'd505: return 9'd415; 9'd506: return 9'd439; 9'd507: return 9'd447;
9'd508: return 9'd471; 9'd509: return 9'd479; 9'd510: return 9'd503; default: return 9'd511;
endcase
endfunction
// Decompose pixel coord into page / block / pixel-in-block.
logic [11:0] page_x;
logic [11:0] page_y;
logic [2:0] by;
logic [1:0] bx;
logic [4:0] xb;
logic [3:0] yb;
logic [5:0] bw_pg;
assign page_x = x >> 7; // x / 128
assign page_y = y >> 7; // y / 128
assign by = y[6:4]; // (y % 128) / 16
assign bx = x[6:5]; // (x % 128) / 32
assign xb = x[4:0]; // x % 32
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_psmt4(by, bx);
logic [8:0] nibble_idx;
assign nibble_idx = col_idx_psmt4(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 + {23'd0, nibble_idx[8:1]}; // byte_in_block = nibble_idx >> 1
assign nibble_hi = nibble_idx[0]; // 0=low, 1=high
endmodule : gs_swizzle_psmt4_stub