Files
retroDE_ps2/sim/tb/integration/tb_ee_install_agent_smoke.sv
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

328 lines
12 KiB
Systemverilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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