// retroDE_ps2 — ee_dmac_passive_chan_stub
//
// Ch288 — Lightweight per-channel register surface for the EE DMAC
// channels NOT covered by a dedicated transfer-FSM stub. Hosts the
// four standard per-channel registers (CHCR/MADR/QWC/TADR) for each
// covered channel; reset to zero, writes latch, reads return the
// latched value. NO transfer FSM, NO start-bit side effects, NO
// D_STAT interaction. This is the "init-time channel-clear / quiet
// register surface" Codex framed for Ch288.
//
// Channels covered (4 KiB window each, starting at 0x1000_8000):
//   ch0 (VIF0)     0x1000_8000-0x1000_8FFF
//   ch1 (VIF1)     0x1000_9000-0x1000_9FFF
//   ch3 (IPU_FROM) 0x1000_B000-0x1000_BFFF
//   ch4 (IPU_TO)   0x1000_C000-0x1000_CFFF   ← qbert's first hit
//   ch5 (SIF0)     0x1000_D000-0x1000_DFFF
//
// SKIPPED:
//   ch2 (GIF)      0x1000_A000-0x1000_AFFF — already routed
//                  externally to dmac_reg_stub via the map's
//                  ee_dmac_ch2_* ports. Do NOT shadow it here.
//
// Channel index extracted from chan_addr[15:12]:
//   0x8 → ch0, 0x9 → ch1, 0xB → ch3, 0xC → ch4, 0xD → ch5
//   (0xA / ch2 is filtered by the caller; if chan_addr[15:12]==0xA
//    arrives here the module silently drops it.)
//
// Register offsets (chan_addr[11:0], matches dmac_reg_stub layout):
//   0x00 CHCR  — control (start bit at [0]); latched, no FSM
//   0x10 MADR  — main address
//   0x20 QWC   — quadword count
//   0x30 TADR  — tag address
//   any other offset: read = 0, write dropped + traced

`timescale 1ns/1ps

module ee_dmac_passive_chan_stub
    import trace_pkg::*;
(
    input  logic          clk,
    input  logic          rst_n,

    // Write port. chan_addr is the 16-bit offset into the entire
    // 0x1000_8000-base window: chan_addr[15:12] = channel selector,
    // chan_addr[11:0] = register offset within that channel.
    input  logic          reg_wr_en,
    input  logic [15:0]   chan_addr,
    input  logic [31:0]   reg_wr_data,

    // Read port (1-cycle latency).
    input  logic          reg_rd_en,
    output logic [31:0]   reg_rd_data,
    output logic          reg_rd_valid,

    // 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 logic [11:0] CHCR_OFFSET = 12'h000;
    localparam logic [11:0] MADR_OFFSET = 12'h010;
    localparam logic [11:0] QWC_OFFSET  = 12'h020;
    localparam logic [11:0] TADR_OFFSET = 12'h030;

    // Channel index from the high nibble of chan_addr. Out-of-range
    // nibbles (anything outside 0x8/0x9/0xB/0xC/0xD) get
    // chan_valid=0 and the access is dropped.
    logic [3:0]  chan_nibble;
    logic [2:0]  chan_idx;     // 0..4 packed: 0=ch0, 1=ch1, 2=ch3, 3=ch4, 4=ch5
    logic        chan_valid;
    always_comb begin
        chan_nibble = chan_addr[15:12];
        chan_idx    = 3'd0;
        chan_valid  = 1'b0;
        unique case (chan_nibble)
            4'h8: begin chan_idx = 3'd0; chan_valid = 1'b1; end // ch0
            4'h9: begin chan_idx = 3'd1; chan_valid = 1'b1; end // ch1
            4'hB: begin chan_idx = 3'd2; chan_valid = 1'b1; end // ch3
            4'hC: begin chan_idx = 3'd3; chan_valid = 1'b1; end // ch4
            4'hD: begin chan_idx = 3'd4; chan_valid = 1'b1; end // ch5
            default: ;
        endcase
    end

    logic [11:0] reg_offset;
    assign reg_offset = chan_addr[11:0];

    // ------------------------------------------------------------------
    // Register file: 5 channels × 4 registers
    // ------------------------------------------------------------------
    logic [31:0] chcr [0:4];
    logic [31:0] madr [0:4];
    logic [31:0] qwc  [0:4];
    logic [31:0] tadr [0:4];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            for (int i = 0; i < 5; i++) begin
                chcr[i] <= 32'd0;
                madr[i] <= 32'd0;
                qwc[i]  <= 32'd0;
                tadr[i] <= 32'd0;
            end
        end else if (reg_wr_en && chan_valid) begin
            unique case (reg_offset)
                CHCR_OFFSET: chcr[chan_idx] <= reg_wr_data;
                MADR_OFFSET: madr[chan_idx] <= reg_wr_data;
                QWC_OFFSET:  qwc[chan_idx]  <= reg_wr_data;
                TADR_OFFSET: tadr[chan_idx] <= reg_wr_data;
                default: ;
            endcase
        end
    end

    // Read mux (1-cycle latency). Returns 0 for invalid channel /
    // unknown offset.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            reg_rd_data  <= 32'd0;
            reg_rd_valid <= 1'b0;
        end else begin
            reg_rd_valid <= reg_rd_en;
            if (reg_rd_en && chan_valid) begin
                unique case (reg_offset)
                    CHCR_OFFSET: reg_rd_data <= chcr[chan_idx];
                    MADR_OFFSET: reg_rd_data <= madr[chan_idx];
                    QWC_OFFSET:  reg_rd_data <= qwc[chan_idx];
                    TADR_OFFSET: reg_rd_data <= tadr[chan_idx];
                    default:     reg_rd_data <= 32'd0;
                endcase
            end else if (reg_rd_en) begin
                reg_rd_data <= 32'd0;   // invalid channel
            end
        end
    end

    // ------------------------------------------------------------------
    // Trace — write priority over read; tagged SUBSYS_DMAC with
    // arg0 = chan_nibble (0x8/0x9/0xB/0xC/0xD = phys channel), arg1
    // = data, arg2 = reg_offset, arg3 = chan_idx (packed 0..4).
    // ------------------------------------------------------------------
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ev_valid  <= 1'b0;
            ev_subsys <= SUBSYS_DMAC;
            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 (reg_wr_en) begin
            ev_valid  <= 1'b1;
            ev_subsys <= SUBSYS_DMAC;
            ev_event  <= EV_WRITE;
            ev_arg0   <= {60'd0, chan_nibble};
            ev_arg1   <= {32'd0, reg_wr_data};
            ev_arg2   <= {52'd0, reg_offset};
            ev_arg3   <= {61'd0, chan_idx};
            ev_flags  <= {31'd0, chan_valid};
        end else if (reg_rd_en) begin
            ev_valid  <= 1'b1;
            ev_subsys <= SUBSYS_DMAC;
            ev_event  <= EV_READ;
            ev_arg0   <= {60'd0, chan_nibble};
            ev_arg1   <= 64'd0;
            ev_arg2   <= {52'd0, reg_offset};
            ev_arg3   <= {61'd0, chan_idx};
            ev_flags  <= {31'd0, chan_valid};
        end else begin
            ev_valid <= 1'b0;
        end
    end

endmodule : ee_dmac_passive_chan_stub
