// retroDE_ps2 — tb_ee_core_dmac_poll
//
// Fourth EE-core chapter. Chapter 3 let the EE CPU program the DMAC
// (via SW) but gave software no architectural way to observe
// completion — the bootstrap had to busy-wait with a fixed NOP pad.
// This chapter closes that loop: dmac_reg_stub gained a reg_rd_*
// surface (CHCR/MADR/QWC/TADR + DONE_COUNT at 0x40), and the EE
// memory map forwards CPU reads in the same 0x1000_A000-0x1000_AFFF
// window to it. The core therefore polls CHCR.start until the DMAC
// clears it, then reads DONE_COUNT and writes the witness to RAM so
// the TB can verify that the poll really observed a completed
// transfer rather than some accidental pre-start zero.
//
// Chain (added to chapter 3):
//   ee_core_stub  ──(LW/SW)──►  ee_memory_map_stub
//       ├── ee_dmac_ch2_wr_*  ──►  dmac_reg_stub.reg_wr_*
//       └── ee_dmac_ch2_rd_*  ──►  dmac_reg_stub.reg_rd_*   (NEW)
//                                    reg_rd_data ──► ee_rd_data mux
//
// Bootstrap layout (at 0xBFC0_0100, slot 0x40):
//   setup      lui $3, 0x1000            ; $3 upper
//              ori $3, $3, 0xA000         ; $3 = 0x1000_A000 (ch2 base)
//              ori $5, $0,  0x0001        ; $5 = 1
//              lw  $6, 0x40($3)           ; $6 = initial DONE_COUNT
//   program    sw  $0,  0x10($3)          ; MADR = 0
//              sw  $5,  0x20($3)          ; QWC  = 1
//              sw  $5,  0x00($3)          ; CHCR = 1 (start)
//   poll_loop  lw  $7,  0x00($3)          ; $7 = CHCR
//              bne $7, $0, poll_loop      ; loop while CHCR != 0
//              nop                         ; delay slot
//   witness    lw  $8,  0x40($3)          ; $8 = final DONE_COUNT
//              ori $9, $0, 0x0200         ; $9 = witness RAM addr
//              sw  $8, 0x00($9)           ; RAM[0x200] = $8
//   halt       syscall
//
// Acceptance:
//   - core halt_o asserts exactly once
//   - retire count >= 16 (exactly 16 with zero extra poll iterations,
//     +3 per additional poll iteration; DMAC takes ~4 cycles to clear
//     CHCR vs ~10 cycles per poll iteration, so usually 0 extra iters)
//   - exactly 1 DMA_START, 1 DMA_BEAT, 1 DMA_DONE
//   - map shows exactly 3 EE_DMAC writes (MADR/QWC/CHCR)
//   - map shows >= 3 EE_DMAC reads (initial DONE_COUNT + poll LW + final
//     DONE_COUNT; poll_loop is >=1 by construction)
//   - at least 1 CHCR-offset (0x00) EE_DMAC read — the successful poll
//   - the cycle of the last CHCR-offset read (= the exiting poll) is
//     strictly > the DMA_DONE cycle (causality: the poll saw the state
//     cleared by S_DONE, not some pre-start zero)
//   - RAM[0x200][31:0] == 1 (witness DONE_COUNT delta)
//   - core does not take any exception or trap

`timescale 1ns/1ps

module tb_ee_core_dmac_poll;

    localparam int EE_RAM_BYTES   = 4 * 1024;
    localparam int EE_RAM_ADDR_W  = $clog2(EE_RAM_BYTES);
    localparam int BIOS_BYTES     = 4 * 1024 * 1024;

    localparam logic [31:0] RESET_VECTOR = 32'hBFC0_0000;
    localparam logic [31:0] BOOTSTRAP_VA = 32'hBFC0_0100;
    localparam int          RESET_SLOT     = 0;
    localparam int          BOOTSTRAP_SLOT = 32'h100 >> 2;

    // Bootstrap slot layout
    localparam int B_LUI_3    = BOOTSTRAP_SLOT + 0;
    localparam int B_ORI_3    = BOOTSTRAP_SLOT + 1;
    localparam int B_ORI_5    = BOOTSTRAP_SLOT + 2;
    localparam int B_LW_DC0   = BOOTSTRAP_SLOT + 3;   // initial DONE_COUNT
    localparam int B_SW_MADR  = BOOTSTRAP_SLOT + 4;
    localparam int B_SW_QWC   = BOOTSTRAP_SLOT + 5;
    localparam int B_SW_CHCR  = BOOTSTRAP_SLOT + 6;
    localparam int B_POLL_LW  = BOOTSTRAP_SLOT + 7;   // poll_loop entry
    localparam int B_POLL_BNE = BOOTSTRAP_SLOT + 8;
    localparam int B_POLL_DS  = BOOTSTRAP_SLOT + 9;   // delay slot NOP
    localparam int B_LW_DC1   = BOOTSTRAP_SLOT + 10;  // final DONE_COUNT
    localparam int B_ORI_9    = BOOTSTRAP_SLOT + 11;  // witness RAM addr
    localparam int B_SW_WIT   = BOOTSTRAP_SLOT + 12;  // witness store
    localparam int B_HALT     = BOOTSTRAP_SLOT + 13;

    localparam int WITNESS_RAM_ADDR = 32'h0000_0200;
    localparam int WITNESS_QW_IDX   = WITNESS_RAM_ADDR >> 4;  // qword index 32

    // Opcodes
    localparam logic [5:0] OP_J    = 6'h02;
    localparam logic [5:0] OP_BNE  = 6'h05;
    localparam logic [5:0] OP_ORI  = 6'h0D;
    localparam logic [5:0] OP_LUI  = 6'h0F;
    localparam logic [5:0] OP_LW   = 6'h23;
    localparam logic [5:0] OP_SW   = 6'h2B;

    logic clk;
    logic rst_n;

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

    // ------------------------------------------------------------------
    // EE core
    // ------------------------------------------------------------------

    logic        core_go;
    logic        core_halt;
    logic [31:0] core_pc;
    logic        core_trap;
    logic [31:0] core_trap_pc;
    logic [31:0] core_trap_instr;

    logic        ee_rd_en;
    logic [31:0] ee_rd_addr;
    logic [31:0] ee_rd_data;
    logic        ee_rd_valid;

    logic        ee_wr_en;
    logic [31:0] ee_wr_addr;
    logic [31:0] ee_wr_data;
    logic [3:0]  ee_wr_be;

    logic        ee_cpu_irq;
    assign ee_cpu_irq = 1'b0;

    logic                core_ev_valid;
    trace_pkg::subsys_e  core_ev_subsys;
    trace_pkg::event_e   core_ev_event;
    logic [63:0]         core_ev_arg0, core_ev_arg1, core_ev_arg2, core_ev_arg3;
    logic [31:0]         core_ev_flags;

    ee_core_stub u_core (
        .clk(clk), .rst_n(rst_n),
        .go_i(core_go),
        .map_rd_en(ee_rd_en), .map_rd_addr(ee_rd_addr),
        .map_rd_data(ee_rd_data), .map_rd_valid(ee_rd_valid),
        .map_wr_en(ee_wr_en), .map_wr_addr(ee_wr_addr),
        .map_wr_data(ee_wr_data), .map_wr_be(ee_wr_be),
        .cpu_irq(ee_cpu_irq),
        .halt_o(core_halt), .pc_o(core_pc),
        .trap_o(core_trap), .trap_pc_o(core_trap_pc),
        .trap_instr_o(core_trap_instr),
        .ev_valid(core_ev_valid), .ev_subsys(core_ev_subsys),
        .ev_event(core_ev_event),
        .ev_arg0(core_ev_arg0), .ev_arg1(core_ev_arg1),
        .ev_arg2(core_ev_arg2), .ev_arg3(core_ev_arg3),
        .ev_flags(core_ev_flags)
    );

    // ------------------------------------------------------------------
    // EE memory map + EE RAM + shared BIOS ROM + DMAC read/write plumbing
    // ------------------------------------------------------------------

    logic         bios_rd_en;
    logic [21:0]  bios_rd_addr;
    logic [31:0]  bios_rd_data;
    logic         bios_rd_valid;

    logic         ee_ram_rd_en;
    logic [24:0]  ee_ram_rd_addr;
    logic [127:0] ee_ram_rd_data;
    logic         ee_ram_rd_valid;
    logic         ee_ram_wr_en;
    logic [24: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;

    logic         dmac_rd_en;
    logic [31:0]  dmac_rd_addr;
    logic [127:0] dmac_rd_data;
    logic         dmac_rd_valid;

    // DMAC register-side plumbing (both directions, chapter 4)
    logic        map_dmac_ch2_wr_en;
    logic [7:0]  map_dmac_ch2_wr_addr;
    logic [31:0] map_dmac_ch2_wr_data;
    logic        map_dmac_ch2_rd_en;
    logic [7:0]  map_dmac_ch2_rd_addr;
    logic [31:0] map_dmac_ch2_rd_data;
    logic        map_dmac_ch2_rd_valid;

    // The dmac_reg_stub has a shared reg_offset for read + write;
    // the map's rd_en and wr_en are mutually exclusive (CPU does
    // either a LW or SW per cycle, never both), so this mux is safe.
    logic [7:0]  dmac_reg_offset_mux;
    assign dmac_reg_offset_mux = map_dmac_ch2_wr_en ? map_dmac_ch2_wr_addr
                                                   : map_dmac_ch2_rd_addr;

    // TB preload into RAM via bridge port
    logic         tb_br_wr_en;
    logic [31:0]  tb_br_wr_addr;
    logic [127:0] tb_br_wr_data;
    logic [15:0]  tb_br_wr_be;

    logic                ee_map_ev_valid;
    trace_pkg::subsys_e  ee_map_ev_subsys;
    trace_pkg::event_e   ee_map_ev_event;
    logic [63:0]         ee_map_ev_arg0, ee_map_ev_arg1, ee_map_ev_arg2, ee_map_ev_arg3;
    logic [31:0]         ee_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(ee_wr_en), .ee_wr_addr(ee_wr_addr),
        .ee_wr_data(ee_wr_data), .ee_wr_be(ee_wr_be),
        .dmac_rd_en(dmac_rd_en), .dmac_rd_addr(dmac_rd_addr),
        .dmac_rd_data(dmac_rd_data), .dmac_rd_valid(dmac_rd_valid),
        .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'd9),                        // "TB preload"
        .bios_rd_en(bios_rd_en), .bios_rd_addr(bios_rd_addr),
        .bios_rd_data(bios_rd_data), .bios_rd_valid(bios_rd_valid),
        .ram_rd_en(ee_ram_rd_en), .ram_rd_addr(ee_ram_rd_addr),
        .ram_rd_data(ee_ram_rd_data), .ram_rd_valid(ee_ram_rd_valid),
        .ram_wr_en(ee_ram_wr_en), .ram_wr_addr(ee_ram_wr_addr),
        .ram_wr_data(ee_ram_wr_data), .ram_wr_be(ee_ram_wr_be),
        .ram_master_id(ee_ram_master_id),
        .ee_dmac_ch2_wr_en(map_dmac_ch2_wr_en),
        .ee_dmac_ch2_wr_addr(map_dmac_ch2_wr_addr),
        .ee_dmac_ch2_wr_data(map_dmac_ch2_wr_data),
        .ee_dmac_ch2_rd_en(map_dmac_ch2_rd_en),
        .ee_dmac_ch2_rd_addr(map_dmac_ch2_rd_addr),
        .ee_dmac_ch2_rd_data(map_dmac_ch2_rd_data),
        .ee_dmac_ch2_rd_valid(map_dmac_ch2_rd_valid),
        .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(ee_map_ev_valid), .ev_subsys(ee_map_ev_subsys),
        .ev_event(ee_map_ev_event),
        .ev_arg0(ee_map_ev_arg0), .ev_arg1(ee_map_ev_arg1),
        .ev_arg2(ee_map_ev_arg2), .ev_arg3(ee_map_ev_arg3),
        .ev_flags(ee_map_ev_flags)
    );

    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[EE_RAM_ADDR_W-1:0]),
        .rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid),
        .wr_en(ee_ram_wr_en), .wr_addr(ee_ram_wr_addr[EE_RAM_ADDR_W-1:0]),
        .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()
    );

    bios_rom_stub #(.SIZE_BYTES(BIOS_BYTES)) u_bios (
        .clk(clk), .rst_n(rst_n),
        .rd_en(bios_rd_en), .rd_addr(bios_rd_addr),
        .rd_data(bios_rd_data), .rd_valid(bios_rd_valid),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // ------------------------------------------------------------------
    // DMAC ch2 — both reg ports now wired
    // ------------------------------------------------------------------

    logic         dmac_ep_valid;
    logic [127:0] dmac_ep_data;
    logic         dmac_ep_last;
    logic         dmac_ep_ready;
    assign dmac_ep_ready = 1'b1;

    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;

    logic dmac_irq_completion;

    dmac_reg_stub #(.CHANNEL(4'd2), .PATH_ID(4'd2)) u_dmac_ch2 (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_dmac_ch2_wr_en),
        .reg_offset(dmac_reg_offset_mux),
        .reg_wr_data(map_dmac_ch2_wr_data),
        .reg_rd_en(map_dmac_ch2_rd_en),
        .reg_rd_data(map_dmac_ch2_rd_data),
        .reg_rd_valid(map_dmac_ch2_rd_valid),
        .mem_rd_en(dmac_rd_en), .mem_rd_addr(dmac_rd_addr),
        .mem_rd_data(dmac_rd_data), .mem_rd_valid(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(dmac_irq_completion),
        .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 sinks
    // ------------------------------------------------------------------

    trace_sink_stub #(.FILENAME("ee_core_dmac_poll_core.trace"), .SINK_LABEL("ee_core"))
        u_trace_core (.clk(clk), .rst_n(rst_n),
            .ev_valid(core_ev_valid), .ev_subsys(core_ev_subsys),
            .ev_event(core_ev_event), .ev_arg0(core_ev_arg0),
            .ev_arg1(core_ev_arg1), .ev_arg2(core_ev_arg2),
            .ev_arg3(core_ev_arg3), .ev_flags(core_ev_flags));

    trace_sink_stub #(.FILENAME("ee_core_dmac_poll_map.trace"), .SINK_LABEL("ee_map"))
        u_trace_map (.clk(clk), .rst_n(rst_n),
            .ev_valid(ee_map_ev_valid), .ev_subsys(ee_map_ev_subsys),
            .ev_event(ee_map_ev_event), .ev_arg0(ee_map_ev_arg0),
            .ev_arg1(ee_map_ev_arg1), .ev_arg2(ee_map_ev_arg2),
            .ev_arg3(ee_map_ev_arg3), .ev_flags(ee_map_ev_flags));

    trace_sink_stub #(.FILENAME("ee_core_dmac_poll_dmac.trace"), .SINK_LABEL("dmac_ch2"))
        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));

    // ------------------------------------------------------------------
    // Observers
    // ------------------------------------------------------------------

    int core_retired_events;
    int core_halt_events;
    int core_except_events;
    int core_trap_events;
    int map_dmac_writes;
    int map_dmac_reads;
    int map_dmac_chcr_reads;
    int dmac_start_events;
    int dmac_beat_events;
    int dmac_done_events;
    int chcr_write_cycle;
    int last_chcr_read_cycle;
    int dma_done_cycle;
    int errors;

    initial begin
        core_retired_events  = 0;
        core_halt_events     = 0;
        core_except_events   = 0;
        core_trap_events     = 0;
        map_dmac_writes      = 0;
        map_dmac_reads       = 0;
        map_dmac_chcr_reads  = 0;
        dmac_start_events    = 0;
        dmac_beat_events     = 0;
        dmac_done_events     = 0;
        chcr_write_cycle     = -1;
        last_chcr_read_cycle = -1;
        dma_done_cycle       = -1;
        errors               = 0;
    end

    int cycle_ctr;
    always_ff @(posedge clk) begin
        if (!rst_n) cycle_ctr <= 0;
        else        cycle_ctr <= cycle_ctr + 1;
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            core_retired_events <= core_retired_events + 1;
            if ((core_ev_flags & 32'h08) != 32'd0)
                core_halt_events <= core_halt_events + 1;
            if ((core_ev_flags & 32'h20) != 32'd0)
                core_except_events <= core_except_events + 1;
            if ((core_ev_flags & 32'h80) != 32'd0)
                core_trap_events <= core_trap_events + 1;
        end

        // Map-layer trace: count writes + reads by region
        if (rst_n && ee_map_ev_valid &&
            ee_map_ev_subsys == trace_pkg::SUBSYS_MEM) begin
            if (ee_map_ev_event == trace_pkg::EV_WRITE &&
                ee_map_ev_arg3[7:0] == 8'd7 &&      // REGION_EE_DMAC
                (ee_map_ev_flags & 32'h1) != 32'd0) begin
                map_dmac_writes <= map_dmac_writes + 1;
                if (ee_map_ev_arg0[7:0] == 8'h00)
                    chcr_write_cycle <= cycle_ctr;
            end
            if (ee_map_ev_event == trace_pkg::EV_READ &&
                ee_map_ev_arg3[7:0] == 8'd7) begin    // REGION_EE_DMAC
                map_dmac_reads <= map_dmac_reads + 1;
                if (ee_map_ev_arg0[7:0] == 8'h00) begin
                    map_dmac_chcr_reads <= map_dmac_chcr_reads + 1;
                    last_chcr_read_cycle <= cycle_ctr;
                end
            end
        end

        if (rst_n && dmac_ev_valid &&
            dmac_ev_subsys == trace_pkg::SUBSYS_DMAC) begin
            if (dmac_ev_event == trace_pkg::EV_DMA_START)
                dmac_start_events <= dmac_start_events + 1;
            if (dmac_ev_event == trace_pkg::EV_DMA_BEAT)
                dmac_beat_events <= dmac_beat_events + 1;
            if (dmac_ev_event == trace_pkg::EV_DMA_DONE) begin
                dmac_done_events <= dmac_done_events + 1;
                dma_done_cycle   <= cycle_ctr;
            end
        end
    end

    // ------------------------------------------------------------------
    // BIOS preload helpers
    // ------------------------------------------------------------------

    task automatic bios_word(input int slot, input logic [31:0] word);
        u_bios.mem[slot] = word;
    endtask

    function automatic logic [31:0] enc_i(input logic [5:0] op,
                                          input int rs, input int rt,
                                          input logic [15:0] imm);
        return {op, rs[4:0], rt[4:0], imm};
    endfunction

    function automatic logic [31:0] enc_j(input logic [31:0] target_addr);
        return {OP_J, target_addr[27:2]};
    endfunction

    function automatic logic [15:0] branch_imm(input int branch_slot,
                                               input int target_slot);
        int diff;
        diff = target_slot - branch_slot - 1;
        return diff[15:0];
    endfunction

    task automatic bios_j    (input int slot, input logic [31:0] tgt);
        bios_word(slot, enc_j(tgt));
    endtask
    task automatic bios_nop  (input int slot);
        bios_word(slot, 32'd0);
    endtask
    task automatic bios_lui  (input int slot, input int rt, input logic [15:0] imm);
        bios_word(slot, enc_i(OP_LUI, 0, rt, imm));
    endtask
    task automatic bios_ori  (input int slot, input int rt, input int rs,
                              input logic [15:0] imm);
        bios_word(slot, enc_i(OP_ORI, rs, rt, imm));
    endtask
    task automatic bios_lw   (input int slot, input int rt, input int rs,
                              input logic [15:0] offs);
        bios_word(slot, enc_i(OP_LW, rs, rt, offs));
    endtask
    task automatic bios_sw   (input int slot, input int rt, input int rs,
                              input logic [15:0] offs);
        bios_word(slot, enc_i(OP_SW, rs, rt, offs));
    endtask
    task automatic bios_bne  (input int slot, input int rs, input int rt,
                              input int target_slot);
        bios_word(slot, enc_i(OP_BNE, rs, rt,
                              branch_imm(slot, target_slot)));
    endtask
    task automatic bios_syscall(input int slot);
        bios_word(slot, 32'h0000_000C);
    endtask

    // ------------------------------------------------------------------
    // RAM preload via map bridge port
    // ------------------------------------------------------------------

    task automatic bridge_qword(input logic [31:0] addr,
                                input logic [127:0] data);
        @(negedge clk);
        tb_br_wr_en   = 1'b1;
        tb_br_wr_addr = addr;
        tb_br_wr_data = data;
        tb_br_wr_be   = 16'hFFFF;
        @(negedge clk);
        tb_br_wr_en   = 1'b0;
        tb_br_wr_addr = 32'd0;
        tb_br_wr_data = 128'd0;
        tb_br_wr_be   = 16'd0;
    endtask

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

    initial begin
        rst_n         = 1'b0;
        core_go       = 1'b0;
        tb_br_wr_en   = 1'b0;
        tb_br_wr_addr = 32'd0;
        tb_br_wr_data = 128'd0;
        tb_br_wr_be   = 16'd0;

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

        // Preload RAM[0] with a witness qword for the DMAC payload
        bridge_qword(32'h0000_0000,
                     128'hC0FFEE00_C0FFEE01_C0FFEE02_C0FFEE03);

        // Reset entry
        bios_j  (RESET_SLOT,     BOOTSTRAP_VA);
        bios_nop(RESET_SLOT + 1);

        // setup
        bios_lui (B_LUI_3,    3,    16'h1000);           // $3 = 0x1000_0000
        bios_ori (B_ORI_3,    3, 3, 16'hA000);           // $3 = 0x1000_A000
        bios_ori (B_ORI_5,    5, 0, 16'h0001);           // $5 = 1
        bios_lw  (B_LW_DC0,   6, 3, 16'h0040);           // $6 = DONE_COUNT_0
        // program
        bios_sw  (B_SW_MADR,  0, 3, 16'h0010);           // MADR = 0
        bios_sw  (B_SW_QWC,   5, 3, 16'h0020);           // QWC  = 1
        bios_sw  (B_SW_CHCR,  5, 3, 16'h0000);           // CHCR = 1 (start)
        // poll loop: while (CHCR != 0) re-poll
        bios_lw  (B_POLL_LW,  7, 3, 16'h0000);           // $7 = CHCR
        bios_bne (B_POLL_BNE, 7, 0, B_POLL_LW);          // loop back if !=0
        bios_nop (B_POLL_DS);                             // delay slot
        // witness
        bios_lw  (B_LW_DC1,   8, 3, 16'h0040);           // $8 = DONE_COUNT_1
        // Ch33: witness address via kseg0. Useg-shadow is separate
        // from kseg0 RAM now, and the post-run check reads
        // u_ee_ram.mem[WITNESS_QW_IDX] (= kseg0-backed phys 0x200).
        bios_lui (B_ORI_9,    9,    16'h8000);           // $9 = 0x80000000
        bios_sw  (B_SW_WIT,   8, 9, 16'h0200);           // RAM[0x200] = $8
        // halt
        bios_syscall(B_HALT);

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

        while (!core_halt) @(posedge clk);
        repeat (8) @(posedge clk);

        // --------------------------------------------------------------
        // Checks
        // --------------------------------------------------------------
        if (core_halt_events != 1) begin
            $error("[tb_ee_core_dmac_poll] expected 1 halt, got %0d",
                   core_halt_events);
            errors = errors + 1;
        end
        if (core_retired_events < 16) begin
            $error("[tb_ee_core_dmac_poll] retired=%0d; expected >= 16",
                   core_retired_events);
            errors = errors + 1;
        end
        if (core_except_events != 0 || core_trap_events != 0) begin
            $error("[tb_ee_core_dmac_poll] unexpected exception/trap: except=%0d trap=%0d",
                   core_except_events, core_trap_events);
            errors = errors + 1;
        end
        if (map_dmac_writes != 3) begin
            $error("[tb_ee_core_dmac_poll] expected exactly 3 EE_DMAC writes, got %0d",
                   map_dmac_writes);
            errors = errors + 1;
        end
        if (map_dmac_reads < 3) begin
            $error("[tb_ee_core_dmac_poll] expected >= 3 EE_DMAC reads (DC0 + poll + DC1), got %0d",
                   map_dmac_reads);
            errors = errors + 1;
        end
        if (map_dmac_chcr_reads < 1) begin
            $error("[tb_ee_core_dmac_poll] expected >= 1 CHCR-offset read (poll exit), got %0d",
                   map_dmac_chcr_reads);
            errors = errors + 1;
        end
        if (dmac_start_events != 1 || dmac_beat_events != 1 ||
            dmac_done_events  != 1) begin
            $error("[tb_ee_core_dmac_poll] expected 1 start/1 beat/1 done; got %0d/%0d/%0d",
                   dmac_start_events, dmac_beat_events, dmac_done_events);
            errors = errors + 1;
        end
        // Causality: the exiting CHCR read must land strictly after
        // DMA_DONE (the state machine clears CHCR[0] at S_DONE; so the
        // read that actually observed chcr[0]=0 happened post-DONE).
        if (dma_done_cycle < 0 || last_chcr_read_cycle < 0 ||
            last_chcr_read_cycle <= dma_done_cycle) begin
            $error("[tb_ee_core_dmac_poll] causality: dma_done_cycle=%0d last_chcr_read_cycle=%0d",
                   dma_done_cycle, last_chcr_read_cycle);
            errors = errors + 1;
        end
        // Witness: RAM[0x200], low 32 bits == final DONE_COUNT == 1
        //  (initial was 0, DMAC completed once, so final = 1).
        if (u_ee_ram.mem[WITNESS_QW_IDX][31:0] !== 32'd1) begin
            $error("[tb_ee_core_dmac_poll] RAM[0x200] low32 = 0x%08h; expected 1",
                   u_ee_ram.mem[WITNESS_QW_IDX][31:0]);
            errors = errors + 1;
        end

        $display("[tb_ee_core_dmac_poll] retired=%0d halt=%0d dmac_wr=%0d dmac_rd=%0d chcr_rd=%0d start=%0d beat=%0d done=%0d chcr_wr_cyc=%0d last_chcr_rd_cyc=%0d dma_done_cyc=%0d witness_lo32=0x%08h errors=%0d",
                 core_retired_events, core_halt_events,
                 map_dmac_writes, map_dmac_reads, map_dmac_chcr_reads,
                 dmac_start_events, dmac_beat_events, dmac_done_events,
                 chcr_write_cycle, last_chcr_read_cycle, dma_done_cycle,
                 u_ee_ram.mem[WITNESS_QW_IDX][31:0], errors);

        if (errors == 0 &&
            core_halt_events     == 1 &&
            map_dmac_writes      == 3 &&
            dmac_start_events    == 1 &&
            dmac_beat_events     == 1 &&
            dmac_done_events     == 1 &&
            map_dmac_chcr_reads  >= 1 &&
            last_chcr_read_cycle >  dma_done_cycle &&
            u_ee_ram.mem[WITNESS_QW_IDX][31:0] == 32'd1)
            $display("[tb_ee_core_dmac_poll] PASS");
        else
            $display("[tb_ee_core_dmac_poll] FAIL");

        $finish;
    end

    initial begin
        #2000000;
        $error("[tb_ee_core_dmac_poll] timeout");
        $finish;
    end

endmodule : tb_ee_core_dmac_poll
