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