Files
FPGA-Neural/hardware/v2/sim/tb_maxtree_n16.v
T
micheleandClaude Sonnet 5 1ce78dff6e exp: N=16 timing closure fixed (EXP-0056), weight-reuse gives real 7.16x memory speedup without DDR3 (EXP-0057)
EXP-0056: N_SLOTS=16 failed timing on LFE5U-85F (23-24MHz vs 64MHz
target). First hypothesis (dependency_manager.v's serial ready-scan)
was wrong but real -- built and verified priority_encoder_lsb.v (a
generic recursive tree encoder) and dependency_manager_fast.v, bit-
exact equivalent to the original, but integrated it made no real
difference (24.26MHz). The real cause, found from nextpnr's own
critical-path report: nms_activation_fill_ctrl_v3.v's balanced max-
tree was only ever extended to N_SLOTS in {1,2,4,8}, silently falling
back to the original slow scan for 16. Added the missing case
(nms_activation_fill_ctrl_v3_n16.v), verified isolated (10017/10017)
and functionally (D-Stress N=16 still 256/256 bit-exact). Real result:
71.01MHz, PASS at 64MHz (single seed so far).

EXP-0057: built layer_weight_buffer.v, a double-buffered per-layer
weight scratchpad (fill one buffer in the background from SDRAM while
compute reads many times from the other -- weight-stationary reuse,
as opposed to D-Stress's own deliberately zero-reuse pattern). Wired
to the real sdram_controller_openrow.v + sdram_model.v, no new
hardware. For the same 32768 bytes of useful data: zero-reuse costs
27048 real cycles, reuse costs 3777 -- 7.16x real measured speedup on
the SAME SDR SDRAM, no DDR3, no clock change. This is the answer to
whether DDR3 is necessary for a workload class that actually has
reuse (e.g. conv-style face recognition, unlike D-Stress) -- it isn't,
at least not for this reason.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 12:01:11 +02:00

73 lines
2.9 KiB
Verilog

`timescale 1ns/1ps
// ============================================================
// EXP-0056 -- isolated correctness check for the N_SLOTS==16
// balanced max-tree added to nms_activation_fill_ctrl_v3_n16.v,
// against the SAME flat-scan reference the original module's own
// GEN_MAXTREE_FALLBACK uses (the two must always agree -- that
// fallback path is explicitly documented as "correct but not
// optimized", i.e. the golden reference for what ANY replacement
// must compute). Random 16-way max over 10000 vectors.
// ============================================================
module tb;
reg [15:0] v [0:15];
integer i, k, seed, errors, tests;
reg [15:0] ref_max;
// exact mirror of nms_activation_fill_ctrl_v3_n16.v's own
// GEN_MAXTREE_N16 combinational tree
wire [15:0] m0 = (v[0] > v[1]) ? v[0] : v[1];
wire [15:0] m1 = (v[2] > v[3]) ? v[2] : v[3];
wire [15:0] m2 = (v[4] > v[5]) ? v[4] : v[5];
wire [15:0] m3 = (v[6] > v[7]) ? v[6] : v[7];
wire [15:0] m4 = (v[8] > v[9]) ? v[8] : v[9];
wire [15:0] m5 = (v[10] > v[11]) ? v[10] : v[11];
wire [15:0] m6 = (v[12] > v[13]) ? v[12] : v[13];
wire [15:0] m7 = (v[14] > v[15]) ? v[14] : v[15];
wire [15:0] m01 = (m0 > m1) ? m0 : m1;
wire [15:0] m23 = (m2 > m3) ? m2 : m3;
wire [15:0] m45 = (m4 > m5) ? m4 : m5;
wire [15:0] m67 = (m6 > m7) ? m6 : m7;
wire [15:0] m0123 = (m01 > m23) ? m01 : m23;
wire [15:0] m4567 = (m45 > m67) ? m45 : m67;
wire [15:0] max_final = (m0123 > m4567) ? m0123 : m4567;
initial begin
errors = 0; tests = 0; seed = 32'hA5A5F00D;
// targeted: all-zero, max at every single position
for (k = 0; k < 16; k = k + 1) v[k] = 16'h0;
#1; tests = tests + 1;
if (max_final !== 16'h0) begin
$display("FAIL all-zero: got %0d", max_final); errors = errors + 1;
end
for (i = 0; i < 16; i = i + 1) begin
for (k = 0; k < 16; k = k + 1) v[k] = 16'h1;
v[i] = 16'hFFFF;
#1; tests = tests + 1;
if (max_final !== 16'hFFFF) begin
$display("FAIL max-at-%0d: got %0d", i, max_final); errors = errors + 1;
end
end
// random
for (i = 0; i < 10000; i = i + 1) begin
ref_max = 16'h0;
for (k = 0; k < 16; k = k + 1) begin
v[k] = $random(seed);
if (v[k] > ref_max) ref_max = v[k];
end
#1;
tests = tests + 1;
if (max_final !== ref_max) begin
$display("FAIL random iter=%0d: expected %0d got %0d", i, ref_max, max_final);
errors = errors + 1;
end
end
$display("=== %0d/%0d passed, %0d errors ===", tests-errors, tests, errors);
if (errors == 0) $display("ALL TESTS PASSED (tb_maxtree_n16)");
$finish;
end
endmodule