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>
129 lines
6.7 KiB
Systemverilog
129 lines
6.7 KiB
Systemverilog
// retroDE_ps2 — tb_top_psmct32_texalpha_demo (Ch344)
|
|
//
|
|
// TOP-LEVEL TEXTURED + source-over ALPHA SPRITE demo, end-to-end through the BRAM BOARD VARIANT
|
|
// (top_psmct32_raster_demo_bram) with SPRITE_TEX_ALPHA=1 — the integration the de25 board top fits,
|
|
// driving gs_stub through the REGISTERED read2 wrapper (the model the unit TB now matches).
|
|
//
|
|
// EE bootlet (bios_texalpha.mem) + GIF payload (payload_texalpha.mem):
|
|
// U1 -> upload an 8x8 checkerboard-ALPHA texture (opaque-white / fully-transparent).
|
|
// U2 -> opaque BG SPRITE (ABE=0), solid blue, full 64x64.
|
|
// U3 -> textured-alpha SPRITE (PRIM SPRITE+TME+ABE, source-over) over screen (16,16)-(48,48).
|
|
//
|
|
// Per pixel of the overlay: As = TEXEL alpha. Opaque texel (A=0x80) -> Cv = Cs (the gray texel,
|
|
// identity MODULATE by a white tint); transparent texel (A=0) -> Cv = Cd (the blue BG shows through).
|
|
//
|
|
// Verification (PCRTC scanout RGB — what a board wires to the video PHY):
|
|
// - the BORDER (outside the overlay rect) is the pure blue BG.
|
|
// - EVERY overlay pixel is EITHER gray (0xC0,0xC0,0xC0) OR blue BG — nothing else (binary alpha,
|
|
// no garbage), and BOTH appear in quantity (the checkerboard + per-texel alpha both rendered).
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module tb_top_psmct32_texalpha_demo;
|
|
|
|
localparam int H_ACTIVE = 64, V_ACTIVE = 64;
|
|
localparam int SX0 = 16, SY0 = 16, SX1 = 48, SY1 = 48; // overlay sprite rect
|
|
|
|
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(32*1024), .PSMCT32_SWIZZLE(1'b0),
|
|
.SPRITE_TEX_ALPHA(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)
|
|
);
|
|
|
|
// expected colors
|
|
localparam logic [7:0] BG_R=8'h00, BG_G=8'h00, BG_B=8'hC0; // blue BG
|
|
localparam logic [7:0] GR_R=8'hC0, GR_G=8'hC0, GR_B=8'hC0; // opaque-texel gray
|
|
|
|
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;y<V_ACTIVE;y++) for (int x=0;x<H_ACTIVE;x++) begin cap_r[y][x]=0;cap_g[y][x]=0;cap_b[y][x]=0;cap_de[y][x]=0; end
|
|
capture_armed=1'b0;
|
|
end
|
|
logic [31:0] hcnt_d, vcnt_d;
|
|
always_ff @(posedge clk) begin
|
|
if (!rst_n) begin hcnt_d<=0; vcnt_d<=0; end
|
|
else begin hcnt_d<=32'(dut.u_pcrtc.hcnt); vcnt_d<=32'(dut.u_pcrtc.vcnt); end
|
|
end
|
|
always_ff @(posedge clk)
|
|
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
|
|
|
|
function automatic bit in_sprite(input int x, input int y);
|
|
in_sprite = (x>=SX0) && (x<SX1) && (y>=SY0) && (y<SY1);
|
|
endfunction
|
|
function automatic bit is_bg (input int x, input int y); is_bg = (cap_b[y][x]==BG_B)&&(cap_g[y][x]==BG_G)&&(cap_r[y][x]==BG_R); endfunction
|
|
function automatic bit is_gray(input int x, input int y); is_gray= (cap_b[y][x]==GR_B)&&(cap_g[y][x]==GR_G)&&(cap_r[y][x]==GR_R); endfunction
|
|
|
|
int errors, border_ok, spr_gray, spr_bg, spr_bad;
|
|
initial begin errors=0; border_ok=0; spr_gray=0; spr_bg=0; spr_bad=0; end
|
|
|
|
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;
|
|
|
|
for (int y=0;y<V_ACTIVE;y++) begin
|
|
for (int x=0;x<H_ACTIVE;x++) begin
|
|
if (!cap_de[y][x]) begin
|
|
if (errors<8) $error("[texalpha] (%0d,%0d) DE never asserted", x, y); errors++;
|
|
end else if (in_sprite(x,y)) begin
|
|
if (is_gray(x,y)) spr_gray++;
|
|
else if (is_bg(x,y)) spr_bg++;
|
|
else begin
|
|
spr_bad++;
|
|
if (errors<12) $error("[texalpha] overlay (%0d,%0d)=(%02x,%02x,%02x) is neither gray nor BG",
|
|
x, y, cap_b[y][x], cap_g[y][x], cap_r[y][x]);
|
|
errors++;
|
|
end
|
|
end else begin
|
|
// border: pure BG
|
|
if (is_bg(x,y)) border_ok++;
|
|
else begin
|
|
if (errors<12) $error("[texalpha] border (%0d,%0d)=(%02x,%02x,%02x) not BG blue",
|
|
x, y, cap_b[y][x], cap_g[y][x], cap_r[y][x]);
|
|
errors++;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// The checkerboard + per-texel alpha must BOTH render (gray opaque checks AND BG-through checks).
|
|
if (spr_gray < 64) begin $error("[texalpha] too few opaque (gray) overlay px (%0d) — texel/blend dead", spr_gray); errors++; end
|
|
if (spr_bg < 64) begin $error("[texalpha] too few transparent overlay px (%0d) — texel ALPHA not honored", spr_bg); errors++; end
|
|
if (spr_bad != 0) begin $error("[texalpha] %0d overlay px were neither gray nor BG", spr_bad); errors++; end
|
|
if (raster_overflow) begin $error("[texalpha] raster_overflow set"); errors++; end
|
|
|
|
$display("[tb_top_psmct32_texalpha_demo] border_ok=%0d overlay gray=%0d bg=%0d bad=%0d emits=%0d errors=%0d",
|
|
border_ok, spr_gray, spr_bg, spr_bad, dut.u_gs.raster_pixel_emit_count, errors);
|
|
if (errors==0) begin $display("[tb_top_psmct32_texalpha_demo] PASS"); $finish; end
|
|
else $fatal(1, "[tb_top_psmct32_texalpha_demo] FAIL (%0d errors)", errors);
|
|
end
|
|
initial begin #30000000; $error("[tb_top_psmct32_texalpha_demo] TIMEOUT"); $finish; end
|
|
endmodule : tb_top_psmct32_texalpha_demo
|