// retroDE_ps2 — tb_ee_dmac_ctrl_stub
//
// Ch287 — Focused TB for ee_dmac_ctrl_stub. The DMAC global
// control/status registers at 0x1000_E000..0x1000_E0FF — D_CTRL at
// 0x00, D_STAT at 0x10 (W1C semantics on the low half), D_PCR at
// 0x20, D_SQWC at 0x30, D_RBSR at 0x40, D_RBOR at 0x50.
//
// Test sequence:
//   1. Reset-init: every named offset reads 0.
//   2. D_CTRL latch round-trip (write/read at 0x00).
//   3. D_STAT W1C semantics. Since nothing in the stub *sets*
//      d_stat bits (real PS2 channels would), we hierarchically
//      poke d_stat to a known value, then issue a W1C write and
//      verify only the requested CIS bits clear (low half) while
//      CIM bits in the high half are *unconditionally* written.
//   4. D_PCR / D_SQWC / D_RBSR / D_RBOR round-trip (latched).
//   5. Unknown offset (0x80) read returns 0; write doesn't crash
//      anything and the next valid read still works.
//
// Strict mode irrelevant here — the stub is driven directly, not
// through ee_core_stub.

`timescale 1ns/1ps

module tb_ee_dmac_ctrl_stub;

    logic clk;
    logic rst_n;

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

    // DUT ports
    logic        reg_wr_en;
    logic [7:0]  reg_offset;
    logic [31:0] reg_wr_data;
    logic        reg_rd_en;
    logic [31:0] reg_rd_data;
    logic        reg_rd_valid;

    logic                ev_valid;
    trace_pkg::subsys_e  ev_subsys;
    trace_pkg::event_e   ev_event;
    logic [63:0]         ev_arg0, ev_arg1, ev_arg2, ev_arg3;
    logic [31:0]         ev_flags;

    ee_dmac_ctrl_stub dut (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(reg_wr_en),
        .reg_offset(reg_offset),
        .reg_wr_data(reg_wr_data),
        .reg_rd_en(reg_rd_en),
        .reg_rd_data(reg_rd_data),
        .reg_rd_valid(reg_rd_valid),
        .ev_valid(ev_valid),
        .ev_subsys(ev_subsys),
        .ev_event(ev_event),
        .ev_arg0(ev_arg0), .ev_arg1(ev_arg1),
        .ev_arg2(ev_arg2), .ev_arg3(ev_arg3),
        .ev_flags(ev_flags)
    );

    int errors = 0;

    task automatic do_write(input logic [7:0] off, input logic [31:0] data);
        @(negedge clk);
        reg_wr_en   = 1'b1;
        reg_offset  = off;
        reg_wr_data = data;
        @(negedge clk);
        reg_wr_en   = 1'b0;
        reg_offset  = 8'd0;
        reg_wr_data = 32'd0;
    endtask

    task automatic do_read(input  logic [7:0]  off,
                           output logic [31:0] data_out);
        @(negedge clk);
        reg_rd_en  = 1'b1;
        reg_offset = off;
        @(negedge clk);
        reg_rd_en  = 1'b0;
        reg_offset = 8'd0;
        @(posedge clk);
        data_out = reg_rd_data;
    endtask

    task automatic check(input string tag,
                         input logic [31:0] got,
                         input logic [31:0] exp);
        if (got !== exp) begin
            $display("[tb_ee_dmac_ctrl_stub] FAIL %s got=0x%08h exp=0x%08h",
                     tag, got, exp);
            errors++;
        end else begin
            $display("[tb_ee_dmac_ctrl_stub]   ok %s = 0x%08h", tag, got);
        end
    endtask

    logic [31:0] rd0;

    initial begin
        rst_n       = 1'b0;
        reg_wr_en   = 1'b0;
        reg_offset  = 8'd0;
        reg_wr_data = 32'd0;
        reg_rd_en   = 1'b0;

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

        // ===== 1) Reset-init: every named offset reads 0 =====
        do_read(8'h00, rd0); check("reset_d_ctrl",  rd0, 32'd0);
        do_read(8'h10, rd0); check("reset_d_stat",  rd0, 32'd0);
        do_read(8'h20, rd0); check("reset_d_pcr",   rd0, 32'd0);
        do_read(8'h30, rd0); check("reset_d_sqwc",  rd0, 32'd0);
        do_read(8'h40, rd0); check("reset_d_rbsr",  rd0, 32'd0);
        do_read(8'h50, rd0); check("reset_d_rbor",  rd0, 32'd0);

        // ===== 2) D_CTRL latch round-trip =====
        do_write(8'h00, 32'h1234_5678);
        do_read (8'h00, rd0); check("d_ctrl_write_read", rd0, 32'h1234_5678);

        // ===== 3) D_STAT W1C semantics =====
        // Pre-load d_stat hierarchically (CIS = 0x00AB, CIM = 0xFFFF
        // → full word 0xFFFF_00AB). Verify the read sees it.
        @(negedge clk);
        dut.d_stat = 32'hFFFF_00AB;
        @(negedge clk);
        do_read(8'h10, rd0); check("d_stat_preload", rd0, 32'hFFFF_00AB);

        // W1C the low byte: write 0x0000_00A0 — should clear bits 7,5,3 in
        // the low half (0x00AB & ~0x00A0 = 0x000B) and overwrite the high
        // half (CIM) with the write value (0x0000_0000).
        do_write(8'h10, 32'h0000_00A0);
        do_read (8'h10, rd0); check("d_stat_w1c_lo_cim_zero",
                                    rd0, 32'h0000_000B);

        // Re-pre-load to test that high-half write doesn't W1C the low.
        @(negedge clk);
        dut.d_stat = 32'h0000_00FF;
        @(negedge clk);
        do_write(8'h10, 32'hDEAD_0000);   // CIM = 0xDEAD, CIS write = 0 (no clear)
        do_read (8'h10, rd0); check("d_stat_cim_write_cis_unchanged",
                                    rd0, 32'hDEAD_00FF);

        // ===== 4) D_PCR / D_SQWC / D_RBSR / D_RBOR latch =====
        do_write(8'h20, 32'hCAFE_BABE);
        do_write(8'h30, 32'h1357_9BDF);
        do_write(8'h40, 32'h2468_ACE0);
        do_write(8'h50, 32'hFEED_FACE);
        do_read (8'h20, rd0); check("d_pcr_rt",  rd0, 32'hCAFE_BABE);
        do_read (8'h30, rd0); check("d_sqwc_rt", rd0, 32'h1357_9BDF);
        do_read (8'h40, rd0); check("d_rbsr_rt", rd0, 32'h2468_ACE0);
        do_read (8'h50, rd0); check("d_rbor_rt", rd0, 32'hFEED_FACE);

        // D_CTRL untouched by D_PCR write.
        do_read (8'h00, rd0); check("d_ctrl_distinct", rd0, 32'h1234_5678);

        // ===== 5) Unknown offset read returns 0; write doesn't break =====
        do_read (8'h80, rd0); check("unknown_offset_reads_zero", rd0, 32'd0);
        do_write(8'h80, 32'hAAAA_5555);    // dropped
        do_read (8'h80, rd0); check("unknown_offset_still_zero", rd0, 32'd0);
        // Next valid read still works after an unknown write.
        do_read (8'h00, rd0); check("d_ctrl_after_unknown_write",
                                    rd0, 32'h1234_5678);

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

        $finish;
    end

    initial begin
        #5_000_000;
        $display("[tb_ee_dmac_ctrl_stub] TIMEOUT");
        $finish;
    end

endmodule : tb_ee_dmac_ctrl_stub
