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,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
|
||||
Reference in New Issue
Block a user