// retroDE_ps2 — tb_ee_install_agent_smoke (Ch55)
//
// Ch55 unit verification: the boot_install_agent_stub, when driven once,
// streams its Ch54-image payload through sif_dma_ee_ram_bridge_stub into
// the EE memory map, where the new bridge→useg_shadow shadowing path
// deposits the image into useg_shadow_mem. The EE CPU read port reads
// the image back byte-exact.
//
// Chain (no IOP, no DMAC — install agent is the dumb producer):
//   boot_install_agent_stub (32-bit beats)
//     └─► sif_dma_ee_ram_bridge_stub (4×32 → 128 qword)
//           └─► ee_memory_map_stub (bridge_wr_* → useg_shadow_mem)
//                 └─► ee_ram_stub (also written; verified via readback
//                                 through the EE CPU port)
//
// PASS criteria:
//   1. boot agent asserts done_o.
//   2. sif bridge's last_seen_o asserts.
//   3. Reading useg addresses 0x80..0x1FC via the EE CPU read port
//      returns the exact Ch54 image word-for-word (96 words).
//   4. No FAIL prints, no timeout.

`timescale 1ns/1ps

module tb_ee_install_agent_smoke;
    import trace_pkg::*;

    localparam int EE_RAM_BYTES   = 8 * 1024;
    localparam int EE_RAM_ADDR_W  = $clog2(EE_RAM_BYTES);
    localparam int TOTAL_WORDS    = 96;
    localparam logic [31:0] DEST_BASE_BYTES = 32'h0000_0080;
    localparam logic [31:0] USEG_BASE_ADDR  = 32'h0000_0080;  // kuseg phys 0x80

    logic clk;
    logic rst_n;

    initial clk = 1'b0;
    always #5 clk = ~clk;

    // ------------------------------------------------------------------
    // Expected payload mirror (built the same way boot_install_agent_stub
    // builds its ROM). Used purely for post-install comparison.
    // ------------------------------------------------------------------
    logic [31:0] expected [0:TOTAL_WORDS-1];
    initial begin
        expected[0] = 32'h401A7000;
        expected[1] = 32'h275A0004;
        expected[2] = 32'h03400008;
        expected[3] = 32'h42000010;
        for (int i = 4; i < TOTAL_WORDS; i = i + 2) begin
            expected[i]     = 32'h03E00008;
            expected[i + 1] = 32'h00000000;
        end
    end

    // ------------------------------------------------------------------
    // Install agent
    // ------------------------------------------------------------------
    logic        go;
    logic        agent_valid;
    logic [31:0] agent_data;
    logic        agent_last;
    logic        agent_ready;
    logic        agent_busy;
    logic        agent_done;

    logic                agent_ev_valid;
    subsys_e             agent_ev_subsys;
    event_e              agent_ev_event;
    logic [63:0]         agent_ev_arg0, agent_ev_arg1, agent_ev_arg2, agent_ev_arg3;
    logic [31:0]         agent_ev_flags;

    boot_install_agent_stub #(
        .TOTAL_WORDS(TOTAL_WORDS),
        .MASTER_ID(8'd6)
    ) u_agent (
        .clk(clk), .rst_n(rst_n),
        .go_i(go),
        .out_valid(agent_valid), .out_data(agent_data),
        .out_last(agent_last), .out_ready(agent_ready),
        .busy_o(agent_busy), .done_o(agent_done),
        .ev_valid(agent_ev_valid), .ev_subsys(agent_ev_subsys),
        .ev_event(agent_ev_event),
        .ev_arg0(agent_ev_arg0), .ev_arg1(agent_ev_arg1),
        .ev_arg2(agent_ev_arg2), .ev_arg3(agent_ev_arg3),
        .ev_flags(agent_ev_flags)
    );

    // ------------------------------------------------------------------
    // SIF EE-RAM bridge (agent → 128-bit map write)
    // ------------------------------------------------------------------
    logic        br_wr_en;
    logic [31:0] br_wr_addr;
    logic [127:0] br_wr_data;
    logic [15:0] br_wr_be;
    logic [7:0]  br_master_id;
    logic        br_last_seen;

    sif_dma_ee_ram_bridge_stub #(
        .DEST_BASE_ADDR(DEST_BASE_BYTES),
        .MASTER_ID(8'd5)
    ) u_bridge (
        .clk(clk), .rst_n(rst_n),
        .in_valid(agent_valid), .in_data(agent_data),
        .in_last(agent_last), .in_ready(agent_ready),
        .bridge_wr_en(br_wr_en), .bridge_wr_addr(br_wr_addr),
        .bridge_wr_data(br_wr_data), .bridge_wr_be(br_wr_be),
        .bridge_master_id(br_master_id),
        .last_seen_o(br_last_seen)
    );

    // ------------------------------------------------------------------
    // EE memory map + EE RAM
    // ------------------------------------------------------------------
    logic        ee_rd_en;
    logic [31:0] ee_rd_addr;
    logic [31:0] ee_rd_data;
    logic        ee_rd_valid;

    logic        ram_rd_en;
    logic [24:0] ram_rd_addr;
    logic [127:0] ram_rd_data;
    logic        ram_rd_valid;

    logic        ram_wr_en;
    logic [24:0] ram_wr_addr;
    logic [127:0] ram_wr_data;
    logic [15:0] ram_wr_be;
    logic [7:0]  ram_master_id;

    logic                map_ev_valid;
    subsys_e             map_ev_subsys;
    event_e              map_ev_event;
    logic [63:0]         map_ev_arg0, map_ev_arg1, map_ev_arg2, map_ev_arg3;
    logic [31:0]         map_ev_flags;

    ee_memory_map_stub u_ee_map (
        .clk(clk), .rst_n(rst_n),
        .ee_rd_en(ee_rd_en), .ee_rd_addr(ee_rd_addr),
        .ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
        .ee_wr_en(1'b0), .ee_wr_addr(32'd0),
        .ee_wr_data(32'd0), .ee_wr_be(4'd0),
        .dmac_rd_en(1'b0), .dmac_rd_addr(32'd0),
        .dmac_rd_data(), .dmac_rd_valid(),
        .bridge_wr_en(br_wr_en), .bridge_wr_addr(br_wr_addr),
        .bridge_wr_data(br_wr_data), .bridge_wr_be(br_wr_be),
        .bridge_master_id(br_master_id),
        .bios_rd_en(), .bios_rd_addr(),
        .bios_rd_data(32'd0), .bios_rd_valid(1'b0),
        .ram_rd_en(ram_rd_en), .ram_rd_addr(ram_rd_addr),
        .ram_rd_data(ram_rd_data), .ram_rd_valid(ram_rd_valid),
        .ram_wr_en(ram_wr_en), .ram_wr_addr(ram_wr_addr),
        .ram_wr_data(ram_wr_data), .ram_wr_be(ram_wr_be),
        .ram_master_id(ram_master_id),
        .ee_dmac_ch2_wr_en(), .ee_dmac_ch2_wr_addr(), .ee_dmac_ch2_wr_data(),
        .ee_dmac_ch2_rd_en(), .ee_dmac_ch2_rd_addr(),
        .ee_dmac_ch2_rd_data(32'd0), .ee_dmac_ch2_rd_valid(1'b0),
        .ee_intc_wr_en(), .ee_intc_wr_addr(), .ee_intc_wr_data(),
        .ee_intc_rd_en(), .ee_intc_rd_addr(),
        .ee_intc_rd_data(32'd0), .ee_intc_rd_valid(1'b0),
        .ee_misc_mmio_wr_en(), .ee_misc_mmio_wr_addr(), .ee_misc_mmio_wr_data(), .ee_misc_mmio_wr_be(),
        .ee_misc_mmio_rd_en(), .ee_misc_mmio_rd_addr(),
        .ee_misc_mmio_rd_data(32'd0), .ee_misc_mmio_rd_valid(1'b0),
        .ee_biu_wr_en(), .ee_biu_wr_addr(), .ee_biu_wr_data(), .ee_biu_wr_be(),
        .ee_biu_rd_en(), .ee_biu_rd_addr(),
        .ee_biu_rd_data(32'd0), .ee_biu_rd_valid(1'b0),
        .ev_valid(map_ev_valid), .ev_subsys(map_ev_subsys),
        .ev_event(map_ev_event),
        .ev_arg0(map_ev_arg0), .ev_arg1(map_ev_arg1),
        .ev_arg2(map_ev_arg2), .ev_arg3(map_ev_arg3),
        .ev_flags(map_ev_flags)
    );

    ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(ram_rd_en), .rd_addr(ram_rd_addr[EE_RAM_ADDR_W-1:0]),
        .rd_data(ram_rd_data), .rd_valid(ram_rd_valid),
        .wr_en(ram_wr_en), .wr_addr(ram_wr_addr[EE_RAM_ADDR_W-1:0]),
        .wr_data(ram_wr_data), .wr_be(ram_wr_be),
        .master_id(ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // ------------------------------------------------------------------
    // Counters / observation
    // ------------------------------------------------------------------
    int agent_start_count;
    int agent_beat_count;
    int agent_done_count;
    int map_useg_writes;

    initial begin
        agent_start_count = 0;
        agent_beat_count  = 0;
        agent_done_count  = 0;
        map_useg_writes   = 0;
    end

    always_ff @(posedge clk) begin
        if (rst_n && agent_ev_valid && agent_ev_subsys == SUBSYS_SIF) begin
            case (agent_ev_event)
                EV_DMA_START: agent_start_count <= agent_start_count + 1;
                EV_DMA_BEAT:  agent_beat_count  <= agent_beat_count  + 1;
                EV_DMA_DONE:  agent_done_count  <= agent_done_count  + 1;
                default: ;
            endcase
        end
        if (rst_n && map_ev_valid && map_ev_subsys == SUBSYS_MEM &&
            map_ev_event == EV_WRITE && map_ev_arg3[7:0] == 8'd1) begin
            // bridge writes to ee_ram emit REGION_EE_RAM; the shadowing
            // side-effect is internal (no separate ev tag). Count these
            // as evidence that bridge actually drove the map.
            map_useg_writes <= map_useg_writes + 1;
        end
    end

    // ------------------------------------------------------------------
    // Readback via EE CPU port (1-cycle latency)
    // ------------------------------------------------------------------
    task automatic ee_read_word(input logic [31:0] addr,
                                output logic [31:0] data);
        @(negedge clk);
        ee_rd_en   = 1'b1;
        ee_rd_addr = addr;
        @(negedge clk);
        ee_rd_en   = 1'b0;
        ee_rd_addr = 32'd0;
        @(posedge clk);
        data = ee_rd_data;
    endtask

    // ------------------------------------------------------------------
    // Main
    // ------------------------------------------------------------------
    int errors;
    int timeout_cycles;

    initial begin
        rst_n          = 1'b0;
        go             = 1'b0;
        ee_rd_en       = 1'b0;
        ee_rd_addr     = 32'd0;
        errors         = 0;
        timeout_cycles = 0;

        // Reset
        repeat (4) @(posedge clk);
        rst_n = 1'b1;
        repeat (2) @(posedge clk);

        // Kick the installer
        @(negedge clk);
        go = 1'b1;
        @(negedge clk);
        go = 1'b0;

        // Wait for done or timeout
        while (!agent_done && timeout_cycles < 2000) begin
            @(posedge clk);
            timeout_cycles = timeout_cycles + 1;
        end

        if (!agent_done) begin
            $display("[tb_ee_install_agent_smoke] FAIL: timeout waiting for agent done (cyc=%0d)",
                     timeout_cycles);
            errors = errors + 1;
        end

        // Drain any in-flight bridge beats (the last qword emit takes
        // one extra cycle after the accumulator fills).
        repeat (8) @(posedge clk);

        if (!br_last_seen) begin
            $display("[tb_ee_install_agent_smoke] FAIL: bridge last_seen never asserted");
            errors = errors + 1;
        end

        // Byte-compare all 96 words through the EE CPU read port.
        for (int i = 0; i < TOTAL_WORDS; i++) begin
            logic [31:0] got;
            ee_read_word(USEG_BASE_ADDR + i * 4, got);
            if (got !== expected[i]) begin
                $display("[tb_ee_install_agent_smoke] FAIL word %0d @0x%08x: got %08x exp %08x",
                         i, USEG_BASE_ADDR + i * 4, got, expected[i]);
                errors = errors + 1;
            end
        end

        $display("[tb_ee_install_agent_smoke] start_events=%0d beat_events=%0d done_events=%0d bridge_writes=%0d",
                 agent_start_count, agent_beat_count, agent_done_count, map_useg_writes);

        if (agent_start_count != 1) begin
            $display("[tb_ee_install_agent_smoke] FAIL: expected 1 start event, got %0d", agent_start_count);
            errors = errors + 1;
        end
        if (agent_beat_count != TOTAL_WORDS) begin
            $display("[tb_ee_install_agent_smoke] FAIL: expected %0d beat events, got %0d",
                     TOTAL_WORDS, agent_beat_count);
            errors = errors + 1;
        end
        if (agent_done_count != 1) begin
            $display("[tb_ee_install_agent_smoke] FAIL: expected 1 done event, got %0d", agent_done_count);
            errors = errors + 1;
        end
        // 96 words / 4 words per qword = 24 bridge qword writes expected
        if (map_useg_writes != TOTAL_WORDS / 4) begin
            $display("[tb_ee_install_agent_smoke] FAIL: expected %0d bridge qword writes, got %0d",
                     TOTAL_WORDS / 4, map_useg_writes);
            errors = errors + 1;
        end

        if (errors == 0)
            $display("[tb_ee_install_agent_smoke] PASS");
        else
            $display("[tb_ee_install_agent_smoke] FAIL total=%0d", errors);

        $finish;
    end

    initial begin
        #200000;
        $display("[tb_ee_install_agent_smoke] FAIL: absolute timeout");
        $finish;
    end

endmodule : tb_ee_install_agent_smoke
