// retroDE_ps2 — sif_dma_ee_ack_peer_stub
//
// Protocol combiner for the first reverse-direction (IOP→EE) combined
// control+data SIF milestone. Mirror of sif_dma_ack_peer_stub with
// polarity swapped to observe the IOP's doorbell from the EE side and
// respond back IOP-ward through the mailbox.
//
// Explicitly NOT an EE. This module has no code execution, no bus
// master, no capability beyond composing two existing SIF primitives.
// Kept under `rtl/sif/` with the other protocol scaffolding so it
// does not get misread as EE maturity progress.
//
// Contract refs:
//   docs/contracts/sif.md
//
// Layering:
//   sif_mailbox_stub               — storage primitive
//   sif_dma_ee_ram_bridge_stub     — reverse-direction data-plane landing
//   sif_dma_ee_ack_peer_stub       — THIS module. Ties them together on
//                                    the EE side.
//
// Protocol (one-shot, reverse direction):
//   1. IOP writes SMCOM = cmd                 (what the IOP wants to say)
//   2. IOP writes SMFLG = CMD_PENDING_BIT     (doorbell IOP→EE)
//   3. IOP DMAC ch9 transfers bounded payload through the SIF egress
//      bridge, which lands qwords in EE RAM. Bridge's last_seen_o rises
//      on the final beat and stays high.
//   4. this peer observes (SMFLG & CMD_PENDING_BIT) AND payload_complete
//   5. peer reads SMCOM (captures the command)
//   6. peer writes MSCOM = cmd                (echo back IOP-ward)
//   7. peer writes MSFLG = CMD_ACK_BIT        (ack back IOP-ward)
//   8. terminal DONE (one-shot for this milestone)
//
// The peer does NOT clear SMFLG or MSFLG — lifecycle is the TB's
// responsibility, consistent with sif_mailbox_peer_stub's guardrail.
//
// Ordering guarantee: the load-bearing behaviour is that the peer will
// not advance to the ack write sequence unless BOTH the doorbell AND
// payload_complete are observed simultaneously. The milestone is about
// verifying that the ack is gated on data arriving, not just control.
//
// Ports connect to:
//   obs_*   → sif_mailbox_stub ee_rd_* (peer reads SMFLG, then SMCOM)
//   resp_*  → sif_mailbox_stub ee_wr_* (peer writes MSCOM, then MSFLG)
//   payload_complete ← sif_dma_ee_ram_bridge_stub.last_seen_o

`timescale 1ns/1ps

module sif_dma_ee_ack_peer_stub
#(
    parameter logic [7:0]  MSCOM_OFF       = 8'h00,
    parameter logic [7:0]  SMCOM_OFF       = 8'h10,
    parameter logic [7:0]  MSFLG_OFF       = 8'h20,
    parameter logic [7:0]  SMFLG_OFF       = 8'h30,
    parameter logic [31:0] CMD_PENDING_BIT = 32'h0000_0001,
    parameter logic [31:0] CMD_ACK_BIT     = 32'h0000_0002
) (
    input  logic        clk,
    input  logic        rst_n,

    // Mailbox observation (EE-side read port)
    output logic        obs_rd_en,
    output logic [7:0]  obs_rd_addr,
    input  logic [31:0] obs_rd_data,
    input  logic        obs_rd_valid,

    // Mailbox response (EE-side write port)
    output logic        resp_wr_en,
    output logic [7:0]  resp_wr_addr,
    output logic [31:0] resp_wr_data,

    // Payload completion indication from sif_dma_ee_ram_bridge_stub (level)
    input  logic        payload_complete,

    // Status
    output logic        done_o,
    output logic [31:0] ack_count_o
);

    typedef enum logic [2:0] {
        S_POLL_REQ    = 3'd0,  // pulse rd_en for SMFLG
        S_POLL_WAIT   = 3'd1,  // wait for rd_valid, gate on BOTH conditions
        S_SMCOM_REQ   = 3'd2,  // pulse rd_en for SMCOM
        S_SMCOM_WAIT  = 3'd3,  // wait for rd_valid, latch cmd
        S_WRITE_MSCOM = 3'd4,  // drive wr_en, addr=MSCOM, data=cmd
        S_WRITE_MSFLG = 3'd5,  // drive wr_en, addr=MSFLG, data=ACK
        S_DONE        = 3'd6   // terminal (one-shot for this milestone)
    } state_e;

    state_e      state;
    logic [31:0] latched_cmd;

    // ------------------------------------------------------------------
    // State machine — advance to SMCOM_REQ only when SMFLG pending is set
    // AND payload_complete is observed simultaneously. This is the
    // load-bearing guarantee of the whole combiner.
    // ------------------------------------------------------------------

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            state       <= S_POLL_REQ;
            latched_cmd <= 32'd0;
        end else begin
            unique case (state)
                S_POLL_REQ: state <= S_POLL_WAIT;

                S_POLL_WAIT: begin
                    if (obs_rd_valid) begin
                        if (((obs_rd_data & CMD_PENDING_BIT) != 32'd0) &&
                            payload_complete)
                            state <= S_SMCOM_REQ;
                        else
                            state <= S_POLL_REQ;       // keep polling
                    end
                end

                S_SMCOM_REQ: state <= S_SMCOM_WAIT;

                S_SMCOM_WAIT: begin
                    if (obs_rd_valid) begin
                        latched_cmd <= obs_rd_data;
                        state       <= S_WRITE_MSCOM;
                    end
                end

                S_WRITE_MSCOM: state <= S_WRITE_MSFLG;

                S_WRITE_MSFLG: state <= S_DONE;

                S_DONE: state <= S_DONE;

                default: state <= S_POLL_REQ;
            endcase
        end
    end

    // ------------------------------------------------------------------
    // Output drive (combinational, one-hot on state)
    // ------------------------------------------------------------------

    always_comb begin
        obs_rd_en     = 1'b0;
        obs_rd_addr   = 8'd0;
        resp_wr_en    = 1'b0;
        resp_wr_addr  = 8'd0;
        resp_wr_data  = 32'd0;

        unique case (state)
            S_POLL_REQ: begin
                obs_rd_en   = 1'b1;
                obs_rd_addr = SMFLG_OFF;
            end
            S_SMCOM_REQ: begin
                obs_rd_en   = 1'b1;
                obs_rd_addr = SMCOM_OFF;
            end
            S_WRITE_MSCOM: begin
                resp_wr_en   = 1'b1;
                resp_wr_addr = MSCOM_OFF;
                resp_wr_data = latched_cmd;
            end
            S_WRITE_MSFLG: begin
                resp_wr_en   = 1'b1;
                resp_wr_addr = MSFLG_OFF;
                resp_wr_data = CMD_ACK_BIT;
            end
            default: ;
        endcase
    end

    // ------------------------------------------------------------------
    // Ack bookkeeping
    // ------------------------------------------------------------------

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ack_count_o <= 32'd0;
            done_o      <= 1'b0;
        end else if (state == S_WRITE_MSFLG) begin
            ack_count_o <= ack_count_o + 32'd1;
            done_o      <= 1'b1;
        end
    end

endmodule : sif_dma_ee_ack_peer_stub
