// retroDE_ps2 — tb_gs_texture_bilinear (Ch308) // // Focused TB for BILINEAR (4-tap) PSMCT32 texture filtering at the sampler // level. gs_texture_unit is instantiated with BILINEAR_ENABLE=1 + // TEX_WRAP_ENABLE=1; for a fractional texel coord it fetches the 4 surrounding // texels (u,v)(u+1,v)(u,v+1)(u+1,v+1), each independently wrapped/clamped, and // blends them per channel by the 4-bit fractional u_frac/v_frac using the // >>>4 fixed-point lerp: // lerp(a,b,f) = a + (($signed(b)-$signed(a)) * $signed(f)) >>> 4 , f in 0..15 // The expected values below mirror that lerp EXACTLY (same >>4, same rounding), // so every check is an exact == match, not a tolerance. // // 4x4 PSMCT32 texture (TW=TH=2 => width=height=4), TBW=1, distinct ABGR per // texel so 4-tap blends are individually checkable. // // PROOF CASES: // 1. EXACT CENTER (uf=0,vf=0) : result == texel(u,v) (== nearest). // 2. HALFWAY (uf=8,vf=8) : result == 4-neighbor blend (lerp). // 3. HALFWAY-U (uf=8,vf=0) : result == lerp(texel(u,v),texel(u+1,v)). // 4. CLAMP edge (wms=wmt=1,u=W-1) : (u+1) clamps to W-1 => result == // texel(W-1,v); issued addrs <= last col. // 5. REPEAT edge (wms=wmt=0,u=W-1) : (u+1) wraps to 0 => result == // lerp(texel(W-1,v),texel(0,v),uf). // 6. NEAREST UNCHANGED: a 2nd instance with BILINEAR_ENABLE=0 returns the // single nearest texel and ignores u_frac/v_frac. // // Prints [tb_gs_texture_bilinear] PASS / FAIL + $finish. `timescale 1ns/1ps module tb_gs_texture_bilinear; localparam logic [5:0] PSMCT32 = 6'h00; localparam logic [1:0] WM_REPEAT = 2'd0; localparam logic [1:0] WM_CLAMP = 2'd1; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; // ---- tiny combinational VRAM model (word-addressed little-endian) ---- localparam int VRAM_BYTES = 16384; logic [7:0] vram [0:VRAM_BYTES-1]; logic [31:0] tex_rd_addr; logic [31:0] tex_rd_data; assign tex_rd_data = { vram[(tex_rd_addr & ~32'd3) + 3], vram[(tex_rd_addr & ~32'd3) + 2], vram[(tex_rd_addr & ~32'd3) + 1], vram[(tex_rd_addr & ~32'd3) + 0] }; task automatic poke_word(input logic [31:0] a, input logic [31:0] d); vram[a+0] = d[7:0]; vram[a+1] = d[15:8]; vram[a+2] = d[23:16]; vram[a+3] = d[31:24]; endtask // Ch314 — PSMT8: one index byte per texel at the linear byte address. task automatic poke_t8(input int x, input int y, input logic [7:0] idx); vram[T8_BASE + (y * (TBWv*64)) + x] = idx; // 1 byte/texel endtask // Ch314 — PSMT4: pack 2 indices per byte; even texel-offset -> LOW nibble, // odd -> HIGH nibble (matches gs_texel_addr's texel_offset[0]==nibble_hi). task automatic poke_t4(input int x, input int y, input logic [3:0] idx); int off; int ba; begin off = (y * (TBWv*64)) + x; ba = T4_BASE + (off >> 1); if (off[0]) vram[ba] = {idx, vram[ba][3:0]}; // high nibble else vram[ba] = {vram[ba][7:4], idx}; // low nibble end endtask // ---- bilinear DUT ---- logic in_valid; logic [10:0] u, v; logic [3:0] u_frac, v_frac; logic [1:0] wms, wmt; logic [3:0] tw, th; logic [31:0] tbp0_base_bytes; logic [13:0] tbw; logic [5:0] psm; logic tex_rd_en; logic [7:0] clut_rd_idx; logic [31:0] clut_rd_data; logic out_valid; logic [31:0] tex_color; logic busy; // Ch314 — CLUT model (256 PSMCT32 entries) shared by both DUTs. The sampler // drives clut_rd_idx combinationally from the extracted index; we return the // palette color, exactly like clut_stub's 2nd (combinational) read port. logic [31:0] clut [0:255]; assign clut_rd_data = clut[clut_rd_idx]; gs_texture_unit #(.BILINEAR_ENABLE(1'b1), .PALETTE_BILINEAR(1'b1), .TEX_WRAP_ENABLE(1'b1), .RD_LATENCY(1)) dut ( .clk(clk), .rst_n(rst_n), .in_valid(in_valid), .u(u), .v(v), .u_frac(u_frac), .v_frac(v_frac), .wms(wms), .wmt(wmt), .tw(tw), .th(th), .tbp0_base_bytes(tbp0_base_bytes), .tbw(tbw), .psm(psm), .tex_rd_en(tex_rd_en), .tex_rd_addr(tex_rd_addr), .tex_rd_data(tex_rd_data), .clut_rd_idx(clut_rd_idx), .clut_rd_data(clut_rd_data), .out_valid(out_valid), .tex_color(tex_color), .busy(busy) ); // ---- nearest reference DUT (BILINEAR_ENABLE=0) ---- logic n_in_valid; logic [10:0] n_u, n_v; logic [3:0] n_uf, n_vf; logic [31:0] n_tex_rd_addr; logic [31:0] n_tex_rd_data; logic n_out_valid; logic [31:0] n_tex_color; logic n_busy; logic [7:0] n_clut_rd_idx; // Ch314 — nearest DUT's CLUT index (for PSMT8 nearest x-check) assign n_tex_rd_data = { vram[(n_tex_rd_addr & ~32'd3) + 3], vram[(n_tex_rd_addr & ~32'd3) + 2], vram[(n_tex_rd_addr & ~32'd3) + 1], vram[(n_tex_rd_addr & ~32'd3) + 0] }; gs_texture_unit #(.BILINEAR_ENABLE(1'b0), .TEX_WRAP_ENABLE(1'b1), .RD_LATENCY(1)) dut_near ( .clk(clk), .rst_n(rst_n), .in_valid(n_in_valid), .u(n_u), .v(n_v), .u_frac(n_uf), .v_frac(n_vf), .wms(WM_REPEAT), .wmt(WM_REPEAT), .tw(tw), .th(th), .tbp0_base_bytes(tbp0_base_bytes), .tbw(tbw), .psm(psm), .tex_rd_en(), .tex_rd_addr(n_tex_rd_addr), .tex_rd_data(n_tex_rd_data), .clut_rd_idx(n_clut_rd_idx), .clut_rd_data(clut[n_clut_rd_idx]), .out_valid(n_out_valid), .tex_color(n_tex_color), .busy(n_busy) ); // ---- texture geometry: 4x4 (TW=TH=2), TBW=1 ---- localparam int TEXW = 4; localparam int TEXH = 4; localparam [3:0] TWv = 4'd2; // log2(4) localparam [3:0] THv = 4'd2; localparam [13:0] TBWv = 14'd1; // row stride = TBW*64 texels localparam [31:0] CT32_BASE = 32'h0000_2000; // Ch314 — indexed textures (same 4x4 geometry, TBW=1). localparam logic [5:0] PSMT8v = 6'h13; localparam logic [5:0] PSMT4v = 6'h14; localparam [31:0] T8_BASE = 32'h0000_1000; // PSMT8: 1 byte/texel, row stride TBW*64=64 B localparam [31:0] T4_BASE = 32'h0000_1800; // PSMT4: 1 nibble/texel, row stride TBW*64/2=32 B // Palette: distinct, well-separated colors so a COLOR interpolation is // visibly different from CLUT-of-an-interpolated-index. idx 0=red 1=blue // 2=green 3=white. The column-striped index layout idx(x,y)=x gives // y-independent taps so vf is irrelevant and the blend is a clean U-lerp. localparam [31:0] CLUT0 = 32'hFF_00_00_FF; // red (A,B,G,R) localparam [31:0] CLUT1 = 32'hFF_FF_00_00; // blue localparam [31:0] CLUT2 = 32'hFF_00_FF_00; // green localparam [31:0] CLUT3 = 32'hFF_FF_FF_FF; // white // distinct PSMCT32 ABGR word per (x,y): pack varied per-channel values so // each tap differs in R,G,B AND A (so blends are individually checkable). function automatic logic [31:0] ct32_word(input int x, input int y); logic [7:0] r, g, b, a; begin r = 8'(16 + (y*TEXW + x) * 12); // 16,28,40,... distinct g = 8'(8 + (x) * 30); // varies with x b = 8'(4 + (y) * 40); // varies with y a = 8'(255 - (y*TEXW + x) * 8); // varies, stays high ct32_word = {a, b, g, r}; end endfunction // reference lerp — mirrors the RTL EXACTLY (same >>>4, same clamp). function automatic logic [7:0] ref_lerp8(input logic [7:0] a, input logic [7:0] b, input logic [3:0] f); logic signed [16:0] diff; logic signed [21:0] prod; logic signed [21:0] shifted; logic signed [21:0] res; begin diff = $signed({1'b0, b}) - $signed({1'b0, a}); prod = diff * $signed({1'b0, f}); shifted = prod >>> 4; res = $signed({14'd0, a}) + shifted; if (res < 0) ref_lerp8 = 8'd0; else if (res > 22'sd255) ref_lerp8 = 8'd255; else ref_lerp8 = res[7:0]; end endfunction // expected bilinear blend of four ABGR words by (uf,vf) function automatic logic [31:0] ref_blend(input logic [31:0] w00, input logic [31:0] w10, input logic [31:0] w01, input logic [31:0] w11, input logic [3:0] uf, input logic [3:0] vf); logic [7:0] tr, tg, tb, ta; // top logic [7:0] br, bg, bb, ba; // bottom logic [7:0] cr, cg, cb, ca; // out begin tr = ref_lerp8(w00[ 7: 0], w10[ 7: 0], uf); tg = ref_lerp8(w00[15: 8], w10[15: 8], uf); tb = ref_lerp8(w00[23:16], w10[23:16], uf); ta = ref_lerp8(w00[31:24], w10[31:24], uf); br = ref_lerp8(w01[ 7: 0], w11[ 7: 0], uf); bg = ref_lerp8(w01[15: 8], w11[15: 8], uf); bb = ref_lerp8(w01[23:16], w11[23:16], uf); ba = ref_lerp8(w01[31:24], w11[31:24], uf); cr = ref_lerp8(tr, br, vf); cg = ref_lerp8(tg, bg, vf); cb = ref_lerp8(tb, bb, vf); ca = ref_lerp8(ta, ba, vf); ref_blend = {ca, cb, cg, cr}; end endfunction int errors; initial errors = 0; // track the max issued texel column/row during a bilinear sample (for the // clamp proof: addresses must never read beyond the texture). logic watch_addr; int max_addr_seen; always @(posedge clk) begin if (watch_addr && tex_rd_en) begin if ($signed(tex_rd_addr) > max_addr_seen) max_addr_seen = tex_rd_addr; end end // drive one BILINEAR sample, wait for out_valid, return tex_color. task automatic bsample(input int su, input int sv, input logic [3:0] suf, input logic [3:0] svf, output logic [31:0] col); int guard; begin @(negedge clk); in_valid = 1'b1; u = 11'(su); v = 11'(sv); u_frac = suf; v_frac = svf; @(negedge clk); in_valid = 1'b0; // single-cycle request; FSM is running guard = 0; while (out_valid !== 1'b1 && guard < 200) begin @(negedge clk); guard++; end col = tex_color; if (guard >= 200) begin $error("[bilinear] out_valid never asserted (u=%0d v=%0d)", su, sv); errors++; end end endtask // drive the NEAREST reference instance, settle combinationally. task automatic nsample(input int su, input int sv, input logic [3:0] suf, input logic [3:0] svf, output logic [31:0] col); begin @(negedge clk); n_in_valid = 1'b1; n_u = 11'(su); n_v = 11'(sv); n_uf = suf; n_vf = svf; @(posedge clk); // RD_LATENCY=1 -> out_valid next cycle #2; col = n_tex_color; @(negedge clk); n_in_valid = 1'b0; end endtask logic [31:0] got, exp; logic [31:0] w00, w10, w01, w11; int x, y; initial begin rst_n = 1'b0; in_valid = 1'b0; n_in_valid = 1'b0; u = 0; v = 0; u_frac = 0; v_frac = 0; n_u = 0; n_v = 0; n_uf = 0; n_vf = 0; wms = WM_REPEAT; wmt = WM_REPEAT; tw = TWv; th = THv; tbp0_base_bytes = CT32_BASE; tbw = TBWv; psm = PSMCT32; watch_addr = 1'b0; max_addr_seen = 0; for (int i = 0; i < VRAM_BYTES; i++) vram[i] = 8'd0; // upload 4x4 PSMCT32 (row stride TBW*64 texels * 4 bytes) for (y = 0; y < TEXH; y++) for (x = 0; x < TEXW; x++) poke_word(CT32_BASE + ((y * (TBWv*64)) + x) * 4, ct32_word(x, y)); // Ch314 — CLUT palette + column-striped indexed textures idx(x,y)=x. for (int i = 0; i < 256; i++) clut[i] = 32'hFF_80_80_80; // neutral filler clut[0] = CLUT0; clut[1] = CLUT1; clut[2] = CLUT2; clut[3] = CLUT3; for (y = 0; y < TEXH; y++) for (x = 0; x < TEXW; x++) begin poke_t8(x, y, 8'(x)); // PSMT8 index = column poke_t4(x, y, 4'(x)); // PSMT4 index = column end repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); // ============================================================ // CASE 1 — EXACT TEXEL CENTER (uf=0,vf=0) == nearest texel(u,v) // ============================================================ wms = WM_REPEAT; wmt = WM_REPEAT; bsample(1, 2, 4'd0, 4'd0, got); exp = ct32_word(1, 2); if (got !== exp) begin $error("[case1 center] u=1 v=2 uf=0 vf=0 got=%08x exp=%08x", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE1 center pass: got=%08x == texel(1,2)", got); // ============================================================ // CASE 2 — HALFWAY (uf=8,vf=8) == blend of 4 neighbors // ============================================================ bsample(1, 1, 4'd8, 4'd8, got); w00 = ct32_word(1,1); w10 = ct32_word(2,1); w01 = ct32_word(1,2); w11 = ct32_word(2,2); exp = ref_blend(w00, w10, w01, w11, 4'd8, 4'd8); if (got !== exp) begin $error("[case2 halfway] u=1 v=1 uf=8 vf=8 got=%08x exp=%08x", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE2 halfway pass: got=%08x == 4-tap blend", got); // ============================================================ // CASE 3 — HALFWAY-U only (uf=8,vf=0) == lerp(texel(u,v),texel(u+1,v)) // ============================================================ bsample(0, 0, 4'd8, 4'd0, got); w00 = ct32_word(0,0); w10 = ct32_word(1,0); w01 = ct32_word(0,1); w11 = ct32_word(1,1); exp = ref_blend(w00, w10, w01, w11, 4'd8, 4'd0); // vf=0 -> top only if (got !== exp) begin $error("[case3 halfway-u] u=0 v=0 uf=8 vf=0 got=%08x exp=%08x", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE3 halfway-u pass: got=%08x == lerp((0,0),(1,0))", got); // ============================================================ // CASE 4 — CLAMP edge (wms=wmt=1, u=W-1, uf>0): (u+1) clamps to W-1 // => result == texel(W-1,v). Also assert the issued addresses never // exceed the last texel's address (no read outside texture). // ============================================================ wms = WM_CLAMP; wmt = WM_CLAMP; watch_addr = 1'b1; max_addr_seen = 0; bsample(TEXW-1, 1, 4'd8, 4'd0, got); // u=3, u+1 clamps back to 3 watch_addr = 1'b0; // all 4 taps collapse to (3,1) for u; vf=0 keeps top row (v and v+1 // both clamp/stay -> here v=1, v+1=2 but vf=0 picks v=1). With u // clamped, w00==w10 and w01==w11, and vf=0 -> top only == texel(3,1). w00 = ct32_word(3,1); w10 = ct32_word(3,1); // (u+1) clamped to 3 w01 = ct32_word(3,2); w11 = ct32_word(3,2); exp = ref_blend(w00, w10, w01, w11, 4'd8, 4'd0); if (got !== ct32_word(3,1)) begin $error("[case4 clamp] u=3 v=1 uf=8 got=%08x exp=texel(3,1)=%08x", got, ct32_word(3,1)); errors++; end else if (got !== exp) begin $error("[case4 clamp blend] got=%08x exp(blend)=%08x", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE4 clamp pass: got=%08x == texel(3,1), no OOB read", got); // last legal PSMCT32 texel addr = base + (3*64+3)*4 region; the issued // word addr for texel(3,3) is base + (3*64+3)*4 = base + 0x30C. Any // higher would mean reading past column 3. if (max_addr_seen > $signed(CT32_BASE + ((3*(TBWv*64)) + 3)*4)) begin $error("[case4 clamp addr] max issued addr=0x%08x exceeds last-texel addr 0x%08x", max_addr_seen, CT32_BASE + ((3*(TBWv*64)) + 3)*4); errors++; end else $display("[tb_gs_texture_bilinear] CASE4 max issued addr=0x%08x <= last texel 0x%08x", max_addr_seen, CT32_BASE + ((3*(TBWv*64)) + 3)*4); // ============================================================ // CASE 5 — REPEAT edge (wms=wmt=0, u=W-1, uf>0): (u+1) wraps to 0 // => result == lerp(texel(W-1,v),texel(0,v),uf) (vf=0, top row). // ============================================================ wms = WM_REPEAT; wmt = WM_REPEAT; bsample(TEXW-1, 1, 4'd8, 4'd0, got); // u=3, u+1=4 wraps to 0 w00 = ct32_word(3,1); w10 = ct32_word(0,1); // (u+1) wraps to col 0 w01 = ct32_word(3,2); w11 = ct32_word(0,2); exp = ref_blend(w00, w10, w01, w11, 4'd8, 4'd0); if (got !== exp) begin $error("[case5 repeat] u=3 v=1 uf=8 got=%08x exp=lerp(t(3,1),t(0,1))=%08x", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE5 repeat pass: got=%08x == lerp(t(3,1),t(0,1))", got); // ============================================================ // CASE 6 — NEAREST UNCHANGED: BILINEAR_ENABLE=0 instance returns the // single nearest texel and IGNORES u_frac/v_frac. // ============================================================ nsample(1, 2, 4'd0, 4'd0, got); exp = ct32_word(1, 2); if (got !== exp) begin $error("[case6 nearest] u=1 v=2 got=%08x exp=%08x", got, exp); errors++; end // same (u,v) but NON-ZERO frac must give the SAME nearest texel. nsample(1, 2, 4'd15, 4'd15, got); if (got !== exp) begin $error("[case6 nearest frac-ignored] u=1 v=2 uf=15 vf=15 got=%08x exp=%08x (frac must be ignored)", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE6 nearest pass: got=%08x (frac ignored), busy=%0b", got, n_busy); if (n_busy !== 1'b0) begin $error("[case6 nearest busy] nearest instance busy=%0b (must be 0)", n_busy); errors++; end // ============================================================ // CASE 7 — PSMT8 BILINEAR halfway-U: the GUARDRAIL. taps idx(0,*)=0(red) // and idx(1,*)=1(blue) -> CLUT to red/blue/red/blue, lerp blends the // COLORS (purple). Result must equal the color-blend AND be NEITHER // endpoint color (which is what interpolating INDICES then CLUT'ing // once would give). idx(x,y)=x so vf is irrelevant. // ============================================================ psm = PSMT8v; tbp0_base_bytes = T8_BASE; wms = WM_REPEAT; wmt = WM_REPEAT; bsample(0, 0, 4'd8, 4'd0, got); exp = ref_blend(CLUT0, CLUT1, CLUT0, CLUT1, 4'd8, 4'd0); if (got !== exp) begin $error("[case7 t8 color-interp] got=%08x exp(blend red,blue)=%08x", got, exp); errors++; end else if (got === CLUT0 || got === CLUT1) begin $error("[case7 t8 GUARDRAIL] got=%08x is an endpoint color -> indices were interpolated, not colors", got); errors++; end else $display("[tb_gs_texture_bilinear] CASE7 PSMT8 CLUT-before-interp pass: got=%08x (blended red<->blue, neither endpoint)", got); // ============================================================ // CASE 8 — PSMT8 halfway BOTH (uf=vf=8) blue<->green at (1,1)..(2,2). // ============================================================ bsample(1, 1, 4'd8, 4'd8, got); exp = ref_blend(CLUT1, CLUT2, CLUT1, CLUT2, 4'd8, 4'd8); if (got !== exp) begin $error("[case8 t8 halfway-both] got=%08x exp=%08x", got, exp); errors++; end else if (got === CLUT1 || got === CLUT2) begin $error("[case8 t8 GUARDRAIL] got=%08x endpoint color", got); errors++; end else $display("[tb_gs_texture_bilinear] CASE8 PSMT8 halfway-both pass: got=%08x (blue<->green)", got); // ============================================================ // CASE 9 — PSMT8 EXACT texel center == CLUT[idx(2,2)]=green. Also the // NEAREST instance (BILINEAR_ENABLE=0) must return the same CLUT color. // ============================================================ bsample(2, 2, 4'd0, 4'd0, got); if (got !== CLUT2) begin $error("[case9 t8 center] got=%08x exp=CLUT[2]=%08x", got, CLUT2); errors++; end else $display("[tb_gs_texture_bilinear] CASE9 PSMT8 center pass: got=%08x == CLUT[2]", got); nsample(2, 2, 4'd0, 4'd0, got); if (got !== CLUT2) begin $error("[case9 t8 nearest] nearest got=%08x exp=CLUT[2]=%08x", got, CLUT2); errors++; end else $display("[tb_gs_texture_bilinear] CASE9 PSMT8 nearest x-check pass: got=%08x == CLUT[2]", got); // ============================================================ // CASE 10 — PSMT4 halfway-U ACROSS A BYTE BOUNDARY. At y=0 the nibbles // are: off0=low(b0)=idx0, off1=high(b0)=idx1, off2=low(b1)=idx2, // off3=high(b1)=idx3. Sampling u=1 (idx1=blue, HIGH nibble of byte0) // with u+1=2 (idx2=green, LOW nibble of byte1) proves nibble extraction // for ALL FOUR taps + the byte-boundary crossing + CLUT-before-interp. // ============================================================ psm = PSMT4v; tbp0_base_bytes = T4_BASE; bsample(1, 0, 4'd8, 4'd0, got); exp = ref_blend(CLUT1, CLUT2, CLUT1, CLUT2, 4'd8, 4'd0); if (got !== exp) begin $error("[case10 t4 nibble blend] got=%08x exp(blue,green)=%08x", got, exp); errors++; end else if (got === CLUT1 || got === CLUT2) begin $error("[case10 t4 GUARDRAIL] got=%08x endpoint color", got); errors++; end else $display("[tb_gs_texture_bilinear] CASE10 PSMT4 nibble + CLUT-before-interp pass: got=%08x", got); // ============================================================ // CASE 11 — PSMT8 REPEAT edge: u=3,u+1 wraps to col0. idx(3,*)=3(white), // idx(0,*)=0(red) -> blend white<->red. // ============================================================ psm = PSMT8v; tbp0_base_bytes = T8_BASE; wms = WM_REPEAT; wmt = WM_REPEAT; bsample(TEXW-1, 1, 4'd8, 4'd0, got); exp = ref_blend(CLUT3, CLUT0, CLUT3, CLUT0, 4'd8, 4'd0); if (got !== exp) begin $error("[case11 t8 repeat] got=%08x exp(white,red)=%08x", got, exp); errors++; end else $display("[tb_gs_texture_bilinear] CASE11 PSMT8 repeat-edge pass: got=%08x", got); // ============================================================ // CASE 12 — PSMT8 CLAMP edge: u=3,u+1 clamps to col3 -> all u-taps white; // vf=0 -> result == CLUT[idx(3,1)]=white, and NO address reads past the // last texel column. // ============================================================ wms = WM_CLAMP; wmt = WM_CLAMP; watch_addr = 1'b1; max_addr_seen = 0; bsample(TEXW-1, 1, 4'd8, 4'd0, got); watch_addr = 1'b0; if (got !== CLUT3) begin $error("[case12 t8 clamp] got=%08x exp=CLUT[3]=white=%08x", got, CLUT3); errors++; end else $display("[tb_gs_texture_bilinear] CASE12 PSMT8 clamp-edge pass: got=%08x == white", got); if (max_addr_seen > $signed(T8_BASE + ((3*(TBWv*64)) + 3))) begin $error("[case12 t8 clamp addr] max issued=0x%08x exceeds last texel 0x%08x", max_addr_seen, T8_BASE + ((3*(TBWv*64)) + 3)); errors++; end else $display("[tb_gs_texture_bilinear] CASE12 max issued addr=0x%08x <= last texel 0x%08x", max_addr_seen, T8_BASE + ((3*(TBWv*64)) + 3)); $display("[tb_gs_texture_bilinear] errors=%0d", errors); if (errors == 0) $display("[tb_gs_texture_bilinear] PASS"); else $display("[tb_gs_texture_bilinear] FAIL"); $finish; end initial begin #2000000; $error("[tb_gs_texture_bilinear] TIMEOUT"); $finish; end endmodule : tb_gs_texture_bilinear