// ============================================================================
// gs_lpddr_rd_probe.sv  (Ch319 Brick 3)
//
// HPS-triggered single-word AXI4 READ probe for FPGA-private LPDDR4B.
//
// Lets the HPS read framebuffer bytes back THROUGH THE HPS BRIDGE (never
// /dev/mem) for checksum + screen-dump. Drives the EMIF user port's READ
// channel (AR/R) only — the write channel (AW/W/B) is the GS tile-flush
// writer (gs_lpddr_axi_master); read and write channels are independent, so
// the two masters share the one EMIF port with no arbitration.
//
// Runs on axi_clk (= emif_clk, ~310 MHz). The control input `rd_pulse` is a
// TOGGLE in the bridge (design_clk) domain; it is 2-FF synced + edge-detected
// here. The outputs `rd_done` (toggle) + `rd_data` are produced in axi_clk;
// the bridge syncs `rd_done` and latches `rd_data` on its edge (same return-
// path CDC contract as ao486 ao486_hps_bridge ↔ lpddr4b_loader).
//
// One AXI read = one 32-byte (256-bit) beat; the requested 32-bit word is the
// lane selected by addr[4:2] (8 lanes per beat). araddr is 32-byte aligned.
// ============================================================================

module gs_lpddr_rd_probe #(
    parameter ADDR_W = 30
)(
    input  logic              axi_clk,    // emif_clk
    input  logic              axi_rst_n,  // emif_reset_n (EMIF cal-ready)

    // ---- control / status (rd_pulse is a design_clk-domain toggle) ----
    input  logic              rd_pulse,   // toggles when the HPS requests a read
    input  logic [31:0]       rd_addr,    // byte address (stable when rd_pulse toggles)
    output logic              rd_done,     // toggles (axi_clk) on completion
    output logic [31:0]       rd_data,     // 32-bit word (stable after rd_done edge)
    output logic              rd_busy,

    // ---- AXI4 READ channel to the EMIF user port (axi_clk, 256-bit data) ----
    output logic [ADDR_W-1:0] araddr,
    output logic [1:0]        arburst,
    output logic [6:0]        arid,
    output logic [7:0]        arlen,
    output logic [2:0]        arsize,
    output logic              arvalid,
    input  logic              arready,
    input  logic [255:0]      rdata,
    input  logic [1:0]        rresp,
    input  logic              rlast,
    input  logic              rvalid,
    output logic              rready
);
    // AXI read-address constants
    assign arburst = 2'b01;   // INCR
    assign arid    = 7'd1;    // distinct from the writer (awid = 0)
    assign arlen   = 8'd0;    // single beat
    assign arsize  = 3'b101;  // 32 bytes (full 256-bit width)

    // CDC: sync rd_pulse (design_clk) into axi_clk + edge-detect.
    reg [2:0] pulse_sync;
    wire pulse_edge = (pulse_sync[2] != pulse_sync[1]);

    reg [2:0] rd_lane;        // which 32-bit lane of the 256-bit beat (addr[4:2])

    // Capture the complete EMIF beat before lane selection.  Selecting one of
    // eight 32-bit lanes directly from the EMIF response into rd_data placed a
    // two-level mux plus long EMIF routing in one 310 MHz cycle.
    logic [255:0] rdata_q;
    typedef enum logic [1:0] { S_IDLE, S_AR, S_R, S_SEL } st_t;
    st_t st;

    always_ff @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            pulse_sync <= 3'd0;
            st         <= S_IDLE;
            araddr     <= '0;
            arvalid    <= 1'b0;
            rready     <= 1'b0;
            rd_done    <= 1'b0;
            rd_data    <= 32'd0;
            rd_busy    <= 1'b0;
            rd_lane    <= 3'd0;
            rdata_q    <= 256'd0;
        end else begin
            pulse_sync <= {pulse_sync[1:0], rd_pulse};

            case (st)
                S_IDLE: begin
                    if (pulse_edge) begin
                        rd_lane <= rd_addr[4:2];
                        araddr  <= {rd_addr[ADDR_W-1:5], 5'd0};  // 32-byte aligned
                        arvalid <= 1'b1;
                        rd_busy <= 1'b1;
                        st      <= S_AR;
                    end
                end
                S_AR: begin
                    if (arready) begin
                        arvalid <= 1'b0;
                        rready  <= 1'b1;
                        st      <= S_R;
                    end
                end
                S_R: begin
                    if (rvalid) begin
                        rready <= 1'b0;
                        rdata_q <= rdata;
                        st      <= S_SEL;
                    end
                end
                S_SEL: begin
                        case (rd_lane)
                            3'd0: rd_data <= rdata_q[31:0];
                            3'd1: rd_data <= rdata_q[63:32];
                            3'd2: rd_data <= rdata_q[95:64];
                            3'd3: rd_data <= rdata_q[127:96];
                            3'd4: rd_data <= rdata_q[159:128];
                            3'd5: rd_data <= rdata_q[191:160];
                            3'd6: rd_data <= rdata_q[223:192];
                            default: rd_data <= rdata_q[255:224];
                        endcase
                        rd_busy <= 1'b0;
                        rd_done <= ~rd_done;
                        st      <= S_IDLE;
                end
                default: st <= S_IDLE;
            endcase
        end
    end
endmodule
