Files
retroDE_ps2/sim/tb/gif_gs/tb_gs_swizzle_psmt8.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

752 lines
44 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 — tb_gs_swizzle_psmt8 (Ch131)
//
// Source-locked focused contract for `gs_swizzle_psmt8_stub`.
// Mirrors the Ch125 PSMCT16 TB shape — 5-phase verification with
// independent source-table locks for both the column and block
// tables, plus a full-page bijectivity sweep over 128×64 = 8192
// pixels. Per Codex's Ch131 framing: PSMT8 is the third PSM
// swizzle math primitive (after Ch119 PSMCT32 + Ch125 PSMCT16);
// byte-aligned (1 byte/pixel) so simpler than PSMT4's nibble
// packing, but with a wider 128 px page (vs 64 px for direct-
// color PSMs) and the new FBW>>1 page-stride constant.
//
// SOURCE-TABLE PROVENANCE (per Codex's Ch125/Ch131 guidance):
// - PCSX2 master HEAD commit
// 3000e113e2b3a76357c08dfa80d3c747f40e2706 (current at TB
// creation).
// - File pcsx2/GS/GSTables.cpp, blob SHA
// 3581209b8217378f473f9de22a9dbc8c45ca49b6.
// - blockTable8 at lines 5359 (4 rows × 8 cols, indexed
// [block_y_in_page][block_x_in_page]).
// - columnTable8 at lines 111145 (16 rows × 16 cols, indexed
// [yb][xb], values are byte-within-block 0..255).
// - Cross-check: GSLocalMemory.h:551 BlockNumber8 +
// pxOffset template at GSTables.cpp:247258 (blockSize=256,
// pageSize=8192, pageWidth=128) + GSLocalMemory.h:553 asserts
// `(bw & 1) == 0` for PSMT8 (FBW must be even).
//
// Phase layout:
// 1 — spot-checks at hand-computed corners (origin, intra-
// block, block-grid origins, page boundaries, FBP shift).
// 2a — INDEPENDENT column-table source lock: 256 hard-coded
// check() calls (one per (yb, xb) inside block (0,0)) where
// the expected byte index is taken VERBATIM from PCSX2
// columnTable8 with `1 * <literal>` arithmetic, NOT derived
// from the in-TB ref_col_idx8 function. Catches any case
// where DUT and ref_col_idx8 happen to share the same
// miscopy (the same kind of trap Ch125 added Phase 2a for
// with PSMCT16's column table).
// 2b — within-block 16×16 walk via the in-TB ref_col_idx8.
// Self-check that ref_col_idx8 agrees with the DUT (the
// bijectivity sweep below relies on ref_col_idx8 being
// correct).
// 3 — INDEPENDENT block-table source lock: 32 hard-coded
// check() calls (one per block in page 0). Expected block
// index taken VERBATIM from PCSX2 blockTable8.
// 4 — block-swizzle walk via the in-TB ref_block_idx8.
// 5 — bijectivity sweep over the 128×64 page (8192 pixels).
// Every pixel must hit a unique byte address in [0, 8192)
// and agree with ref_addr8.
// + multi-page sanity at FBW=4 (bw_pg=2) crossing both page-x
// and page-y, plus non-page-aligned FBP coverage.
//
// Standalone — no other modules in the dependency chain.
`timescale 1ns/1ps
module tb_gs_swizzle_psmt8;
// -----------------------------------------------------------
// DUT
// -----------------------------------------------------------
logic [8:0] fbp;
logic [5:0] fbw;
logic [11:0] x;
logic [11:0] y;
logic [31:0] addr;
gs_swizzle_psmt8_stub u_dut (
.fbp(fbp), .fbw(fbw), .x(x), .y(y), .addr(addr)
);
int errors;
initial errors = 0;
// -----------------------------------------------------------
// Helpers.
// -----------------------------------------------------------
task automatic compute(
input logic [8:0] fbp_v,
input logic [5:0] fbw_v,
input logic [11:0] x_v,
input logic [11:0] y_v,
output logic [31:0] addr_o);
fbp = fbp_v; fbw = fbw_v; x = x_v; y = y_v;
#1;
addr_o = addr;
endtask
task automatic check(
input logic [8:0] fbp_v,
input logic [5:0] fbw_v,
input logic [11:0] x_v,
input logic [11:0] y_v,
input logic [31:0] expected,
input string tag);
logic [31:0] got;
compute(fbp_v, fbw_v, x_v, y_v, got);
if (got !== expected) begin
$error("[%s] fbp=%0d fbw=%0d (x=%0d,y=%0d) got 0x%08x expected 0x%08x",
tag, fbp_v, fbw_v, x_v, y_v, got, expected);
errors = errors + 1;
end
endtask
// -----------------------------------------------------------
// Reference tables (verbatim from pcsx2/GS/GSTables.cpp).
// -----------------------------------------------------------
function automatic int ref_block_idx8(input int by, input int bx);
// _blockTable8[4][8]:
// 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}
case ({by[1:0], bx[2:0]})
// by=0
5'd0: return 0; 5'd1: return 1; 5'd2: return 4; 5'd3: return 5;
5'd4: return 16; 5'd5: return 17; 5'd6: return 20; 5'd7: return 21;
// by=1
5'd8: return 2; 5'd9: return 3; 5'd10: return 6; 5'd11: return 7;
5'd12: return 18; 5'd13: return 19; 5'd14: return 22; 5'd15: return 23;
// by=2
5'd16: return 8; 5'd17: return 9; 5'd18: return 12; 5'd19: return 13;
5'd20: return 24; 5'd21: return 25; 5'd22: return 28; 5'd23: return 29;
// by=3
5'd24: return 10; 5'd25: return 11; 5'd26: return 14; 5'd27: return 15;
5'd28: return 26; 5'd29: return 27; 5'd30: return 30; default: return 31;
endcase
endfunction
function automatic int ref_col_idx8(input int yb, input int xb);
// columnTable8[16][16] (PCSX2 master GSTables.cpp lines 111145):
// 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
case ({yb[3:0], xb[3:0]})
// yb=0
8'd0: return 0; 8'd1: return 4; 8'd2: return 16; 8'd3: return 20;
8'd4: return 32; 8'd5: return 36; 8'd6: return 48; 8'd7: return 52;
8'd8: return 2; 8'd9: return 6; 8'd10: return 18; 8'd11: return 22;
8'd12: return 34; 8'd13: return 38; 8'd14: return 50; 8'd15: return 54;
// yb=1
8'd16: return 8; 8'd17: return 12; 8'd18: return 24; 8'd19: return 28;
8'd20: return 40; 8'd21: return 44; 8'd22: return 56; 8'd23: return 60;
8'd24: return 10; 8'd25: return 14; 8'd26: return 26; 8'd27: return 30;
8'd28: return 42; 8'd29: return 46; 8'd30: return 58; 8'd31: return 62;
// yb=2
8'd32: return 33; 8'd33: return 37; 8'd34: return 49; 8'd35: return 53;
8'd36: return 1; 8'd37: return 5; 8'd38: return 17; 8'd39: return 21;
8'd40: return 35; 8'd41: return 39; 8'd42: return 51; 8'd43: return 55;
8'd44: return 3; 8'd45: return 7; 8'd46: return 19; 8'd47: return 23;
// yb=3
8'd48: return 41; 8'd49: return 45; 8'd50: return 57; 8'd51: return 61;
8'd52: return 9; 8'd53: return 13; 8'd54: return 25; 8'd55: return 29;
8'd56: return 43; 8'd57: return 47; 8'd58: return 59; 8'd59: return 63;
8'd60: return 11; 8'd61: return 15; 8'd62: return 27; 8'd63: return 31;
// yb=4
8'd64: return 96; 8'd65: return 100; 8'd66: return 112; 8'd67: return 116;
8'd68: return 64; 8'd69: return 68; 8'd70: return 80; 8'd71: return 84;
8'd72: return 98; 8'd73: return 102; 8'd74: return 114; 8'd75: return 118;
8'd76: return 66; 8'd77: return 70; 8'd78: return 82; 8'd79: return 86;
// yb=5
8'd80: return 104; 8'd81: return 108; 8'd82: return 120; 8'd83: return 124;
8'd84: return 72; 8'd85: return 76; 8'd86: return 88; 8'd87: return 92;
8'd88: return 106; 8'd89: return 110; 8'd90: return 122; 8'd91: return 126;
8'd92: return 74; 8'd93: return 78; 8'd94: return 90; 8'd95: return 94;
// yb=6
8'd96: return 65; 8'd97: return 69; 8'd98: return 81; 8'd99: return 85;
8'd100: return 97; 8'd101: return 101; 8'd102: return 113; 8'd103: return 117;
8'd104: return 67; 8'd105: return 71; 8'd106: return 83; 8'd107: return 87;
8'd108: return 99; 8'd109: return 103; 8'd110: return 115; 8'd111: return 119;
// yb=7
8'd112: return 73; 8'd113: return 77; 8'd114: return 89; 8'd115: return 93;
8'd116: return 105; 8'd117: return 109; 8'd118: return 121; 8'd119: return 125;
8'd120: return 75; 8'd121: return 79; 8'd122: return 91; 8'd123: return 95;
8'd124: return 107; 8'd125: return 111; 8'd126: return 123; 8'd127: return 127;
// yb=8
8'd128: return 128; 8'd129: return 132; 8'd130: return 144; 8'd131: return 148;
8'd132: return 160; 8'd133: return 164; 8'd134: return 176; 8'd135: return 180;
8'd136: return 130; 8'd137: return 134; 8'd138: return 146; 8'd139: return 150;
8'd140: return 162; 8'd141: return 166; 8'd142: return 178; 8'd143: return 182;
// yb=9
8'd144: return 136; 8'd145: return 140; 8'd146: return 152; 8'd147: return 156;
8'd148: return 168; 8'd149: return 172; 8'd150: return 184; 8'd151: return 188;
8'd152: return 138; 8'd153: return 142; 8'd154: return 154; 8'd155: return 158;
8'd156: return 170; 8'd157: return 174; 8'd158: return 186; 8'd159: return 190;
// yb=10
8'd160: return 161; 8'd161: return 165; 8'd162: return 177; 8'd163: return 181;
8'd164: return 129; 8'd165: return 133; 8'd166: return 145; 8'd167: return 149;
8'd168: return 163; 8'd169: return 167; 8'd170: return 179; 8'd171: return 183;
8'd172: return 131; 8'd173: return 135; 8'd174: return 147; 8'd175: return 151;
// yb=11
8'd176: return 169; 8'd177: return 173; 8'd178: return 185; 8'd179: return 189;
8'd180: return 137; 8'd181: return 141; 8'd182: return 153; 8'd183: return 157;
8'd184: return 171; 8'd185: return 175; 8'd186: return 187; 8'd187: return 191;
8'd188: return 139; 8'd189: return 143; 8'd190: return 155; 8'd191: return 159;
// yb=12
8'd192: return 224; 8'd193: return 228; 8'd194: return 240; 8'd195: return 244;
8'd196: return 192; 8'd197: return 196; 8'd198: return 208; 8'd199: return 212;
8'd200: return 226; 8'd201: return 230; 8'd202: return 242; 8'd203: return 246;
8'd204: return 194; 8'd205: return 198; 8'd206: return 210; 8'd207: return 214;
// yb=13
8'd208: return 232; 8'd209: return 236; 8'd210: return 248; 8'd211: return 252;
8'd212: return 200; 8'd213: return 204; 8'd214: return 216; 8'd215: return 220;
8'd216: return 234; 8'd217: return 238; 8'd218: return 250; 8'd219: return 254;
8'd220: return 202; 8'd221: return 206; 8'd222: return 218; 8'd223: return 222;
// yb=14
8'd224: return 193; 8'd225: return 197; 8'd226: return 209; 8'd227: return 213;
8'd228: return 225; 8'd229: return 229; 8'd230: return 241; 8'd231: return 245;
8'd232: return 195; 8'd233: return 199; 8'd234: return 211; 8'd235: return 215;
8'd236: return 227; 8'd237: return 231; 8'd238: return 243; 8'd239: return 247;
// yb=15
8'd240: return 201; 8'd241: return 205; 8'd242: return 217; 8'd243: return 221;
8'd244: return 233; 8'd245: return 237; 8'd246: return 249; 8'd247: return 253;
8'd248: return 203; 8'd249: return 207; 8'd250: return 219; 8'd251: return 223;
8'd252: return 235; 8'd253: return 239; 8'd254: return 251; default: return 255;
endcase
endfunction
function automatic logic [31:0] ref_addr8(
input int fbp_v, input int fbw_v, input int x_v, input int y_v);
int page_x, page_y, bw_pg_v, page_idx, page_base;
int by, bx, blk_idx, xb, yb, byte_idx;
int addr_v;
page_x = x_v / 128;
page_y = y_v / 64;
bw_pg_v = fbw_v / 2;
page_idx = page_y * bw_pg_v + page_x;
page_base = fbp_v * 2048 + page_idx * 8192;
by = (y_v % 64) / 16;
bx = (x_v % 128) / 16;
blk_idx = ref_block_idx8(by, bx);
xb = x_v % 16;
yb = y_v % 16;
byte_idx = ref_col_idx8(yb, xb);
addr_v = page_base + blk_idx * 256 + byte_idx;
return addr_v[31:0];
endfunction
initial begin
fbp = 9'd0;
fbw = 6'd2; // PSMT8: FBW must be even (PCSX2 asserts (bw & 1) == 0)
x = 12'd0;
y = 12'd0;
#1;
// -----------------------------------------------------------
// Phase 1 — spot-checks (hand-computed corners). FBW=2 →
// bw_pg=1, so a single page per row of pages.
// -----------------------------------------------------------
// (0, 0) → block (0,0)=0, byte=0 → addr 0.
check(9'd0, 6'd2, 12'd0, 12'd0, 32'd0, "origin");
// (1, 0) → block 0, xb=1 yb=0 → byte=4 → addr 4.
check(9'd0, 6'd2, 12'd1, 12'd0, 32'd4, "intra-block-x1");
// (4, 0) → block 0, xb=4 yb=0 → byte=32.
check(9'd0, 6'd2, 12'd4, 12'd0, 32'd32, "intra-block-x4");
// (8, 0) → block 0, xb=8 yb=0 → byte=2.
check(9'd0, 6'd2, 12'd8, 12'd0, 32'd2, "intra-block-x8");
// (0, 1) → block 0, xb=0 yb=1 → byte=8.
check(9'd0, 6'd2, 12'd0, 12'd1, 32'd8, "intra-block-y1");
// (15, 15) → last byte of block (0,0): xb=15 yb=15 → byte=255.
check(9'd0, 6'd2, 12'd15, 12'd15, 32'd255, "block-(0,0)-last");
// (16, 0) → block (0,1) (by=0, bx=1) → blockTable8[0][1]=1 →
// block_base=256. xb=0 yb=0 → byte=0 → addr 256.
check(9'd0, 6'd2, 12'd16, 12'd0, 32'd256, "block-(0,1)-origin");
// (0, 16) → block (1,0) (by=1, bx=0) → blockTable8[1][0]=2 →
// block_base=512.
check(9'd0, 6'd2, 12'd0, 12'd16, 32'd512, "block-(1,0)-origin");
// (32, 0) → block (0,2) → blockTable8[0][2]=4 → block_base=1024.
check(9'd0, 6'd2, 12'd32, 12'd0, 32'd1024, "block-(0,2)-origin");
// (0, 32) → block (2,0) → blockTable8[2][0]=8 → block_base=2048.
check(9'd0, 6'd2, 12'd0, 12'd32, 32'd2048, "block-(2,0)-origin");
// (64, 0) → block (0,4) → blockTable8[0][4]=16 → block_base=4096.
check(9'd0, 6'd2, 12'd64, 12'd0, 32'd4096, "block-(0,4)-origin");
// (127, 63) → last pixel of last block of page 0:
// block (3,7) → blockTable8[3][7]=31 → block_base=7936.
// xb=15 yb=15 → byte=255 → addr = 7936 + 255 = 8191.
check(9'd0, 6'd2, 12'd127, 12'd63, 32'd8191, "page0-last-pixel");
// (128, 0) → start of page 1 (FBW=2, bw_pg=1) → page_idx=1,
// page_base=8192 → addr 8192.
check(9'd0, 6'd2, 12'd128, 12'd0, 32'd8192, "page1-x-origin");
// (0, 64) → start of page-row 1 (bw_pg=1) → page_idx=1,
// page_base=8192.
check(9'd0, 6'd2, 12'd0, 12'd64, 32'd8192, "page1-y-origin");
// FBP=4 → FBP*2048=8192 = 1 page. (0,0) → addr 8192.
check(9'd4, 6'd2, 12'd0, 12'd0, 32'd8192, "fbp4-origin");
// -----------------------------------------------------------
// Phase 2a — INDEPENDENT column-table source lock. 256 hard-
// coded address checks (one per (yb, xb) inside block (0,0))
// where the expected byte index is taken VERBATIM from PCSX2
// columnTable8, NOT derived from the in-TB ref_col_idx8.
// pixel (xb, yb) at FBP=0, FBW=2 lands in page (0,0), block
// (0,0) → swizzle 0 → block_base 0; expected addr = byte.
// -----------------------------------------------------------
// yb=0: 0 4 16 20 32 36 48 52 2 6 18 22 34 38 50 54
check(9'd0, 6'd2, 12'd0, 12'd0, 32'd0, "col-yb0-xb0");
check(9'd0, 6'd2, 12'd1, 12'd0, 32'd4, "col-yb0-xb1");
check(9'd0, 6'd2, 12'd2, 12'd0, 32'd16, "col-yb0-xb2");
check(9'd0, 6'd2, 12'd3, 12'd0, 32'd20, "col-yb0-xb3");
check(9'd0, 6'd2, 12'd4, 12'd0, 32'd32, "col-yb0-xb4");
check(9'd0, 6'd2, 12'd5, 12'd0, 32'd36, "col-yb0-xb5");
check(9'd0, 6'd2, 12'd6, 12'd0, 32'd48, "col-yb0-xb6");
check(9'd0, 6'd2, 12'd7, 12'd0, 32'd52, "col-yb0-xb7");
check(9'd0, 6'd2, 12'd8, 12'd0, 32'd2, "col-yb0-xb8");
check(9'd0, 6'd2, 12'd9, 12'd0, 32'd6, "col-yb0-xb9");
check(9'd0, 6'd2, 12'd10, 12'd0, 32'd18, "col-yb0-xb10");
check(9'd0, 6'd2, 12'd11, 12'd0, 32'd22, "col-yb0-xb11");
check(9'd0, 6'd2, 12'd12, 12'd0, 32'd34, "col-yb0-xb12");
check(9'd0, 6'd2, 12'd13, 12'd0, 32'd38, "col-yb0-xb13");
check(9'd0, 6'd2, 12'd14, 12'd0, 32'd50, "col-yb0-xb14");
check(9'd0, 6'd2, 12'd15, 12'd0, 32'd54, "col-yb0-xb15");
// yb=1: 8 12 24 28 40 44 56 60 10 14 26 30 42 46 58 62
check(9'd0, 6'd2, 12'd0, 12'd1, 32'd8, "col-yb1-xb0");
check(9'd0, 6'd2, 12'd1, 12'd1, 32'd12, "col-yb1-xb1");
check(9'd0, 6'd2, 12'd2, 12'd1, 32'd24, "col-yb1-xb2");
check(9'd0, 6'd2, 12'd3, 12'd1, 32'd28, "col-yb1-xb3");
check(9'd0, 6'd2, 12'd4, 12'd1, 32'd40, "col-yb1-xb4");
check(9'd0, 6'd2, 12'd5, 12'd1, 32'd44, "col-yb1-xb5");
check(9'd0, 6'd2, 12'd6, 12'd1, 32'd56, "col-yb1-xb6");
check(9'd0, 6'd2, 12'd7, 12'd1, 32'd60, "col-yb1-xb7");
check(9'd0, 6'd2, 12'd8, 12'd1, 32'd10, "col-yb1-xb8");
check(9'd0, 6'd2, 12'd9, 12'd1, 32'd14, "col-yb1-xb9");
check(9'd0, 6'd2, 12'd10, 12'd1, 32'd26, "col-yb1-xb10");
check(9'd0, 6'd2, 12'd11, 12'd1, 32'd30, "col-yb1-xb11");
check(9'd0, 6'd2, 12'd12, 12'd1, 32'd42, "col-yb1-xb12");
check(9'd0, 6'd2, 12'd13, 12'd1, 32'd46, "col-yb1-xb13");
check(9'd0, 6'd2, 12'd14, 12'd1, 32'd58, "col-yb1-xb14");
check(9'd0, 6'd2, 12'd15, 12'd1, 32'd62, "col-yb1-xb15");
// yb=2: 33 37 49 53 1 5 17 21 35 39 51 55 3 7 19 23
check(9'd0, 6'd2, 12'd0, 12'd2, 32'd33, "col-yb2-xb0");
check(9'd0, 6'd2, 12'd1, 12'd2, 32'd37, "col-yb2-xb1");
check(9'd0, 6'd2, 12'd2, 12'd2, 32'd49, "col-yb2-xb2");
check(9'd0, 6'd2, 12'd3, 12'd2, 32'd53, "col-yb2-xb3");
check(9'd0, 6'd2, 12'd4, 12'd2, 32'd1, "col-yb2-xb4");
check(9'd0, 6'd2, 12'd5, 12'd2, 32'd5, "col-yb2-xb5");
check(9'd0, 6'd2, 12'd6, 12'd2, 32'd17, "col-yb2-xb6");
check(9'd0, 6'd2, 12'd7, 12'd2, 32'd21, "col-yb2-xb7");
check(9'd0, 6'd2, 12'd8, 12'd2, 32'd35, "col-yb2-xb8");
check(9'd0, 6'd2, 12'd9, 12'd2, 32'd39, "col-yb2-xb9");
check(9'd0, 6'd2, 12'd10, 12'd2, 32'd51, "col-yb2-xb10");
check(9'd0, 6'd2, 12'd11, 12'd2, 32'd55, "col-yb2-xb11");
check(9'd0, 6'd2, 12'd12, 12'd2, 32'd3, "col-yb2-xb12");
check(9'd0, 6'd2, 12'd13, 12'd2, 32'd7, "col-yb2-xb13");
check(9'd0, 6'd2, 12'd14, 12'd2, 32'd19, "col-yb2-xb14");
check(9'd0, 6'd2, 12'd15, 12'd2, 32'd23, "col-yb2-xb15");
// yb=3: 41 45 57 61 9 13 25 29 43 47 59 63 11 15 27 31
check(9'd0, 6'd2, 12'd0, 12'd3, 32'd41, "col-yb3-xb0");
check(9'd0, 6'd2, 12'd1, 12'd3, 32'd45, "col-yb3-xb1");
check(9'd0, 6'd2, 12'd2, 12'd3, 32'd57, "col-yb3-xb2");
check(9'd0, 6'd2, 12'd3, 12'd3, 32'd61, "col-yb3-xb3");
check(9'd0, 6'd2, 12'd4, 12'd3, 32'd9, "col-yb3-xb4");
check(9'd0, 6'd2, 12'd5, 12'd3, 32'd13, "col-yb3-xb5");
check(9'd0, 6'd2, 12'd6, 12'd3, 32'd25, "col-yb3-xb6");
check(9'd0, 6'd2, 12'd7, 12'd3, 32'd29, "col-yb3-xb7");
check(9'd0, 6'd2, 12'd8, 12'd3, 32'd43, "col-yb3-xb8");
check(9'd0, 6'd2, 12'd9, 12'd3, 32'd47, "col-yb3-xb9");
check(9'd0, 6'd2, 12'd10, 12'd3, 32'd59, "col-yb3-xb10");
check(9'd0, 6'd2, 12'd11, 12'd3, 32'd63, "col-yb3-xb11");
check(9'd0, 6'd2, 12'd12, 12'd3, 32'd11, "col-yb3-xb12");
check(9'd0, 6'd2, 12'd13, 12'd3, 32'd15, "col-yb3-xb13");
check(9'd0, 6'd2, 12'd14, 12'd3, 32'd27, "col-yb3-xb14");
check(9'd0, 6'd2, 12'd15, 12'd3, 32'd31, "col-yb3-xb15");
// yb=4: 96 100 112 116 64 68 80 84 98 102 114 118 66 70 82 86
check(9'd0, 6'd2, 12'd0, 12'd4, 32'd96, "col-yb4-xb0");
check(9'd0, 6'd2, 12'd1, 12'd4, 32'd100, "col-yb4-xb1");
check(9'd0, 6'd2, 12'd2, 12'd4, 32'd112, "col-yb4-xb2");
check(9'd0, 6'd2, 12'd3, 12'd4, 32'd116, "col-yb4-xb3");
check(9'd0, 6'd2, 12'd4, 12'd4, 32'd64, "col-yb4-xb4");
check(9'd0, 6'd2, 12'd5, 12'd4, 32'd68, "col-yb4-xb5");
check(9'd0, 6'd2, 12'd6, 12'd4, 32'd80, "col-yb4-xb6");
check(9'd0, 6'd2, 12'd7, 12'd4, 32'd84, "col-yb4-xb7");
check(9'd0, 6'd2, 12'd8, 12'd4, 32'd98, "col-yb4-xb8");
check(9'd0, 6'd2, 12'd9, 12'd4, 32'd102, "col-yb4-xb9");
check(9'd0, 6'd2, 12'd10, 12'd4, 32'd114, "col-yb4-xb10");
check(9'd0, 6'd2, 12'd11, 12'd4, 32'd118, "col-yb4-xb11");
check(9'd0, 6'd2, 12'd12, 12'd4, 32'd66, "col-yb4-xb12");
check(9'd0, 6'd2, 12'd13, 12'd4, 32'd70, "col-yb4-xb13");
check(9'd0, 6'd2, 12'd14, 12'd4, 32'd82, "col-yb4-xb14");
check(9'd0, 6'd2, 12'd15, 12'd4, 32'd86, "col-yb4-xb15");
// yb=5: 104 108 120 124 72 76 88 92 106 110 122 126 74 78 90 94
check(9'd0, 6'd2, 12'd0, 12'd5, 32'd104, "col-yb5-xb0");
check(9'd0, 6'd2, 12'd1, 12'd5, 32'd108, "col-yb5-xb1");
check(9'd0, 6'd2, 12'd2, 12'd5, 32'd120, "col-yb5-xb2");
check(9'd0, 6'd2, 12'd3, 12'd5, 32'd124, "col-yb5-xb3");
check(9'd0, 6'd2, 12'd4, 12'd5, 32'd72, "col-yb5-xb4");
check(9'd0, 6'd2, 12'd5, 12'd5, 32'd76, "col-yb5-xb5");
check(9'd0, 6'd2, 12'd6, 12'd5, 32'd88, "col-yb5-xb6");
check(9'd0, 6'd2, 12'd7, 12'd5, 32'd92, "col-yb5-xb7");
check(9'd0, 6'd2, 12'd8, 12'd5, 32'd106, "col-yb5-xb8");
check(9'd0, 6'd2, 12'd9, 12'd5, 32'd110, "col-yb5-xb9");
check(9'd0, 6'd2, 12'd10, 12'd5, 32'd122, "col-yb5-xb10");
check(9'd0, 6'd2, 12'd11, 12'd5, 32'd126, "col-yb5-xb11");
check(9'd0, 6'd2, 12'd12, 12'd5, 32'd74, "col-yb5-xb12");
check(9'd0, 6'd2, 12'd13, 12'd5, 32'd78, "col-yb5-xb13");
check(9'd0, 6'd2, 12'd14, 12'd5, 32'd90, "col-yb5-xb14");
check(9'd0, 6'd2, 12'd15, 12'd5, 32'd94, "col-yb5-xb15");
// yb=6: 65 69 81 85 97 101 113 117 67 71 83 87 99 103 115 119
check(9'd0, 6'd2, 12'd0, 12'd6, 32'd65, "col-yb6-xb0");
check(9'd0, 6'd2, 12'd1, 12'd6, 32'd69, "col-yb6-xb1");
check(9'd0, 6'd2, 12'd2, 12'd6, 32'd81, "col-yb6-xb2");
check(9'd0, 6'd2, 12'd3, 12'd6, 32'd85, "col-yb6-xb3");
check(9'd0, 6'd2, 12'd4, 12'd6, 32'd97, "col-yb6-xb4");
check(9'd0, 6'd2, 12'd5, 12'd6, 32'd101, "col-yb6-xb5");
check(9'd0, 6'd2, 12'd6, 12'd6, 32'd113, "col-yb6-xb6");
check(9'd0, 6'd2, 12'd7, 12'd6, 32'd117, "col-yb6-xb7");
check(9'd0, 6'd2, 12'd8, 12'd6, 32'd67, "col-yb6-xb8");
check(9'd0, 6'd2, 12'd9, 12'd6, 32'd71, "col-yb6-xb9");
check(9'd0, 6'd2, 12'd10, 12'd6, 32'd83, "col-yb6-xb10");
check(9'd0, 6'd2, 12'd11, 12'd6, 32'd87, "col-yb6-xb11");
check(9'd0, 6'd2, 12'd12, 12'd6, 32'd99, "col-yb6-xb12");
check(9'd0, 6'd2, 12'd13, 12'd6, 32'd103, "col-yb6-xb13");
check(9'd0, 6'd2, 12'd14, 12'd6, 32'd115, "col-yb6-xb14");
check(9'd0, 6'd2, 12'd15, 12'd6, 32'd119, "col-yb6-xb15");
// yb=7: 73 77 89 93 105 109 121 125 75 79 91 95 107 111 123 127
check(9'd0, 6'd2, 12'd0, 12'd7, 32'd73, "col-yb7-xb0");
check(9'd0, 6'd2, 12'd1, 12'd7, 32'd77, "col-yb7-xb1");
check(9'd0, 6'd2, 12'd2, 12'd7, 32'd89, "col-yb7-xb2");
check(9'd0, 6'd2, 12'd3, 12'd7, 32'd93, "col-yb7-xb3");
check(9'd0, 6'd2, 12'd4, 12'd7, 32'd105, "col-yb7-xb4");
check(9'd0, 6'd2, 12'd5, 12'd7, 32'd109, "col-yb7-xb5");
check(9'd0, 6'd2, 12'd6, 12'd7, 32'd121, "col-yb7-xb6");
check(9'd0, 6'd2, 12'd7, 12'd7, 32'd125, "col-yb7-xb7");
check(9'd0, 6'd2, 12'd8, 12'd7, 32'd75, "col-yb7-xb8");
check(9'd0, 6'd2, 12'd9, 12'd7, 32'd79, "col-yb7-xb9");
check(9'd0, 6'd2, 12'd10, 12'd7, 32'd91, "col-yb7-xb10");
check(9'd0, 6'd2, 12'd11, 12'd7, 32'd95, "col-yb7-xb11");
check(9'd0, 6'd2, 12'd12, 12'd7, 32'd107, "col-yb7-xb12");
check(9'd0, 6'd2, 12'd13, 12'd7, 32'd111, "col-yb7-xb13");
check(9'd0, 6'd2, 12'd14, 12'd7, 32'd123, "col-yb7-xb14");
check(9'd0, 6'd2, 12'd15, 12'd7, 32'd127, "col-yb7-xb15");
// yb=8: 128 132 144 148 160 164 176 180 130 134 146 150 162 166 178 182
check(9'd0, 6'd2, 12'd0, 12'd8, 32'd128, "col-yb8-xb0");
check(9'd0, 6'd2, 12'd1, 12'd8, 32'd132, "col-yb8-xb1");
check(9'd0, 6'd2, 12'd2, 12'd8, 32'd144, "col-yb8-xb2");
check(9'd0, 6'd2, 12'd3, 12'd8, 32'd148, "col-yb8-xb3");
check(9'd0, 6'd2, 12'd4, 12'd8, 32'd160, "col-yb8-xb4");
check(9'd0, 6'd2, 12'd5, 12'd8, 32'd164, "col-yb8-xb5");
check(9'd0, 6'd2, 12'd6, 12'd8, 32'd176, "col-yb8-xb6");
check(9'd0, 6'd2, 12'd7, 12'd8, 32'd180, "col-yb8-xb7");
check(9'd0, 6'd2, 12'd8, 12'd8, 32'd130, "col-yb8-xb8");
check(9'd0, 6'd2, 12'd9, 12'd8, 32'd134, "col-yb8-xb9");
check(9'd0, 6'd2, 12'd10, 12'd8, 32'd146, "col-yb8-xb10");
check(9'd0, 6'd2, 12'd11, 12'd8, 32'd150, "col-yb8-xb11");
check(9'd0, 6'd2, 12'd12, 12'd8, 32'd162, "col-yb8-xb12");
check(9'd0, 6'd2, 12'd13, 12'd8, 32'd166, "col-yb8-xb13");
check(9'd0, 6'd2, 12'd14, 12'd8, 32'd178, "col-yb8-xb14");
check(9'd0, 6'd2, 12'd15, 12'd8, 32'd182, "col-yb8-xb15");
// yb=9: 136 140 152 156 168 172 184 188 138 142 154 158 170 174 186 190
check(9'd0, 6'd2, 12'd0, 12'd9, 32'd136, "col-yb9-xb0");
check(9'd0, 6'd2, 12'd1, 12'd9, 32'd140, "col-yb9-xb1");
check(9'd0, 6'd2, 12'd2, 12'd9, 32'd152, "col-yb9-xb2");
check(9'd0, 6'd2, 12'd3, 12'd9, 32'd156, "col-yb9-xb3");
check(9'd0, 6'd2, 12'd4, 12'd9, 32'd168, "col-yb9-xb4");
check(9'd0, 6'd2, 12'd5, 12'd9, 32'd172, "col-yb9-xb5");
check(9'd0, 6'd2, 12'd6, 12'd9, 32'd184, "col-yb9-xb6");
check(9'd0, 6'd2, 12'd7, 12'd9, 32'd188, "col-yb9-xb7");
check(9'd0, 6'd2, 12'd8, 12'd9, 32'd138, "col-yb9-xb8");
check(9'd0, 6'd2, 12'd9, 12'd9, 32'd142, "col-yb9-xb9");
check(9'd0, 6'd2, 12'd10, 12'd9, 32'd154, "col-yb9-xb10");
check(9'd0, 6'd2, 12'd11, 12'd9, 32'd158, "col-yb9-xb11");
check(9'd0, 6'd2, 12'd12, 12'd9, 32'd170, "col-yb9-xb12");
check(9'd0, 6'd2, 12'd13, 12'd9, 32'd174, "col-yb9-xb13");
check(9'd0, 6'd2, 12'd14, 12'd9, 32'd186, "col-yb9-xb14");
check(9'd0, 6'd2, 12'd15, 12'd9, 32'd190, "col-yb9-xb15");
// yb=10: 161 165 177 181 129 133 145 149 163 167 179 183 131 135 147 151
check(9'd0, 6'd2, 12'd0, 12'd10, 32'd161, "col-yb10-xb0");
check(9'd0, 6'd2, 12'd1, 12'd10, 32'd165, "col-yb10-xb1");
check(9'd0, 6'd2, 12'd2, 12'd10, 32'd177, "col-yb10-xb2");
check(9'd0, 6'd2, 12'd3, 12'd10, 32'd181, "col-yb10-xb3");
check(9'd0, 6'd2, 12'd4, 12'd10, 32'd129, "col-yb10-xb4");
check(9'd0, 6'd2, 12'd5, 12'd10, 32'd133, "col-yb10-xb5");
check(9'd0, 6'd2, 12'd6, 12'd10, 32'd145, "col-yb10-xb6");
check(9'd0, 6'd2, 12'd7, 12'd10, 32'd149, "col-yb10-xb7");
check(9'd0, 6'd2, 12'd8, 12'd10, 32'd163, "col-yb10-xb8");
check(9'd0, 6'd2, 12'd9, 12'd10, 32'd167, "col-yb10-xb9");
check(9'd0, 6'd2, 12'd10, 12'd10, 32'd179, "col-yb10-xb10");
check(9'd0, 6'd2, 12'd11, 12'd10, 32'd183, "col-yb10-xb11");
check(9'd0, 6'd2, 12'd12, 12'd10, 32'd131, "col-yb10-xb12");
check(9'd0, 6'd2, 12'd13, 12'd10, 32'd135, "col-yb10-xb13");
check(9'd0, 6'd2, 12'd14, 12'd10, 32'd147, "col-yb10-xb14");
check(9'd0, 6'd2, 12'd15, 12'd10, 32'd151, "col-yb10-xb15");
// yb=11: 169 173 185 189 137 141 153 157 171 175 187 191 139 143 155 159
check(9'd0, 6'd2, 12'd0, 12'd11, 32'd169, "col-yb11-xb0");
check(9'd0, 6'd2, 12'd1, 12'd11, 32'd173, "col-yb11-xb1");
check(9'd0, 6'd2, 12'd2, 12'd11, 32'd185, "col-yb11-xb2");
check(9'd0, 6'd2, 12'd3, 12'd11, 32'd189, "col-yb11-xb3");
check(9'd0, 6'd2, 12'd4, 12'd11, 32'd137, "col-yb11-xb4");
check(9'd0, 6'd2, 12'd5, 12'd11, 32'd141, "col-yb11-xb5");
check(9'd0, 6'd2, 12'd6, 12'd11, 32'd153, "col-yb11-xb6");
check(9'd0, 6'd2, 12'd7, 12'd11, 32'd157, "col-yb11-xb7");
check(9'd0, 6'd2, 12'd8, 12'd11, 32'd171, "col-yb11-xb8");
check(9'd0, 6'd2, 12'd9, 12'd11, 32'd175, "col-yb11-xb9");
check(9'd0, 6'd2, 12'd10, 12'd11, 32'd187, "col-yb11-xb10");
check(9'd0, 6'd2, 12'd11, 12'd11, 32'd191, "col-yb11-xb11");
check(9'd0, 6'd2, 12'd12, 12'd11, 32'd139, "col-yb11-xb12");
check(9'd0, 6'd2, 12'd13, 12'd11, 32'd143, "col-yb11-xb13");
check(9'd0, 6'd2, 12'd14, 12'd11, 32'd155, "col-yb11-xb14");
check(9'd0, 6'd2, 12'd15, 12'd11, 32'd159, "col-yb11-xb15");
// yb=12: 224 228 240 244 192 196 208 212 226 230 242 246 194 198 210 214
check(9'd0, 6'd2, 12'd0, 12'd12, 32'd224, "col-yb12-xb0");
check(9'd0, 6'd2, 12'd1, 12'd12, 32'd228, "col-yb12-xb1");
check(9'd0, 6'd2, 12'd2, 12'd12, 32'd240, "col-yb12-xb2");
check(9'd0, 6'd2, 12'd3, 12'd12, 32'd244, "col-yb12-xb3");
check(9'd0, 6'd2, 12'd4, 12'd12, 32'd192, "col-yb12-xb4");
check(9'd0, 6'd2, 12'd5, 12'd12, 32'd196, "col-yb12-xb5");
check(9'd0, 6'd2, 12'd6, 12'd12, 32'd208, "col-yb12-xb6");
check(9'd0, 6'd2, 12'd7, 12'd12, 32'd212, "col-yb12-xb7");
check(9'd0, 6'd2, 12'd8, 12'd12, 32'd226, "col-yb12-xb8");
check(9'd0, 6'd2, 12'd9, 12'd12, 32'd230, "col-yb12-xb9");
check(9'd0, 6'd2, 12'd10, 12'd12, 32'd242, "col-yb12-xb10");
check(9'd0, 6'd2, 12'd11, 12'd12, 32'd246, "col-yb12-xb11");
check(9'd0, 6'd2, 12'd12, 12'd12, 32'd194, "col-yb12-xb12");
check(9'd0, 6'd2, 12'd13, 12'd12, 32'd198, "col-yb12-xb13");
check(9'd0, 6'd2, 12'd14, 12'd12, 32'd210, "col-yb12-xb14");
check(9'd0, 6'd2, 12'd15, 12'd12, 32'd214, "col-yb12-xb15");
// yb=13: 232 236 248 252 200 204 216 220 234 238 250 254 202 206 218 222
check(9'd0, 6'd2, 12'd0, 12'd13, 32'd232, "col-yb13-xb0");
check(9'd0, 6'd2, 12'd1, 12'd13, 32'd236, "col-yb13-xb1");
check(9'd0, 6'd2, 12'd2, 12'd13, 32'd248, "col-yb13-xb2");
check(9'd0, 6'd2, 12'd3, 12'd13, 32'd252, "col-yb13-xb3");
check(9'd0, 6'd2, 12'd4, 12'd13, 32'd200, "col-yb13-xb4");
check(9'd0, 6'd2, 12'd5, 12'd13, 32'd204, "col-yb13-xb5");
check(9'd0, 6'd2, 12'd6, 12'd13, 32'd216, "col-yb13-xb6");
check(9'd0, 6'd2, 12'd7, 12'd13, 32'd220, "col-yb13-xb7");
check(9'd0, 6'd2, 12'd8, 12'd13, 32'd234, "col-yb13-xb8");
check(9'd0, 6'd2, 12'd9, 12'd13, 32'd238, "col-yb13-xb9");
check(9'd0, 6'd2, 12'd10, 12'd13, 32'd250, "col-yb13-xb10");
check(9'd0, 6'd2, 12'd11, 12'd13, 32'd254, "col-yb13-xb11");
check(9'd0, 6'd2, 12'd12, 12'd13, 32'd202, "col-yb13-xb12");
check(9'd0, 6'd2, 12'd13, 12'd13, 32'd206, "col-yb13-xb13");
check(9'd0, 6'd2, 12'd14, 12'd13, 32'd218, "col-yb13-xb14");
check(9'd0, 6'd2, 12'd15, 12'd13, 32'd222, "col-yb13-xb15");
// yb=14: 193 197 209 213 225 229 241 245 195 199 211 215 227 231 243 247
check(9'd0, 6'd2, 12'd0, 12'd14, 32'd193, "col-yb14-xb0");
check(9'd0, 6'd2, 12'd1, 12'd14, 32'd197, "col-yb14-xb1");
check(9'd0, 6'd2, 12'd2, 12'd14, 32'd209, "col-yb14-xb2");
check(9'd0, 6'd2, 12'd3, 12'd14, 32'd213, "col-yb14-xb3");
check(9'd0, 6'd2, 12'd4, 12'd14, 32'd225, "col-yb14-xb4");
check(9'd0, 6'd2, 12'd5, 12'd14, 32'd229, "col-yb14-xb5");
check(9'd0, 6'd2, 12'd6, 12'd14, 32'd241, "col-yb14-xb6");
check(9'd0, 6'd2, 12'd7, 12'd14, 32'd245, "col-yb14-xb7");
check(9'd0, 6'd2, 12'd8, 12'd14, 32'd195, "col-yb14-xb8");
check(9'd0, 6'd2, 12'd9, 12'd14, 32'd199, "col-yb14-xb9");
check(9'd0, 6'd2, 12'd10, 12'd14, 32'd211, "col-yb14-xb10");
check(9'd0, 6'd2, 12'd11, 12'd14, 32'd215, "col-yb14-xb11");
check(9'd0, 6'd2, 12'd12, 12'd14, 32'd227, "col-yb14-xb12");
check(9'd0, 6'd2, 12'd13, 12'd14, 32'd231, "col-yb14-xb13");
check(9'd0, 6'd2, 12'd14, 12'd14, 32'd243, "col-yb14-xb14");
check(9'd0, 6'd2, 12'd15, 12'd14, 32'd247, "col-yb14-xb15");
// yb=15: 201 205 217 221 233 237 249 253 203 207 219 223 235 239 251 255
check(9'd0, 6'd2, 12'd0, 12'd15, 32'd201, "col-yb15-xb0");
check(9'd0, 6'd2, 12'd1, 12'd15, 32'd205, "col-yb15-xb1");
check(9'd0, 6'd2, 12'd2, 12'd15, 32'd217, "col-yb15-xb2");
check(9'd0, 6'd2, 12'd3, 12'd15, 32'd221, "col-yb15-xb3");
check(9'd0, 6'd2, 12'd4, 12'd15, 32'd233, "col-yb15-xb4");
check(9'd0, 6'd2, 12'd5, 12'd15, 32'd237, "col-yb15-xb5");
check(9'd0, 6'd2, 12'd6, 12'd15, 32'd249, "col-yb15-xb6");
check(9'd0, 6'd2, 12'd7, 12'd15, 32'd253, "col-yb15-xb7");
check(9'd0, 6'd2, 12'd8, 12'd15, 32'd203, "col-yb15-xb8");
check(9'd0, 6'd2, 12'd9, 12'd15, 32'd207, "col-yb15-xb9");
check(9'd0, 6'd2, 12'd10, 12'd15, 32'd219, "col-yb15-xb10");
check(9'd0, 6'd2, 12'd11, 12'd15, 32'd223, "col-yb15-xb11");
check(9'd0, 6'd2, 12'd12, 12'd15, 32'd235, "col-yb15-xb12");
check(9'd0, 6'd2, 12'd13, 12'd15, 32'd239, "col-yb15-xb13");
check(9'd0, 6'd2, 12'd14, 12'd15, 32'd251, "col-yb15-xb14");
check(9'd0, 6'd2, 12'd15, 12'd15, 32'd255, "col-yb15-xb15");
// -----------------------------------------------------------
// Phase 2b — within-block 16×16 walk via the in-TB
// ref_col_idx8. Self-check that ref_col_idx8 agrees with
// the DUT.
// -----------------------------------------------------------
for (int yb_i = 0; yb_i < 16; yb_i++) begin
for (int xb_i = 0; xb_i < 16; xb_i++) begin
logic [31:0] expected;
expected = 32'(ref_col_idx8(yb_i, xb_i));
check(9'd0, 6'd2, 12'(xb_i), 12'(yb_i), expected,
"within-block-col-table");
end
end
// -----------------------------------------------------------
// Phase 3 — INDEPENDENT block-table source lock. 32 hard-
// coded address checks, one per block in page 0. Expected
// block index taken VERBATIM from PCSX2 blockTable8 (NOT
// derived from ref_block_idx8). For each block (block_y,
// block_x), we use the pixel (16*block_x, 16*block_y) which
// lands at xb=yb=0 → byte=0 within the block;
// total addr = block_idx * 256.
//
// blockTable8[4][8] (PCSX2 master GSTables.cpp lines 5359):
// 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
// -----------------------------------------------------------
// by=0
check(9'd0, 6'd2, 12'(0*16), 12'(0*16), 32'(0*256), "src-lock-by0bx0");
check(9'd0, 6'd2, 12'(1*16), 12'(0*16), 32'(1*256), "src-lock-by0bx1");
check(9'd0, 6'd2, 12'(2*16), 12'(0*16), 32'(4*256), "src-lock-by0bx2");
check(9'd0, 6'd2, 12'(3*16), 12'(0*16), 32'(5*256), "src-lock-by0bx3");
check(9'd0, 6'd2, 12'(4*16), 12'(0*16), 32'(16*256), "src-lock-by0bx4");
check(9'd0, 6'd2, 12'(5*16), 12'(0*16), 32'(17*256), "src-lock-by0bx5");
check(9'd0, 6'd2, 12'(6*16), 12'(0*16), 32'(20*256), "src-lock-by0bx6");
check(9'd0, 6'd2, 12'(7*16), 12'(0*16), 32'(21*256), "src-lock-by0bx7");
// by=1
check(9'd0, 6'd2, 12'(0*16), 12'(1*16), 32'(2*256), "src-lock-by1bx0");
check(9'd0, 6'd2, 12'(1*16), 12'(1*16), 32'(3*256), "src-lock-by1bx1");
check(9'd0, 6'd2, 12'(2*16), 12'(1*16), 32'(6*256), "src-lock-by1bx2");
check(9'd0, 6'd2, 12'(3*16), 12'(1*16), 32'(7*256), "src-lock-by1bx3");
check(9'd0, 6'd2, 12'(4*16), 12'(1*16), 32'(18*256), "src-lock-by1bx4");
check(9'd0, 6'd2, 12'(5*16), 12'(1*16), 32'(19*256), "src-lock-by1bx5");
check(9'd0, 6'd2, 12'(6*16), 12'(1*16), 32'(22*256), "src-lock-by1bx6");
check(9'd0, 6'd2, 12'(7*16), 12'(1*16), 32'(23*256), "src-lock-by1bx7");
// by=2
check(9'd0, 6'd2, 12'(0*16), 12'(2*16), 32'(8*256), "src-lock-by2bx0");
check(9'd0, 6'd2, 12'(1*16), 12'(2*16), 32'(9*256), "src-lock-by2bx1");
check(9'd0, 6'd2, 12'(2*16), 12'(2*16), 32'(12*256), "src-lock-by2bx2");
check(9'd0, 6'd2, 12'(3*16), 12'(2*16), 32'(13*256), "src-lock-by2bx3");
check(9'd0, 6'd2, 12'(4*16), 12'(2*16), 32'(24*256), "src-lock-by2bx4");
check(9'd0, 6'd2, 12'(5*16), 12'(2*16), 32'(25*256), "src-lock-by2bx5");
check(9'd0, 6'd2, 12'(6*16), 12'(2*16), 32'(28*256), "src-lock-by2bx6");
check(9'd0, 6'd2, 12'(7*16), 12'(2*16), 32'(29*256), "src-lock-by2bx7");
// by=3
check(9'd0, 6'd2, 12'(0*16), 12'(3*16), 32'(10*256), "src-lock-by3bx0");
check(9'd0, 6'd2, 12'(1*16), 12'(3*16), 32'(11*256), "src-lock-by3bx1");
check(9'd0, 6'd2, 12'(2*16), 12'(3*16), 32'(14*256), "src-lock-by3bx2");
check(9'd0, 6'd2, 12'(3*16), 12'(3*16), 32'(15*256), "src-lock-by3bx3");
check(9'd0, 6'd2, 12'(4*16), 12'(3*16), 32'(26*256), "src-lock-by3bx4");
check(9'd0, 6'd2, 12'(5*16), 12'(3*16), 32'(27*256), "src-lock-by3bx5");
check(9'd0, 6'd2, 12'(6*16), 12'(3*16), 32'(30*256), "src-lock-by3bx6");
check(9'd0, 6'd2, 12'(7*16), 12'(3*16), 32'(31*256), "src-lock-by3bx7");
// -----------------------------------------------------------
// Phase 4 — block-swizzle walk. Cross-checks the in-TB
// ref_block_idx8 against the DUT (the bijectivity sweep
// below relies on ref_block_idx8 being correct).
// -----------------------------------------------------------
for (int by_i = 0; by_i < 4; by_i++) begin
for (int bx_i = 0; bx_i < 8; bx_i++) begin
logic [31:0] expected;
expected = 32'(ref_block_idx8(by_i, bx_i) * 256);
check(9'd0, 6'd2, 12'(bx_i*16), 12'(by_i*16), expected,
"block-swizzle-walk");
end
end
// -----------------------------------------------------------
// Phase 5 — bijectivity sweep over the 128×64 page (8192
// pixels = 8192 byte slots since PSMT8 is 1 byte/pixel).
// Every pixel must hit a unique byte address in [0, 8192)
// and agree with ref_addr8. Catches any swap in either table.
// -----------------------------------------------------------
begin : sweep
logic seen [0:8191];
for (int i = 0; i < 8192; i++) seen[i] = 1'b0;
for (int yy = 0; yy < 64; yy++) begin
for (int xx = 0; xx < 128; xx++) begin
logic [31:0] got, ref_;
int byte_slot;
compute(9'd0, 6'd2, 12'(xx), 12'(yy), got);
ref_ = ref_addr8(0, 2, xx, yy);
if (got !== ref_) begin
$error("sweep: (%0d,%0d) DUT=0x%08x ref=0x%08x",
xx, yy, got, ref_);
errors = errors + 1;
end
if (got >= 32'd8192) begin
$error("sweep: (%0d,%0d) addr=0x%08x out of page bounds",
xx, yy, got);
errors = errors + 1;
end
byte_slot = got[12:0];
if (seen[byte_slot]) begin
$error("sweep: (%0d,%0d) addr=0x%08x duplicate (slot=%0d)",
xx, yy, got, byte_slot);
errors = errors + 1;
end
seen[byte_slot] = 1'b1;
end
end
for (int i = 0; i < 8192; i++) begin
if (!seen[i]) begin
$error("sweep: byte_slot %0d never reached", i);
errors = errors + 1;
end
end
end
// -----------------------------------------------------------
// Multi-page sanity at FBW=4 (bw_pg=2): pixel (192, 16) →
// page (1, 0) horizontally.
// page_x = 192/128 = 1, page_y = 0, page_idx = 0*2+1 = 1,
// page_base = 1 * 8192 = 8192.
// bx = (192 % 128) / 16 = 64/16 = 4.
// by = (16 % 64) / 16 = 16/16 = 1.
// block (by=1, bx=4) → blockTable8[1][4] = 18 →
// block_base = 8192 + 18*256 = 12800.
// xb = 192%16 = 0, yb = 16%16 = 0 → byte = 0.
// addr = 12800.
// -----------------------------------------------------------
check(9'd0, 6'd4, 12'd192, 12'd16, 32'd12800, "fbw4-multi-page-x");
// FBW=4 + page-y crossing: (0, 64) → page (0, 1),
// page_idx = 1*2 + 0 = 2, page_base = 16384.
check(9'd0, 6'd4, 12'd0, 12'd64, 32'd16384, "fbw4-page-y-origin");
// -----------------------------------------------------------
// Non-page-aligned FBP coverage (real PS2 supports any
// 2048-byte-aligned FBP). FBP=1 → base 2048; FBP=2 → 4096;
// FBP=3 → 6144. (0,0) → addr = FBP*2048.
// -----------------------------------------------------------
check(9'd1, 6'd2, 12'd0, 12'd0, 32'd2048, "fbp1-mid-page-origin");
check(9'd2, 6'd2, 12'd0, 12'd0, 32'd4096, "fbp2-mid-page-origin");
check(9'd3, 6'd2, 12'd0, 12'd0, 32'd6144, "fbp3-mid-page-origin");
// FBP=3 + FBW=4 + intra-block + page-(1,1) crossing:
// (129, 65) → page (1, 1), page_idx = 1*2+1 = 3,
// page_base = 6144 + 3*8192 = 30720.
// bx = (129 % 128) / 16 = 1/16 = 0.
// by = (65 % 64) / 16 = 1/16 = 0.
// block (0,0) → swizzle 0 → block_base = 30720.
// xb = 129%16 = 1, yb = 65%16 = 1.
// byte = ref_col_idx8(1,1) = 12 → addr = 30732.
check(9'd3, 6'd4, 12'd129, 12'd65, 32'd30732,
"fbp3-fbw4-page1-1-intra");
$display("[tb_gs_swizzle_psmt8] errors=%0d", errors);
if (errors == 0) $display("[tb_gs_swizzle_psmt8] PASS");
else $display("[tb_gs_swizzle_psmt8] FAIL");
$finish;
end
initial begin
#500000;
$error("[tb_gs_swizzle_psmt8] timeout");
$finish;
end
endmodule : tb_gs_swizzle_psmt8