// retroDE_ps2 — tb_iop_fetch_through_map // // First IOP-side execution-visible traffic. Wires: // // iop_fetch_stub → iop_memory_map_stub → iop_ram_stub // // TB preloads RAM with a distinctive pattern through the map's write // port, then enables the fetcher and verifies that the resulting PC // stream and data payloads match what was loaded. // // Pass criteria: // - sequential PC progression from RESET_VECTOR, step +4 per fetch // - returned data matches the preloaded pattern // - trace tagging correct: SUBSYS_IOP, IFETCH event, IOP_RAM region // at the map layer, IOP CPU master_id (2) propagated through // - at least N fetches observed (16 in this test) `timescale 1ns/1ps module tb_iop_fetch_through_map; localparam int RAM_BYTES = 4 * 1024; localparam int RAM_ADDR_W = $clog2(RAM_BYTES); localparam logic [31:0] RESET_VECTOR = 32'h0000_0000; localparam int N_FETCHES = 16; localparam int PRELOAD_CNT = 32; // cover fetch window plus slack logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; // ------------------------------------------------------------------ // Fetcher // ------------------------------------------------------------------ logic fetch_enable; logic fetch_rd_en; logic [31:0] fetch_rd_addr; logic [31:0] fetch_rd_data; logic fetch_rd_valid; logic fetch_ev_valid; trace_pkg::subsys_e fetch_ev_subsys; trace_pkg::event_e fetch_ev_event; logic [63:0] fetch_ev_arg0, fetch_ev_arg1, fetch_ev_arg2, fetch_ev_arg3; logic [31:0] fetch_ev_flags; iop_fetch_stub #(.RESET_VECTOR(RESET_VECTOR)) u_fetch ( .clk(clk), .rst_n(rst_n), .enable(fetch_enable), .rd_en(fetch_rd_en), .rd_addr(fetch_rd_addr), .rd_data(fetch_rd_data), .rd_valid(fetch_rd_valid), .ev_valid(fetch_ev_valid), .ev_subsys(fetch_ev_subsys), .ev_event(fetch_ev_event), .ev_arg0(fetch_ev_arg0), .ev_arg1(fetch_ev_arg1), .ev_arg2(fetch_ev_arg2), .ev_arg3(fetch_ev_arg3), .ev_flags(fetch_ev_flags) ); // ------------------------------------------------------------------ // Map — shared by TB (preload writes) and fetcher (runtime reads) // ------------------------------------------------------------------ // Preload-side write signals (driven by TB) logic tb_wr_en; logic [31:0] tb_wr_addr; logic [31:0] tb_wr_data; logic [3:0] tb_wr_be; // When the fetcher is enabled, master_id reflects IOP CPU (2). During // the preload window, the TB owns writes and tags them as TB_direct (0). logic [7:0] map_master_id; assign map_master_id = fetch_enable ? 8'd2 : 8'd0; logic map_ram_rd_en; logic [20:0] map_ram_rd_addr; logic [31:0] ram_rd_data; logic ram_rd_valid; logic map_ram_wr_en; logic [20:0] map_ram_wr_addr; logic [31:0] map_ram_wr_data; logic [3:0] map_ram_wr_be; logic [7:0] map_ram_master_id; logic map_ev_valid; trace_pkg::subsys_e map_ev_subsys; trace_pkg::event_e map_ev_event; logic [63:0] map_ev_arg0, map_ev_arg1, map_ev_arg2, map_ev_arg3; logic [31:0] map_ev_flags; iop_memory_map_stub u_map ( .clk(clk), .rst_n(rst_n), // Read: from fetcher .iop_rd_en(fetch_rd_en), .iop_rd_addr(fetch_rd_addr), .iop_rd_data(fetch_rd_data), .iop_rd_valid(fetch_rd_valid), // Write: from TB preload .iop_wr_en(tb_wr_en), .iop_wr_addr(tb_wr_addr), .iop_wr_data(tb_wr_data), .iop_wr_be(tb_wr_be), .master_id(map_master_id), // Bridge port — unused by this TB .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 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), .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), .ram_wr_en(map_ram_wr_en), .ram_wr_addr(map_ram_wr_addr), .ram_wr_data(map_ram_wr_data), .ram_wr_be(map_ram_wr_be), .ram_master_id(map_ram_master_id), .ev_valid(map_ev_valid), .ev_subsys(map_ev_subsys), .ev_event(map_ev_event), .ev_arg0(map_ev_arg0), .ev_arg1(map_ev_arg1), .ev_arg2(map_ev_arg2), .ev_arg3(map_ev_arg3), .ev_flags(map_ev_flags) ); // ------------------------------------------------------------------ // IOP RAM backing // ------------------------------------------------------------------ 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; iop_ram_stub #(.SIZE_BYTES(RAM_BYTES)) u_ram ( .clk(clk), .rst_n(rst_n), .rd_en(map_ram_rd_en), .rd_addr(map_ram_rd_addr[RAM_ADDR_W-1:0]), .rd_data(ram_rd_data), .rd_valid(ram_rd_valid), .wr_en(map_ram_wr_en), .wr_addr(map_ram_wr_addr[RAM_ADDR_W-1:0]), .wr_data(map_ram_wr_data), .wr_be(map_ram_wr_be), .master_id(map_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) ); // ------------------------------------------------------------------ // Trace sinks // ------------------------------------------------------------------ trace_sink_stub #(.FILENAME("iop_fetch_fetcher.trace"), .SINK_LABEL("iop_fetch")) u_trace_fetch (.clk(clk), .rst_n(rst_n), .ev_valid(fetch_ev_valid), .ev_subsys(fetch_ev_subsys), .ev_event(fetch_ev_event), .ev_arg0(fetch_ev_arg0), .ev_arg1(fetch_ev_arg1), .ev_arg2(fetch_ev_arg2), .ev_arg3(fetch_ev_arg3), .ev_flags(fetch_ev_flags)); trace_sink_stub #(.FILENAME("iop_fetch_map.trace"), .SINK_LABEL("iop_map")) u_trace_map (.clk(clk), .rst_n(rst_n), .ev_valid(map_ev_valid), .ev_subsys(map_ev_subsys), .ev_event(map_ev_event), .ev_arg0(map_ev_arg0), .ev_arg1(map_ev_arg1), .ev_arg2(map_ev_arg2), .ev_arg3(map_ev_arg3), .ev_flags(map_ev_flags)); trace_sink_stub #(.FILENAME("iop_fetch_ram.trace"), .SINK_LABEL("iop_ram")) u_trace_ram (.clk(clk), .rst_n(rst_n), .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)); // ------------------------------------------------------------------ // Checkers // ------------------------------------------------------------------ int ifetch_count; int map_ifetch_routed; // map-level IOP reads with region=IOP_RAM int map_master_iop_cpu_count; // map events tagged master_id=2 int errors; wire [31:0] obs_addr = fetch_ev_arg0[31:0]; wire [31:0] obs_data = fetch_ev_arg1[31:0]; wire [31:0] obs_offset = obs_addr - RESET_VECTOR; wire [31:0] obs_wordidx = obs_offset >> 2; // Preloaded pattern: 0xABCD_0000 | word_idx wire [31:0] obs_expected = 32'hABCD_0000 | {16'd0, obs_wordidx[15:0]}; initial begin ifetch_count = 0; map_ifetch_routed = 0; map_master_iop_cpu_count = 0; errors = 0; end always_ff @(posedge clk) begin if (rst_n && fetch_ev_valid && fetch_ev_event == trace_pkg::EV_IFETCH) begin ifetch_count <= ifetch_count + 1; if (obs_data !== obs_expected) begin $error("[tb_iop_fetch_through_map] IFETCH mismatch: addr=0x%08h data=0x%08h expected=0x%08h", obs_addr, obs_data, obs_expected); errors <= errors + 1; end end if (rst_n && map_ev_valid && map_ev_subsys == trace_pkg::SUBSYS_IOP) begin if (map_ev_event == trace_pkg::EV_READ && map_ev_arg3[7:0] == 8'd2) // region=IOP_RAM map_ifetch_routed <= map_ifetch_routed + 1; if (map_ev_arg2[7:0] == 8'd2) // master=IOP_CPU map_master_iop_cpu_count <= map_master_iop_cpu_count + 1; end end // ------------------------------------------------------------------ // Helpers // ------------------------------------------------------------------ task automatic preload_word(input logic [31:0] addr, input logic [31:0] data); @(negedge clk); tb_wr_en = 1'b1; tb_wr_addr = addr; tb_wr_data = data; tb_wr_be = 4'b1111; @(negedge clk); tb_wr_en = 1'b0; tb_wr_addr = 32'd0; tb_wr_data = 32'd0; tb_wr_be = 4'd0; endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ initial begin rst_n = 1'b0; fetch_enable = 1'b0; tb_wr_en = 1'b0; tb_wr_addr = 32'd0; tb_wr_data = 32'd0; tb_wr_be = 4'd0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); // Preload a window wider than N_FETCHES so any overshoot in the // run loop still hits preloaded words. Pattern is consistent: // mem[word_idx] = 0xABCD_0000 | word_idx for (int i = 0; i < PRELOAD_CNT; i++) begin preload_word(RESET_VECTOR + (i * 32'd4), 32'hABCD_0000 | {16'd0, 16'(i)}); end // Enable the fetcher and let it run through the preloaded window. @(negedge clk); fetch_enable = 1'b1; repeat (N_FETCHES + 6) @(posedge clk); fetch_enable = 1'b0; repeat (4) @(posedge clk); // ------------------------------------------------------------------ $display("[tb_iop_fetch_through_map] ifetches=%0d map_routed=%0d map_iop_cpu_tagged=%0d errors=%0d", ifetch_count, map_ifetch_routed, map_master_iop_cpu_count, errors); if (ifetch_count < N_FETCHES) $error("expected >= %0d IFETCHes, got %0d", N_FETCHES, ifetch_count); if (map_ifetch_routed < N_FETCHES) $error("expected >= %0d map-routed IOP reads (region=IOP_RAM), got %0d", N_FETCHES, map_ifetch_routed); if (map_master_iop_cpu_count < N_FETCHES) $error("expected >= %0d map events tagged master=IOP_CPU, got %0d", N_FETCHES, map_master_iop_cpu_count); if (errors == 0 && ifetch_count >= N_FETCHES && map_ifetch_routed >= N_FETCHES && map_master_iop_cpu_count >= N_FETCHES) $display("[tb_iop_fetch_through_map] PASS"); else $display("[tb_iop_fetch_through_map] FAIL"); $finish; end initial begin #200000; $error("[tb_iop_fetch_through_map] timeout"); $finish; end endmodule : tb_iop_fetch_through_map