// retroDE_ps2 — tb_iop_ram_stub // // Unit test for iop_ram_stub. Mirrors the style of other primitive tests // (bios_rom_stub / ee_ram_stub) — drive reads and writes from the TB, // verify round-trip data, check trace emission and region/master tagging. // // Scenarios: // 1. Post-reset read returns zero (zero-init) // 2. Byte-wide writes with partial byte enables // 3. Word-wide writes // 4. Read-back matches write pattern // 5. Trace events tagged as SUBSYS_IOP with region=IOP_RAM and // caller-provided master_id `timescale 1ns/1ps module tb_iop_ram_stub; localparam int SIZE_BYTES = 4 * 1024; localparam int ADDR_W = $clog2(SIZE_BYTES); logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; logic rd_en; logic [ADDR_W-1:0] rd_addr; logic [31:0] rd_data; logic rd_valid; logic wr_en; logic [ADDR_W-1:0] wr_addr; logic [31:0] wr_data; logic [3:0] wr_be; logic [7:0] master_id; 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; iop_ram_stub #(.SIZE_BYTES(SIZE_BYTES)) u_iop_ram ( .clk(clk), .rst_n(rst_n), .rd_en(rd_en), .rd_addr(rd_addr), .rd_data(rd_data), .rd_valid(rd_valid), .wr_en(wr_en), .wr_addr(wr_addr), .wr_data(wr_data), .wr_be(wr_be), .master_id(master_id), .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) ); trace_sink_stub #(.FILENAME("iop_ram.trace"), .SINK_LABEL("iop_ram")) u_trace (.clk(clk), .rst_n(rst_n), .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)); // ------------------------------------------------------------------ // Counters // ------------------------------------------------------------------ int iop_reads; int iop_writes; int iop_region_hits; int errors; initial begin iop_reads = 0; iop_writes = 0; iop_region_hits = 0; errors = 0; end always_ff @(posedge clk) begin if (rst_n && ev_valid && ev_subsys == trace_pkg::SUBSYS_IOP) begin if (ev_event == trace_pkg::EV_READ) iop_reads <= iop_reads + 1; if (ev_event == trace_pkg::EV_WRITE) iop_writes <= iop_writes + 1; if (ev_arg3[7:0] == 8'd2) iop_region_hits <= iop_region_hits + 1; end end // ------------------------------------------------------------------ // Helpers // ------------------------------------------------------------------ task automatic do_write(input logic [ADDR_W-1:0] addr, input logic [31:0] data, input logic [3:0] be); @(negedge clk); wr_en = 1'b1; wr_addr = addr; wr_data = data; wr_be = be; @(negedge clk); wr_en = 1'b0; wr_addr = '0; wr_data = 32'd0; wr_be = 4'd0; endtask task automatic do_read_expect(input logic [ADDR_W-1:0] addr, input logic [31:0] expected, input string label); @(negedge clk); rd_en = 1'b1; rd_addr = addr; @(negedge clk); rd_en = 1'b0; rd_addr = '0; if (rd_data !== expected || rd_valid !== 1'b1) begin $error("[tb_iop_ram_stub] read %s: got 0x%08h valid=%0b expected 0x%08h", label, rd_data, rd_valid, expected); errors = errors + 1; end endtask // ------------------------------------------------------------------ // Stimulus // ------------------------------------------------------------------ logic [31:0] before_read; initial begin rst_n = 1'b0; rd_en = 1'b0; rd_addr = '0; wr_en = 1'b0; wr_addr = '0; wr_data = 32'd0; wr_be = 4'd0; master_id = 8'd0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (2) @(posedge clk); // 1. Zero-init check do_read_expect({ADDR_W{1'b0}}, 32'h0000_0000, "addr 0x0 after reset"); // 2. Word-wide write + read-back master_id = 8'd0; // TB direct do_write({ADDR_W{1'b0}}, 32'hCAFE_BABE, 4'b1111); do_read_expect({ADDR_W{1'b0}}, 32'hCAFE_BABE, "word write round-trip"); // 3. Partial byte-enable write: preserve upper half, overwrite lower // (start from known word value, write only be=0x3 with new data) do_write({ADDR_W{1'b0}}, 32'h1234_0000, 4'b0011); do_read_expect({ADDR_W{1'b0}}, 32'hCAFE_0000, "partial byte-enable low"); // 4. Non-zero master_id — IOP CPU hypothetical master_id = 8'd2; do_write(ADDR_W'(32'h0010), 32'hDEAD_BEEF, 4'b1111); do_read_expect(ADDR_W'(32'h0010), 32'hDEAD_BEEF, "IOP-tagged write"); // 5. Address-distinct read — confirm storage independence do_read_expect(ADDR_W'(32'h0020), 32'h0000_0000, "untouched slot"); repeat (4) @(posedge clk); // ------------------------------------------------------------------ $display("[tb_iop_ram_stub] iop_reads=%0d iop_writes=%0d iop_region_hits=%0d errors=%0d", iop_reads, iop_writes, iop_region_hits, errors); if (iop_reads < 4) $error("expected >= 4 IOP read events, got %0d", iop_reads); if (iop_writes < 3) $error("expected >= 3 IOP write events, got %0d", iop_writes); if (iop_region_hits != iop_reads + iop_writes) $error("region tag inconsistent: reads+writes=%0d region_hits=%0d", iop_reads + iop_writes, iop_region_hits); if (errors == 0 && iop_reads >= 4 && iop_writes >= 3 && iop_region_hits == iop_reads + iop_writes) $display("[tb_iop_ram_stub] PASS"); else $display("[tb_iop_ram_stub] FAIL"); $finish; end initial begin #200000; $error("[tb_iop_ram_stub] timeout"); $finish; end endmodule : tb_iop_ram_stub