// retroDE_ps2 — tb_iop_responder_ee_ram_landing (Ch261)
// ============================================================================
// Codex Ch261 framing: synthetic IOP responder skeleton. Prove the existing
// IOP-side stubs can produce ONE explicit EE-visible side effect — a known
// 128-bit qword landing in EE RAM at a fixed offset — entirely autonomously
// from a single `go_i` pulse, with no BIOS image, no long watchdog, and a
// deterministic ~hundreds-of-microseconds runtime.
//
// Chain (all composed from existing primitives):
//
//   iop_exec_stub      (5-op RAM-resident script driver, single go_i pulse)
//     │
//     │ map.cpu_rd/wr
//     ▼
//   iop_memory_map_stub (architectural decode at 0x0000xxxx / 0x1F801xxx)
//     ├── ram_rd/wr    → iop_ram_stub (holds script + DMA payload)
//     ├── iop_dmac_rd/wr → iop_dmac_reg_stub (ch9 SIF0 IOP→EE)
//     │       │
//     │       │ ep_valid / ep_data[31:0] / ep_last / ep_ready
//     │       ▼
//     │   sif_dma_ee_ram_bridge_stub  (32→128 width adapter, 4 beats per qword)
//     │       │
//     │       │ bridge_wr_en / addr / data[127:0] / be[15:0]
//     │       ▼
//     │   ee_ram_stub  (DMA-write port; TB peeks mem[] to verify landing)
//     │
//     └── iop_intc_rd/wr → intc_stub  (DMA_DONE pulse → cpu_irq → WAIT_IRQ exit)
//
// Acceptance (per Codex Ch261):
//   - No BIOS image involved.
//   - No long watchdog (5 ms simulation watchdog is generous; PASS lands
//     well before the 1 ms mark in practice).
//   - Deterministic. Single go_i pulse → script runs → DMA fires → bridge
//     packs four 32-bit beats into one 128-bit qword → ee_ram_stub.mem[]
//     contains exactly the expected pattern at EE_LANDING_BASE.
//   - One clear PASS: `dut peek of ee_ram_stub.mem[EE_LANDING_BASE/16]`
//     equals the packed expected qword. Plus DMA_DONE_COUNT == 1,
//     INTC assert+ack pair seen, exec halts exactly once.
//
// Out of scope for Ch261 (per Codex):
//   - BIOS-long composition. Ch262 wires this responder into the BIOS-long
//     setup and asks one question.
//   - Multiple in-flight transactions, real R3000 (iop_core_stub), real IOP
//     BIOS image, SIF mailbox handshake. All deferred.

`timescale 1ns/1ps

module tb_iop_responder_ee_ram_landing;

    // ------------------------------------------------------------------
    // Geometry
    // ------------------------------------------------------------------
    localparam int IOP_RAM_BYTES  = 4 * 1024;
    localparam int IOP_RAM_ADDR_W = $clog2(IOP_RAM_BYTES);

    // EE RAM big enough to cover the landing base + one qword. The bridge
    // writes at EE_LANDING_BASE; we keep the buffer at 1 MiB so the
    // qword index fits and we don't need any extra plumbing.
    localparam int EE_RAM_BYTES   = 1024 * 1024;
    localparam int EE_RAM_ADDR_W  = $clog2(EE_RAM_BYTES);

    // IOP-side script + DMA payload offsets (script at +0x400, payload
    // at +0x200 — same convention as tb_iop_self_driven).
    localparam logic [31:0] SCRIPT_BASE   = 32'h0000_0400;
    localparam logic [31:0] PAYLOAD_BASE  = 32'h0000_0200;

    // EE-side landing address (byte). 0x80000 chosen to match the
    // existing tb_pad_state_via_sif_to_ee convention; the qword index
    // is 0x80000 / 16 = 0x8000.
    localparam logic [31:0] EE_LANDING_BASE = 32'h0008_0000;

    // Architectural addresses the script reaches
    localparam logic [31:0] DMAC_MADR_ADDR = 32'h1F80_1520;
    localparam logic [31:0] DMAC_BCR_ADDR  = 32'h1F80_1524;
    localparam logic [31:0] DMAC_CHCR_ADDR = 32'h1F80_1528;
    localparam logic [31:0] INTC_STAT_ADDR = 32'h1F80_1070;
    localparam logic [31:0] INTC_MASK_ADDR = 32'h1F80_1074;

    // Opcodes (keep in sync with iop_exec_stub)
    localparam logic [31:0] OP_HALT     = 32'h0000_0000;
    localparam logic [31:0] OP_WRITE    = 32'h0000_0001;
    localparam logic [31:0] OP_READ     = 32'h0000_0002;
    localparam logic [31:0] OP_WAIT_IRQ = 32'h0000_0003;

    // The four distinctive 32-bit payload words. Bridge packs little-
    // endian: beat 0 → bits[31:0], beat 1 → bits[63:32], etc. Pick
    // sentinels that are visually unmistakable in a hex dump.
    localparam logic [31:0] PAYLOAD_W0 = 32'hDEAD_BEEF;
    localparam logic [31:0] PAYLOAD_W1 = 32'hC0FF_EE00;
    localparam logic [31:0] PAYLOAD_W2 = 32'h1234_5678;
    localparam logic [31:0] PAYLOAD_W3 = 32'hCAFE_F00D;
    localparam logic [127:0] EXPECTED_QWORD = {PAYLOAD_W3, PAYLOAD_W2, PAYLOAD_W1, PAYLOAD_W0};

    // ------------------------------------------------------------------
    // Clock + reset
    // ------------------------------------------------------------------
    logic clk;
    logic rst_n;
    initial clk = 1'b0;
    always #5 clk = ~clk;

    // ==================================================================
    // IOP exec stub — drives the IOP map CPU-side port from a RAM-
    // resident script. Single go_i pulse, no further orchestration.
    // ==================================================================
    logic        exec_go;
    logic        exec_halt;
    logic [7:0]  exec_pc;

    logic        map_cpu_rd_en;
    logic [31:0] map_cpu_rd_addr;
    logic [31:0] map_cpu_rd_data;
    logic        map_cpu_rd_valid;

    logic        map_cpu_wr_en;
    logic [31:0] map_cpu_wr_addr;
    logic [31:0] map_cpu_wr_data;
    logic [3:0]  map_cpu_wr_be;

    logic        iop_cpu_irq;

    logic                exec_ev_valid;
    trace_pkg::subsys_e  exec_ev_subsys;
    trace_pkg::event_e   exec_ev_event;
    logic [63:0]         exec_ev_arg0, exec_ev_arg1, exec_ev_arg2, exec_ev_arg3;
    logic [31:0]         exec_ev_flags;

    iop_exec_stub #(.SCRIPT_BASE(SCRIPT_BASE)) u_exec (
        .clk(clk), .rst_n(rst_n),
        .go_i(exec_go),
        .map_rd_en(map_cpu_rd_en), .map_rd_addr(map_cpu_rd_addr),
        .map_rd_data(map_cpu_rd_data), .map_rd_valid(map_cpu_rd_valid),
        .map_wr_en(map_cpu_wr_en), .map_wr_addr(map_cpu_wr_addr),
        .map_wr_data(map_cpu_wr_data), .map_wr_be(map_cpu_wr_be),
        .cpu_irq(iop_cpu_irq),
        .halt_o(exec_halt), .pc_o(exec_pc),
        .ev_valid(exec_ev_valid), .ev_subsys(exec_ev_subsys),
        .ev_event(exec_ev_event),
        .ev_arg0(exec_ev_arg0), .ev_arg1(exec_ev_arg1),
        .ev_arg2(exec_ev_arg2), .ev_arg3(exec_ev_arg3),
        .ev_flags(exec_ev_flags)
    );

    // ==================================================================
    // IOP memory map + IOP RAM. TB drives bridge_wr_* for preload.
    // ==================================================================
    logic        iop_ram_rd_en;
    logic [20:0] iop_ram_rd_addr;
    logic [31:0] iop_ram_rd_data;
    logic        iop_ram_rd_valid;
    logic        iop_ram_wr_en;
    logic [20:0] iop_ram_wr_addr;
    logic [31:0] iop_ram_wr_data;
    logic [3:0]  iop_ram_wr_be;
    logic [7:0]  iop_ram_master_id;

    logic        map_dmac_rd_en;
    logic [3:0]  map_dmac_rd_addr;
    logic [31:0] map_dmac_rd_data;
    logic        map_dmac_rd_valid;
    logic        map_dmac_wr_en;
    logic [3:0]  map_dmac_wr_addr;
    logic [31:0] map_dmac_wr_data;

    logic        map_intc_rd_en;
    logic [7:0]  map_intc_rd_addr;
    logic [31:0] map_intc_rd_data;
    logic        map_intc_rd_valid;
    logic        map_intc_wr_en;
    logic [7:0]  map_intc_wr_addr;
    logic [31:0] map_intc_wr_data;

    logic        dma_rd_en;
    logic [31:0] dma_rd_addr;
    logic [7:0]  dma_master_id;
    logic [31:0] dma_rd_data;
    logic        dma_rd_valid;

    logic        tb_br_wr_en;
    logic [31:0] tb_br_wr_addr;
    logic [31:0] tb_br_wr_data;
    logic [3:0]  tb_br_wr_be;

    iop_memory_map_stub u_iop_map (
        .clk(clk), .rst_n(rst_n),
        .iop_rd_en(map_cpu_rd_en), .iop_rd_addr(map_cpu_rd_addr),
        .iop_rd_data(map_cpu_rd_data), .iop_rd_valid(map_cpu_rd_valid),
        .iop_wr_en(map_cpu_wr_en), .iop_wr_addr(map_cpu_wr_addr),
        .iop_wr_data(map_cpu_wr_data), .iop_wr_be(map_cpu_wr_be),
        .master_id(8'd2),
        .bridge_wr_en(tb_br_wr_en), .bridge_wr_addr(tb_br_wr_addr),
        .bridge_wr_data(tb_br_wr_data), .bridge_wr_be(tb_br_wr_be),
        .bridge_master_id(8'd0),
        .dma_rd_en(dma_rd_en), .dma_rd_addr(dma_rd_addr),
        .dma_master_id(dma_master_id),
        .dma_rd_data(dma_rd_data), .dma_rd_valid(dma_rd_valid),
        .sif_rd_en(), .sif_rd_addr(),
        .sif_rd_data(32'd0), .sif_rd_valid(1'b0),
        .sif_wr_en(), .sif_wr_addr(), .sif_wr_data(),
        .iop_dmac_rd_en(map_dmac_rd_en), .iop_dmac_rd_addr(map_dmac_rd_addr),
        .iop_dmac_rd_data(map_dmac_rd_data), .iop_dmac_rd_valid(map_dmac_rd_valid),
        .iop_dmac_wr_en(map_dmac_wr_en), .iop_dmac_wr_addr(map_dmac_wr_addr),
        .iop_dmac_wr_data(map_dmac_wr_data),
        .iop_intc_rd_en(map_intc_rd_en), .iop_intc_rd_addr(map_intc_rd_addr),
        .iop_intc_rd_data(map_intc_rd_data), .iop_intc_rd_valid(map_intc_rd_valid),
        .iop_intc_wr_en(map_intc_wr_en), .iop_intc_wr_addr(map_intc_wr_addr),
        .iop_intc_wr_data(map_intc_wr_data),
        .input_p1(32'd0), .input_p2(32'd0),
        .bios_rd_en(), .bios_rd_addr(),
        .bios_rd_data(32'd0), .bios_rd_valid(1'b0),
        .ram_rd_en(iop_ram_rd_en), .ram_rd_addr(iop_ram_rd_addr),
        .ram_rd_data(iop_ram_rd_data), .ram_rd_valid(iop_ram_rd_valid),
        .ram_wr_en(iop_ram_wr_en), .ram_wr_addr(iop_ram_wr_addr),
        .ram_wr_data(iop_ram_wr_data), .ram_wr_be(iop_ram_wr_be),
        .ram_master_id(iop_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    iop_ram_stub #(.SIZE_BYTES(IOP_RAM_BYTES)) u_iop_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(iop_ram_rd_en),
        .rd_addr(iop_ram_rd_addr[IOP_RAM_ADDR_W-1:0]),
        .rd_data(iop_ram_rd_data), .rd_valid(iop_ram_rd_valid),
        .wr_en(iop_ram_wr_en),
        .wr_addr(iop_ram_wr_addr[IOP_RAM_ADDR_W-1:0]),
        .wr_data(iop_ram_wr_data), .wr_be(iop_ram_wr_be),
        .master_id(iop_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // ==================================================================
    // IOP DMAC ch9 — endpoint drives the SIF→EE bridge.
    // ==================================================================
    logic        ep_valid;
    logic [31:0] ep_data;
    logic        ep_last;
    logic        ep_ready;

    logic        dmac_irq_completion;
    logic        dmac_busy;
    logic [31:0] dmac_done_count;

    logic                dmac_ev_valid;
    trace_pkg::subsys_e  dmac_ev_subsys;
    trace_pkg::event_e   dmac_ev_event;
    logic [63:0]         dmac_ev_arg0, dmac_ev_arg1, dmac_ev_arg2, dmac_ev_arg3;
    logic [31:0]         dmac_ev_flags;

    iop_dmac_reg_stub u_dmac (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_dmac_wr_en), .reg_rd_en(map_dmac_rd_en),
        .reg_offset(map_dmac_wr_en ? map_dmac_wr_addr : map_dmac_rd_addr),
        .reg_wr_data(map_dmac_wr_data),
        .reg_rd_data(map_dmac_rd_data), .reg_rd_valid(map_dmac_rd_valid),
        .mem_rd_en(dma_rd_en), .mem_rd_addr(dma_rd_addr),
        .mem_master_id(dma_master_id),
        .mem_rd_data(dma_rd_data), .mem_rd_valid(dma_rd_valid),
        .ep_valid(ep_valid), .ep_data(ep_data), .ep_last(ep_last),
        .ep_ready(ep_ready),
        .irq_completion_o(dmac_irq_completion),
        .busy_o(dmac_busy), .done_count_o(dmac_done_count),
        .ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys),
        .ev_event(dmac_ev_event),
        .ev_arg0(dmac_ev_arg0), .ev_arg1(dmac_ev_arg1),
        .ev_arg2(dmac_ev_arg2), .ev_arg3(dmac_ev_arg3),
        .ev_flags(dmac_ev_flags)
    );

    // ==================================================================
    // SIF→EE bridge: accumulates the 4×32-bit beats into a single 128-bit
    // qword and writes it to EE RAM at EE_LANDING_BASE.
    // ==================================================================
    wire         eebr_wr_en;
    wire [31:0]  eebr_wr_addr;
    wire [127:0] eebr_wr_data;
    wire [15:0]  eebr_wr_be;
    wire [7:0]   eebr_master_id;
    wire         eebr_last_seen;

    sif_dma_ee_ram_bridge_stub #(
        .DEST_BASE_ADDR(EE_LANDING_BASE),
        .MASTER_ID(8'd5)
    ) u_ee_bridge (
        .clk(clk), .rst_n(rst_n),
        .in_valid(ep_valid), .in_data(ep_data), .in_last(ep_last),
        .in_ready(ep_ready),
        .bridge_wr_en(eebr_wr_en), .bridge_wr_addr(eebr_wr_addr),
        .bridge_wr_data(eebr_wr_data), .bridge_wr_be(eebr_wr_be),
        .bridge_master_id(eebr_master_id),
        .last_seen_o(eebr_last_seen),
        .rewind_i(1'b0)
    );

    // ==================================================================
    // EE RAM landing target. Bridge writes directly to its write port.
    // TB samples mem[] for the assertion.
    // ==================================================================
    ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
        .clk(clk), .rst_n(rst_n),
        // Read port unused — TB peeks mem[] hierarchically.
        .rd_en(1'b0),
        .rd_addr({EE_RAM_ADDR_W{1'b0}}),
        .rd_data(), .rd_valid(),
        // Write port driven straight from the bridge.
        .wr_en(eebr_wr_en),
        .wr_addr(eebr_wr_addr[EE_RAM_ADDR_W-1:0]),
        .wr_data(eebr_wr_data), .wr_be(eebr_wr_be),
        .master_id(eebr_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // ==================================================================
    // IOP INTC. DMA_DONE pulse drives bit 0; exec stub WAIT_IRQ exits
    // when cpu_irq asserts. Mask + W1C-ack are driven by the script.
    // ==================================================================
    logic [15:0] iop_irq_src;
    assign iop_irq_src = {15'd0, dmac_irq_completion};

    intc_stub #(
        .INTC_STAT_OFFSET(8'h70),
        .INTC_MASK_OFFSET(8'h74)
    ) u_intc (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_intc_wr_en), .reg_rd_en(map_intc_rd_en),
        .reg_addr(map_intc_wr_en ? map_intc_wr_addr : map_intc_rd_addr),
        .reg_wr_data(map_intc_wr_data),
        .reg_rd_data(map_intc_rd_data), .reg_rd_valid(map_intc_rd_valid),
        .irq_src(iop_irq_src), .cpu_irq(iop_cpu_irq),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // ==================================================================
    // Observers (passive)
    // ==================================================================
    int dmac_done_events;
    int halt_events;
    int errors;

    initial begin
        dmac_done_events = 0;
        halt_events      = 0;
        errors           = 0;
    end

    always_ff @(posedge clk) begin
        if (rst_n && dmac_ev_valid &&
            dmac_ev_subsys == trace_pkg::SUBSYS_DMAC &&
            dmac_ev_event  == trace_pkg::EV_DMA_DONE)
            dmac_done_events <= dmac_done_events + 1;
        if (rst_n && exec_ev_valid && (exec_ev_flags & 32'h4) != 32'd0)
            halt_events <= halt_events + 1;
    end

    // ==================================================================
    // Preload helpers — drive bridge_wr_* into IOP RAM.
    // ==================================================================
    task automatic bridge_write(input logic [31:0] addr,
                                input logic [31:0] data);
        @(negedge clk);
        tb_br_wr_en   = 1'b1;
        tb_br_wr_addr = addr;
        tb_br_wr_data = data;
        tb_br_wr_be   = 4'b1111;
        @(negedge clk);
        tb_br_wr_en   = 1'b0;
        tb_br_wr_addr = 32'd0;
        tb_br_wr_data = 32'd0;
        tb_br_wr_be   = 4'd0;
    endtask

    task automatic emit_op(input int slot,
                           input logic [31:0] opcode,
                           input logic [31:0] addr_or_tgt,
                           input logic [31:0] data_or_exp);
        logic [31:0] base;
        base = SCRIPT_BASE + (slot << 4);
        bridge_write(base + 32'd0,  opcode);
        bridge_write(base + 32'd4,  addr_or_tgt);
        bridge_write(base + 32'd8,  data_or_exp);
        bridge_write(base + 32'd12, 32'd0);
    endtask

    // ==================================================================
    // Stimulus
    // ==================================================================
    logic [127:0] landed_qword;
    initial landed_qword = 128'd0;

    initial begin
        rst_n         = 1'b0;
        exec_go       = 1'b0;
        tb_br_wr_en   = 1'b0;
        tb_br_wr_addr = 32'd0;
        tb_br_wr_data = 32'd0;
        tb_br_wr_be   = 4'd0;

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

        // Preload DMA payload — four distinctive 32-bit words at the
        // payload base. The DMAC ch9 reads them sequentially via the
        // IOP map's dma_rd port; the bridge accumulates them into one
        // 128-bit qword.
        bridge_write(PAYLOAD_BASE + 32'h00, PAYLOAD_W0);
        bridge_write(PAYLOAD_BASE + 32'h04, PAYLOAD_W1);
        bridge_write(PAYLOAD_BASE + 32'h08, PAYLOAD_W2);
        bridge_write(PAYLOAD_BASE + 32'h0C, PAYLOAD_W3);

        // Preload control program at SCRIPT_BASE — 8 ops:
        //   0  WRITE INTC_MASK = 1     (enable bit 0 = DMA completion)
        //   1  WRITE DMAC_MADR = payload base
        //   2  WRITE DMAC_BCR  = 4     (4 × 32-bit beats = one qword)
        //   3  WRITE DMAC_CHCR = 1     (start DMAC)
        //   4  WAIT_IRQ                (block until cpu_irq from DMA done)
        //   5  WRITE INTC_STAT = 1     (W1C ack)
        //   6  READ  DMAC_DONE_COUNT   (sanity readback)
        //   7  HALT
        //
        // The initial Ch261 draft of this script tripped a real
        // arbitration bug in iop_memory_map_stub line 318 (CPU always
        // won CPU+DMA collision on the shared RAM port, silently
        // routing CPU's data to the DMA path). The Ch261-closing fix
        // replaces that with a one-entry deferred-CPU-RAM-read slot
        // (DMA wins, CPU latches into pending, serviced next non-DMA
        // cycle). With the fix in place, the script can be its natural
        // shape — exec stub fetches WAIT_IRQ while DMA is in flight,
        // collisions resolve correctly, beat 0 carries the real
        // PAYLOAD_W0. See tb_iop_memory_map_collision for the
        // direct-arbitration test of the fix.
        emit_op(0, OP_WRITE,    INTC_MASK_ADDR, 32'h0000_0001);
        emit_op(1, OP_WRITE,    DMAC_MADR_ADDR, PAYLOAD_BASE);
        emit_op(2, OP_WRITE,    DMAC_BCR_ADDR,  32'h0000_0004);
        emit_op(3, OP_WRITE,    DMAC_CHCR_ADDR, 32'h0000_0001);
        emit_op(4, OP_WAIT_IRQ, 32'd0,          32'd0);
        emit_op(5, OP_WRITE,    INTC_STAT_ADDR, 32'h0000_0001);
        emit_op(6, OP_READ,     32'h1F80_152C,  32'd0);  // DMAC_DONE_COUNT
        emit_op(7, OP_HALT,     32'd0,          32'd0);

        // Single-shot go.
        @(negedge clk);
        exec_go = 1'b1;
        @(negedge clk);
        exec_go = 1'b0;

        // Wait for autonomous completion. The script reaches HALT
        // only AFTER WAIT_IRQ exits — which happens when the DMA
        // completion pulse latches into INTC — so exec_halt implies
        // the DMA pipeline has fully drained. eebr_last_seen is a
        // belt-and-braces gate for the bridge's final emit cycle.
        while (!exec_halt) @(posedge clk);
        while (!eebr_last_seen) @(posedge clk);
        repeat (4) @(posedge clk);   // settle one bridge emit cycle + slack

        // Peek the EE-RAM qword. EE_LANDING_BASE / 16 is the qword
        // index. ee_ram_stub stores 128-bit qwords directly.
        landed_qword = u_ee_ram.mem[EE_LANDING_BASE / 16];

        // -----------------------------------------------------------
        // Assertions
        // -----------------------------------------------------------
        if (landed_qword !== EXPECTED_QWORD) begin
            $error("[tb_iop_responder_ee_ram_landing] EE-RAM qword mismatch: got 0x%032h expected 0x%032h",
                   landed_qword, EXPECTED_QWORD);
            errors = errors + 1;
        end
        if (dmac_done_events != 1) begin
            $error("[tb_iop_responder_ee_ram_landing] expected 1 DMA_DONE, got %0d",
                   dmac_done_events);
            errors = errors + 1;
        end
        if (halt_events != 1) begin
            $error("[tb_iop_responder_ee_ram_landing] expected 1 halt, got %0d",
                   halt_events);
            errors = errors + 1;
        end
        if (eebr_last_seen !== 1'b1) begin
            $error("[tb_iop_responder_ee_ram_landing] bridge last_seen_o never latched");
            errors = errors + 1;
        end

        $display("[tb_iop_responder_ee_ram_landing] landed=0x%032h done=%0d halt=%0d last_seen=%0d errors=%0d",
                 landed_qword, dmac_done_events, halt_events,
                 eebr_last_seen, errors);
        if (errors == 0) $display("[tb_iop_responder_ee_ram_landing] PASS");
        else             $display("[tb_iop_responder_ee_ram_landing] FAIL");
        $finish;
    end

    // ==================================================================
    // Watchdog — Codex Ch261 acceptance: "no long watchdog,
    // deterministic runtime." 5 ms is more than 100× the expected
    // runtime; PASS lands well before this.
    // ==================================================================
    initial begin
        #5_000_000;
        $error("[tb_iop_responder_ee_ram_landing] TIMEOUT");
        $finish;
    end

endmodule : tb_iop_responder_ee_ram_landing
