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:
@@ -0,0 +1,121 @@
|
||||
// ============================================================================
|
||||
// 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
|
||||
Reference in New Issue
Block a user