ec82764bef
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>
245 lines
8.3 KiB
Systemverilog
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
|