// 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