`timescale 1ns/1ps // ============================================================ // NMS STEP18 -- isolated correctness regression for // sdram_weight_backend_pack128.v (BURST_LEN=8 packed weight-fetch // wrapper) against the real, timing-checked sdram_model.v. Covers: // A) sequential access (natural weight_prefetch_engine_wide.v // pattern: addr, addr+8, addr+16, ... ) -- verifies every SECOND // fetch is served from the cache with a cache HIT, and that // every returned 64-bit word bit-exactly matches what was // poked into the backing SDRAM model beforehand. // B) non-sequential / odd-half-first access -- verifies the // cache-miss fallback path still returns correct data even when // the natural pairing assumption doesn't hold. // C) address-limit pattern -- near the top of the real 8MB address // space. // ============================================================ module tb; localparam ADDR_WIDTH = 23; localparam CLK_FREQ_MHZ = 80; localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ; reg clk = 0; always #(CLK_PERIOD_NS/2.0) clk = ~clk; reg rst; reg req, wr; reg [ADDR_WIDTH-1:0] addr; reg [63:0] wdata; wire [63:0] rdata; wire ready; wire sdram_cke, sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n; wire [1:0] sdram_ba; wire [11:0] sdram_a; wire [15:0] sdram_dq; wire [1:0] sdram_dqm; sdram_weight_backend_pack128 #(.ADDR_WIDTH(ADDR_WIDTH), .CLK_FREQ_MHZ(CLK_FREQ_MHZ)) dut ( .clk(clk), .rst(rst), .mem_req(req), .mem_wr(wr), .mem_addr(addr), .mem_wdata(wdata), .mem_rdata(rdata), .mem_ready(ready), .sdram_cke(sdram_cke), .sdram_cs_n(sdram_cs_n), .sdram_ras_n(sdram_ras_n), .sdram_cas_n(sdram_cas_n), .sdram_we_n(sdram_we_n), .sdram_ba(sdram_ba), .sdram_a(sdram_a), .sdram_dq(sdram_dq), .sdram_dqm(sdram_dqm) ); sdram_model #(.CLK_FREQ_MHZ(CLK_FREQ_MHZ)) mem ( .clk(clk), .cke(sdram_cke), .cs_n(sdram_cs_n), .ras_n(sdram_ras_n), .cas_n(sdram_cas_n), .we_n(sdram_we_n), .ba(sdram_ba), .a(sdram_a), .dq(sdram_dq), .dqm(sdram_dqm) ); integer errors, tests, cyc; integer cache_hit_count, cache_miss_count; always @(posedge clk) if (!rst) cyc <= cyc + 1; // backdoor poke: byte_addr -> flat 16-bit-word index (mirrors // tb_nms_dstress_sdram.v's own poke_byte_weight convention) task automatic poke64(input [ADDR_WIDTH-1:0] byte_addr, input [63:0] val); integer w; reg [21:0] word_addr; begin for (w = 0; w < 4; w = w + 1) begin word_addr = (byte_addr + w*2) >> 1; mem.mem[word_addr] = val[w*16 +: 16]; end end endtask task automatic do_read(input [ADDR_WIDTH-1:0] a, output [63:0] r, output integer cyc_taken); integer t0; begin @(posedge clk); t0 = cyc; req = 1'b1; wr = 1'b0; addr = a; @(posedge clk); req = 1'b0; while (!ready) @(posedge clk); r = rdata; cyc_taken = cyc - t0; end endtask reg [63:0] got; integer elapsed; integer i; reg [ADDR_WIDTH-1:0] base; task automatic check64(input [ADDR_WIDTH-1:0] a, input [63:0] expected, input [255:0] label); begin do_read(a, got, elapsed); tests = tests + 1; if (elapsed <= 3) cache_hit_count = cache_hit_count + 1; else cache_miss_count = cache_miss_count + 1; if (got !== expected) begin $display("FAIL %0s addr=%0d: got=%h expected=%h", label, a, got, expected); errors = errors + 1; end else begin $display("PASS %0s addr=%0d: bit-exact, cycles=%0d (%0s)", label, a, elapsed, (elapsed<=3) ? "CACHE HIT" : "real SDRAM fetch"); end end endtask initial begin errors = 0; tests = 0; cyc = 0; cache_hit_count = 0; cache_miss_count = 0; rst = 1; req = 0; wr = 0; addr = 0; wdata = 0; repeat(5) @(posedge clk); rst = 0; while (dut.u_sdram_ctrl.state != 5'd7) @(posedge clk); // wait for real power-up (S_IDLE) @(posedge clk); // ---- pre-load a known pattern into the backing SDRAM: 16 // sequential 64-bit tiles (matching P8*INT8=64-bit tile size) // starting at a 16-byte-aligned base, each tile = its own // tile index replicated as a 16-bit pattern per word ---- base = 23'h010000; // 16-byte aligned for (i = 0; i < 16; i = i + 1) poke64(base + i*8, {4{16'hA000 + i[15:0]}}); // ---- Test A: sequential access (the REAL weight_prefetch_ // engine_wide.v pattern) -- every SECOND read must be a cache // hit (elapsed<=3 cycles), all data bit-exact ---- for (i = 0; i < 16; i = i + 1) check64(base + i*8, {4{16'hA000 + i[15:0]}}, "A-sequential"); // ---- Test B: non-sequential (odd-half-first) access -- // deliberately request the UPPER half of a pair before its // LOWER half has ever been fetched -- must still be correct // (real fallback fetch), then confirm the (now-cached) lower // half is ALSO correct on a follow-up request ---- poke64(23'h020008, 64'hDEAD_BEEF_0BAD_F00D); poke64(23'h020000, 64'h1234_5678_9ABC_DEF0); check64(23'h020008, 64'hDEAD_BEEF_0BAD_F00D, "B-odd-first"); check64(23'h020000, 64'h1234_5678_9ABC_DEF0, "B-even-after"); // ---- Test C: address-limit pattern (near top of the real // 8MB space, 23-bit byte address) ---- base = 23'h7FFFF0; // last 16-byte-aligned block in 8MB (0x800000) poke64(base, 64'h1111_2222_3333_4444); poke64(base+8, 64'h5555_6666_7777_8888); check64(base, 64'h1111_2222_3333_4444, "C-limit-low"); check64(base+8, 64'h5555_6666_7777_8888, "C-limit-high"); $display("=== %0d/%0d tests, %0d errors, cache_hits=%0d cache_misses=%0d ===", tests-errors, tests, errors, cache_hit_count, cache_miss_count); if (errors == 0) $display("ALL TESTS PASSED (tb_sdram_weight_backend_pack128)"); $finish; end endmodule