Files
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

175 lines
6.0 KiB
Systemverilog

// retroDE_ps2 — tb_gs_stub
//
// Milestone A slice: verifies gs_stub's BGCOLOR latch, non-BGCOLOR
// privileged-register logging, stable-state holding, and trace emission.
// Does not instantiate platform_video_stub (covered separately where a full
// Milestone A test is warranted).
//
// Plan ref: docs/stub_module_plan.md (Initial testbench plan).
`timescale 1ns/1ps
module tb_gs_stub;
logic clk;
logic rst_n;
initial clk = 1'b0;
always #5 clk = ~clk;
// ------------------------------------------------------------------
// DUT and trace sink
// ------------------------------------------------------------------
logic reg_wr_en;
logic [15:0] reg_wr_addr;
logic [63:0] reg_wr_data;
logic [7:0] bg_r, bg_g, bg_b;
logic ev_valid;
trace_pkg::subsys_e ev_subsys;
trace_pkg::event_e ev_event;
logic [63:0] ev_arg0, ev_arg1, ev_arg2, ev_arg3;
logic [31:0] ev_flags;
gs_stub u_gs (
.clk(clk), .rst_n(rst_n),
.reg_wr_en(reg_wr_en), .reg_wr_addr(reg_wr_addr), .reg_wr_data(reg_wr_data),
.gif_reg_wr_en(1'b0), .gif_reg_num(8'd0), .gif_reg_data(64'd0),
.prim_q(), .rgbaq_q(), .xyz2_q(), .xyzf2_q(), .frame_1_q(), .zbuf_1_q(),
.prim_complete(), .prim_complete_count(),
.prim_v0_q(), .prim_v1_q(), .prim_v2_q(),
.prim_color_q(),
.prim_color_v0_q(), .prim_color_v1_q(), .prim_color_v2_q(),
.prim_v0_decoded_q(), .prim_v1_decoded_q(), .prim_v2_decoded_q(),
.prim_v0_color_decoded_q(), .prim_v1_color_decoded_q(), .prim_v2_color_decoded_q(),
.pixel_emit(), .pixel_emit_count(),
.pixel_x_q(), .pixel_y_q(),
.pixel_color_q(),
.pixel_fbp_q(), .pixel_fbw_q(), .pixel_psm_q(),
.pixel_fb_addr_q(),
.raster_pixel_emit(), .raster_pixel_emit_count(),
.raster_pixel_x_q(), .raster_pixel_y_q(),
.raster_pixel_color_q(),
.raster_pixel_fb_addr_q(),
.raster_active(), .raster_overflow(),
.raster_degenerate(),
.bg_r(bg_r), .bg_g(bg_g), .bg_b(bg_b),
.ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event),
.ev_arg0(ev_arg0), .ev_arg1(ev_arg1),
.ev_arg2(ev_arg2), .ev_arg3(ev_arg3),
.ev_flags(ev_flags)
);
trace_sink_stub #(.FILENAME("gs.trace"), .SINK_LABEL("gs")) u_trace_gs (
.clk(clk), .rst_n(rst_n),
.ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event),
.ev_arg0(ev_arg0), .ev_arg1(ev_arg1),
.ev_arg2(ev_arg2), .ev_arg3(ev_arg3),
.ev_flags(ev_flags)
);
// ------------------------------------------------------------------
// Checkers
// ------------------------------------------------------------------
int bgcolor_events;
int mode_events;
int errors;
initial begin
bgcolor_events = 0;
mode_events = 0;
errors = 0;
end
always_ff @(posedge clk) begin
if (rst_n && ev_valid) begin
if (ev_event == trace_pkg::EV_BGCOLOR) bgcolor_events <= bgcolor_events + 1;
if (ev_event == trace_pkg::EV_MODE) mode_events <= mode_events + 1;
end
end
task automatic write_reg(input logic [15:0] offset, input logic [63:0] data);
@(negedge clk);
reg_wr_en = 1'b1;
reg_wr_addr = offset;
reg_wr_data = data;
@(negedge clk);
reg_wr_en = 1'b0;
reg_wr_addr = 16'd0;
reg_wr_data = 64'd0;
endtask
task automatic expect_bg(input logic [7:0] r, input logic [7:0] g, input logic [7:0] b,
input string label);
if (bg_r !== r || bg_g !== g || bg_b !== b) begin
$error("[tb_gs_stub] %s: bg=(%02h,%02h,%02h) expected=(%02h,%02h,%02h)",
label, bg_r, bg_g, bg_b, r, g, b);
errors = errors + 1;
end
endtask
// ------------------------------------------------------------------
// Stimulus
// ------------------------------------------------------------------
initial begin
rst_n = 1'b0;
reg_wr_en = 1'b0;
reg_wr_addr = 16'd0;
reg_wr_data = 64'd0;
repeat (4) @(posedge clk);
rst_n = 1'b1;
repeat (2) @(posedge clk);
// Default reset value should be mid-grey.
expect_bg(8'h40, 8'h40, 8'h40, "post-reset default");
// Write BGCOLOR = (R=0xAA, G=0xBB, B=0xCC).
write_reg(16'h00E0, 64'h0000_0000_00AA_BBCC);
repeat (2) @(posedge clk);
expect_bg(8'hAA, 8'hBB, 8'hCC, "after BGCOLOR write");
// Stable-state: color should hold for many cycles without additional writes.
repeat (20) @(posedge clk);
expect_bg(8'hAA, 8'hBB, 8'hCC, "stable hold");
// Write to a non-BGCOLOR privileged register (PMODE at offset 0x000) —
// should log EV_MODE but leave BGCOLOR untouched.
write_reg(16'h0000, 64'h0000_0000_0000_0001);
repeat (2) @(posedge clk);
expect_bg(8'hAA, 8'hBB, 8'hCC, "after PMODE write");
// Overwrite BGCOLOR with a second value.
write_reg(16'h00E0, 64'h0000_0000_0011_2233);
repeat (2) @(posedge clk);
expect_bg(8'h11, 8'h22, 8'h33, "after second BGCOLOR write");
// ------------------------------------------------------------------
$display("[tb_gs_stub] bgcolor_events=%0d mode_events=%0d errors=%0d",
bgcolor_events, mode_events, errors);
if (bgcolor_events < 2)
$error("[tb_gs_stub] expected at least 2 BGCOLOR events, got %0d", bgcolor_events);
if (mode_events < 1)
$error("[tb_gs_stub] expected at least 1 MODE event, got %0d", mode_events);
if (errors == 0 && bgcolor_events >= 2 && mode_events >= 1)
$display("[tb_gs_stub] PASS");
else
$display("[tb_gs_stub] FAIL");
$finish;
end
initial begin
#100000;
$error("[tb_gs_stub] timeout");
$finish;
end
endmodule : tb_gs_stub