Files
retroDE_ps2/sim/tb/top/tb_gs_tile_zflush.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

138 lines
7.1 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_tile_zflush (Ch323 Brick 1b — focused FSM-sequencing TB)
//
// Proves the Ch323 TP_ZFLUSH phase BEFORE the big two-batch proof (Codex: fast failure
// surface for state-sequencing bugs). Renders ONE 16x16 combined-TAZ tile with
// TILE_SPILL_ENABLE=1 and checks the Z-flush stream:
// - exactly 256 Z emits (one per tile pixel — no missing last / no duplicate first)
// - each z_flush_addr = pixel_index*4 (Z-backing-relative, 32-bit Z), each index once
// - z_flush_data == the tile Z RAM contents at flush time (snooped via tile_z writes:
// CLEAR sets all 256 to TILE_CLEAR_Z, RENDER overwrites covered pixels) — Z is NOT
// truncated/endian-swapped
// - color flush count UNCHANGED (256, full tile) — the color path is untouched
// - the FSM actually entered TP_ZFLUSH (after the color FLUSH)
`timescale 1ns/1ps
module tb_gs_tile_zflush;
localparam int H_ACTIVE = 16, V_ACTIVE = 16;
localparam logic [2:0] TP_FLUSH = 3'd3, TP_ZFLUSH = 3'd6;
localparam logic [31:0] TILE_CLEAR_Z = 32'h0000_4000; // matches gs_stub
logic clk = 0; always #5 clk = ~clk;
logic rst_n, core_go;
logic [7:0] r,g,b; logic hsync,vsync,de;
logic core_halt, dma_done_seen, frame_seen, raster_overflow, frame_toggle, dma_done_toggle;
logic z_flush_emit; logic [31:0] z_flush_addr, z_flush_data;
// Reload staging: this TB exercises Z-FLUSH, so make reload a re-clear (staging returns the
// clear values, always warm) — the tile starts as if cleared, so the Z-flush check is unchanged.
logic reload_start; logic [7:0] tile_reload_raddr;
wire tile_reload_ready = 1'b1;
wire [31:0] tile_reload_color = 32'hFF00_8000; // TILE_CLEAR_COLOR
wire [31:0] tile_reload_z = 32'h0000_4000; // TILE_CLEAR_Z
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), .TILE_SPILL_ENABLE(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),
.z_flush_emit_o(z_flush_emit), .z_flush_addr_o(z_flush_addr), .z_flush_data_o(z_flush_data),
.reload_start_o(reload_start), .tile_reload_raddr_o(tile_reload_raddr),
.tile_reload_ready_i(tile_reload_ready), .tile_reload_color_i(tile_reload_color),
.tile_reload_z_i(tile_reload_z)
);
// ---- snoop the tile Z RAM contents (mirror), indexed by TILE index (waddr 0..255):
// CLEAR writes all 256, RENDER overwrites covered pixels.
logic [31:0] z_mirror [0:255];
// ---- capture the Z-flush stream keyed by SCREEN pixel index (addr>>2). The Z is flushed
// at the screen position (screen_index*4), mirroring the color flush, so we map tile index
// -> screen index = (tile_oy+y)*fbw*64 + (tile_ox+x) at compare time. Single tile @ origin.
logic [31:0] z_got [0:2047];
bit z_seen [0:2047];
int z_count, color_count;
logic [5:0] fbw_seen;
bit saw_zflush, zflush_after_flush;
logic [2:0] phase_d;
initial begin
for (int i=0;i<256;i++) z_mirror[i]=32'hX;
for (int i=0;i<2048;i++) begin z_got[i]=32'h0; z_seen[i]=1'b0; end
z_count=0; color_count=0; fbw_seen=6'd1; saw_zflush=0; zflush_after_flush=0; phase_d=3'd0;
end
always_ff @(posedge clk) begin
if (rst_n) begin
// tile Z RAM mirror (any phase that writes Z: CLEAR + RENDER).
if (dut.u_gs.tile_z_we) z_mirror[dut.u_gs.tile_z_waddr] <= dut.u_gs.tile_z_wdata;
// color flush emits (raster_pixel_emit) — must stay 256 (full tile), unchanged.
if (dut.u_gs.raster_pixel_emit) begin
color_count <= color_count + 1;
fbw_seen <= dut.u_gs.ras_fbw; // FB width (×64 px) for the screen-index map
end
// Z-flush stream capture, keyed by screen pixel index = addr>>2.
if (z_flush_emit) begin
int sidx; sidx = int'(z_flush_addr >> 2);
if (sidx >= 0 && sidx < 2048) begin
z_got[sidx] <= z_flush_data; z_seen[sidx] <= 1'b1;
end
z_count <= z_count + 1;
end
// phase tracking: did we enter TP_ZFLUSH, and did it follow TP_FLUSH?
phase_d <= dut.u_gs.tile_phase;
if (dut.u_gs.tile_phase == TP_ZFLUSH) begin
saw_zflush <= 1'b1;
if (phase_d == TP_FLUSH) zflush_after_flush <= 1'b1;
end
end
end
int errors;
task automatic chk(input bit c, input string m);
if (!c) begin errors++; $display("[zflush] FAIL: %s", m); end endtask
initial begin
errors=0; rst_n=0; core_go=0;
repeat(4) @(posedge clk); rst_n=1; 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);
@(posedge dut.u_gs.raster_active);
@(negedge dut.u_gs.raster_active); // falls only after TP_ZFLUSH completes
repeat(10) @(posedge clk);
// ---- assertions ----
chk(saw_zflush, "FSM never entered TP_ZFLUSH");
chk(zflush_after_flush, "TP_ZFLUSH did not follow TP_FLUSH");
chk(z_count == 256, $sformatf("z_flush count=%0d exp 256 (off-by-one?)", z_count));
chk(color_count == 256, $sformatf("color flush count=%0d exp 256 (color path disturbed!)", color_count));
begin int miss=0, mism=0, stride; stride = int'(fbw_seen) * 64;
for (int i=0;i<256;i++) begin
int x, y, sidx; x = i & 8'h0F; y = (i >> 4) & 8'h0F;
sidx = y*stride + x; // single tile @ origin (ox=oy=0)
if (!z_seen[sidx]) miss++;
else if (z_got[sidx] !== z_mirror[i]) begin
mism++;
if (mism<=6) $display("[zflush] Z mismatch tile idx %0d (screen %0d): flushed %08x exp(tileZ) %08x",
i, sidx, z_got[sidx], z_mirror[i]);
end
end
chk(miss==0, $sformatf("%0d tile pixels never Z-flushed at their screen addr", miss));
chk(mism==0, $sformatf("%0d Z values != tile Z RAM", mism));
end
chk(!raster_overflow, "raster_overflow set");
$display("[zflush] z_count=%0d color_count=%0d saw_zflush=%0d after_flush=%0d errors=%0d",
z_count, color_count, saw_zflush, zflush_after_flush, errors);
if (errors==0) $display("[tb_gs_tile_zflush] PASS");
else $display("[tb_gs_tile_zflush] FAIL");
$finish;
end
initial begin #20000000; $display("[tb_gs_tile_zflush] TIMEOUT"); $finish; end
endmodule