// retroDE_ps2 — ee_biu_mmio_stub
//
// Narrow latched-register-file stub for the EE Bus Interface Unit /
// cache-control window at virtual `0xFFFE_0000 - 0xFFFE_0FFF`
// (physical `0x1FFE_0000 - 0x1FFE_0FFF` after kseg1-stripping).
// Architecturally this is the R5900's privileged BIU/control
// register space — the same place the BIOS writes CACHE-control
// and BIU-config values during boot.
//
// Chapter 9: chapter 8 closed the 0x1F80_xxxx hole. The first-
// unmapped observer in tb_ee_core_bios_smoke then showed the next
// unmapped event was a WRITE at 0xFFFE_0130 (pc=0xBFC0_21BC,
// cycle 808). Multiple more writes to that same offset fire later
// with values 0xCC4, 0xCC0, 0x1E988, 0xC04, 0x3202_000F —
// classic cache/BIU config dance. Without a stub, these writes
// land as UNMAPPED events; the first one reads back to this stub
// would return 0xDEADBEEF and re-poison the pointer chain chapter
// 8 just cleaned up.
//
// Codex's call for chapter 9: give this its own dedicated stub
// with its own region tag, NOT a broad "everything else" fallback.
// Keep architecturally distinct surfaces distinct. If the BIOS
// later touches 0x1FA0_0000 (next unmapped in the observer), that
// will be its own chapter, not folded in here.
//
// Semantics (same shape as ee_bootstrap_mmio_stub):
//   - 4 KiB window = 1024 × 32-bit latched registers, zero-init.
//   - Writes latch per-byte: for each `wr_be[i]`, byte[i] of the
//     addressed register updates; untouched lanes preserve their
//     prior value. Makes SB/SH through the window safe.
//   - Reads return currently-latched value, one-cycle latency.
//   - No side effects. BIOS read-modify-write sequences stay
//     self-consistent.
//
// Size cost: 1024 × 32 bits = 4 KiB sim memory. Negligible.
//
// Trace: per-access event on SUBSYS_MEM with region tag
// `REGION_EE_BIU = 10` (distinct from REGION_EE_MISC_MMIO=9 so
// post-run analysis can separate the two windows).

`timescale 1ns/1ps

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

    // Write port — 12-bit offset within the 4 KiB window
    input  logic          reg_wr_en,
    input  logic [11:0]   reg_wr_addr,
    input  logic [31:0]   reg_wr_data,
    input  logic [3:0]    reg_wr_be,

    // Read port — 1-cycle latency
    input  logic          reg_rd_en,
    input  logic [11:0]   reg_rd_addr,
    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 int WORDS = 1024;  // 4 KiB / 4
    localparam logic [63:0] REGION_EE_BIU = 64'd10;

    logic [31:0] regs [0:WORDS-1];

    initial begin
        for (int i = 0; i < WORDS; i++) regs[i] = 32'd0;
    end

    logic [9:0] wr_idx;
    logic [9:0] rd_idx;
    assign wr_idx = reg_wr_addr[11:2];
    assign rd_idx = reg_rd_addr[11:2];

    // Per-byte write latch
    always_ff @(posedge clk) begin
        if (rst_n && reg_wr_en) begin
            if (reg_wr_be[0]) regs[wr_idx][ 7: 0] <= reg_wr_data[ 7: 0];
            if (reg_wr_be[1]) regs[wr_idx][15: 8] <= reg_wr_data[15: 8];
            if (reg_wr_be[2]) regs[wr_idx][23:16] <= reg_wr_data[23:16];
            if (reg_wr_be[3]) regs[wr_idx][31:24] <= reg_wr_data[31:24];
        end
    end

    // Read — 1-cycle latency
    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) reg_rd_data <= regs[rd_idx];
        end
    end

    // Trace — write wins same-cycle collision (defensive; map enforces
    // mutual exclusion)
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ev_valid  <= 1'b0;
            ev_subsys <= SUBSYS_MEM;
            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_MEM;
            ev_event  <= EV_WRITE;
            ev_arg0   <= {52'd0, reg_wr_addr};
            ev_arg1   <= {32'd0, reg_wr_data};
            ev_arg2   <= {60'd0, reg_wr_be};
            ev_arg3   <= REGION_EE_BIU;
            ev_flags  <= 32'h0000_0001;
        end else if (reg_rd_en) begin
            ev_valid  <= 1'b1;
            ev_subsys <= SUBSYS_MEM;
            ev_event  <= EV_READ;
            ev_arg0   <= {52'd0, reg_rd_addr};
            ev_arg1   <= {32'd0, regs[rd_idx]};
            ev_arg2   <= 64'd0;
            ev_arg3   <= REGION_EE_BIU;
            ev_flags  <= 32'd0;
        end else begin
            ev_valid <= 1'b0;
        end
    end

endmodule : ee_biu_mmio_stub
