// retroDE_ps2 — tb_sif_combined_ctrl_data
//
// First combined control+data SIF protocol proof. EE raises a mailbox
// doorbell and kicks off a bounded DMA transfer; sif_dma_ack_peer_stub
// gates the mailbox ack on payload completion. Two seams composed into
// one PS2-shaped handshake.
//
// Scope (narrow, per Codex):
//   - one direction (EE → SIF receive side)
//   - DMAC channel 5, QWC=4, direct-to-RAM payload
//   - sif_dma_ack_peer_stub as a dedicated combiner (not a fattened
//     mailbox peer)
//   - no whitebox peeks — interface + trace only
//
// Key interface-visible properties:
//   1. No ack traffic observed between doorbell-up and DMA-done.
//   2. Ack traffic appears only AFTER `last_seen` has gone high.
//   3. SMCOM after ack == the command EE wrote into MSCOM.
//   4. SMFLG after ack == CMD_ACK_BIT.
//   5. All four expected payload qwords land in sif_dma_stub in order.

`timescale 1ns/1ps

module tb_sif_combined_ctrl_data;

    logic clk;
    logic rst_n;

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

    localparam int QWC_VAL         = 4;
    localparam int RAM_SIZE_BYTES  = 4 * 1024;
    localparam int RAM_ADDR_W      = $clog2(RAM_SIZE_BYTES);
    localparam int SIF_DEPTH       = 8;
    localparam int SIF_IDX_W       = $clog2(SIF_DEPTH);

    localparam logic [31:0] PAYLOAD_MADR    = 32'h0000_0500;
    localparam logic [31:0] CMD_VALUE       = 32'h5ABBA5AB;
    localparam logic [31:0] CMD_PENDING_BIT = 32'h0000_0001;
    localparam logic [31:0] CMD_ACK_BIT     = 32'h0000_0002;
    localparam logic [7:0]  MSCOM = 8'h00;
    localparam logic [7:0]  SMCOM = 8'h10;
    localparam logic [7:0]  MSFLG = 8'h20;
    localparam logic [7:0]  SMFLG = 8'h30;

    // ------------------------------------------------------------------
    // ee_ram_stub
    // ------------------------------------------------------------------

    logic                  ram_rd_en;
    logic [RAM_ADDR_W-1:0] ram_rd_addr;
    logic [127:0]          ram_rd_data;
    logic                  ram_rd_valid;

    logic                  ram_wr_en;
    logic [RAM_ADDR_W-1:0] ram_wr_addr;
    logic [127:0]          ram_wr_data;
    logic [15:0]           ram_wr_be;

    logic [7:0]            ram_master_id;

    logic                ram_ev_valid;
    trace_pkg::subsys_e  ram_ev_subsys;
    trace_pkg::event_e   ram_ev_event;
    logic [63:0]         ram_ev_arg0, ram_ev_arg1, ram_ev_arg2, ram_ev_arg3;
    logic [31:0]         ram_ev_flags;

    ee_ram_stub #(.SIZE_BYTES(RAM_SIZE_BYTES)) u_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(ram_rd_en), .rd_addr(ram_rd_addr),
        .rd_data(ram_rd_data), .rd_valid(ram_rd_valid),
        .wr_en(ram_wr_en), .wr_addr(ram_wr_addr),
        .wr_data(ram_wr_data), .wr_be(ram_wr_be),
        .master_id(ram_master_id),
        .ev_valid(ram_ev_valid), .ev_subsys(ram_ev_subsys),
        .ev_event(ram_ev_event),
        .ev_arg0(ram_ev_arg0), .ev_arg1(ram_ev_arg1),
        .ev_arg2(ram_ev_arg2), .ev_arg3(ram_ev_arg3),
        .ev_flags(ram_ev_flags)
    );

    assign ram_master_id = ram_rd_en ? 8'd1 : 8'd0;

    // ------------------------------------------------------------------
    // DMAC (channel 5)
    // ------------------------------------------------------------------

    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)
    );

    // ------------------------------------------------------------------
    // ee_memory_map_stub
    // ------------------------------------------------------------------

    logic        map_ram_rd_en;
    logic [24:0] map_ram_rd_addr;

    ee_memory_map_stub u_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(map_ram_rd_en), .ram_rd_addr(map_ram_rd_addr),
        .ram_rd_data(ram_rd_data), .ram_rd_valid(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 ram_rd_en   = map_ram_rd_en;
    assign ram_rd_addr = map_ram_rd_addr[RAM_ADDR_W-1:0];

    // ------------------------------------------------------------------
    // sif_dma_stub (receive side)
    // ------------------------------------------------------------------

    logic                  sif_rd_en;
    logic [SIF_IDX_W-1:0]  sif_rd_idx;
    logic [127:0]          sif_rd_data;
    logic                  sif_rd_valid;

    logic [31:0]           sif_rx_count;
    logic                  sif_last_seen;
    logic                  sif_full;

    logic                sif_dma_ev_valid;
    trace_pkg::subsys_e  sif_dma_ev_subsys;
    trace_pkg::event_e   sif_dma_ev_event;
    logic [63:0]         sif_dma_ev_arg0, sif_dma_ev_arg1, sif_dma_ev_arg2, sif_dma_ev_arg3;
    logic [31:0]         sif_dma_ev_flags;

    sif_dma_stub #(.DEPTH(SIF_DEPTH)) u_sif_dma (
        .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),
        .rd_en(sif_rd_en), .rd_idx(sif_rd_idx),
        .rd_data(sif_rd_data), .rd_valid(sif_rd_valid),
        .stall_in(1'b0),
        .rx_count(sif_rx_count), .last_seen(sif_last_seen),
        .full_o(sif_full),
        .ev_valid(sif_dma_ev_valid), .ev_subsys(sif_dma_ev_subsys),
        .ev_event(sif_dma_ev_event),
        .ev_arg0(sif_dma_ev_arg0), .ev_arg1(sif_dma_ev_arg1),
        .ev_arg2(sif_dma_ev_arg2), .ev_arg3(sif_dma_ev_arg3),
        .ev_flags(sif_dma_ev_flags)
    );

    // ------------------------------------------------------------------
    // sif_mailbox_stub (control plane storage)
    // ------------------------------------------------------------------

    logic        mbx_ee_wr_en, mbx_ee_rd_en;
    logic [7:0]  mbx_ee_addr;
    logic [31:0] mbx_ee_wr_data;
    logic [31:0] mbx_ee_rd_data;
    logic        mbx_ee_rd_valid;

    logic        peer_rd_en;
    logic [7:0]  peer_rd_addr;
    logic [31:0] peer_rd_data;
    logic        peer_rd_valid;
    logic        peer_wr_en;
    logic [7:0]  peer_wr_addr;
    logic [31:0] peer_wr_data;

    logic                mbx_ev_valid;
    trace_pkg::subsys_e  mbx_ev_subsys;
    trace_pkg::event_e   mbx_ev_event;
    logic [63:0]         mbx_ev_arg0, mbx_ev_arg1, mbx_ev_arg2, mbx_ev_arg3;
    logic [31:0]         mbx_ev_flags;

    sif_mailbox_stub u_mailbox (
        .clk(clk), .rst_n(rst_n),
        .ee_wr_en(mbx_ee_wr_en), .ee_rd_en(mbx_ee_rd_en),
        .ee_addr(mbx_ee_addr), .ee_wr_data(mbx_ee_wr_data),
        .ee_rd_data(mbx_ee_rd_data), .ee_rd_valid(mbx_ee_rd_valid),
        .iop_wr_en(peer_wr_en), .iop_rd_en(peer_rd_en),
        .iop_addr(peer_wr_en ? peer_wr_addr : peer_rd_addr),
        .iop_wr_data(peer_wr_data),
        .iop_rd_data(peer_rd_data), .iop_rd_valid(peer_rd_valid),
        .ev_valid(mbx_ev_valid), .ev_subsys(mbx_ev_subsys),
        .ev_event(mbx_ev_event),
        .ev_arg0(mbx_ev_arg0), .ev_arg1(mbx_ev_arg1),
        .ev_arg2(mbx_ev_arg2), .ev_arg3(mbx_ev_arg3),
        .ev_flags(mbx_ev_flags)
    );

    // ------------------------------------------------------------------
    // sif_dma_ack_peer_stub (the combiner under test)
    // ------------------------------------------------------------------

    logic         peer_done;
    logic [31:0]  peer_ack_count;

    sif_dma_ack_peer_stub u_peer (
        .clk(clk), .rst_n(rst_n),
        .obs_rd_en(peer_rd_en), .obs_rd_addr(peer_rd_addr),
        .obs_rd_data(peer_rd_data), .obs_rd_valid(peer_rd_valid),
        .resp_wr_en(peer_wr_en), .resp_wr_addr(peer_wr_addr),
        .resp_wr_data(peer_wr_data),
        .payload_complete(sif_last_seen),
        .done_o(peer_done), .ack_count_o(peer_ack_count)
    );

    // ------------------------------------------------------------------
    // Trace sinks (just the ones we reason about)
    // ------------------------------------------------------------------

    trace_sink_stub #(.FILENAME("sif_combined_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_combined_sif_dma.trace"), .SINK_LABEL("sif_dma"))
        u_trace_sifdma (.clk(clk), .rst_n(rst_n),
            .ev_valid(sif_dma_ev_valid), .ev_subsys(sif_dma_ev_subsys),
            .ev_event(sif_dma_ev_event), .ev_arg0(sif_dma_ev_arg0),
            .ev_arg1(sif_dma_ev_arg1), .ev_arg2(sif_dma_ev_arg2),
            .ev_arg3(sif_dma_ev_arg3), .ev_flags(sif_dma_ev_flags));

    trace_sink_stub #(.FILENAME("sif_combined_mailbox.trace"), .SINK_LABEL("mbx"))
        u_trace_mbx (.clk(clk), .rst_n(rst_n),
            .ev_valid(mbx_ev_valid), .ev_subsys(mbx_ev_subsys),
            .ev_event(mbx_ev_event), .ev_arg0(mbx_ev_arg0),
            .ev_arg1(mbx_ev_arg1), .ev_arg2(mbx_ev_arg2),
            .ev_arg3(mbx_ev_arg3), .ev_flags(mbx_ev_flags));

    // ------------------------------------------------------------------
    // Interface-visible cycle counter + ordering observer
    // ------------------------------------------------------------------

    longint unsigned cycle_count;
    logic            sif_last_seen_d1;
    longint unsigned first_last_seen_cycle;
    longint unsigned first_peer_smcom_write_cycle;
    int              dma_done_count;
    int              peer_writes_iop_side;
    int              errors;

    initial begin
        cycle_count                   = 0;
        sif_last_seen_d1              = 1'b0;
        first_last_seen_cycle         = 64'hFFFF_FFFF_FFFF_FFFF;
        first_peer_smcom_write_cycle  = 64'hFFFF_FFFF_FFFF_FFFF;
        dma_done_count                = 0;
        peer_writes_iop_side          = 0;
        errors                        = 0;
    end

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

        // First rising edge of last_seen.
        if (!sif_last_seen_d1 && sif_last_seen &&
            first_last_seen_cycle == 64'hFFFF_FFFF_FFFF_FFFF)
            first_last_seen_cycle <= cycle_count;

        // First IOP-side (peer) WRITE to SMCOM on the mailbox trace.
        if (mbx_ev_valid &&
            mbx_ev_event == trace_pkg::EV_WRITE &&
            mbx_ev_arg2[7:0] == 8'd1 &&               // side_id == IOP (peer)
            mbx_ev_arg0[7:0] == SMCOM &&
            first_peer_smcom_write_cycle == 64'hFFFF_FFFF_FFFF_FFFF)
            first_peer_smcom_write_cycle <= cycle_count;

        // Count peer's IOP-side mailbox writes (expect 2: SMCOM, SMFLG).
        if (mbx_ev_valid && mbx_ev_event == trace_pkg::EV_WRITE &&
            mbx_ev_arg2[7:0] == 8'd1)
            peer_writes_iop_side <= peer_writes_iop_side + 1;

        if (dmac_ev_valid && dmac_ev_event == trace_pkg::EV_DMA_DONE)
            dma_done_count <= dma_done_count + 1;
    end

    // ------------------------------------------------------------------
    // Payload + helpers
    // ------------------------------------------------------------------

    logic [127:0] expected_payload [0:QWC_VAL-1];

    initial begin
        expected_payload[0] = 128'h0A0A_0A0A_1010_1010_0A0A_0A0A_1010_1010;
        expected_payload[1] = 128'h0B0B_0B0B_2020_2020_0B0B_0B0B_2020_2020;
        expected_payload[2] = 128'h0C0C_0C0C_3030_3030_0C0C_0C0C_3030_3030;
        expected_payload[3] = 128'h0D0D_0D0D_4040_4040_0D0D_0D0D_4040_4040;
    end

    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_ram_qword(input logic [RAM_ADDR_W-1:0] addr,
                                     input logic [127:0] data);
        @(negedge clk);
        ram_wr_en   = 1'b1;
        ram_wr_addr = addr;
        ram_wr_data = data;
        ram_wr_be   = 16'hFFFF;
        @(negedge clk);
        ram_wr_en   = 1'b0;
        ram_wr_addr = '0;
        ram_wr_data = 128'd0;
        ram_wr_be   = 16'd0;
    endtask

    task automatic ee_mbx_write(input logic [7:0] addr, input logic [31:0] data);
        @(negedge clk);
        mbx_ee_wr_en   = 1'b1;
        mbx_ee_addr    = addr;
        mbx_ee_wr_data = data;
        @(negedge clk);
        mbx_ee_wr_en   = 1'b0;
        mbx_ee_addr    = 8'd0;
        mbx_ee_wr_data = 32'd0;
    endtask

    task automatic ee_mbx_read_expect(input logic [7:0] addr,
                                       input logic [31:0] expected,
                                       input string label);
        @(negedge clk);
        mbx_ee_rd_en = 1'b1;
        mbx_ee_addr  = addr;
        @(negedge clk);
        mbx_ee_rd_en = 1'b0;
        mbx_ee_addr  = 8'd0;
        if (mbx_ee_rd_data !== expected || mbx_ee_rd_valid !== 1'b1) begin
            $error("[tb_sif_combined_ctrl_data] EE read %s: got 0x%08h valid=%0b expected 0x%08h",
                   label, mbx_ee_rd_data, mbx_ee_rd_valid, expected);
            errors = errors + 1;
        end
    endtask

    task automatic sif_read_slot(input logic [SIF_IDX_W-1:0] idx,
                                 output logic [127:0] data);
        @(negedge clk);
        sif_rd_en  = 1'b1;
        sif_rd_idx = idx;
        @(negedge clk);
        sif_rd_en  = 1'b0;
        sif_rd_idx = '0;
        data = sif_rd_data;
    endtask

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

    int           ack_before_dma_count;
    logic [127:0] rb;

    initial begin
        rst_n            = 1'b0;
        dmac_reg_wr_en   = 1'b0;
        dmac_reg_offset  = 8'd0;
        dmac_reg_wr_data = 32'd0;
        ram_wr_en        = 1'b0;
        ram_wr_addr      = '0;
        ram_wr_data      = 128'd0;
        ram_wr_be        = 16'd0;
        sif_rd_en        = 1'b0;
        sif_rd_idx       = '0;
        mbx_ee_wr_en     = 1'b0;
        mbx_ee_rd_en     = 1'b0;
        mbx_ee_addr      = 8'd0;
        mbx_ee_wr_data   = 32'd0;

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

        // Preload RAM payload.
        begin : do_preload
            logic [31:0] a;
            for (int i = 0; i < QWC_VAL; i++) begin
                a = PAYLOAD_MADR + (i * 32'd16);
                preload_ram_qword(a[RAM_ADDR_W-1:0], expected_payload[i]);
            end
        end

        // 1. EE publishes command and raises the doorbell BEFORE kicking DMA.
        ee_mbx_write(MSCOM, CMD_VALUE);
        ee_mbx_write(MSFLG, CMD_PENDING_BIT);

        // 2. Observation window: doorbell is up, DMA has NOT started. The
        //    peer must not ack yet (payload_complete is still 0).
        repeat (40) @(posedge clk);
        ack_before_dma_count = peer_ack_count;
        if (ack_before_dma_count !== 0) begin
            $error("[tb_sif_combined_ctrl_data] peer acked before DMA even started (count=%0d)",
                   ack_before_dma_count);
            errors = errors + 1;
        end

        // 3. Kick off the DMA transfer.
        write_dmac(8'h10, PAYLOAD_MADR);
        write_dmac(8'h20, 32'd4);
        write_dmac(8'h00, 32'h0000_0001);

        // 4. Wait for the peer to ack (should happen only after last_seen).
        begin : wait_for_ack
            int spin;
            spin = 0;
            while (peer_ack_count < 32'd1 && spin < 400) begin
                @(posedge clk);
                spin = spin + 1;
            end
            if (peer_ack_count < 32'd1) begin
                $error("[tb_sif_combined_ctrl_data] peer never acked");
                errors = errors + 1;
            end
        end

        // 5. Ordering property: peer's first SMCOM write must happen AFTER
        //    last_seen's rising edge, observed interface-side only.
        if (first_last_seen_cycle == 64'hFFFF_FFFF_FFFF_FFFF) begin
            $error("[tb_sif_combined_ctrl_data] last_seen never went high");
            errors = errors + 1;
        end
        if (first_peer_smcom_write_cycle == 64'hFFFF_FFFF_FFFF_FFFF) begin
            $error("[tb_sif_combined_ctrl_data] peer never wrote SMCOM");
            errors = errors + 1;
        end
        if (first_peer_smcom_write_cycle <= first_last_seen_cycle) begin
            $error("[tb_sif_combined_ctrl_data] peer wrote SMCOM at cycle %0d, not after last_seen (cycle %0d)",
                   first_peer_smcom_write_cycle, first_last_seen_cycle);
            errors = errors + 1;
        end

        // 6. EE reads SMCOM and SMFLG — interface-visible ack arrived.
        repeat (2) @(posedge clk);
        ee_mbx_read_expect(SMCOM, CMD_VALUE,   "SMCOM after combined ack");
        ee_mbx_read_expect(SMFLG, CMD_ACK_BIT, "SMFLG after combined ack");

        // 7. Payload integrity — all 4 qwords landed in order.
        for (int i = 0; i < QWC_VAL; i++) begin
            sif_read_slot(SIF_IDX_W'(i), rb);
            if (rb !== expected_payload[i]) begin
                $error("[tb_sif_combined_ctrl_data] slot[%0d]: got 0x%032h expected 0x%032h",
                       i, rb, expected_payload[i]);
                errors = errors + 1;
            end
        end

        repeat (4) @(posedge clk);

        // ------------------------------------------------------------------
        $display("[tb_sif_combined_ctrl_data] pre_dma_acks=%0d ack_count=%0d dma_done=%0d peer_writes=%0d last_seen_cy=%0d smcom_wr_cy=%0d errors=%0d",
                 ack_before_dma_count, peer_ack_count, dma_done_count,
                 peer_writes_iop_side, first_last_seen_cycle,
                 first_peer_smcom_write_cycle, errors);

        if (errors == 0 &&
            ack_before_dma_count == 0 &&
            peer_ack_count == 32'd1 &&
            dma_done_count == 1 &&
            peer_writes_iop_side == 2 &&
            first_peer_smcom_write_cycle > first_last_seen_cycle)
            $display("[tb_sif_combined_ctrl_data] PASS");
        else
            $display("[tb_sif_combined_ctrl_data] FAIL");

        $finish;
    end

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

endmodule : tb_sif_combined_ctrl_data
