// retroDE_ps2 — tb_ee_dmac_passive_chan_stub
//
// Ch288 — Focused TB for ee_dmac_passive_chan_stub. The lightweight
// per-channel register surface covering DMAC channels 0/1/3/4/5
// (ch2 GIF stays on its dedicated dmac_reg_stub). Four registers
// per channel (CHCR/MADR/QWC/TADR), latched, no FSM.
//
// Test cases:
//   1. Reset reads zero for all 4 regs of the qbert-blocking channel
//      ch4 (chan_addr[15:12] == 0xC).
//   2. Round-trip write/read of CHCR/MADR/QWC/TADR on ch4.
//   3. Channel independence: write distinct values to ch4 and ch5,
//      verify ch4 readback is unchanged after ch5 writes.
//   4. ch2 (chan_addr[15:12] == 0xA) is OUTSIDE this stub's channel
//      set — verify reads return 0 (invalid-channel path).
//   5. Unknown register offset within a valid channel: read returns 0,
//      next valid read still works.

`timescale 1ns/1ps

module tb_ee_dmac_passive_chan_stub;

    logic clk;
    logic rst_n;

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

    // DUT ports
    logic        reg_wr_en;
    logic [15:0] chan_addr;
    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_passive_chan_stub dut (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(reg_wr_en),
        .chan_addr(chan_addr),
        .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;

    // Helpers — chan_idx in {0x8, 0x9, 0xB, 0xC, 0xD}, reg_offset is the
    // 12-bit register offset within that channel (0x000 CHCR, 0x010
    // MADR, 0x020 QWC, 0x030 TADR).
    task automatic do_write(input logic [3:0]  chan_nibble,
                            input logic [11:0] reg_off,
                            input logic [31:0] data);
        @(negedge clk);
        reg_wr_en   = 1'b1;
        chan_addr   = {chan_nibble, reg_off};
        reg_wr_data = data;
        @(negedge clk);
        reg_wr_en   = 1'b0;
        chan_addr   = 16'd0;
        reg_wr_data = 32'd0;
    endtask

    task automatic do_read(input  logic [3:0]  chan_nibble,
                           input  logic [11:0] reg_off,
                           output logic [31:0] data_out);
        @(negedge clk);
        reg_rd_en = 1'b1;
        chan_addr = {chan_nibble, reg_off};
        @(negedge clk);
        reg_rd_en = 1'b0;
        chan_addr = 16'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_passive_chan_stub] FAIL %s got=0x%08h exp=0x%08h",
                     tag, got, exp);
            errors++;
        end else begin
            $display("[tb_ee_dmac_passive_chan_stub]   ok %s = 0x%08h",
                     tag, got);
        end
    endtask

    logic [31:0] rd0;

    initial begin
        rst_n       = 1'b0;
        reg_wr_en   = 1'b0;
        chan_addr   = 16'd0;
        reg_wr_data = 32'd0;
        reg_rd_en   = 1'b0;

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

        // ===== 1) ch4 reset reads zero =====
        do_read(4'hC, 12'h000, rd0); check("ch4_chcr_reset",  rd0, 32'd0);
        do_read(4'hC, 12'h010, rd0); check("ch4_madr_reset",  rd0, 32'd0);
        do_read(4'hC, 12'h020, rd0); check("ch4_qwc_reset",   rd0, 32'd0);
        do_read(4'hC, 12'h030, rd0); check("ch4_tadr_reset",  rd0, 32'd0);

        // ===== 2) ch4 round-trip =====
        do_write(4'hC, 12'h000, 32'h1234_5678);
        do_write(4'hC, 12'h010, 32'h89AB_CDEF);
        do_write(4'hC, 12'h020, 32'hCAFE_BABE);
        do_write(4'hC, 12'h030, 32'hDEAD_BEEF);
        do_read (4'hC, 12'h000, rd0); check("ch4_chcr_rt", rd0, 32'h1234_5678);
        do_read (4'hC, 12'h010, rd0); check("ch4_madr_rt", rd0, 32'h89AB_CDEF);
        do_read (4'hC, 12'h020, rd0); check("ch4_qwc_rt",  rd0, 32'hCAFE_BABE);
        do_read (4'hC, 12'h030, rd0); check("ch4_tadr_rt", rd0, 32'hDEAD_BEEF);

        // ===== 3) Channel independence (ch4 vs ch5) =====
        do_write(4'hD, 12'h000, 32'hAAAA_5555);   // ch5 CHCR
        do_write(4'hD, 12'h010, 32'h5555_AAAA);   // ch5 MADR
        // ch4 values must be unchanged.
        do_read (4'hC, 12'h000, rd0); check("ch4_chcr_after_ch5_wr",
                                             rd0, 32'h1234_5678);
        do_read (4'hC, 12'h010, rd0); check("ch4_madr_after_ch5_wr",
                                             rd0, 32'h89AB_CDEF);
        // ch5 reads its own values.
        do_read (4'hD, 12'h000, rd0); check("ch5_chcr_rt", rd0, 32'hAAAA_5555);
        do_read (4'hD, 12'h010, rd0); check("ch5_madr_rt", rd0, 32'h5555_AAAA);

        // ===== 4) ch2 (chan_nibble=0xA) is OUTSIDE the passive set =====
        // Reads return 0 (invalid channel); writes are dropped. The
        // real ch2 GIF surface lives in dmac_reg_stub on dedicated
        // ports — this stub must not shadow it.
        do_write(4'hA, 12'h000, 32'hF00D_F00D);   // dropped
        do_read (4'hA, 12'h000, rd0); check("ch2_chcr_unmapped_returns_zero",
                                             rd0, 32'd0);

        // Also confirm ch0 (0x8), ch1 (0x9), ch3 (0xB) reset to 0.
        do_read (4'h8, 12'h000, rd0); check("ch0_chcr_reset", rd0, 32'd0);
        do_read (4'h9, 12'h000, rd0); check("ch1_chcr_reset", rd0, 32'd0);
        do_read (4'hB, 12'h000, rd0); check("ch3_chcr_reset", rd0, 32'd0);

        // ===== 5) Unknown register offset on a valid channel =====
        do_read (4'hC, 12'h040, rd0); check("ch4_unknown_offset_reads_zero",
                                             rd0, 32'd0);
        do_write(4'hC, 12'h040, 32'hFEED_FACE);   // dropped
        do_read (4'hC, 12'h000, rd0); check("ch4_chcr_after_unknown_wr",
                                             rd0, 32'h1234_5678);

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

        $finish;
    end

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

endmodule : tb_ee_dmac_passive_chan_stub
