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>
This commit is contained in:
2026-06-29 20:10:50 -04:00
commit ec82764bef
2462 changed files with 2174303 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
// 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