`timescale 1ns/1ps // ============================================================ // Isolated correctness test for act_tile_fetch.v -- EXP-0084: re-run // against the new 32-bit-wide ctrl bus / 4-tiles-per-burst layout // (real 32-bit DDR3 channel widening). Backend switched from the real // AS4C32M16SA x16 SDR model (sdram_controller.v/sdram_model.v -- a // REAL chip, genuinely fixed at 16-bit, not reusable here) to // burst_mem_model32.v, an explicitly synthetic 32-bit test-only // burst memory built for exactly this purpose (see its own header). // Checks: (1) all four lanes of a burst read back bit-exact from // their own quarter-slot; (2) different tile indices correctly // compute different burst addresses (tile_offset = (tcnt>>2)* // BURST_LEN); (3) back-to-back requests (multiple tiles in a row, // including crossing a burst boundary) all stay correct, exercising // the S_GAP busy-wait logic. // ============================================================ module tb; localparam BURST_LEN = 8; localparam ADDR_WIDTH = 25; localparam CLK_FREQ_MHZ = 64; localparam CLK_PERIOD_NS = 1000.0/CLK_FREQ_MHZ; localparam DATA_WIDTH = 8; localparam P_IN = 8; reg clk = 0; always #(CLK_PERIOD_NS/2.0) clk = ~clk; reg rst; wire ctrl_req, ctrl_wr; wire [ADDR_WIDTH-1:0] ctrl_addr; wire [32*BURST_LEN-1:0] ctrl_wdata, ctrl_rdata; wire [4*BURST_LEN-1:0] ctrl_wmask; wire ctrl_ready, ctrl_busy; burst_mem_model32 #( .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH) ) u_mem ( .clk(clk), .rst(rst), .req(ctrl_req), .wr(ctrl_wr), .addr(ctrl_addr), .wdata(ctrl_wdata), .wmask(ctrl_wmask), .rdata(ctrl_rdata), .ready(ctrl_ready), .busy(ctrl_busy) ); // single requester -> tie grant = active, same precedent as // tb_host_mem_bridge.v (a 1-requester arbiter would produce this). wire req_active_dut; wire mem_grant = req_active_dut; reg req; reg [ADDR_WIDTH-1:0] base_a, base_b; reg [15:0] tcnt; wire valid; wire signed [DATA_WIDTH*P_IN-1:0] data_a, data_b; wire dut_ctrl_req, dut_ctrl_wr; wire [ADDR_WIDTH-1:0] dut_ctrl_addr; wire [32*BURST_LEN-1:0] dut_ctrl_wdata; wire [4*BURST_LEN-1:0] dut_ctrl_wmask; act_tile_fetch #( .DATA_WIDTH(DATA_WIDTH), .P_IN(P_IN), .BURST_LEN(BURST_LEN), .ADDR_WIDTH(ADDR_WIDTH) ) u_dut ( .clk(clk), .rst(rst), .req(req), .base_a(base_a), .base_b(base_b), .tcnt(tcnt), .valid(valid), .data_a(data_a), .data_b(data_b), .mem_active(req_active_dut), .mem_grant(mem_grant), .ctrl_req(dut_ctrl_req), .ctrl_wr(dut_ctrl_wr), .ctrl_addr(dut_ctrl_addr), .ctrl_wdata(dut_ctrl_wdata), .ctrl_wmask(dut_ctrl_wmask), .ctrl_rdata(ctrl_rdata), .ctrl_ready(ctrl_ready), .ctrl_busy(ctrl_busy) ); // ---- preload path: direct access to the memory model, bypassing // act_tile_fetch.v entirely, same "pre_active" mux pattern as // every other testbench in this project ---- reg pre_active; reg pre_req, pre_wr; reg [ADDR_WIDTH-1:0] pre_addr; reg [32*BURST_LEN-1:0] pre_wdata; assign ctrl_req = pre_active ? pre_req : dut_ctrl_req; assign ctrl_wr = pre_active ? pre_wr : dut_ctrl_wr; assign ctrl_addr = pre_active ? pre_addr : dut_ctrl_addr; assign ctrl_wdata = pre_active ? pre_wdata : dut_ctrl_wdata; assign ctrl_wmask = pre_active ? {(4*BURST_LEN){1'b0}} : dut_ctrl_wmask; task automatic mem_write_burst(input [ADDR_WIDTH-1:0] word_addr, input [32*BURST_LEN-1:0] data); begin @(posedge clk); while (ctrl_busy) @(posedge clk); pre_req = 1'b1; pre_wr = 1'b1; pre_addr = word_addr; pre_wdata = data; @(posedge clk); pre_req = 1'b0; while (!ctrl_ready) @(posedge clk); end endtask function automatic signed [7:0] act_byte(input integer base, input integer t, input integer k); act_byte = $signed(8'((base*13 + t*31 + k*7 + 5) & 8'hFF)); endfunction integer errors, tests; task automatic check(input cond, input [255:0] name); begin tests = tests + 1; if (!cond) begin errors = errors + 1; $display("FAIL: %0s", name); end else $display("PASS: %0s", name); end endtask task automatic do_fetch(input [ADDR_WIDTH-1:0] ba, input [ADDR_WIDTH-1:0] bb, input [15:0] tc); begin @(posedge clk); base_a <= ba; base_b <= bb; tcnt <= tc; req <= 1'b1; @(posedge clk); req <= 1'b0; while (!valid) @(posedge clk); @(posedge clk); end endtask reg signed [DATA_WIDTH*P_IN-1:0] exp_a, exp_b; integer k, wi; reg [32*BURST_LEN-1:0] burst; initial begin errors = 0; tests = 0; rst = 1; pre_active = 1'b1; pre_req = 0; pre_wr = 0; pre_addr = 0; pre_wdata = 0; req = 0; base_a = 0; base_b = 0; tcnt = 0; repeat(5) @(posedge clk); rst = 0; @(posedge clk); while (ctrl_busy) @(posedge clk); $display("=== preload 2 bursts/lane, 4 tiles packed per burst (EXP-0084 layout) ==="); // lane A base = 0, lane B base = 100 (arbitrary, word-address units). // burst pair p holds tiles 4p, 4p+1, 4p+2, 4p+3 in quarters 0..3. for (wi = 0; wi < 2; wi = wi + 1) begin // wi = burst index (0 -> tiles 0-3, 1 -> tiles 4-7) for (k = 0; k < 4; k = k + 1) burst[k*64 +: 64] = {act_byte(0, 4*wi+k, 7), act_byte(0, 4*wi+k, 6), act_byte(0, 4*wi+k, 5), act_byte(0, 4*wi+k, 4), act_byte(0, 4*wi+k, 3), act_byte(0, 4*wi+k, 2), act_byte(0, 4*wi+k, 1), act_byte(0, 4*wi+k, 0)}; mem_write_burst(0 + wi*BURST_LEN, burst); for (k = 0; k < 4; k = k + 1) burst[k*64 +: 64] = {act_byte(100, 4*wi+k, 7), act_byte(100, 4*wi+k, 6), act_byte(100, 4*wi+k, 5), act_byte(100, 4*wi+k, 4), act_byte(100, 4*wi+k, 3), act_byte(100, 4*wi+k, 2), act_byte(100, 4*wi+k, 1), act_byte(100, 4*wi+k, 0)}; mem_write_burst(100 + wi*BURST_LEN, burst); end @(posedge clk); pre_active = 1'b0; $display("=== TEST 1: fetch tile 0 (quarter 0), both lanes ==="); do_fetch(25'd0, 25'd100, 16'd0); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k); for (k = 0; k < P_IN; k = k + 1) exp_b[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(100, 0, k); check(data_a === exp_a, "T1: lane A tile 0 bit-exact"); check(data_b === exp_b, "T1: lane B tile 0 bit-exact"); $display("=== TEST 2: fetch tile 1 (quarter 1, SAME burst address as tile 0) ==="); do_fetch(25'd0, 25'd100, 16'd1); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 1, k); for (k = 0; k < P_IN; k = k + 1) exp_b[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(100, 1, k); check(data_a === exp_a, "T2: lane A tile 1 bit-exact"); check(data_b === exp_b, "T2: lane B tile 1 bit-exact"); $display("=== TEST 2b: fetch tile 2 (quarter 2, SAME burst) ==="); do_fetch(25'd0, 25'd100, 16'd2); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 2, k); check(data_a === exp_a, "T2b: lane A tile 2 bit-exact"); $display("=== TEST 2c: fetch tile 3 (quarter 3, SAME burst) ==="); do_fetch(25'd0, 25'd100, 16'd3); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 3, k); check(data_a === exp_a, "T2c: lane A tile 3 bit-exact"); $display("=== TEST 3: fetch tile 4 (quarter 0, NEW burst address) ==="); do_fetch(25'd0, 25'd100, 16'd4); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 4, k); check(data_a === exp_a, "T3: lane A tile 4 bit-exact (new burst)"); $display("=== TEST 4: back-to-back fetches, cycling through all 4 quarters ==="); do_fetch(25'd0, 25'd100, 16'd0); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 0, k); check(data_a === exp_a, "T4a: back-to-back fetch 1 (tile 0, quarter 0), lane A correct"); do_fetch(25'd0, 25'd100, 16'd2); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 2, k); check(data_a === exp_a, "T4b: back-to-back fetch 2 (tile 2, quarter 2), lane A correct"); do_fetch(25'd0, 25'd100, 16'd7); for (k = 0; k < P_IN; k = k + 1) exp_a[k*DATA_WIDTH +: DATA_WIDTH] = act_byte(0, 7, k); check(data_a === exp_a, "T4c: back-to-back fetch 3 (tile 7, quarter 3, new burst), lane A correct"); $display("=== %0d/%0d tests, %0d errors ===", tests-errors, tests, errors); if (errors == 0) $display("ALL TESTS PASSED (tb_act_tile_fetch)"); $finish; end endmodule