Files
retroDE_ps2/sim/tb/gif_gs/tb_gs_tile_reload.sv
T
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

122 lines
6.2 KiB
Systemverilog

// ============================================================================
// tb_gs_tile_reload.sv (Ch323 Brick 2)
//
// Unit-proves the tile color+Z reload staging engine (the gs_texture_cache clone):
// preload a behavioral single-beat EMIF with a 16x16 tile's color (at COLOR_BASE)
// and Z (at a DISTINCT Z_BASE), strided STRIDE_BYTES/row; fill; then verify:
// (1) reload_done; color_beats=32, z_beats=32 (16 rows x 2 beats each); rd_errs=0
// (2) every served (color,Z) for tile index 0..255 == the source (1-cycle read)
// (3) a forced non-OKAY rresp increments rd_errs
// ============================================================================
`timescale 1ns/1ps
module tb_gs_tile_reload;
localparam int TILE_W=16, TILE_H=16, STRIDE_BYTES=256, ROW_BEATS=2, COLOR_W=32;
localparam [29:0] COLOR_BASE = 30'd0;
localparam [29:0] Z_BASE = 30'h0000_4000; // distinct, small for the TB mem
localparam int N_ENTRIES = TILE_W*TILE_H; // 256
logic axi_clk=0, serve_clk=0, axi_rst_n=0;
always #5 axi_clk = ~axi_clk;
always #7 serve_clk = ~serve_clk; // asynchronous
logic reload_start, reload_done;
logic [31:0] color_beats, z_beats, rd_errs;
logic [29:0] araddr; logic [1:0] arburst; logic [6:0] arid;
logic [7:0] arlen; logic [2:0] arsize; logic arvalid, arready;
logic [255:0] rdata; logic [1:0] rresp; logic rlast, rvalid, rready;
logic [7:0] raddr; logic [31:0] color_o, z_o; logic reload_ready;
function automatic [31:0] cval(input int i); cval = 32'hC0DE_0000 | i[31:0]; endfunction
function automatic [31:0] zval(input int i); zval = 32'h5A5A_0000 | i[31:0]; endfunction
int err_beat = -1;
gs_tile_reload #(
.COLOR_BASE(COLOR_BASE), .Z_BASE(Z_BASE), .TILE_W(TILE_W), .TILE_H(TILE_H),
.STRIDE_BYTES(STRIDE_BYTES), .ROW_BEATS(ROW_BEATS), .COLOR_W(COLOR_W)
) dut (
.axi_clk(axi_clk), .axi_rst_n(axi_rst_n), .reload_start(reload_start), .reload_base(30'd0),
.reload_done(reload_done), .color_beats(color_beats), .z_beats(z_beats), .rd_errs(rd_errs),
.araddr(araddr), .arburst(arburst), .arid(arid), .arlen(arlen), .arsize(arsize),
.arvalid(arvalid), .arready(arready), .rdata(rdata), .rresp(rresp), .rlast(rlast),
.rvalid(rvalid), .rready(rready),
.serve_clk(serve_clk), .raddr(raddr), .color_o(color_o), .z_o(z_o), .reload_ready(reload_ready)
);
// behavioral 256-bit EMIF read model, preloaded with the strided tile color+Z.
logic [255:0] mem [0:1023];
initial begin
for (int i=0;i<1024;i++) mem[i]=256'd0;
for (int row=0; row<TILE_H; row++) for (int col=0; col<TILE_W; col++) begin
int idx; logic [29:0] ca, za;
idx = row*TILE_W + col;
ca = COLOR_BASE + row*STRIDE_BYTES + col*4;
za = Z_BASE + row*STRIDE_BYTES + col*4;
mem[ca >> 5][((ca >> 2) & 3'd7)*32 +: 32] = cval(idx);
mem[za >> 5][((za >> 2) & 3'd7)*32 +: 32] = zval(idx);
end
end
int beat_cnt;
typedef enum logic [1:0] { S_IDLE,S_WAIT,S_DATA } sst_t; sst_t sst;
logic [29:0] ba; logic [3:0] dly;
always_ff @(posedge axi_clk) begin
if (!axi_rst_n) begin sst<=S_IDLE; arready<=0; rvalid<=0; rlast<=0; rresp<=0; rdata<=0; dly<=0; beat_cnt<=0;
end else begin
arready<=0; rvalid<=0; rlast<=0;
case (sst)
S_IDLE: if (arvalid) begin ba<=araddr; arready<=1; dly<=4'd3; sst<=S_WAIT; end
S_WAIT: if (dly==0) sst<=S_DATA; else dly<=dly-1'b1;
S_DATA: if (rready) begin
rdata <= mem[ba >> 5];
rresp <= (beat_cnt==err_beat) ? 2'b10 : 2'b00;
rvalid <= 1; rlast <= 1; beat_cnt <= beat_cnt+1; sst<=S_IDLE;
end
endcase
end
end
int errors=0;
task automatic chk(input bit c, input string m); if (!c) begin errors++; $display("[tilereload] FAIL: %s", m); end endtask
task automatic rd(input int idx, output logic [31:0] cg, output logic [31:0] zg);
@(posedge serve_clk); raddr <= idx[7:0];
@(posedge serve_clk); @(posedge serve_clk); cg = color_o; zg = z_o;
endtask
logic [31:0] cg, zg;
initial begin
reload_start=0; raddr=0;
repeat(4) @(posedge axi_clk); axi_rst_n=1; repeat(2) @(posedge axi_clk);
// clean fill (PULSE reload_start once — rising-edge strobe)
@(posedge axi_clk) reload_start<=1'b1;
@(posedge axi_clk) reload_start<=1'b0;
begin int g=0; while(!reload_done && g<5000) begin @(posedge axi_clk); g++; end end
chk(reload_done, "reload_done never asserted");
chk(color_beats==32, $sformatf("color_beats=%0d exp 32", color_beats));
chk(z_beats==32, $sformatf("z_beats=%0d exp 32", z_beats));
chk(rd_errs==0, $sformatf("rd_errs=%0d exp 0", rd_errs));
begin int g=0; while(!reload_ready && g<200) begin @(posedge serve_clk); g++; end end
chk(reload_ready, "reload_ready never synced");
for (int i=0;i<N_ENTRIES;i++) begin
rd(i, cg, zg);
chk(cg===cval(i), $sformatf("color[%0d]=%08x exp %08x", i, cg, cval(i)));
chk(zg===zval(i), $sformatf("z[%0d]=%08x exp %08x", i, zg, zval(i)));
end
$display("[tilereload] clean: done=%0d color_beats=%0d z_beats=%0d rd_errs=%0d entries=%0d errors=%0d",
reload_done, color_beats, z_beats, rd_errs, N_ENTRIES, errors);
// err-inject: reset, force SLVERR on beat 5
axi_rst_n=0; err_beat=5; repeat(3) @(posedge axi_clk); axi_rst_n=1; repeat(2) @(posedge axi_clk);
@(posedge axi_clk) reload_start<=1'b1; // PULSE again -> rising edge -> refill
@(posedge axi_clk) reload_start<=1'b0;
begin int g=0; while(!reload_done && g<5000) begin @(posedge axi_clk); g++; end end
chk(rd_errs==1, $sformatf("forced-err rd_errs=%0d exp 1", rd_errs));
$display("[tilereload] err-inject: rd_errs=%0d (exp 1)", rd_errs);
if (errors==0) $display("[tb_gs_tile_reload] PASS"); else $display("[tb_gs_tile_reload] FAIL (%0d errors)", errors);
$finish;
end
initial begin #800000; $display("[tb_gs_tile_reload] TIMEOUT"); $finish; end
endmodule