// retroDE_ps2 — sif_dma_stub
//
// Minimal SIF DMA receive-side endpoint. First data-plane step on the SIF
// seam. NOT an IOP — this is a bounded receive buffer that accepts qwords
// from a DMAC channel and exposes them to the TB via a small read port.
// No IOP CPU, no live peer logic, no directional policy beyond "incoming
// qwords land in sequential slots."
//
// Contract refs:
//   docs/contracts/sif.md            (DMA-linked data movement endpoints)
//
// Receive interface (connects to DMAC's ep_* endpoint):
//   in_valid / in_data / in_last / in_ready
//   One-cycle accept per beat when in_ready is high. in_last observed
//   alongside the final qword of a transfer.
//
// Read interface (TB-side verification):
//   rd_en pulses with rd_idx; rd_data / rd_valid return the stored qword
//   one cycle later.
//
// Stall input:
//   stall_in (level) forces in_ready low while asserted. Used by the
//   negative-path test to prove that a not-ready receiver does not let
//   the DMAC spuriously complete.
//
// Buffer:
//   Small internal array (DEPTH qwords). Full detection is tracked from
//   `rx_count`: once `rx_count >= DEPTH` the buffer is full and `in_ready`
//   drops so the DMAC stalls in ACTIVE_SEND. No silent wrap. There is no
//   consume path yet — once full, the buffer stays full (intentional for
//   the current scope). `full_o` is exposed for testbench observation.
//
// Trace:
//   One SIF EV_WRITE per accepted beat (one event per cycle).
//   arg0 = slot index into the receive buffer
//   arg1 = data[63:0] (low half)
//   arg2 = source id (hard-wired to 8'd1 = DMAC for Wave 3)
//   arg3 = 0
//   flags bit 0 = in_last value for this beat
//   flags bit 1 = 1 (distinguishes DMA-receive writes from mailbox writes
//                    if both subsystems are ever instantiated together)

`timescale 1ns/1ps

module sif_dma_stub
    import trace_pkg::*;
#(
    parameter int DEPTH = 8          // max qwords buffered
) (
    input  logic          clk,
    input  logic          rst_n,

    // DMAC-facing receive
    input  logic          in_valid,
    input  logic [127:0]  in_data,
    input  logic          in_last,
    output logic          in_ready,

    // TB verification read port
    input  logic          rd_en,
    input  logic [$clog2(DEPTH)-1:0] rd_idx,
    output logic [127:0]  rd_data,
    output logic          rd_valid,

    // Negative-path control
    input  logic          stall_in,

    // Status
    output logic [31:0]   rx_count,          // monotonic accepted-beat count
    output logic          last_seen,         // sticky: in_last observed
    output logic          full_o,            // buffer full, in_ready=0

    // Trace
    output logic          ev_valid,
    output subsys_e       ev_subsys,
    output event_e        ev_event,
    output logic [63:0]   ev_arg0,
    output logic [63:0]   ev_arg1,
    output logic [63:0]   ev_arg2,
    output logic [63:0]   ev_arg3,
    output logic [31:0]   ev_flags
);

    localparam int IDX_W = $clog2(DEPTH);

    logic [127:0] buf_mem [0:DEPTH-1];
    logic [IDX_W-1:0] wr_ptr;

    logic beat_accepted;
    assign full_o        = (rx_count >= DEPTH);
    assign in_ready      = !stall_in && !full_o;
    assign beat_accepted = in_valid && in_ready;

    // ------------------------------------------------------------------
    // Receive path
    // ------------------------------------------------------------------

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            wr_ptr    <= '0;
            rx_count  <= 32'd0;
            last_seen <= 1'b0;
            for (int i = 0; i < DEPTH; i++) buf_mem[i] <= 128'd0;
        end else if (beat_accepted) begin
            buf_mem[wr_ptr] <= in_data;
            wr_ptr          <= wr_ptr + IDX_W'(1);
            rx_count        <= rx_count + 32'd1;
            if (in_last) last_seen <= 1'b1;
        end
    end

    // ------------------------------------------------------------------
    // Read port (1-cycle latency)
    // ------------------------------------------------------------------

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            rd_data  <= 128'd0;
            rd_valid <= 1'b0;
        end else begin
            rd_valid <= rd_en;
            if (rd_en) rd_data <= buf_mem[rd_idx];
        end
    end

    // ------------------------------------------------------------------
    // Trace — one event per accepted beat
    // ------------------------------------------------------------------

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ev_valid  <= 1'b0;
            ev_subsys <= SUBSYS_SIF;
            ev_event  <= EV_WRITE;
            ev_arg0   <= 64'd0;
            ev_arg1   <= 64'd0;
            ev_arg2   <= 64'd0;
            ev_arg3   <= 64'd0;
            ev_flags  <= 32'd0;
        end else if (beat_accepted) begin
            ev_valid  <= 1'b1;
            ev_subsys <= SUBSYS_SIF;
            ev_event  <= EV_WRITE;
            ev_arg0   <= {{(64-IDX_W){1'b0}}, wr_ptr};
            ev_arg1   <= in_data[63:0];
            ev_arg2   <= 64'd1;                    // DMAC
            ev_arg3   <= 64'd0;
            ev_flags  <= {30'd0, 1'b1, in_last};   // bit1=DMA, bit0=in_last
        end else begin
            ev_valid <= 1'b0;
        end
    end

endmodule : sif_dma_stub
