// retroDE_ps2 — tb_sif_iop_bridge_smoke // // First bridge milestone: EE-sourced SIF DMA landing in architecturally // visible IOP RAM via the IOP memory map. Explicitly does NOT yet exercise // iop_fetch_stub reading the landed payload — that's the follow-up // milestone. Scope here is limited to proving the write side: // // ee_ram_stub (TB preload) // ↓ (via ee_memory_map_stub, DMAC ch5 fetch) // dmac_reg_stub (ch5) // ↓ ep_* // sif_dma_iop_ram_bridge_stub (128-bit → 4× 32-bit) // ↓ bridge_wr_* // iop_memory_map_stub (routes physical RAM-window writes) // ↓ ram_wr_* // iop_ram_stub (landing site) // // TB then reads iop_ram_stub back through the IOP map's CPU-side read // port to verify: // - 4 words per qword landed at consecutive addresses // - little-endian unpacking (qword[31:0] at lowest address) // - ordering across multiple qwords is preserved // - no drops, no duplicates // - map trace tags bridge writes with master_id=3 (bridge) and // region=IOP_RAM `timescale 1ns/1ps module tb_sif_iop_bridge_smoke; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; localparam int QWC_VAL = 2; localparam int EE_RAM_BYTES = 4 * 1024; localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES); localparam int IOP_RAM_BYTES = 4 * 1024; localparam int IOP_RAM_ADDR_W = $clog2(IOP_RAM_BYTES); localparam logic [31:0] SOURCE_MADR = 32'h0000_0600; // EE-side src localparam logic [31:0] DEST_BASE_ADDR = 32'h0000_0400; // IOP RAM dest // ------------------------------------------------------------------ // EE-side RAM (source) // ------------------------------------------------------------------ logic ee_ram_rd_en; logic [EE_RAM_ADDR_W-1:0] ee_ram_rd_addr; logic [127:0] ee_ram_rd_data; logic ee_ram_rd_valid; logic ee_ram_wr_en; logic [EE_RAM_ADDR_W-1: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 ee_ram_ev_valid; trace_pkg::subsys_e ee_ram_ev_subsys; trace_pkg::event_e ee_ram_ev_event; logic [63:0] ee_ram_ev_arg0, ee_ram_ev_arg1, ee_ram_ev_arg2, ee_ram_ev_arg3; logic [31:0] ee_ram_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), .rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid), .wr_en(ee_ram_wr_en), .wr_addr(ee_ram_wr_addr), .wr_data(ee_ram_wr_data), .wr_be(ee_ram_wr_be), .master_id(ee_ram_master_id), .ev_valid(ee_ram_ev_valid), .ev_subsys(ee_ram_ev_subsys), .ev_event(ee_ram_ev_event), .ev_arg0(ee_ram_ev_arg0), .ev_arg1(ee_ram_ev_arg1), .ev_arg2(ee_ram_ev_arg2), .ev_arg3(ee_ram_ev_arg3), .ev_flags(ee_ram_ev_flags) ); assign ee_ram_master_id = ee_ram_rd_en ? 8'd1 : 8'd0; // ------------------------------------------------------------------ // EE DMAC (channel 5) + EE memory map // ------------------------------------------------------------------ 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) ); logic ee_map_ram_rd_en; logic [24:0] ee_map_ram_rd_addr; 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(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(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(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 ee_ram_rd_en = ee_map_ram_rd_en; assign ee_ram_rd_addr = ee_map_ram_rd_addr[EE_RAM_ADDR_W-1:0]; // ------------------------------------------------------------------ // SIF → IOP-RAM bridge // ------------------------------------------------------------------ logic bridge_wr_en; logic [31:0] bridge_wr_addr; logic [31:0] bridge_wr_data; logic [3:0] bridge_wr_be; logic [7:0] bridge_master_id; sif_dma_iop_ram_bridge_stub #(.DEST_BASE_ADDR(DEST_BASE_ADDR), .MASTER_ID(8'd3)) u_bridge ( .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), .bridge_wr_en(bridge_wr_en), .bridge_wr_addr(bridge_wr_addr), .bridge_wr_data(bridge_wr_data), .bridge_wr_be(bridge_wr_be), .bridge_master_id(bridge_master_id) ); // ------------------------------------------------------------------ // IOP map + IOP RAM // ------------------------------------------------------------------ // TB-side CPU port: used to READ the landed payload for verification. logic iop_rd_en; logic [31:0] iop_rd_addr; logic [31:0] iop_rd_data; logic iop_rd_valid; logic iop_map_ram_rd_en; logic [20:0] iop_map_ram_rd_addr; logic [31:0] iop_ram_rd_data; logic iop_ram_rd_valid; logic iop_map_ram_wr_en; logic [20:0] iop_map_ram_wr_addr; logic [31:0] iop_map_ram_wr_data; logic [3:0] iop_map_ram_wr_be; logic [7:0] iop_map_ram_master_id; 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), // CPU-side read (TB verification) .iop_rd_en(iop_rd_en), .iop_rd_addr(iop_rd_addr), .iop_rd_data(iop_rd_data), .iop_rd_valid(iop_rd_valid), // No CPU writes this pass .iop_wr_en(1'b0), .iop_wr_addr(32'd0), .iop_wr_data(32'd0), .iop_wr_be(4'd0), .master_id(8'd2), // IOP_CPU (read tag) // Bridge write port — wired .bridge_wr_en(bridge_wr_en), .bridge_wr_addr(bridge_wr_addr), .bridge_wr_data(bridge_wr_data), .bridge_wr_be(bridge_wr_be), .bridge_master_id(bridge_master_id), // DMA read-master port — unused by this TB .dma_rd_en(1'b0), .dma_rd_addr(32'd0), .dma_master_id(8'd0), .dma_rd_data(), .dma_rd_valid(), // SIF register-shell port — unused by this TB .sif_rd_en(), .sif_rd_addr(), .sif_rd_data(32'd0), .sif_rd_valid(1'b0), .sif_wr_en(), .sif_wr_addr(), .sif_wr_data(), // IOP DMAC port — unused by this TB .iop_dmac_rd_en(), .iop_dmac_rd_addr(), .iop_dmac_rd_data(32'd0), .iop_dmac_rd_valid(1'b0), .iop_dmac_wr_en(), .iop_dmac_wr_addr(), .iop_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), // Downstream to IOP RAM .ram_rd_en(iop_map_ram_rd_en), .ram_rd_addr(iop_map_ram_rd_addr), .ram_rd_data(iop_ram_rd_data), .ram_rd_valid(iop_ram_rd_valid), .ram_wr_en(iop_map_ram_wr_en), .ram_wr_addr(iop_map_ram_wr_addr), .ram_wr_data(iop_map_ram_wr_data), .ram_wr_be(iop_map_ram_wr_be), .ram_master_id(iop_map_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) ); logic iop_ram_ev_valid; trace_pkg::subsys_e iop_ram_ev_subsys; trace_pkg::event_e iop_ram_ev_event; logic [63:0] iop_ram_ev_arg0, iop_ram_ev_arg1, iop_ram_ev_arg2, iop_ram_ev_arg3; logic [31:0] iop_ram_ev_flags; iop_ram_stub #(.SIZE_BYTES(IOP_RAM_BYTES)) u_iop_ram ( .clk(clk), .rst_n(rst_n), .rd_en(iop_map_ram_rd_en), .rd_addr(iop_map_ram_rd_addr[IOP_RAM_ADDR_W-1:0]), .rd_data(iop_ram_rd_data), .rd_valid(iop_ram_rd_valid), .wr_en(iop_map_ram_wr_en), .wr_addr(iop_map_ram_wr_addr[IOP_RAM_ADDR_W-1:0]), .wr_data(iop_map_ram_wr_data), .wr_be(iop_map_ram_wr_be), .master_id(iop_map_ram_master_id), .ev_valid(iop_ram_ev_valid), .ev_subsys(iop_ram_ev_subsys), .ev_event(iop_ram_ev_event), .ev_arg0(iop_ram_ev_arg0), .ev_arg1(iop_ram_ev_arg1), .ev_arg2(iop_ram_ev_arg2), .ev_arg3(iop_ram_ev_arg3), .ev_flags(iop_ram_ev_flags) ); // ------------------------------------------------------------------ // Trace sinks // ------------------------------------------------------------------ trace_sink_stub #(.FILENAME("sif_iop_bridge_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_iop_bridge_iopmap.trace"), .SINK_LABEL("iop_map")) u_trace_iopmap (.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("sif_iop_bridge_iopram.trace"), .SINK_LABEL("iop_ram")) u_trace_iopram (.clk(clk), .rst_n(rst_n), .ev_valid(iop_ram_ev_valid), .ev_subsys(iop_ram_ev_subsys), .ev_event(iop_ram_ev_event), .ev_arg0(iop_ram_ev_arg0), .ev_arg1(iop_ram_ev_arg1), .ev_arg2(iop_ram_ev_arg2), .ev_arg3(iop_ram_ev_arg3), .ev_flags(iop_ram_ev_flags)); // ------------------------------------------------------------------ // Counters // ------------------------------------------------------------------ int dma_done_count; int bridge_write_count_map; // map-layer writes from bridge int bridge_write_count_ram; // ram-layer writes from bridge int errors; logic dma_done_seen; initial begin dma_done_count = 0; bridge_write_count_map = 0; bridge_write_count_ram = 0; errors = 0; dma_done_seen = 1'b0; end always_ff @(posedge clk) begin if (rst_n && dmac_ev_valid && dmac_ev_event == trace_pkg::EV_DMA_DONE) begin dma_done_count <= dma_done_count + 1; dma_done_seen <= 1'b1; end // Map-layer bridge writes: master=3 (bridge), region=IOP_RAM=2 if (rst_n && iop_map_ev_valid && iop_map_ev_event == trace_pkg::EV_WRITE && iop_map_ev_arg2[7:0] == 8'd3 && iop_map_ev_arg3[7:0] == 8'd2) bridge_write_count_map <= bridge_write_count_map + 1; // RAM-layer bridge writes (same attribution propagated) if (rst_n && iop_ram_ev_valid && iop_ram_ev_event == trace_pkg::EV_WRITE && iop_ram_ev_arg2[7:0] == 8'd3) bridge_write_count_ram <= bridge_write_count_ram + 1; end // ------------------------------------------------------------------ // Payload table // ------------------------------------------------------------------ logic [127:0] expected_qword [0:QWC_VAL-1]; logic [31:0] expected_word [0:(QWC_VAL*4)-1]; initial begin expected_qword[0] = 128'hDDDD_DDDD_CCCC_CCCC_BBBB_BBBB_AAAA_AAAA; expected_qword[1] = 128'h4444_4444_3333_3333_2222_2222_1111_1111; // Little-endian unpacking expectation expected_word[0] = 32'hAAAA_AAAA; expected_word[1] = 32'hBBBB_BBBB; expected_word[2] = 32'hCCCC_CCCC; expected_word[3] = 32'hDDDD_DDDD; expected_word[4] = 32'h1111_1111; expected_word[5] = 32'h2222_2222; expected_word[6] = 32'h3333_3333; expected_word[7] = 32'h4444_4444; end // ------------------------------------------------------------------ // Helpers // ------------------------------------------------------------------ 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_ee(input logic [EE_RAM_ADDR_W-1:0] addr, input logic [127:0] data); @(negedge clk); ee_ram_wr_en = 1'b1; ee_ram_wr_addr = addr; ee_ram_wr_data = data; ee_ram_wr_be = 16'hFFFF; @(negedge clk); ee_ram_wr_en = 1'b0; ee_ram_wr_addr = '0; ee_ram_wr_data = 128'd0; ee_ram_wr_be = 16'd0; endtask task automatic iop_read_expect(input logic [31:0] addr, input logic [31:0] expected, input string label); @(negedge clk); iop_rd_en = 1'b1; iop_rd_addr = addr; @(negedge clk); iop_rd_en = 1'b0; iop_rd_addr = 32'd0; if (iop_rd_data !== expected || iop_rd_valid !== 1'b1) begin $error("[tb_sif_iop_bridge_smoke] IOP read %s at 0x%08h: got 0x%08h valid=%0b expected 0x%08h", label, addr, iop_rd_data, iop_rd_valid, expected); errors = errors + 1; end endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ initial begin rst_n = 1'b0; dmac_reg_wr_en = 1'b0; dmac_reg_offset = 8'd0; dmac_reg_wr_data = 32'd0; ee_ram_wr_en = 1'b0; ee_ram_wr_addr = '0; ee_ram_wr_data = 128'd0; ee_ram_wr_be = 16'd0; iop_rd_en = 1'b0; iop_rd_addr = 32'd0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); // Preload EE RAM with the source qwords. begin : do_preload logic [31:0] a; for (int i = 0; i < QWC_VAL; i++) begin a = SOURCE_MADR + (i * 32'd16); preload_ee(a[EE_RAM_ADDR_W-1:0], expected_qword[i]); end end // Program DMAC ch5. write_dmac(8'h10, SOURCE_MADR); write_dmac(8'h20, 32'd2); write_dmac(8'h00, 32'h0000_0001); // Wait for DMA completion. begin : wait_done int spin; spin = 0; while (!dma_done_seen && spin < 500) begin @(posedge clk); spin = spin + 1; end if (!dma_done_seen) begin $error("[tb_sif_iop_bridge_smoke] DMA never completed"); errors = errors + 1; end end // Give the bridge a few cycles to finish flushing its final qword // out to the IOP map after DMA_DONE. repeat (20) @(posedge clk); // Verify: 8 32-bit words landed at DEST_BASE, DEST_BASE+4, ... for (int i = 0; i < QWC_VAL * 4; i++) begin iop_read_expect(DEST_BASE_ADDR + (i * 32'd4), expected_word[i], $sformatf("word[%0d]", i)); end repeat (4) @(posedge clk); // ------------------------------------------------------------------ $display("[tb_sif_iop_bridge_smoke] dma_done=%0d bridge_writes_map=%0d bridge_writes_ram=%0d errors=%0d", dma_done_count, bridge_write_count_map, bridge_write_count_ram, errors); if (dma_done_count != 1) $error("expected exactly 1 DMA_DONE, got %0d", dma_done_count); if (bridge_write_count_map != QWC_VAL * 4) $error("expected %0d map-layer bridge writes, got %0d", QWC_VAL * 4, bridge_write_count_map); if (bridge_write_count_ram != QWC_VAL * 4) $error("expected %0d RAM-layer bridge writes, got %0d", QWC_VAL * 4, bridge_write_count_ram); if (errors == 0 && dma_done_count == 1 && bridge_write_count_map == QWC_VAL * 4 && bridge_write_count_ram == QWC_VAL * 4) $display("[tb_sif_iop_bridge_smoke] PASS"); else $display("[tb_sif_iop_bridge_smoke] FAIL"); $finish; end initial begin #400000; $error("[tb_sif_iop_bridge_smoke] timeout"); $finish; end endmodule : tb_sif_iop_bridge_smoke