// retroDE_ps2 — tb_top_psmct32_zbuffer_demo (Brick 2b) // // TOP-LEVEL Z-buffer (depth test) demo TB for the BRAM BOARD VARIANT. // Proves the synthesizable FLAT Z-tested PSMCT32 SPRITE path renders // end-to-end through `top_psmct32_raster_demo_bram` exactly as a board // load would SHOW on HDMI. // // EE bootlet (bios_zbuffer.mem) + GIF payload (payload_zbuffer.mem): // U1 PACKED -> NEAR red SPRITE, Z=0x200, x[0..11] y[0..7], // TEST_1.ZTE=1 ZTST=GEQUAL, ZBUF_1 ZBP=1 PSMZ32 ZMSK=0. // U2 PACKED -> FAR blue SPRITE, Z=0x100, x[4..15] y[0..7] (drawn // second; GIF context PRIM/FRAME/TEST/ZBUF persist). // // The Z buffer (ZBP=1 -> byte base 0x800) starts cleared (VRAM power-on // zero). NEAR draws first (Z 0x200 >= 0 everywhere it covers, passes), // stamping Z=0x200 at x[0..11]. FAR draws second; with GEQUAL its // Z=0x100 is BEHIND the near Z(0x200) in the overlap x[4..11], so it // FAILS and does NOT overwrite — near wins regardless of draw order. // // near-only x[0..3] -> RED (0xFF,0x00,0x00) // overlap x[4..11] -> RED (near wins; NOT blue last-drawn) // far-only x[12..15] -> BLUE (0x00,0x00,0xFF; stored Z=0 there) // // The overlap guard asserts the overlap is RED, NOT BLUE — proving the // depth test actually gated the FAR write rather than last-write-wins. `timescale 1ns/1ps module tb_top_psmct32_zbuffer_demo; localparam int H_ACTIVE = 16; localparam int V_ACTIVE = 8; logic clk; logic 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; logic dma_done_seen; logic frame_seen; logic raster_overflow; logic frame_toggle; logic dma_done_toggle; top_psmct32_raster_demo_bram #( .H_ACTIVE (H_ACTIVE), .V_ACTIVE (V_ACTIVE), .PSMCT32_SWIZZLE(1'b0) ) 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) ); // ----- Expected colors ----- localparam logic [7:0] NEAR_R = 8'hFF, NEAR_G = 8'h00, NEAR_B = 8'h00; // red localparam logic [7:0] FAR_R = 8'h00, FAR_G = 8'h00, FAR_B = 8'hFF; // blue // ----- Frame capture (delayed counters: BRAM read is registered) ----- logic [7:0] cap_r [0:V_ACTIVE-1][0:H_ACTIVE-1]; logic [7:0] cap_g [0:V_ACTIVE-1][0:H_ACTIVE-1]; logic [7:0] 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; y < V_ACTIVE; y++) for (int x = 0; x < H_ACTIVE; x++) begin cap_r[y][x] = 8'd0; cap_g[y][x] = 8'd0; cap_b[y][x] = 8'd0; cap_de[y][x] = 1'b0; end capture_armed = 1'b0; end logic [31:0] hcnt_d; logic [31:0] vcnt_d; always_ff @(posedge clk) begin if (!rst_n) begin hcnt_d <= 32'd0; vcnt_d <= 32'd0; end else begin hcnt_d <= 32'(dut.u_pcrtc.hcnt); vcnt_d <= 32'(dut.u_pcrtc.vcnt); end end always_ff @(posedge clk) begin if (rst_n && capture_armed && de && (vcnt_d < V_ACTIVE) && (hcnt_d < H_ACTIVE)) begin cap_r [vcnt_d][hcnt_d] <= r; cap_g [vcnt_d][hcnt_d] <= g; cap_b [vcnt_d][hcnt_d] <= b; cap_de[vcnt_d][hcnt_d] <= 1'b1; end end int errors; int near_ok; int overlap_ok; int far_ok; initial begin errors = 0; near_ok = 0; overlap_ok = 0; far_ok = 0; end // Returns 1 (via `ok` output) when the pixel matched; iverilog-12 // doesn't support task reference ports, so the caller increments // the per-region counter from `ok`. task automatic chk(input int x, input int y, input logic [7:0] er, input logic [7:0] eg, input logic [7:0] eb, input string label, output bit ok); ok = 1'b0; if (!cap_de[y][x]) begin $error("%s (%0d,%0d) DE never asserted", label, x, y); errors = errors + 1; end else if (cap_r[y][x] !== er || cap_g[y][x] !== eg || cap_b[y][x] !== eb) begin $error("[%s] (%0d,%0d) got (%02x,%02x,%02x) expected (%02x,%02x,%02x)", label, x, y, cap_r[y][x], cap_g[y][x], cap_b[y][x], er, eg, eb); errors = errors + 1; end else begin ok = 1'b1; end endtask initial begin rst_n = 1'b0; core_go = 1'b0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (8) @(posedge clk); @(negedge clk); core_go = 1'b1; @(negedge clk); core_go = 1'b0; wait (core_halt == 1'b1); repeat (4) @(posedge clk); wait (dma_done_seen == 1'b1); repeat (10) @(posedge clk); if (dut.xfer_busy == 1'b1) wait (dut.xfer_busy == 1'b0); if (dut.u_gs.raster_active == 1'b1) wait (dut.u_gs.raster_active == 1'b0); 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; begin bit ok; for (int y = 0; y < 8; y++) begin // near-only x[0..3] -> RED for (int x = 0; x < 4; x++) begin chk(x, y, NEAR_R, NEAR_G, NEAR_B, "near", ok); if (ok) near_ok = near_ok + 1; end // overlap x[4..11] -> RED (near wins, NOT far/last-drawn) for (int x = 4; x < 12; x++) begin chk(x, y, NEAR_R, NEAR_G, NEAR_B, "overlap", ok); if (ok) overlap_ok = overlap_ok + 1; // Guard: overlap must NOT be the FAR (last-drawn) color. if (cap_r[y][x] === FAR_R && cap_g[y][x] === FAR_G && cap_b[y][x] === FAR_B) begin $error("[overlap] (%0d,%0d) is FAR (last-drawn) — depth NOT gated!", x, y); errors = errors + 1; end end // far-only x[12..15] -> BLUE for (int x = 12; x < 16; x++) begin chk(x, y, FAR_R, FAR_G, FAR_B, "far", ok); if (ok) far_ok = far_ok + 1; end end end if (!core_halt) begin $error("core_halt low at end"); errors = errors + 1; end if (!dma_done_seen) begin $error("dma_done_seen never latched"); errors = errors + 1; end if (!frame_seen) begin $error("frame_seen never latched"); errors = errors + 1; end if (raster_overflow) begin $error("raster_overflow set"); errors = errors + 1; end $display("[tb_top_psmct32_zbuffer_demo] near_ok=%0d/32 overlap_ok=%0d/64 far_ok=%0d/32 raster_emits=%0d errors=%0d", near_ok, overlap_ok, far_ok, dut.u_gs.raster_pixel_emit_count, errors); if (errors == 0) $display("[tb_top_psmct32_zbuffer_demo] PASS"); else $display("[tb_top_psmct32_zbuffer_demo] FAIL"); $finish; end initial begin #20000000; $error("[tb_top_psmct32_zbuffer_demo] TIMEOUT"); $finish; end endmodule : tb_top_psmct32_zbuffer_demo