// retroDE_ps2 — tb_ee_install_agent_external_image (Ch56)
//
// Ch56 verification of the boot_install_agent_stub external-image mode:
// the agent's payload ROM is sourced from a hex/mem file at sim start
// (USE_IMAGE_FILE=1, IMAGE_FILE=<path>) instead of the hardcoded Ch54
// built-in. Transport (bridge, map, useg_shadow shadowing, trace
// schema, start/beat/done semantics) is unchanged vs the built-in
// unit TB — only the source bytes differ.
//
// Image: sim/data/ch56_test_image_4w.mem (4 distinctive words, chosen
// so a byte-compare miss against the built-in Ch54 image would be
// unambiguous).
//
// Chain mirrors tb_ee_install_agent_smoke:
//   boot_install_agent_stub (external mode, 4 words)
//     └─► sif_dma_ee_ram_bridge_stub
//           └─► ee_memory_map_stub (bridge_wr → useg_shadow for
//                                   install window [0x80..0x1FF])
//                 └─► ee_ram_stub
//
// PASS criteria:
//   1. Agent done_o asserts; bridge last_seen_o asserts.
//   2. Readback via the EE CPU port at useg 0x80, 0x84, 0x88, 0x8C
//      returns 0xDEADBEEF, 0xCAFEBABE, 0x12345678, 0x87654321.
//   3. Exactly 1 start event, 4 beat events, 1 done event.
//   4. Exactly 1 bridge qword write to the map (4 words / 4 per qword).

`timescale 1ns/1ps

module tb_ee_install_agent_external_image;
    import trace_pkg::*;

    localparam int EE_RAM_BYTES  = 4 * 1024;
    localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES);
    localparam int TOTAL_WORDS   = 4;
    localparam logic [31:0] DEST_BASE_BYTES = 32'h0000_0080;
    localparam logic [31:0] USEG_BASE_ADDR  = 32'h0000_0080;
    // Absolute path resolved via Makefile-supplied macro CH56_TB_IMAGE_PATH
    // (a string literal). Passed directly into the module parameter to
    // dodge iverilog string-localparam binding quirks.
    `define CH56_IMAGE_PATH `CH56_TB_IMAGE_PATH

    logic clk;
    logic rst_n;

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

    logic [31:0] expected [0:TOTAL_WORDS-1];
    initial begin
        expected[0] = 32'hDEADBEEF;
        expected[1] = 32'hCAFEBABE;
        expected[2] = 32'h12345678;
        expected[3] = 32'h87654321;
    end

    // ------------------------------------------------------------------
    // Install agent — external mode
    // ------------------------------------------------------------------
    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),
        .USE_IMAGE_FILE(1'b1),
        .IMAGE_FILE(`CH56_TB_IMAGE_PATH)
    ) 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)
    );

    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;

    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(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .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
    // ------------------------------------------------------------------
    int agent_start_count;
    int agent_beat_count;
    int agent_done_count;

    initial begin
        agent_start_count = 0;
        agent_beat_count  = 0;
        agent_done_count  = 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
    end

    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;

        $display("[tb_ee_install_agent_external_image] image=%s", `CH56_TB_IMAGE_PATH);

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

        @(negedge clk);
        go = 1'b1;
        @(negedge clk);
        go = 1'b0;

        while (!agent_done && timeout_cycles < 200) begin
            @(posedge clk);
            timeout_cycles = timeout_cycles + 1;
        end

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

        repeat (8) @(posedge clk);

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

        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_external_image] 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_external_image] start_events=%0d beat_events=%0d done_events=%0d",
                 agent_start_count, agent_beat_count, agent_done_count);

        if (agent_start_count != 1) begin
            $display("[tb_ee_install_agent_external_image] FAIL: expected 1 start, got %0d", agent_start_count);
            errors = errors + 1;
        end
        if (agent_beat_count != TOTAL_WORDS) begin
            $display("[tb_ee_install_agent_external_image] FAIL: expected %0d beats, got %0d",
                     TOTAL_WORDS, agent_beat_count);
            errors = errors + 1;
        end
        if (agent_done_count != 1) begin
            $display("[tb_ee_install_agent_external_image] FAIL: expected 1 done, got %0d", agent_done_count);
            errors = errors + 1;
        end

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

        $finish;
    end

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

endmodule : tb_ee_install_agent_external_image
