// retroDE_ps2 — tb_sif_iop_driven_combined // // First IOP-driven combined control+data SIF protocol. Mirror of the // EE-driven combined handshake (tb_sif_combined_ctrl_data) in the // reverse direction: the IOP posts a doorbell through the real IOP // map, the IOP DMAC ch9 moves payload IOP→EE, and an EE-side combiner // emits the ack back ONLY after the payload has fully landed in EE RAM. // // Chain: // iop_ram_stub (source) // └─► iop_memory_map_stub (dma_rd_* port) // └─► iop_dmac_reg_stub (ch9 / SIF0 IOP→EE) // └─► sif_dma_ee_ram_bridge_stub (last_seen_o drives // payload_complete) // └─► ee_memory_map_stub (bridge_wr_*) // └─► ee_ram_stub (destination) // // TB (as IOP CPU) also drives iop_memory_map_stub's SIF region at // 0x1D000000 — SMCOM (cmd) + SMFLG (doorbell) writes, plus polling // reads of MSFLG/MSCOM to observe the EE-side ack. // // sif_dma_ee_ack_peer_stub observes the mailbox's EE-side port and // the bridge's last_seen_o, and emits the ack (MSCOM echo + MSFLG // ACK) once both are true. // // Protocol sequence: // 1. IOP preloads payload into IOP RAM at SRC_BASE. // 2. IOP writes SMCOM = CAPTURED_CMD (the IOP→EE command value). // 3. IOP writes SMFLG = CMD_PENDING_BIT (doorbell). // 4. TB asserts that at this point the combiner has NOT fired: // ack_count=0, MSFLG=0. The load-bearing ordering guarantee is // that the doorbell alone must not produce an ack. // 5. IOP programs DMAC ch9: MADR=SRC_BASE, BCR=BEATS. // 6. IOP starts the DMAC (CHCR bit 0). // 7. DMA runs. Bridge accumulates qwords, lands them in EE RAM, and // raises last_seen_o on the final beat. // 8. Combiner now has both conditions true. It reads SMCOM, writes // MSCOM=cmd, writes MSFLG=CMD_ACK_BIT, and goes DONE. // 9. IOP polls MSFLG via the IOP map; observes CMD_ACK_BIT. // 10. IOP reads MSCOM via the IOP map; observes the echoed command. // 11. EE-side readback verifies the payload landed qword-by-qword. // // Explicit one-shot: the combiner fires exactly once and stays DONE. // Re-armable reverse protocol is a future wave. `timescale 1ns/1ps module tb_sif_iop_driven_combined; localparam int IOP_RAM_BYTES = 4 * 1024; localparam int IOP_RAM_ADDR_W = $clog2(IOP_RAM_BYTES); localparam int EE_RAM_BYTES = 4 * 1024; localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES); // IOP DMAC ch9 window (architectural addresses through the map) localparam logic [31:0] DMAC_CH9_BASE = 32'h1F80_1520; localparam logic [31:0] DMAC_MADR_ADDR = DMAC_CH9_BASE | 32'h00; localparam logic [31:0] DMAC_BCR_ADDR = DMAC_CH9_BASE | 32'h04; localparam logic [31:0] DMAC_CHCR_ADDR = DMAC_CH9_BASE | 32'h08; // SIF register window at 0x1D00_0000 (offsets match mailbox layout) localparam logic [31:0] SIF_BASE = 32'h1D00_0000; localparam logic [31:0] SIF_MSCOM = SIF_BASE | 32'h00; localparam logic [31:0] SIF_SMCOM = SIF_BASE | 32'h10; localparam logic [31:0] SIF_MSFLG = SIF_BASE | 32'h20; localparam logic [31:0] SIF_SMFLG = SIF_BASE | 32'h30; localparam logic [31:0] SRC_BASE = 32'h0000_0200; localparam logic [31:0] EE_DEST_BASE = 32'h0000_0100; localparam int BEATS = 16; localparam int QWORDS = BEATS / 4; localparam logic [31:0] CAPTURED_CMD = 32'hDEAD_C0DE; localparam logic [31:0] CMD_PENDING_BIT = 32'h0000_0001; localparam logic [31:0] CMD_ACK_BIT = 32'h0000_0002; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; // ------------------------------------------------------------------ // IOP map + IOP RAM // ------------------------------------------------------------------ logic iop_rd_en; logic [31:0] iop_rd_addr; logic [31:0] iop_rd_data; logic iop_rd_valid; logic iop_wr_en; logic [31:0] iop_wr_addr; logic [31:0] iop_wr_data; logic [3:0] iop_wr_be; logic iop_ram_rd_en; logic [20:0] iop_ram_rd_addr; logic [31:0] iop_ram_rd_data; logic iop_ram_rd_valid; logic iop_ram_wr_en; logic [20:0] iop_ram_wr_addr; logic [31:0] iop_ram_wr_data; logic [3:0] iop_ram_wr_be; logic [7:0] iop_ram_master_id; // IOP map → mailbox IOP-side logic map_sif_rd_en; logic [7:0] map_sif_rd_addr; logic [31:0] map_sif_rd_data; logic map_sif_rd_valid; logic map_sif_wr_en; logic [7:0] map_sif_wr_addr; logic [31:0] map_sif_wr_data; // IOP map → DMAC ch9 register port logic map_dmac_rd_en; logic [3:0] map_dmac_rd_addr; logic [31:0] map_dmac_rd_data; logic map_dmac_rd_valid; logic map_dmac_wr_en; logic [3:0] map_dmac_wr_addr; logic [31:0] map_dmac_wr_data; // IOP DMAC dma_rd_* to IOP map logic dma_rd_en; logic [31:0] dma_rd_addr; logic [7:0] dma_master_id; logic [31:0] dma_rd_data; logic dma_rd_valid; 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), .iop_rd_en(iop_rd_en), .iop_rd_addr(iop_rd_addr), .iop_rd_data(iop_rd_data), .iop_rd_valid(iop_rd_valid), .iop_wr_en(iop_wr_en), .iop_wr_addr(iop_wr_addr), .iop_wr_data(iop_wr_data), .iop_wr_be(iop_wr_be), .master_id(8'd2), // IOP_CPU .bridge_wr_en(1'b0), .bridge_wr_addr(32'd0), .bridge_wr_data(32'd0), .bridge_wr_be(4'd0), .bridge_master_id(8'd0), .dma_rd_en(dma_rd_en), .dma_rd_addr(dma_rd_addr), .dma_master_id(dma_master_id), .dma_rd_data(dma_rd_data), .dma_rd_valid(dma_rd_valid), .sif_rd_en(map_sif_rd_en), .sif_rd_addr(map_sif_rd_addr), .sif_rd_data(map_sif_rd_data), .sif_rd_valid(map_sif_rd_valid), .sif_wr_en(map_sif_wr_en), .sif_wr_addr(map_sif_wr_addr), .sif_wr_data(map_sif_wr_data), .iop_dmac_rd_en(map_dmac_rd_en), .iop_dmac_rd_addr(map_dmac_rd_addr), .iop_dmac_rd_data(map_dmac_rd_data), .iop_dmac_rd_valid(map_dmac_rd_valid), .iop_dmac_wr_en(map_dmac_wr_en), .iop_dmac_wr_addr(map_dmac_wr_addr), .iop_dmac_wr_data(map_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_ram_rd_en), .ram_rd_addr(iop_ram_rd_addr), .ram_rd_data(iop_ram_rd_data), .ram_rd_valid(iop_ram_rd_valid), .ram_wr_en(iop_ram_wr_en), .ram_wr_addr(iop_ram_wr_addr), .ram_wr_data(iop_ram_wr_data), .ram_wr_be(iop_ram_wr_be), .ram_master_id(iop_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) ); iop_ram_stub #(.SIZE_BYTES(IOP_RAM_BYTES)) u_iop_ram ( .clk(clk), .rst_n(rst_n), .rd_en(iop_ram_rd_en), .rd_addr(iop_ram_rd_addr[IOP_RAM_ADDR_W-1:0]), .rd_data(iop_ram_rd_data), .rd_valid(iop_ram_rd_valid), .wr_en(iop_ram_wr_en), .wr_addr(iop_ram_wr_addr[IOP_RAM_ADDR_W-1:0]), .wr_data(iop_ram_wr_data), .wr_be(iop_ram_wr_be), .master_id(iop_ram_master_id), .ev_valid(), .ev_subsys(), .ev_event(), .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags() ); // ------------------------------------------------------------------ // IOP DMAC ch9 // ------------------------------------------------------------------ logic ep_valid; logic [31:0] ep_data; logic ep_last; logic ep_ready; logic dmac_busy; logic [31:0] dmac_done_count; 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; iop_dmac_reg_stub u_dmac ( .clk(clk), .rst_n(rst_n), .reg_wr_en(map_dmac_wr_en), .reg_rd_en(map_dmac_rd_en), .reg_offset(map_dmac_wr_en ? map_dmac_wr_addr : map_dmac_rd_addr), .reg_wr_data(map_dmac_wr_data), .reg_rd_data(map_dmac_rd_data), .reg_rd_valid(map_dmac_rd_valid), .mem_rd_en(dma_rd_en), .mem_rd_addr(dma_rd_addr), .mem_master_id(dma_master_id), .mem_rd_data(dma_rd_data), .mem_rd_valid(dma_rd_valid), .ep_valid(ep_valid), .ep_data(ep_data), .ep_last(ep_last), .ep_ready(ep_ready), .irq_completion_o(), .busy_o(dmac_busy), .done_count_o(dmac_done_count), .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) ); // ------------------------------------------------------------------ // SIF egress bridge with last_seen_o // ------------------------------------------------------------------ logic bridge_in_ready; logic bridge_last_seen; assign ep_ready = bridge_in_ready; // no TB stall injection here logic eebr_wr_en; logic [31:0] eebr_wr_addr; logic [127:0] eebr_wr_data; logic [15:0] eebr_wr_be; logic [7:0] eebr_master_id; sif_dma_ee_ram_bridge_stub #( .DEST_BASE_ADDR(EE_DEST_BASE), .MASTER_ID(8'd5) ) u_ee_bridge ( .clk(clk), .rst_n(rst_n), .in_valid(ep_valid), .in_data(ep_data), .in_last(ep_last), .in_ready(bridge_in_ready), .bridge_wr_en(eebr_wr_en), .bridge_wr_addr(eebr_wr_addr), .bridge_wr_data(eebr_wr_data), .bridge_wr_be(eebr_wr_be), .bridge_master_id(eebr_master_id), .last_seen_o(bridge_last_seen) ); // ------------------------------------------------------------------ // EE map + EE RAM // ------------------------------------------------------------------ logic ee_dmac_rd_en; logic [31:0] ee_dmac_rd_addr; logic [127:0] ee_dmac_rd_data; logic ee_dmac_rd_valid; logic ee_map_ram_rd_en; logic [24:0] ee_map_ram_rd_addr; logic [127:0] ee_ram_rd_data; logic ee_ram_rd_valid; logic ee_map_ram_wr_en; logic [24:0] ee_map_ram_wr_addr; logic [127:0] ee_map_ram_wr_data; logic [15:0] ee_map_ram_wr_be; logic [7:0] ee_map_ram_master_id; 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(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(ee_dmac_rd_en), .dmac_rd_addr(ee_dmac_rd_addr), .dmac_rd_data(ee_dmac_rd_data), .dmac_rd_valid(ee_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(eebr_wr_en), .bridge_wr_addr(eebr_wr_addr), .bridge_wr_data(eebr_wr_data), .bridge_wr_be(eebr_wr_be), .bridge_master_id(eebr_master_id), .ram_wr_en(ee_map_ram_wr_en), .ram_wr_addr(ee_map_ram_wr_addr), .ram_wr_data(ee_map_ram_wr_data), .ram_wr_be(ee_map_ram_wr_be), .ram_master_id(ee_map_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(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_map_ram_rd_en), .rd_addr(ee_map_ram_rd_addr[EE_RAM_ADDR_W-1:0]), .rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid), .wr_en(ee_map_ram_wr_en), .wr_addr(ee_map_ram_wr_addr[EE_RAM_ADDR_W-1:0]), .wr_data(ee_map_ram_wr_data), .wr_be(ee_map_ram_wr_be), .master_id(ee_map_ram_master_id), .ev_valid(), .ev_subsys(), .ev_event(), .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags() ); // ------------------------------------------------------------------ // SIF mailbox // - IOP-side: driven by iop_memory_map_stub.sif_* // - EE-side: driven by the combiner (muxed rd/wr on a shared port) // ------------------------------------------------------------------ logic comb_obs_rd_en; logic [7:0] comb_obs_rd_addr; logic [31:0] comb_obs_rd_data; logic comb_obs_rd_valid; logic comb_resp_wr_en; logic [7:0] comb_resp_wr_addr; logic [31:0] comb_resp_wr_data; logic mbx_ee_wr_en; logic 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; // Combiner share of the EE mailbox port: write wins over read on // the address mux (they don't overlap in the combiner's state // machine — writes in S_WRITE_*, reads in S_POLL_WAIT/S_SMCOM_WAIT). assign mbx_ee_wr_en = comb_resp_wr_en; assign mbx_ee_rd_en = comb_obs_rd_en; assign mbx_ee_addr = comb_resp_wr_en ? comb_resp_wr_addr : comb_obs_rd_addr; assign mbx_ee_wr_data = comb_resp_wr_data; assign comb_obs_rd_data = mbx_ee_rd_data; assign comb_obs_rd_valid = mbx_ee_rd_valid; 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 side driven by the IOP map's sif_* downstream .iop_wr_en(map_sif_wr_en), .iop_rd_en(map_sif_rd_en), .iop_addr(map_sif_wr_en ? map_sif_wr_addr : map_sif_rd_addr), .iop_wr_data(map_sif_wr_data), .iop_rd_data(map_sif_rd_data), .iop_rd_valid(map_sif_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) ); // ------------------------------------------------------------------ // Combiner // ------------------------------------------------------------------ logic comb_done; logic [31:0] comb_ack_count; sif_dma_ee_ack_peer_stub u_combiner ( .clk(clk), .rst_n(rst_n), .obs_rd_en(comb_obs_rd_en), .obs_rd_addr(comb_obs_rd_addr), .obs_rd_data(comb_obs_rd_data), .obs_rd_valid(comb_obs_rd_valid), .resp_wr_en(comb_resp_wr_en), .resp_wr_addr(comb_resp_wr_addr), .resp_wr_data(comb_resp_wr_data), .payload_complete(bridge_last_seen), .done_o(comb_done), .ack_count_o(comb_ack_count) ); // ------------------------------------------------------------------ // Trace sinks // ------------------------------------------------------------------ trace_sink_stub #(.FILENAME("iop_driven_iop_map.trace"), .SINK_LABEL("iop_map")) u_trace_iop_map (.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("iop_driven_ee_map.trace"), .SINK_LABEL("ee_map")) u_trace_ee_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("iop_driven_dmac.trace"), .SINK_LABEL("iop_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("iop_driven_mailbox.trace"), .SINK_LABEL("sif_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)); // ------------------------------------------------------------------ // Counters // ------------------------------------------------------------------ int dmac_start_events; int dmac_beat_events; int dmac_done_events; int ee_map_ram_writes; int errors; initial begin dmac_start_events = 0; dmac_beat_events = 0; dmac_done_events = 0; ee_map_ram_writes = 0; errors = 0; end always_ff @(posedge clk) begin if (rst_n && dmac_ev_valid && dmac_ev_subsys == trace_pkg::SUBSYS_DMAC) begin case (dmac_ev_event) trace_pkg::EV_DMA_START: dmac_start_events <= dmac_start_events + 1; trace_pkg::EV_DMA_BEAT: dmac_beat_events <= dmac_beat_events + 1; trace_pkg::EV_DMA_DONE: dmac_done_events <= dmac_done_events + 1; default: ; endcase end if (rst_n && ee_map_ev_valid && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM && ee_map_ev_event == trace_pkg::EV_WRITE && ee_map_ev_arg3[7:0] == 8'd1) ee_map_ram_writes <= ee_map_ram_writes + 1; end // ------------------------------------------------------------------ // Helpers (IOP-perspective via map) // ------------------------------------------------------------------ task automatic iop_write(input logic [31:0] addr, input logic [31:0] data); @(negedge clk); iop_wr_en = 1'b1; iop_wr_addr = addr; iop_wr_data = data; iop_wr_be = 4'b1111; @(negedge clk); iop_wr_en = 1'b0; iop_wr_addr = 32'd0; iop_wr_data = 32'd0; iop_wr_be = 4'd0; endtask task automatic iop_read(input logic [31:0] addr, output logic [31:0] data); @(negedge clk); iop_rd_en = 1'b1; iop_rd_addr = addr; @(negedge clk); iop_rd_en = 1'b0; iop_rd_addr = 32'd0; data = iop_rd_data; endtask task automatic ee_dmac_read_qword(input logic [31:0] addr, output logic [127:0] data); @(negedge clk); ee_dmac_rd_en = 1'b1; ee_dmac_rd_addr = addr; @(negedge clk); ee_dmac_rd_en = 1'b0; ee_dmac_rd_addr = 32'd0; @(posedge clk); data = ee_dmac_rd_data; endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ logic [31:0] payload [0:BEATS-1]; logic [31:0] pre_dma_ack_count; logic [31:0] msflg_val; logic [31:0] mscom_val; logic [127:0] expected_qw; logic [127:0] got_qw; initial begin rst_n = 1'b0; iop_rd_en = 1'b0; iop_rd_addr = 32'd0; iop_wr_en = 1'b0; iop_wr_addr = 32'd0; iop_wr_data = 32'd0; iop_wr_be = 4'd0; ee_dmac_rd_en = 1'b0; ee_dmac_rd_addr = 32'd0; for (int i = 0; i < BEATS; i++) payload[i] = 32'hCAFE_0000 | i[31:0]; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); // Phase 1: preload IOP RAM. for (int i = 0; i < BEATS; i++) begin iop_write(SRC_BASE + (i << 2), payload[i]); end // Phase 2: IOP posts SMCOM (command value) via IOP map. iop_write(SIF_SMCOM, CAPTURED_CMD); // Phase 3: IOP rings doorbell (SMFLG = CMD_PENDING_BIT). iop_write(SIF_SMFLG, CMD_PENDING_BIT); // Phase 4: ordering guarantee — give the combiner time to poll // SMFLG. Even though SMFLG is set, payload_complete is NOT yet // set, so the combiner must NOT advance past S_POLL_WAIT. repeat (20) @(posedge clk); pre_dma_ack_count = comb_ack_count; if (pre_dma_ack_count != 32'd0) begin $error("[tb_sif_iop_driven_combined] combiner acked before DMA (count=%0d)", pre_dma_ack_count); errors = errors + 1; end if (comb_done) begin $error("[tb_sif_iop_driven_combined] combiner flagged done before DMA"); errors = errors + 1; end iop_read(SIF_MSFLG, msflg_val); if (msflg_val !== 32'd0) begin $error("[tb_sif_iop_driven_combined] MSFLG nonzero pre-DMA: 0x%08h", msflg_val); errors = errors + 1; end // Phase 5: program DMAC ch9 and start. iop_write(DMAC_MADR_ADDR, SRC_BASE); iop_write(DMAC_BCR_ADDR, 32'(BEATS)); iop_write(DMAC_CHCR_ADDR, 32'h0000_0001); // Phase 6: wait for DMA completion + combiner ack. repeat (10 * BEATS) @(posedge clk); if (dmac_done_count != 1) begin $error("[tb_sif_iop_driven_combined] DMA done_count expected 1, got %0d", dmac_done_count); errors = errors + 1; end if (comb_ack_count != 32'd1) begin $error("[tb_sif_iop_driven_combined] combiner ack_count expected 1, got %0d", comb_ack_count); errors = errors + 1; end if (!comb_done) begin $error("[tb_sif_iop_driven_combined] combiner done_o not asserted"); errors = errors + 1; end // Phase 7: IOP polls MSFLG through the real map, expects ACK. iop_read(SIF_MSFLG, msflg_val); if ((msflg_val & CMD_ACK_BIT) == 32'd0) begin $error("[tb_sif_iop_driven_combined] MSFLG missing ACK bit: 0x%08h", msflg_val); errors = errors + 1; end // Phase 8: IOP reads MSCOM through the real map, expects echo. iop_read(SIF_MSCOM, mscom_val); if (mscom_val !== CAPTURED_CMD) begin $error("[tb_sif_iop_driven_combined] MSCOM echo got 0x%08h expected 0x%08h", mscom_val, CAPTURED_CMD); errors = errors + 1; end // Phase 9: EE-side payload readback via ee_map.dmac_rd_*. for (int q = 0; q < QWORDS; q++) begin expected_qw = { payload[q*4 + 3], payload[q*4 + 2], payload[q*4 + 1], payload[q*4 + 0] }; ee_dmac_read_qword(EE_DEST_BASE + (q << 4), got_qw); if (got_qw !== expected_qw) begin $error("[tb_sif_iop_driven_combined] qw[%0d] got 0x%032h expected 0x%032h", q, got_qw, expected_qw); errors = errors + 1; end end repeat (4) @(posedge clk); $display("[tb_sif_iop_driven_combined] start=%0d beat=%0d done=%0d ee_map_writes=%0d pre_dma_ack=%0d ack=%0d errors=%0d", dmac_start_events, dmac_beat_events, dmac_done_events, ee_map_ram_writes, pre_dma_ack_count, comb_ack_count, errors); if (dmac_start_events != 1) $error("expected 1 DMA_START, got %0d", dmac_start_events); if (dmac_beat_events != BEATS) $error("expected %0d DMA_BEAT, got %0d", BEATS, dmac_beat_events); if (dmac_done_events != 1) $error("expected 1 DMA_DONE, got %0d", dmac_done_events); if (ee_map_ram_writes != QWORDS) $error("expected %0d EE map writes, got %0d", QWORDS, ee_map_ram_writes); if (errors == 0 && dmac_start_events == 1 && dmac_beat_events == BEATS && dmac_done_events == 1 && ee_map_ram_writes == QWORDS && pre_dma_ack_count == 32'd0 && comb_ack_count == 32'd1) $display("[tb_sif_iop_driven_combined] PASS"); else $display("[tb_sif_iop_driven_combined] FAIL"); $finish; end initial begin #500000; $error("[tb_sif_iop_driven_combined] timeout"); $finish; end endmodule : tb_sif_iop_driven_combined