// retroDE_ps2 — tb_top_psmct32_tile_demo (Ch303) // // TILE-LOCAL combined renderer TB — first tiled-VRAM rung. The Ch302 combined // TME+ABE+ZTE triangle is rendered into an ON-CHIP 16x16 color+Z tile // (CLEAR -> RENDER -> FLUSH); texture still from VRAM. Proves the tile memory // schedule (per the architect), not just final pixels: // // PROOF CLEAR : TP_CLEAR writes all 256 color + 256 Z tile entries. // PROOF HIDDEN : a depth-FAIL pixel in TP_RENDER does NOT read texture and // does NOT write tile color or tile Z. // PROOF VISIBLE: a depth-PASS pixel reads texture and WRITES tile color + tile Z. // PROOF FLUSH : TP_FLUSH emits 256 framebuffer writes (the tile -> VRAM copy). // PROOF RENDER : final scanout == Ch302 result (cleared green; triangle top // blended red->orange/blue->teal over green; bottom occluded green), // which requires correct tile RAM registered-read latency end-to-end. // // tile_phase: 0=OFF 1=CLEAR 2=RENDER 3=FLUSH. `timescale 1ns/1ps module tb_top_psmct32_tile_demo; localparam int H_ACTIVE = 16, V_ACTIVE = 16; localparam int TP_CLEAR=1, TP_RENDER=2, 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(8*1024), .PSMCT32_SWIZZLE(1'b0), .COMBINED_TAZ(1'b1), .TILE_LOCAL(1'b1) ) 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) ); // ---- reference (same triangle/texture as Ch302) ---- 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 // ---- per-pixel render-phase tracers ---- int txr [0:V_ACTIVE-1][0:H_ACTIVE-1]; // texture reads (VRAM) int tcw [0:V_ACTIVE-1][0:H_ACTIVE-1]; // tile_color writes int tzw [0:V_ACTIVE-1][0:H_ACTIVE-1]; // tile_z writes int clear_cw, clear_zw, flush_emits; initial begin for (int y=0;yFB flush writes (render writes go to tile RAM; clear emits // nothing). Count them unconditionally — the very last emit (idx 255) // fires on the cycle tile_phase transitions FLUSH->OFF, so a // phase==FLUSH gate would undercount by one. if (dut.u_gs.raster_pixel_emit) flush_emits <= flush_emits + 1; end end // ---- scanout capture ---- 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;yRENDER->FLUSH). If FLUSH/R_DRAIN never fires, raster_active // never falls and this hangs to the timeout, exposing the bug. @(posedge dut.u_gs.raster_active); @(negedge dut.u_gs.raster_active); repeat(10) @(posedge clk); @(posedge dut.u_pcrtc.end_of_frame); @(posedge clk); capture_armed=1'b1; @(posedge dut.u_pcrtc.end_of_frame); @(posedge clk); capture_armed=1'b0; // PROOF CLEAR + FLUSH (whole-tile counts) if (clear_cw != 256) begin $error("[tile] CLEAR wrote %0d color entries (want 256)", clear_cw); errors++; end if (clear_zw != 256) begin $error("[tile] CLEAR wrote %0d Z entries (want 256)", clear_zw); errors++; end if (flush_emits != 256) begin $error("[tile] FLUSH emitted %0d FB pixels (want 256)", flush_emits); errors++; end for (int py=0; py0.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); pix_pass = clearly_in && (fragz >= (real'(ZBG)+1024.0)); pix_fail = clearly_in && (fragz <= (real'(ZBG)-1024.0)); if (clearly_out) begin n_out++; // outside the triangle: cleared green shows through. if (cap_de[py][px] && !(cap_r[py][px]==8'h00 && cap_g[py][px]==8'h80 && cap_b[py][px]==8'h00)) begin if (errors<10) $error("[tile] OUT (%0d,%0d) not green (%02x,%02x,%02x)",px,py,cap_r[py][px],cap_g[py][px],cap_b[py][px]); errors++; end end // PROOF HIDDEN — depth fail: no texture read, no tile color/Z write. if (pix_fail) begin n_fail++; if (!(txr[py][px]==0 && tcw[py][px]==0 && tzw[py][px]==0)) begin if (errors<10) $error("[tile] HIDDEN (%0d,%0d) did ops tex=%0d cw=%0d zw=%0d (want 0,0,0)",px,py,txr[py][px],tcw[py][px],tzw[py][px]); errors++; end if (cap_de[py][px] && !(cap_r[py][px]==8'h00 && cap_g[py][px]==8'h80 && cap_b[py][px]==8'h00)) begin if (errors<10) $error("[tile] HIDDEN (%0d,%0d) not green",px,py); errors++; end end // PROOF VISIBLE — depth pass: tex read + tile color/Z write; blended color. if (pix_pass) begin n_pass++; if (!(txr[py][px]>=1 && tcw[py][px]>=1 && tzw[py][px]>=1)) begin if (errors<10) $error("[tile] VISIBLE (%0d,%0d) ops tex=%0d cw=%0d zw=%0d (want >=1 each)",px,py,txr[py][px],tcw[py][px],tzw[py][px]); errors++; end uu = wb*7.0 + wc*3.0; if (uu < 4.0) begin er=8'd127; eg=8'd64; eb=8'd0; end // red over green else begin er=8'd0; eg=8'd64; eb=8'd127; end // blue over green if (cap_de[py][px] && !(cap_r[py][px]===er && cap_g[py][px]===eg && cap_b[py][px]===eb)) begin if (errors<10) $error("[tile] VISIBLE (%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_pass<6) begin $error("[tile] too few PASS pixels (%0d)", n_pass); errors++; end if (n_fail<6) begin $error("[tile] too few FAIL pixels (%0d)", n_fail); 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_tile_demo] clear_cw=%0d clear_zw=%0d flush=%0d PASS-pix=%0d FAIL-pix=%0d OUT-pix=%0d errors=%0d", clear_cw, clear_zw, flush_emits, n_pass, n_fail, n_out, errors); if (errors==0) $display("[tb_top_psmct32_tile_demo] PASS"); else $display("[tb_top_psmct32_tile_demo] FAIL"); $finish; end initial begin #150000000; $error("[tb_top_psmct32_tile_demo] TIMEOUT"); $finish; end endmodule : tb_top_psmct32_tile_demo