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
@@ -0,0 +1,327 @@
// retroDE_ps2 — tb_ee_install_agent_smoke (Ch55)
//
// Ch55 unit verification: the boot_install_agent_stub, when driven once,
// streams its Ch54-image payload through sif_dma_ee_ram_bridge_stub into
// the EE memory map, where the new bridge→useg_shadow shadowing path
// deposits the image into useg_shadow_mem. The EE CPU read port reads
// the image back byte-exact.
//
// Chain (no IOP, no DMAC — install agent is the dumb producer):
// boot_install_agent_stub (32-bit beats)
// └─► sif_dma_ee_ram_bridge_stub (4×32 → 128 qword)
// └─► ee_memory_map_stub (bridge_wr_* → useg_shadow_mem)
// └─► ee_ram_stub (also written; verified via readback
// through the EE CPU port)
//
// PASS criteria:
// 1. boot agent asserts done_o.
// 2. sif bridge's last_seen_o asserts.
// 3. Reading useg addresses 0x80..0x1FC via the EE CPU read port
// returns the exact Ch54 image word-for-word (96 words).
// 4. No FAIL prints, no timeout.
`timescale 1ns/1ps
module tb_ee_install_agent_smoke;
import trace_pkg::*;
localparam int EE_RAM_BYTES = 8 * 1024;
localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES);
localparam int TOTAL_WORDS = 96;
localparam logic [31:0] DEST_BASE_BYTES = 32'h0000_0080;
localparam logic [31:0] USEG_BASE_ADDR = 32'h0000_0080; // kuseg phys 0x80
logic clk;
logic rst_n;
initial clk = 1'b0;
always #5 clk = ~clk;
// ------------------------------------------------------------------
// Expected payload mirror (built the same way boot_install_agent_stub
// builds its ROM). Used purely for post-install comparison.
// ------------------------------------------------------------------
logic [31:0] expected [0:TOTAL_WORDS-1];
initial begin
expected[0] = 32'h401A7000;
expected[1] = 32'h275A0004;
expected[2] = 32'h03400008;
expected[3] = 32'h42000010;
for (int i = 4; i < TOTAL_WORDS; i = i + 2) begin
expected[i] = 32'h03E00008;
expected[i + 1] = 32'h00000000;
end
end
// ------------------------------------------------------------------
// Install agent
// ------------------------------------------------------------------
logic go;
logic agent_valid;
logic [31:0] agent_data;
logic agent_last;
logic agent_ready;
logic agent_busy;
logic agent_done;
logic agent_ev_valid;
subsys_e agent_ev_subsys;
event_e agent_ev_event;
logic [63:0] agent_ev_arg0, agent_ev_arg1, agent_ev_arg2, agent_ev_arg3;
logic [31:0] agent_ev_flags;
boot_install_agent_stub #(
.TOTAL_WORDS(TOTAL_WORDS),
.MASTER_ID(8'd6)
) u_agent (
.clk(clk), .rst_n(rst_n),
.go_i(go),
.out_valid(agent_valid), .out_data(agent_data),
.out_last(agent_last), .out_ready(agent_ready),
.busy_o(agent_busy), .done_o(agent_done),
.ev_valid(agent_ev_valid), .ev_subsys(agent_ev_subsys),
.ev_event(agent_ev_event),
.ev_arg0(agent_ev_arg0), .ev_arg1(agent_ev_arg1),
.ev_arg2(agent_ev_arg2), .ev_arg3(agent_ev_arg3),
.ev_flags(agent_ev_flags)
);
// ------------------------------------------------------------------
// SIF EE-RAM bridge (agent → 128-bit map write)
// ------------------------------------------------------------------
logic br_wr_en;
logic [31:0] br_wr_addr;
logic [127:0] br_wr_data;
logic [15:0] br_wr_be;
logic [7:0] br_master_id;
logic br_last_seen;
sif_dma_ee_ram_bridge_stub #(
.DEST_BASE_ADDR(DEST_BASE_BYTES),
.MASTER_ID(8'd5)
) u_bridge (
.clk(clk), .rst_n(rst_n),
.in_valid(agent_valid), .in_data(agent_data),
.in_last(agent_last), .in_ready(agent_ready),
.bridge_wr_en(br_wr_en), .bridge_wr_addr(br_wr_addr),
.bridge_wr_data(br_wr_data), .bridge_wr_be(br_wr_be),
.bridge_master_id(br_master_id),
.last_seen_o(br_last_seen)
);
// ------------------------------------------------------------------
// EE memory map + EE RAM
// ------------------------------------------------------------------
logic ee_rd_en;
logic [31:0] ee_rd_addr;
logic [31:0] ee_rd_data;
logic ee_rd_valid;
logic ram_rd_en;
logic [24:0] ram_rd_addr;
logic [127:0] ram_rd_data;
logic ram_rd_valid;
logic ram_wr_en;
logic [24:0] ram_wr_addr;
logic [127:0] ram_wr_data;
logic [15:0] ram_wr_be;
logic [7:0] ram_master_id;
logic map_ev_valid;
subsys_e map_ev_subsys;
event_e map_ev_event;
logic [63:0] map_ev_arg0, map_ev_arg1, map_ev_arg2, map_ev_arg3;
logic [31:0] map_ev_flags;
ee_memory_map_stub u_ee_map (
.clk(clk), .rst_n(rst_n),
.ee_rd_en(ee_rd_en), .ee_rd_addr(ee_rd_addr),
.ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
.ee_wr_en(1'b0), .ee_wr_addr(32'd0),
.ee_wr_data(32'd0), .ee_wr_be(4'd0),
.dmac_rd_en(1'b0), .dmac_rd_addr(32'd0),
.dmac_rd_data(), .dmac_rd_valid(),
.bridge_wr_en(br_wr_en), .bridge_wr_addr(br_wr_addr),
.bridge_wr_data(br_wr_data), .bridge_wr_be(br_wr_be),
.bridge_master_id(br_master_id),
.bios_rd_en(), .bios_rd_addr(),
.bios_rd_data(32'd0), .bios_rd_valid(1'b0),
.ram_rd_en(ram_rd_en), .ram_rd_addr(ram_rd_addr),
.ram_rd_data(ram_rd_data), .ram_rd_valid(ram_rd_valid),
.ram_wr_en(ram_wr_en), .ram_wr_addr(ram_wr_addr),
.ram_wr_data(ram_wr_data), .ram_wr_be(ram_wr_be),
.ram_master_id(ram_master_id),
.ee_dmac_ch2_wr_en(), .ee_dmac_ch2_wr_addr(), .ee_dmac_ch2_wr_data(),
.ee_dmac_ch2_rd_en(), .ee_dmac_ch2_rd_addr(),
.ee_dmac_ch2_rd_data(32'd0), .ee_dmac_ch2_rd_valid(1'b0),
.ee_intc_wr_en(), .ee_intc_wr_addr(), .ee_intc_wr_data(),
.ee_intc_rd_en(), .ee_intc_rd_addr(),
.ee_intc_rd_data(32'd0), .ee_intc_rd_valid(1'b0),
.ee_misc_mmio_wr_en(), .ee_misc_mmio_wr_addr(), .ee_misc_mmio_wr_data(), .ee_misc_mmio_wr_be(),
.ee_misc_mmio_rd_en(), .ee_misc_mmio_rd_addr(),
.ee_misc_mmio_rd_data(32'd0), .ee_misc_mmio_rd_valid(1'b0),
.ee_biu_wr_en(), .ee_biu_wr_addr(), .ee_biu_wr_data(), .ee_biu_wr_be(),
.ee_biu_rd_en(), .ee_biu_rd_addr(),
.ee_biu_rd_data(32'd0), .ee_biu_rd_valid(1'b0),
.ev_valid(map_ev_valid), .ev_subsys(map_ev_subsys),
.ev_event(map_ev_event),
.ev_arg0(map_ev_arg0), .ev_arg1(map_ev_arg1),
.ev_arg2(map_ev_arg2), .ev_arg3(map_ev_arg3),
.ev_flags(map_ev_flags)
);
ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
.clk(clk), .rst_n(rst_n),
.rd_en(ram_rd_en), .rd_addr(ram_rd_addr[EE_RAM_ADDR_W-1:0]),
.rd_data(ram_rd_data), .rd_valid(ram_rd_valid),
.wr_en(ram_wr_en), .wr_addr(ram_wr_addr[EE_RAM_ADDR_W-1:0]),
.wr_data(ram_wr_data), .wr_be(ram_wr_be),
.master_id(ram_master_id),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
);
// ------------------------------------------------------------------
// Counters / observation
// ------------------------------------------------------------------
int agent_start_count;
int agent_beat_count;
int agent_done_count;
int map_useg_writes;
initial begin
agent_start_count = 0;
agent_beat_count = 0;
agent_done_count = 0;
map_useg_writes = 0;
end
always_ff @(posedge clk) begin
if (rst_n && agent_ev_valid && agent_ev_subsys == SUBSYS_SIF) begin
case (agent_ev_event)
EV_DMA_START: agent_start_count <= agent_start_count + 1;
EV_DMA_BEAT: agent_beat_count <= agent_beat_count + 1;
EV_DMA_DONE: agent_done_count <= agent_done_count + 1;
default: ;
endcase
end
if (rst_n && map_ev_valid && map_ev_subsys == SUBSYS_MEM &&
map_ev_event == EV_WRITE && map_ev_arg3[7:0] == 8'd1) begin
// bridge writes to ee_ram emit REGION_EE_RAM; the shadowing
// side-effect is internal (no separate ev tag). Count these
// as evidence that bridge actually drove the map.
map_useg_writes <= map_useg_writes + 1;
end
end
// ------------------------------------------------------------------
// Readback via EE CPU port (1-cycle latency)
// ------------------------------------------------------------------
task automatic ee_read_word(input logic [31:0] addr,
output logic [31:0] data);
@(negedge clk);
ee_rd_en = 1'b1;
ee_rd_addr = addr;
@(negedge clk);
ee_rd_en = 1'b0;
ee_rd_addr = 32'd0;
@(posedge clk);
data = ee_rd_data;
endtask
// ------------------------------------------------------------------
// Main
// ------------------------------------------------------------------
int errors;
int timeout_cycles;
initial begin
rst_n = 1'b0;
go = 1'b0;
ee_rd_en = 1'b0;
ee_rd_addr = 32'd0;
errors = 0;
timeout_cycles = 0;
// Reset
repeat (4) @(posedge clk);
rst_n = 1'b1;
repeat (2) @(posedge clk);
// Kick the installer
@(negedge clk);
go = 1'b1;
@(negedge clk);
go = 1'b0;
// Wait for done or timeout
while (!agent_done && timeout_cycles < 2000) begin
@(posedge clk);
timeout_cycles = timeout_cycles + 1;
end
if (!agent_done) begin
$display("[tb_ee_install_agent_smoke] FAIL: timeout waiting for agent done (cyc=%0d)",
timeout_cycles);
errors = errors + 1;
end
// Drain any in-flight bridge beats (the last qword emit takes
// one extra cycle after the accumulator fills).
repeat (8) @(posedge clk);
if (!br_last_seen) begin
$display("[tb_ee_install_agent_smoke] FAIL: bridge last_seen never asserted");
errors = errors + 1;
end
// Byte-compare all 96 words through the EE CPU read port.
for (int i = 0; i < TOTAL_WORDS; i++) begin
logic [31:0] got;
ee_read_word(USEG_BASE_ADDR + i * 4, got);
if (got !== expected[i]) begin
$display("[tb_ee_install_agent_smoke] FAIL word %0d @0x%08x: got %08x exp %08x",
i, USEG_BASE_ADDR + i * 4, got, expected[i]);
errors = errors + 1;
end
end
$display("[tb_ee_install_agent_smoke] start_events=%0d beat_events=%0d done_events=%0d bridge_writes=%0d",
agent_start_count, agent_beat_count, agent_done_count, map_useg_writes);
if (agent_start_count != 1) begin
$display("[tb_ee_install_agent_smoke] FAIL: expected 1 start event, got %0d", agent_start_count);
errors = errors + 1;
end
if (agent_beat_count != TOTAL_WORDS) begin
$display("[tb_ee_install_agent_smoke] FAIL: expected %0d beat events, got %0d",
TOTAL_WORDS, agent_beat_count);
errors = errors + 1;
end
if (agent_done_count != 1) begin
$display("[tb_ee_install_agent_smoke] FAIL: expected 1 done event, got %0d", agent_done_count);
errors = errors + 1;
end
// 96 words / 4 words per qword = 24 bridge qword writes expected
if (map_useg_writes != TOTAL_WORDS / 4) begin
$display("[tb_ee_install_agent_smoke] FAIL: expected %0d bridge qword writes, got %0d",
TOTAL_WORDS / 4, map_useg_writes);
errors = errors + 1;
end
if (errors == 0)
$display("[tb_ee_install_agent_smoke] PASS");
else
$display("[tb_ee_install_agent_smoke] FAIL total=%0d", errors);
$finish;
end
initial begin
#200000;
$display("[tb_ee_install_agent_smoke] FAIL: absolute timeout");
$finish;
end
endmodule : tb_ee_install_agent_smoke