// retroDE_ps2 — sif_dma_iop_ram_bridge_stub
//
// Width-adapting bridge from a 128-bit SIF DMA endpoint to the 32-bit
// IOP memory map. Splits each incoming qword into four 32-bit writes at
// consecutive physical addresses starting from DEST_BASE_ADDR.
//
// First real coupling between the SIF data-plane and the IOP side. NOT an
// IOP, not a DMAC, not a peer — just a width/ordering adapter.
//
// Contract refs:
//   docs/contracts/sif.md        (DMA-linked data movement endpoints)
//   docs/contracts/iop.md        (IOP-local RAM/I/O decode; writes land
//                                 through the IOP memory map)
//
// Handshake (upstream, from DMAC ep_* port or equivalent):
//   in_valid / in_data[127:0] / in_last / in_ready
//   Bridge asserts in_ready only while idle. During the four-write
//   expansion of a qword, in_ready drops — natural backpressure onto
//   whatever's producing qwords.
//
// Handshake (downstream, to iop_memory_map_stub's bridge-write port):
//   bridge_wr_en / bridge_wr_addr[31:0] / bridge_wr_data[31:0] /
//   bridge_wr_be[3:0] / bridge_master_id[7:0]
//   Addresses are physical (no kseg stripping) — the IOP map must treat
//   this port's addresses differently from its CPU-side port.
//
// Data layout:
//   Little-endian unpacking: in_data[31:0]   -> DEST_BASE+0
//                            in_data[63:32]  -> DEST_BASE+4
//                            in_data[95:64]  -> DEST_BASE+8
//                            in_data[127:96] -> DEST_BASE+12
//   Subsequent qwords append: DEST_BASE+16, +20, +24, +28, ...
//
// Parameters:
//   DEST_BASE_ADDR  — where the bridge starts writing. Persistent across
//                     the life of the transfer; would become a register in
//                     a later wave where software programs the target.
//   MASTER_ID       — bridge's identity in MEM / IOP traces (default 3,
//                     distinct from EE IFETCH=0, DMAC=1, IOP_CPU=2).
//
// Non-goals:
//   - multiple in-flight qwords
//   - ack back upstream beyond in_ready / in_last observation
//   - byte-enable variation per write (all writes are full 32-bit)
//   - arbitration against other masters on the map's write path

`timescale 1ns/1ps

module sif_dma_iop_ram_bridge_stub
#(
    parameter logic [31:0] DEST_BASE_ADDR = 32'h0000_0000,
    parameter logic [7:0]  MASTER_ID      = 8'd3
) (
    input  logic          clk,
    input  logic          rst_n,

    // Upstream (DMAC endpoint side)
    input  logic          in_valid,
    input  logic [127:0]  in_data,
    input  logic          in_last,
    output logic          in_ready,

    // Downstream (IOP map bridge-write port)
    output logic          bridge_wr_en,
    output logic [31:0]   bridge_wr_addr,
    output logic [31:0]   bridge_wr_data,
    output logic [3:0]    bridge_wr_be,
    output logic [7:0]    bridge_master_id
);

    typedef enum logic [1:0] {
        S_IDLE  = 2'd0,
        S_WRITE = 2'd1
    } state_e;

    state_e       state;
    logic [127:0] latched_qword;
    logic [1:0]   beat_index;              // 0..3 across the 4 writes
    logic [31:0]  wr_offset;                // running byte offset

    assign in_ready         = (state == S_IDLE);
    assign bridge_master_id = MASTER_ID;

    logic accept_new_qword;
    assign accept_new_qword = in_valid && in_ready;

    // ------------------------------------------------------------------
    // State machine
    // ------------------------------------------------------------------

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            state         <= S_IDLE;
            latched_qword <= 128'd0;
            beat_index    <= 2'd0;
            wr_offset     <= 32'd0;
        end else begin
            unique case (state)
                S_IDLE: begin
                    if (accept_new_qword) begin
                        latched_qword <= in_data;
                        beat_index    <= 2'd0;
                        state         <= S_WRITE;
                    end
                end

                S_WRITE: begin
                    // Each cycle here drives one 32-bit write. After the
                    // fourth, go idle. wr_offset advances per write.
                    wr_offset <= wr_offset + 32'd4;
                    if (beat_index == 2'd3) begin
                        state      <= S_IDLE;
                        beat_index <= 2'd0;
                    end else begin
                        beat_index <= beat_index + 2'd1;
                    end
                end

                default: state <= S_IDLE;
            endcase
        end
    end

    // ------------------------------------------------------------------
    // Downstream write-port drive (combinational on state)
    // ------------------------------------------------------------------

    // Indexed part-select picks the 32-bit slice for the current beat.
    // Avoids the constant-select-in-always_comb pattern that trips
    // portability warnings on some simulators.
    logic [31:0] beat_data;
    assign beat_data = latched_qword[beat_index*32 +: 32];

    assign bridge_wr_en   = (state == S_WRITE);
    assign bridge_wr_addr = DEST_BASE_ADDR + wr_offset;
    assign bridge_wr_data = beat_data;
    assign bridge_wr_be   = 4'b1111;        // full-word writes only

endmodule : sif_dma_iop_ram_bridge_stub
