// retroDE_ps2 — tb_sif_iop_bridge_exec
//
// Execution-visible bridge milestone. Extends the bridge-smoke proof by
// having iop_fetch_stub read back the landed payload through the real
// IOP path — not just a TB-side read port.
//
// Chain:
//   ee_ram_stub
//     → ee_memory_map_stub → dmac_reg_stub (ch5)
//       → sif_dma_iop_ram_bridge_stub
//         → iop_memory_map_stub (bridge write port)
//           → iop_ram_stub
//                    ↑
//             iop_fetch_stub ← iop_memory_map_stub (CPU read port)
//
// Ordering discipline (per Codex): DO NOT overlap DMA and fetch in this
// first pass. The fetcher stays disabled until the full transfer plus
// bridge flush has completed. Overlap is a later milestone.
//
// Key interface-visible / trace-visible assertions:
//   - bridge writes complete before fetch starts
//   - IOP IFETCH arg0 walks 0x00, 0x04, 0x08, ... 0x1C (8 words)
//   - IFETCH arg1 matches the bridged little-endian unpacking of the
//     source qwords
//   - map-layer reads tagged master_id=IOP_CPU (2), region=IOP_RAM (2)
//   - RAM-layer reads carry the same master_id attribution

`timescale 1ns/1ps

module tb_sif_iop_bridge_exec;

    logic clk;
    logic rst_n;

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

    localparam int QWC_VAL        = 2;
    localparam int EE_RAM_BYTES   = 4 * 1024;
    localparam int EE_RAM_ADDR_W  = $clog2(EE_RAM_BYTES);
    localparam int IOP_RAM_BYTES  = 4 * 1024;
    localparam int IOP_RAM_ADDR_W = $clog2(IOP_RAM_BYTES);

    localparam logic [31:0] SOURCE_MADR    = 32'h0000_0700;
    localparam logic [31:0] DEST_BASE_ADDR = 32'h0000_0000;  // = fetcher reset vector

    // ------------------------------------------------------------------
    // EE-side RAM (source)
    // ------------------------------------------------------------------

    logic                      ee_ram_rd_en;
    logic [EE_RAM_ADDR_W-1:0]  ee_ram_rd_addr;
    logic [127:0]              ee_ram_rd_data;
    logic                      ee_ram_rd_valid;

    logic                      ee_ram_wr_en;
    logic [EE_RAM_ADDR_W-1:0]  ee_ram_wr_addr;
    logic [127:0]              ee_ram_wr_data;
    logic [15:0]               ee_ram_wr_be;

    logic [7:0]                ee_ram_master_id;

    ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(ee_ram_rd_en), .rd_addr(ee_ram_rd_addr),
        .rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid),
        .wr_en(ee_ram_wr_en), .wr_addr(ee_ram_wr_addr),
        .wr_data(ee_ram_wr_data), .wr_be(ee_ram_wr_be),
        .master_id(ee_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    assign ee_ram_master_id = ee_ram_rd_en ? 8'd1 : 8'd0;

    // ------------------------------------------------------------------
    // EE DMAC (ch5) + EE memory map
    // ------------------------------------------------------------------

    logic        dmac_reg_wr_en;
    logic [7:0]  dmac_reg_offset;
    logic [31:0] dmac_reg_wr_data;

    logic         dmac_mem_rd_en;
    logic [31:0]  dmac_mem_rd_addr;
    logic [127:0] map_to_dmac_rd_data;
    logic         map_to_dmac_rd_valid;

    logic         dmac_ep_valid;
    logic [127:0] dmac_ep_data;
    logic         dmac_ep_last;
    logic         dmac_ep_ready;

    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;

    dmac_reg_stub #(.CHANNEL(4'd5), .PATH_ID(4'd5)) u_dmac (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(dmac_reg_wr_en), .reg_offset(dmac_reg_offset),
        .reg_wr_data(dmac_reg_wr_data),
        .reg_rd_en(1'b0), .reg_rd_data(), .reg_rd_valid(),
        .mem_rd_en(dmac_mem_rd_en), .mem_rd_addr(dmac_mem_rd_addr),
        .mem_rd_data(map_to_dmac_rd_data), .mem_rd_valid(map_to_dmac_rd_valid),
        .ep_valid(dmac_ep_valid), .ep_data(dmac_ep_data),
        .ep_last(dmac_ep_last), .ep_ready(dmac_ep_ready),
        .irq_completion_o(),
        .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)
    );

    logic        ee_map_ram_rd_en;
    logic [24:0] ee_map_ram_rd_addr;

    ee_memory_map_stub u_ee_map (
        .clk(clk), .rst_n(rst_n),
        .ee_rd_en(1'b0), .ee_rd_addr(32'd0),
        .ee_rd_data(), .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(dmac_mem_rd_en), .dmac_rd_addr(dmac_mem_rd_addr),
        .dmac_rd_data(map_to_dmac_rd_data),
        .dmac_rd_valid(map_to_dmac_rd_valid),
        .bios_rd_en(), .bios_rd_addr(),
        .bios_rd_data(32'd0), .bios_rd_valid(1'b0),
        .ram_rd_en(ee_map_ram_rd_en), .ram_rd_addr(ee_map_ram_rd_addr),
        .ram_rd_data(ee_ram_rd_data), .ram_rd_valid(ee_ram_rd_valid),
        .bridge_wr_en(1'b0), .bridge_wr_addr(32'd0),
        .bridge_wr_data(128'd0), .bridge_wr_be(16'd0),
        .bridge_master_id(8'd0),
        .ram_wr_en(), .ram_wr_addr(), .ram_wr_data(),
        .ram_wr_be(), .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()
    );

    assign ee_ram_rd_en   = ee_map_ram_rd_en;
    assign ee_ram_rd_addr = ee_map_ram_rd_addr[EE_RAM_ADDR_W-1:0];

    // ------------------------------------------------------------------
    // Bridge
    // ------------------------------------------------------------------

    logic        bridge_wr_en;
    logic [31:0] bridge_wr_addr;
    logic [31:0] bridge_wr_data;
    logic [3:0]  bridge_wr_be;
    logic [7:0]  bridge_master_id;

    sif_dma_iop_ram_bridge_stub
      #(.DEST_BASE_ADDR(DEST_BASE_ADDR), .MASTER_ID(8'd3))
      u_bridge (
        .clk(clk), .rst_n(rst_n),
        .in_valid(dmac_ep_valid), .in_data(dmac_ep_data),
        .in_last(dmac_ep_last),   .in_ready(dmac_ep_ready),
        .bridge_wr_en(bridge_wr_en), .bridge_wr_addr(bridge_wr_addr),
        .bridge_wr_data(bridge_wr_data), .bridge_wr_be(bridge_wr_be),
        .bridge_master_id(bridge_master_id)
    );

    // ------------------------------------------------------------------
    // IOP fetch stub (default reset vector = 0x00000000 = DEST_BASE_ADDR)
    // ------------------------------------------------------------------

    logic        fetch_enable;
    logic        fetch_rd_en;
    logic [31:0] fetch_rd_addr;
    logic [31:0] fetch_rd_data;
    logic        fetch_rd_valid;

    logic                fetch_ev_valid;
    trace_pkg::subsys_e  fetch_ev_subsys;
    trace_pkg::event_e   fetch_ev_event;
    logic [63:0]         fetch_ev_arg0, fetch_ev_arg1, fetch_ev_arg2, fetch_ev_arg3;
    logic [31:0]         fetch_ev_flags;

    iop_fetch_stub #(.RESET_VECTOR(DEST_BASE_ADDR)) u_fetch (
        .clk(clk), .rst_n(rst_n), .enable(fetch_enable),
        .rd_en(fetch_rd_en), .rd_addr(fetch_rd_addr),
        .rd_data(fetch_rd_data), .rd_valid(fetch_rd_valid),
        .ev_valid(fetch_ev_valid), .ev_subsys(fetch_ev_subsys),
        .ev_event(fetch_ev_event),
        .ev_arg0(fetch_ev_arg0), .ev_arg1(fetch_ev_arg1),
        .ev_arg2(fetch_ev_arg2), .ev_arg3(fetch_ev_arg3),
        .ev_flags(fetch_ev_flags)
    );

    // ------------------------------------------------------------------
    // IOP map + IOP RAM
    // ------------------------------------------------------------------

    logic         iop_map_ram_rd_en;
    logic [20:0]  iop_map_ram_rd_addr;
    logic [31:0]  iop_ram_rd_data;
    logic         iop_ram_rd_valid;
    logic         iop_map_ram_wr_en;
    logic [20:0]  iop_map_ram_wr_addr;
    logic [31:0]  iop_map_ram_wr_data;
    logic [3:0]   iop_map_ram_wr_be;
    logic [7:0]   iop_map_ram_master_id;

    logic                iop_map_ev_valid;
    trace_pkg::subsys_e  iop_map_ev_subsys;
    trace_pkg::event_e   iop_map_ev_event;
    logic [63:0]         iop_map_ev_arg0, iop_map_ev_arg1, iop_map_ev_arg2, iop_map_ev_arg3;
    logic [31:0]         iop_map_ev_flags;

    iop_memory_map_stub u_iop_map (
        .clk(clk), .rst_n(rst_n),
        // CPU-side read driven by the fetcher
        .iop_rd_en(fetch_rd_en), .iop_rd_addr(fetch_rd_addr),
        .iop_rd_data(fetch_rd_data), .iop_rd_valid(fetch_rd_valid),
        // No CPU writes in this TB
        .iop_wr_en(1'b0), .iop_wr_addr(32'd0),
        .iop_wr_data(32'd0), .iop_wr_be(4'd0),
        .master_id(8'd2),                                 // IOP_CPU
        // Bridge write port
        .bridge_wr_en(bridge_wr_en), .bridge_wr_addr(bridge_wr_addr),
        .bridge_wr_data(bridge_wr_data), .bridge_wr_be(bridge_wr_be),
        .bridge_master_id(bridge_master_id),
        // DMA read-master port — unused by this TB
        .dma_rd_en(1'b0), .dma_rd_addr(32'd0),
        .dma_master_id(8'd0),
        .dma_rd_data(), .dma_rd_valid(),
        // SIF register-shell port — unused by this TB
        .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 port — unused by this TB
        .iop_dmac_rd_en(), .iop_dmac_rd_addr(),
        .iop_dmac_rd_data(32'd0), .iop_dmac_rd_valid(1'b0),
        .iop_dmac_wr_en(), .iop_dmac_wr_addr(), .iop_dmac_wr_data(),
        // IOP INTC port — unused by this TB
        .iop_intc_rd_en(), .iop_intc_rd_addr(),
        .iop_intc_rd_data(32'd0), .iop_intc_rd_valid(1'b0),
        .iop_intc_wr_en(), .iop_intc_wr_addr(), .iop_intc_wr_data(),
        .input_p1(32'd0), .input_p2(32'd0),
        // BIOS ROM port — unused by this TB
        .bios_rd_en(), .bios_rd_addr(),
        .bios_rd_data(32'd0), .bios_rd_valid(1'b0),
        .ram_rd_en(iop_map_ram_rd_en), .ram_rd_addr(iop_map_ram_rd_addr),
        .ram_rd_data(iop_ram_rd_data), .ram_rd_valid(iop_ram_rd_valid),
        .ram_wr_en(iop_map_ram_wr_en), .ram_wr_addr(iop_map_ram_wr_addr),
        .ram_wr_data(iop_map_ram_wr_data), .ram_wr_be(iop_map_ram_wr_be),
        .ram_master_id(iop_map_ram_master_id),
        .ev_valid(iop_map_ev_valid), .ev_subsys(iop_map_ev_subsys),
        .ev_event(iop_map_ev_event),
        .ev_arg0(iop_map_ev_arg0), .ev_arg1(iop_map_ev_arg1),
        .ev_arg2(iop_map_ev_arg2), .ev_arg3(iop_map_ev_arg3),
        .ev_flags(iop_map_ev_flags)
    );

    logic                iop_ram_ev_valid;
    trace_pkg::subsys_e  iop_ram_ev_subsys;
    trace_pkg::event_e   iop_ram_ev_event;
    logic [63:0]         iop_ram_ev_arg0, iop_ram_ev_arg1, iop_ram_ev_arg2, iop_ram_ev_arg3;
    logic [31:0]         iop_ram_ev_flags;

    iop_ram_stub #(.SIZE_BYTES(IOP_RAM_BYTES)) u_iop_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(iop_map_ram_rd_en), .rd_addr(iop_map_ram_rd_addr[IOP_RAM_ADDR_W-1:0]),
        .rd_data(iop_ram_rd_data), .rd_valid(iop_ram_rd_valid),
        .wr_en(iop_map_ram_wr_en), .wr_addr(iop_map_ram_wr_addr[IOP_RAM_ADDR_W-1:0]),
        .wr_data(iop_map_ram_wr_data), .wr_be(iop_map_ram_wr_be),
        .master_id(iop_map_ram_master_id),
        .ev_valid(iop_ram_ev_valid), .ev_subsys(iop_ram_ev_subsys),
        .ev_event(iop_ram_ev_event),
        .ev_arg0(iop_ram_ev_arg0), .ev_arg1(iop_ram_ev_arg1),
        .ev_arg2(iop_ram_ev_arg2), .ev_arg3(iop_ram_ev_arg3),
        .ev_flags(iop_ram_ev_flags)
    );

    // ------------------------------------------------------------------
    // Trace sinks
    // ------------------------------------------------------------------

    trace_sink_stub #(.FILENAME("sif_iop_exec_dmac.trace"), .SINK_LABEL("dmac"))
        u_trace_dmac (.clk(clk), .rst_n(rst_n),
            .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));

    trace_sink_stub #(.FILENAME("sif_iop_exec_iopmap.trace"), .SINK_LABEL("iop_map"))
        u_trace_iopmap (.clk(clk), .rst_n(rst_n),
            .ev_valid(iop_map_ev_valid), .ev_subsys(iop_map_ev_subsys),
            .ev_event(iop_map_ev_event), .ev_arg0(iop_map_ev_arg0),
            .ev_arg1(iop_map_ev_arg1), .ev_arg2(iop_map_ev_arg2),
            .ev_arg3(iop_map_ev_arg3), .ev_flags(iop_map_ev_flags));

    trace_sink_stub #(.FILENAME("sif_iop_exec_iopram.trace"), .SINK_LABEL("iop_ram"))
        u_trace_iopram (.clk(clk), .rst_n(rst_n),
            .ev_valid(iop_ram_ev_valid), .ev_subsys(iop_ram_ev_subsys),
            .ev_event(iop_ram_ev_event), .ev_arg0(iop_ram_ev_arg0),
            .ev_arg1(iop_ram_ev_arg1), .ev_arg2(iop_ram_ev_arg2),
            .ev_arg3(iop_ram_ev_arg3), .ev_flags(iop_ram_ev_flags));

    trace_sink_stub #(.FILENAME("sif_iop_exec_fetch.trace"), .SINK_LABEL("iop_fetch"))
        u_trace_fetch (.clk(clk), .rst_n(rst_n),
            .ev_valid(fetch_ev_valid), .ev_subsys(fetch_ev_subsys),
            .ev_event(fetch_ev_event), .ev_arg0(fetch_ev_arg0),
            .ev_arg1(fetch_ev_arg1), .ev_arg2(fetch_ev_arg2),
            .ev_arg3(fetch_ev_arg3), .ev_flags(fetch_ev_flags));

    // ------------------------------------------------------------------
    // Expected payload
    // ------------------------------------------------------------------

    logic [127:0] src_qword      [0:QWC_VAL-1];
    logic [31:0]  expected_word  [0:(QWC_VAL*4)-1];

    initial begin
        src_qword[0] = 128'hFEED_FEED_FACE_FACE_CAFE_CAFE_BEEF_BEEF;
        src_qword[1] = 128'h8888_8888_7777_7777_6666_6666_5555_5555;

        expected_word[0] = 32'hBEEF_BEEF;   // src_qword[0][31:0]
        expected_word[1] = 32'hCAFE_CAFE;   // src_qword[0][63:32]
        expected_word[2] = 32'hFACE_FACE;
        expected_word[3] = 32'hFEED_FEED;
        expected_word[4] = 32'h5555_5555;
        expected_word[5] = 32'h6666_6666;
        expected_word[6] = 32'h7777_7777;
        expected_word[7] = 32'h8888_8888;
    end

    // ------------------------------------------------------------------
    // Counters / observers (interface and trace only)
    // ------------------------------------------------------------------

    longint unsigned cycle_count;
    int              dma_done_count;
    logic            dma_done_seen;
    int              bridge_writes_ram;
    int              ifetch_count;
    int              map_reads_fetch_tagged;
    int              data_mismatches;
    longint unsigned last_bridge_write_cycle;
    longint unsigned first_fetch_start_cycle;

    initial begin
        cycle_count             = 0;
        dma_done_count          = 0;
        dma_done_seen           = 1'b0;
        bridge_writes_ram       = 0;
        ifetch_count            = 0;
        map_reads_fetch_tagged  = 0;
        data_mismatches         = 0;
        last_bridge_write_cycle = 0;
        first_fetch_start_cycle = 64'hFFFF_FFFF_FFFF_FFFF;
    end

    always_ff @(posedge clk) begin
        cycle_count <= cycle_count + 64'd1;

        if (rst_n && dmac_ev_valid &&
            dmac_ev_event == trace_pkg::EV_DMA_DONE) begin
            dma_done_count <= dma_done_count + 1;
            dma_done_seen  <= 1'b1;
        end

        // Bridge writes observed at RAM layer (master=3).
        if (rst_n && iop_ram_ev_valid &&
            iop_ram_ev_event == trace_pkg::EV_WRITE &&
            iop_ram_ev_arg2[7:0] == 8'd3) begin
            bridge_writes_ram       <= bridge_writes_ram + 1;
            last_bridge_write_cycle <= cycle_count;
        end

        // IOP IFETCH events — check content + walk pattern.
        // Declare locals without inline init (iverilog warns about
        // inline init inside always_ff without explicit lifetime).
        if (rst_n && fetch_ev_valid &&
            fetch_ev_event == trace_pkg::EV_IFETCH) begin : check_fetch
            logic [31:0] obs_addr;
            logic [31:0] obs_data;
            logic [31:0] obs_idx;

            obs_addr = fetch_ev_arg0[31:0];
            obs_data = fetch_ev_arg1[31:0];
            obs_idx  = (obs_addr - DEST_BASE_ADDR) >> 2;

            ifetch_count <= ifetch_count + 1;
            if (first_fetch_start_cycle == 64'hFFFF_FFFF_FFFF_FFFF)
                first_fetch_start_cycle <= cycle_count;

            if (obs_idx < (QWC_VAL * 4)) begin
                if (obs_data !== expected_word[obs_idx[2:0]]) begin
                    $error("[tb_sif_iop_bridge_exec] IFETCH word[%0d]: addr=0x%08h got 0x%08h expected 0x%08h",
                           obs_idx, obs_addr, obs_data, expected_word[obs_idx[2:0]]);
                    data_mismatches <= data_mismatches + 1;
                end
            end
        end

        // IOP map reads tagged as IOP_CPU.
        if (rst_n && iop_map_ev_valid &&
            iop_map_ev_event == trace_pkg::EV_READ &&
            iop_map_ev_arg2[7:0] == 8'd2 &&             // master=IOP_CPU
            iop_map_ev_arg3[7:0] == 8'd2)               // region=IOP_RAM
            map_reads_fetch_tagged <= map_reads_fetch_tagged + 1;
    end

    // ------------------------------------------------------------------
    // Helpers
    // ------------------------------------------------------------------

    task automatic write_dmac(input logic [7:0] offset, input logic [31:0] data);
        @(negedge clk);
        dmac_reg_wr_en   = 1'b1;
        dmac_reg_offset  = offset;
        dmac_reg_wr_data = data;
        @(negedge clk);
        dmac_reg_wr_en   = 1'b0;
        dmac_reg_offset  = 8'd0;
        dmac_reg_wr_data = 32'd0;
    endtask

    task automatic preload_ee(input logic [EE_RAM_ADDR_W-1:0] addr,
                              input logic [127:0] data);
        @(negedge clk);
        ee_ram_wr_en   = 1'b1;
        ee_ram_wr_addr = addr;
        ee_ram_wr_data = data;
        ee_ram_wr_be   = 16'hFFFF;
        @(negedge clk);
        ee_ram_wr_en   = 1'b0;
        ee_ram_wr_addr = '0;
        ee_ram_wr_data = 128'd0;
        ee_ram_wr_be   = 16'd0;
    endtask

    // ------------------------------------------------------------------
    // Stimulus
    // ------------------------------------------------------------------

    int errors;

    initial begin
        rst_n            = 1'b0;
        fetch_enable     = 1'b0;          // fetcher off during DMA
        dmac_reg_wr_en   = 1'b0;
        dmac_reg_offset  = 8'd0;
        dmac_reg_wr_data = 32'd0;
        ee_ram_wr_en     = 1'b0;
        ee_ram_wr_addr   = '0;
        ee_ram_wr_data   = 128'd0;
        ee_ram_wr_be     = 16'd0;
        errors           = 0;

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

        // --- Phase 1: preload EE RAM source ---
        begin : do_preload
            logic [31:0] a;
            for (int i = 0; i < QWC_VAL; i++) begin
                a = SOURCE_MADR + (i * 32'd16);
                preload_ee(a[EE_RAM_ADDR_W-1:0], src_qword[i]);
            end
        end

        // --- Phase 2: run DMA + bridge, keep fetcher disabled ---
        write_dmac(8'h10, SOURCE_MADR);
        write_dmac(8'h20, 32'd2);
        write_dmac(8'h00, 32'h0000_0001);

        begin : wait_dma
            int spin;
            spin = 0;
            while (!dma_done_seen && spin < 500) begin
                @(posedge clk);
                spin = spin + 1;
            end
            if (!dma_done_seen) begin
                $error("[tb_sif_iop_bridge_exec] DMA never completed");
                errors = errors + 1;
            end
        end

        // Let the bridge drain its last qword; overlap-prevention
        // boundary.
        repeat (20) @(posedge clk);

        if (bridge_writes_ram !== (QWC_VAL * 4)) begin
            $error("[tb_sif_iop_bridge_exec] expected %0d bridge writes at RAM layer before fetch, got %0d",
                   QWC_VAL * 4, bridge_writes_ram);
            errors = errors + 1;
        end

        // --- Phase 3: enable fetcher, let it walk the landed payload ---
        @(negedge clk);
        fetch_enable = 1'b1;
        repeat (QWC_VAL * 4 + 8) @(posedge clk);
        fetch_enable = 1'b0;
        repeat (4) @(posedge clk);

        // --- Ordering check: fetch started AFTER last bridge write ---
        if (first_fetch_start_cycle == 64'hFFFF_FFFF_FFFF_FFFF) begin
            $error("[tb_sif_iop_bridge_exec] no IFETCH events observed");
            errors = errors + 1;
        end
        if (first_fetch_start_cycle <= last_bridge_write_cycle) begin
            $error("[tb_sif_iop_bridge_exec] fetch (cy %0d) did not strictly follow last bridge write (cy %0d)",
                   first_fetch_start_cycle, last_bridge_write_cycle);
            errors = errors + 1;
        end

        // ------------------------------------------------------------------
        $display("[tb_sif_iop_bridge_exec] dma_done=%0d bridge_writes=%0d ifetches=%0d iop_cpu_reads=%0d mismatches=%0d errors=%0d last_bw_cy=%0d first_fetch_cy=%0d",
                 dma_done_count, bridge_writes_ram, ifetch_count,
                 map_reads_fetch_tagged, data_mismatches, errors,
                 last_bridge_write_cycle, first_fetch_start_cycle);

        if (dma_done_count != 1)
            $error("expected exactly 1 DMA_DONE, got %0d", dma_done_count);
        if (ifetch_count < (QWC_VAL * 4))
            $error("expected >= %0d IFETCHes, got %0d",
                   QWC_VAL * 4, ifetch_count);
        if (map_reads_fetch_tagged < (QWC_VAL * 4))
            $error("expected >= %0d IOP_CPU-tagged map reads, got %0d",
                   QWC_VAL * 4, map_reads_fetch_tagged);
        if (data_mismatches != 0)
            $error("expected zero data mismatches, got %0d", data_mismatches);

        if (errors == 0 &&
            dma_done_count == 1 &&
            bridge_writes_ram == (QWC_VAL * 4) &&
            ifetch_count >= (QWC_VAL * 4) &&
            map_reads_fetch_tagged >= (QWC_VAL * 4) &&
            data_mismatches == 0 &&
            first_fetch_start_cycle > last_bridge_write_cycle)
            $display("[tb_sif_iop_bridge_exec] PASS");
        else
            $display("[tb_sif_iop_bridge_exec] FAIL");

        $finish;
    end

    initial begin
        #400000;
        $error("[tb_sif_iop_bridge_exec] timeout");
        $finish;
    end

endmodule : tb_sif_iop_bridge_exec
