Files
retroDE_ps2/sim/tb/sif/tb_sif_mailbox_stub.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

245 lines
8.3 KiB
Systemverilog

// retroDE_ps2 — tb_sif_mailbox_stub
//
// Unit test for sif_mailbox_stub. The TB plays both the EE side and the
// IOP side, writing and reading the four mailbox registers to prove that
// storage is consistent across ports and that side-of-origin is recorded
// in traces.
//
// Plan refs:
// docs/stub_module_plan.md (Wave 2, item 10)
// docs/contracts/sif.md (mailbox/flag-only stub)
//
// Scenarios:
// 1. EE writes MSCOM → IOP reads MSCOM back
// 2. IOP writes SMCOM → EE reads SMCOM back
// 3. EE sets MSFLG → IOP observes MSFLG
// 4. IOP sets SMFLG → EE observes SMFLG
// 5. Mixed sequence with interleaved reads/writes
// 6. Trace records correct side_id for each operation
`timescale 1ns/1ps
module tb_sif_mailbox_stub;
logic clk;
logic rst_n;
initial clk = 1'b0;
always #5 clk = ~clk;
// ------------------------------------------------------------------
// DUT
// ------------------------------------------------------------------
logic ee_wr_en;
logic ee_rd_en;
logic [7:0] ee_addr;
logic [31:0] ee_wr_data;
logic [31:0] ee_rd_data;
logic ee_rd_valid;
logic iop_wr_en;
logic iop_rd_en;
logic [7:0] iop_addr;
logic [31:0] iop_wr_data;
logic [31:0] iop_rd_data;
logic iop_rd_valid;
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;
sif_mailbox_stub u_sif (
.clk(clk), .rst_n(rst_n),
.ee_wr_en(ee_wr_en), .ee_rd_en(ee_rd_en),
.ee_addr(ee_addr), .ee_wr_data(ee_wr_data),
.ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
.iop_wr_en(iop_wr_en), .iop_rd_en(iop_rd_en),
.iop_addr(iop_addr), .iop_wr_data(iop_wr_data),
.iop_rd_data(iop_rd_data), .iop_rd_valid(iop_rd_valid),
.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("sif_mailbox.trace"), .SINK_LABEL("sif"))
u_trace_sif (
.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)
);
// ------------------------------------------------------------------
// Counters
// ------------------------------------------------------------------
int ee_writes;
int iop_writes;
int ee_reads;
int iop_reads;
int errors;
initial begin
ee_writes = 0;
iop_writes = 0;
ee_reads = 0;
iop_reads = 0;
errors = 0;
end
always_ff @(posedge clk) begin
if (rst_n && ev_valid) begin
unique case ({ev_event, ev_arg2[7:0]})
{trace_pkg::EV_WRITE, 8'd0}: ee_writes <= ee_writes + 1;
{trace_pkg::EV_WRITE, 8'd1}: iop_writes <= iop_writes + 1;
{trace_pkg::EV_READ, 8'd0}: ee_reads <= ee_reads + 1;
{trace_pkg::EV_READ, 8'd1}: iop_reads <= iop_reads + 1;
default: ;
endcase
end
end
// ------------------------------------------------------------------
// Stimulus helpers
// ------------------------------------------------------------------
localparam logic [7:0] MSCOM = 8'h00;
localparam logic [7:0] SMCOM = 8'h10;
localparam logic [7:0] MSFLG = 8'h20;
localparam logic [7:0] SMFLG = 8'h30;
task automatic ee_write(input logic [7:0] addr, input logic [31:0] data);
@(negedge clk);
ee_wr_en = 1'b1;
ee_addr = addr;
ee_wr_data = data;
@(negedge clk);
ee_wr_en = 1'b0;
ee_addr = 8'd0;
ee_wr_data = 32'd0;
endtask
task automatic iop_write(input logic [7:0] addr, input logic [31:0] data);
@(negedge clk);
iop_wr_en = 1'b1;
iop_addr = addr;
iop_wr_data = data;
@(negedge clk);
iop_wr_en = 1'b0;
iop_addr = 8'd0;
iop_wr_data = 32'd0;
endtask
// Read protocol: assert rd_en during cycle N, sample rd_valid/rd_data
// during cycle N+1 (after the posedge has latched them). We check
// immediately after the second negedge, which sits mid-N+1.
task automatic ee_read_expect(input logic [7:0] addr, input logic [31:0] expected,
input string label);
@(negedge clk);
ee_rd_en = 1'b1;
ee_addr = addr;
@(negedge clk);
ee_rd_en = 1'b0;
ee_addr = 8'd0;
if (ee_rd_data !== expected || ee_rd_valid !== 1'b1) begin
$error("[tb_sif_mailbox_stub] EE read %s: got 0x%08h valid=%0b expected 0x%08h",
label, ee_rd_data, ee_rd_valid, expected);
errors = errors + 1;
end
endtask
task automatic iop_read_expect(input logic [7:0] addr, input logic [31:0] expected,
input string label);
@(negedge clk);
iop_rd_en = 1'b1;
iop_addr = addr;
@(negedge clk);
iop_rd_en = 1'b0;
iop_addr = 8'd0;
if (iop_rd_data !== expected || iop_rd_valid !== 1'b1) begin
$error("[tb_sif_mailbox_stub] IOP read %s: got 0x%08h valid=%0b expected 0x%08h",
label, iop_rd_data, iop_rd_valid, expected);
errors = errors + 1;
end
endtask
// ------------------------------------------------------------------
// Stimulus
// ------------------------------------------------------------------
initial begin
rst_n = 1'b0;
ee_wr_en = 1'b0;
ee_rd_en = 1'b0;
ee_addr = 8'd0;
ee_wr_data = 32'd0;
iop_wr_en = 1'b0;
iop_rd_en = 1'b0;
iop_addr = 8'd0;
iop_wr_data = 32'd0;
repeat (4) @(posedge clk);
rst_n = 1'b1;
repeat (2) @(posedge clk);
// --- Scenario 1: EE→IOP mailbox via MSCOM ---
ee_write(MSCOM, 32'hCAFE_BABE);
iop_read_expect(MSCOM, 32'hCAFE_BABE, "MSCOM after EE write");
// --- Scenario 2: IOP→EE mailbox via SMCOM ---
iop_write(SMCOM, 32'hDEAD_C0DE);
ee_read_expect(SMCOM, 32'hDEAD_C0DE, "SMCOM after IOP write");
// --- Scenario 3: MSFLG EE→IOP signalling ---
ee_write(MSFLG, 32'h0000_00FF);
iop_read_expect(MSFLG, 32'h0000_00FF, "MSFLG after EE set");
// --- Scenario 4: SMFLG IOP→EE signalling ---
iop_write(SMFLG, 32'h0000_AA55);
ee_read_expect(SMFLG, 32'h0000_AA55, "SMFLG after IOP set");
// --- Scenario 5: mixed sequence, ensure storage survives ---
ee_write(MSCOM, 32'h1111_2222);
ee_read_expect(MSCOM, 32'h1111_2222, "MSCOM after self-read");
iop_write(SMCOM, 32'h3333_4444);
iop_read_expect(SMCOM, 32'h3333_4444, "SMCOM after self-read");
// --- Scenario 6: clobber check — second write replaces first ---
ee_write(MSCOM, 32'h9999_8888);
iop_read_expect(MSCOM, 32'h9999_8888, "MSCOM after clobber");
repeat (4) @(posedge clk);
// ------------------------------------------------------------------
$display("[tb_sif_mailbox_stub] ee_writes=%0d iop_writes=%0d ee_reads=%0d iop_reads=%0d errors=%0d",
ee_writes, iop_writes, ee_reads, iop_reads, errors);
if (ee_writes < 4) $error("expected >= 4 EE writes, got %0d", ee_writes);
if (iop_writes < 3) $error("expected >= 3 IOP writes, got %0d", iop_writes);
if (ee_reads < 3) $error("expected >= 3 EE reads, got %0d", ee_reads);
if (iop_reads < 4) $error("expected >= 4 IOP reads, got %0d", iop_reads);
if (errors == 0 && ee_writes >= 4 && iop_writes >= 3 &&
ee_reads >= 3 && iop_reads >= 4)
$display("[tb_sif_mailbox_stub] PASS");
else
$display("[tb_sif_mailbox_stub] FAIL");
$finish;
end
initial begin
#200000;
$error("[tb_sif_mailbox_stub] timeout");
$finish;
end
endmodule : tb_sif_mailbox_stub