ec82764bef
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>
138 lines
7.1 KiB
Systemverilog
138 lines
7.1 KiB
Systemverilog
// 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
|