// retroDE_ps2 — tb_top_psmct32_tile2x2_demo (Ch304) // // 2x2 MULTI-TILE renderer TB. ONE combined TME+ABE+ZTE triangle spanning the // 32x32 region (a 2x2 grid of 16x16 tiles), crossing BOTH tile seams (x=16 and // y=16). The renderer re-tests the triangle against each of the 4 tiles // (CLEAR -> RENDER-clipped -> FLUSH per tile). Proves: // PROOF GRID : all 4 tiles (col,row in {0,1}x{0,1}) clear independently // (256 tile_color writes each) and flush (1024 FB emits total). // PROOF SEAM : the WHOLE 32x32 scanout matches a SINGLE screen-space reference // (one continuous barycentric function of screen x,y) — so the // image is identical whether a pixel was rendered in tile (0,0) // or its neighbour. Matching across x=15|16 and y=15|16 with NO // discontinuity IS the seam proof. (Plus an explicit seam-pixel // match count.) // PROOF DEPTH/BLEND : top half blended (red->orange / blue->teal over green), // bottom half occluded green — same per-pixel rule as Ch302/303. `timescale 1ns/1ps module tb_top_psmct32_tile2x2_demo; localparam int H_ACTIVE = 32, V_ACTIVE = 32; localparam int TP_CLEAR=1, TP_FLUSH=3; localparam int ZBG = 'h4000; logic clk, rst_n; initial clk = 1'b0; always #5 clk = ~clk; logic core_go; logic [7:0] r, g, b; logic hsync, vsync, de; logic core_halt, dma_done_seen, frame_seen, raster_overflow; logic frame_toggle, dma_done_toggle; top_psmct32_raster_demo_bram #( .H_ACTIVE(H_ACTIVE), .V_ACTIVE(V_ACTIVE), .VRAM_BYTES(16*1024), .PSMCT32_SWIZZLE(1'b0), .COMBINED_TAZ(1'b1), .TILE_LOCAL(1'b1), .TILE_COLS(2), .TILE_ROWS(2) ) dut ( .clk(clk), .rst_n(rst_n), .core_go(core_go), .r(r), .g(g), .b(b), .hsync(hsync), .vsync(vsync), .de(de), .core_halt(core_halt), .dma_done_seen(dma_done_seen), .frame_seen(frame_seen), .raster_overflow(raster_overflow), .frame_toggle(frame_toggle), .dma_done_toggle(dma_done_toggle), .joy_a_pressed_i(1'b0), .joy_b_pressed_i(1'b0) ); // ---- single screen-space reference: triangle v0(3,3) v1(28,3) v2(16,29) ---- function automatic real bdet(input real ax,input real ay,input real bx,input real by,input real cx,input real cy); bdet=(by-cy)*(ax-cx)+(cx-bx)*(ay-cy); endfunction function automatic real bwa(input real px,input real py,input real ax,input real ay,input real bx,input real by,input real cx,input real cy); bwa=((by-cy)*(px-cx)+(cx-bx)*(py-cy))/bdet(ax,ay,bx,by,cx,cy); endfunction function automatic real bwb(input real px,input real py,input real ax,input real ay,input real bx,input real by,input real cx,input real cy); bwb=((cy-ay)*(px-cx)+(ax-cx)*(py-cy))/bdet(ax,ay,bx,by,cx,cy); endfunction // ---- grid tracers ---- int clear_cw_tile [0:1][0:1]; // tile_color writes per (row,col) during CLEAR int flush_emits; initial begin for (int rr=0;rr<2;rr++) for (int cc=0;cc<2;cc++) clear_cw_tile[rr][cc]=0; flush_emits=0; end always_ff @(posedge clk) if (rst_n) begin if (dut.u_gs.tile_phase==TP_CLEAR && dut.u_gs.tile_color_we) begin int cc, rr; cc=int'(dut.u_gs.tile_col_r); rr=int'(dut.u_gs.tile_row_r); if (cc<2 && rr<2) clear_cw_tile[rr][cc] <= clear_cw_tile[rr][cc] + 1; end if (dut.u_gs.raster_pixel_emit) flush_emits <= flush_emits + 1; // tile mode: only flush emits end // ---- scanout capture (32x32) ---- logic [7:0] cap_r[0:V_ACTIVE-1][0:H_ACTIVE-1], cap_g[0:V_ACTIVE-1][0:H_ACTIVE-1], cap_b[0:V_ACTIVE-1][0:H_ACTIVE-1]; logic cap_de[0:V_ACTIVE-1][0:H_ACTIVE-1]; bit capture_armed; initial begin for (int y=0;y0.06)&&(wb>0.06)&&(wc>0.06); clearly_out = (wa<-0.06)||(wb<-0.06)||(wc<-0.06); fragz = (wa+wb)*real'('h6000) + wc*real'('h2000); // expected color if (clearly_out) begin er=8'h00; eg=8'h80; eb=8'h00; end // green clear else if (clearly_in && (fragz <= real'(ZBG)-1024.0)) begin er=8'h00; eg=8'h80; eb=8'h00; end // occluded green else if (clearly_in && (fragz >= real'(ZBG)+1024.0)) begin uu = wb*7.0 + wc*3.0; if (uu < 3.5) begin er=8'd127; eg=8'd64; eb=8'd0; end else if (uu > 4.5) begin er=8'd0; eg=8'd64; eb=8'd127; end else begin er=8'hxx; eg=8'hxx; eb=8'hxx; end // cell-boundary: skip end else begin er=8'hxx; eg=8'hxx; eb=8'hxx; end // edge/Z-boundary: skip if (er!==8'hxx && cap_de[py][px]) begin n_check++; if (cap_r[py][px]===er && cap_g[py][px]===eg && cap_b[py][px]===eb) begin n_match++; if (px==15||px==16||py==15||py==16) n_seam++; // seam-region pixels that matched end else begin if (errors<12) $error("[t2x2] (%0d,%0d) got(%02x,%02x,%02x) exp(%02x,%02x,%02x)",px,py,cap_r[py][px],cap_g[py][px],cap_b[py][px],er,eg,eb); errors++; end end end end if (n_check < 200) begin $error("[t2x2] too few checkable pixels (%0d)", n_check); errors++; end if (n_match != n_check) begin $error("[t2x2] image match %0d/%0d (seam/tiling error)", n_match, n_check); errors++; end if (n_seam < 20) begin $error("[t2x2] too few SEAM-region matches (%0d) — seam not exercised", n_seam); errors++; end if (!core_halt) begin $error("core_halt low"); errors++; end if (raster_overflow) begin $error("raster_overflow"); errors++; end $display("[tb_top_psmct32_tile2x2_demo] clears(per-tile)=[%0d %0d %0d %0d] flush=%0d match=%0d/%0d seam_matches=%0d errors=%0d", clear_cw_tile[0][0],clear_cw_tile[0][1],clear_cw_tile[1][0],clear_cw_tile[1][1], flush_emits, n_match, n_check, n_seam, errors); if (errors==0) $display("[tb_top_psmct32_tile2x2_demo] PASS"); else $display("[tb_top_psmct32_tile2x2_demo] FAIL"); $finish; end initial begin #250000000; $error("[tb_top_psmct32_tile2x2_demo] TIMEOUT"); $finish; end endmodule : tb_top_psmct32_tile2x2_demo