# V2 experiments log -- REGISTRO PRINCIPALE (docs/v2-description.md §25). # Un EXP-XXXX per ogni esperimento end-to-end (config -> sim/synth/timing -> # risultato). ID mai riutilizzati, anche per i FAIL. Ogni EXP rimanda a # reports/experiments/EXP-XXXX/ (experiment.yaml, simulation.log, # synthesis.log, timing.log, results.txt, notes.md). # # Nessun esperimento ancora eseguito. EXP-0001 timestamp: 2026-09-05T12:03:12Z git_commit: 07a48e401f56d395f9d1a6e22f1b5ebc6b598e64 (+ uncommitted M1 work) session: v2-M1-neural-processor module: hardware/v2/rtl/neural_processor.v configuration: DATA_WIDTH=8, P_IN=8, ACC_WIDTH=32 action: first M1 implementation -- 8-stage pipelined perceptron unit, bit-exact vs hardware/v1/rtl/neuron_parallel.v + mac8.v + mac_unit.v command (sim, Verilator): verilator --binary --timing -j 0 -Wno-fatal --top-module tb -o /tmp/vtb_np hardware/v1/rtl/neuron_parallel.v hardware/v1/rtl/mac8.v hardware/v1/rtl/mac_unit.v hardware/v2/rtl/neural_processor.v hardware/v2/sim/tb_neural_processor.v && /tmp/vtb_np command (synth): yosys -p "synth_ecp5 -json hardware/v2/synthesis/neural_processor_p8/top.json -top neural_processor" hardware/v2/rtl/neural_processor.v command (timing): nextpnr-ecp5 --45k --package CABGA381 --speed 8 --freq 80 --json hardware/v2/synthesis/neural_processor_p8/top.json --lpf-allow-unconstrained --textcfg .../top.config result: SIMULATED: 7/7 tests PASS, bit-exact vs V1 (functional + extreme-INT8 + back-to-back-tiles + 64-input/8-tile coverage). SYNTHESIZED: 0 CHECK problems, 8 MULT18X18D, 533 TRELLIS_FF, 96 CCU2C, 55 LUT4 (36 benign integer-loop-variable warnings, see hardware/v2/logs/synthesis.log). POST-P&R: Fmax = 183.12 MHz, PASS at 80MHz target (real nextpnr-ecp5 measurement, hardware/v2/synthesis/neural_processor_p8/ nextpnr.log). Compare V1 isolated PARALLEL=8: 61.71 MHz (hardware/v1/synthesis/p8/) -- ~3x higher Fmax for the pipelined single-processor datapath alone (not yet a system-level comparison -- no Memory Manager/Director/multi-processor overhead included at this milestone). errors: ERR-0001, ERR-0002, ERR-0003 (see errors.log) encountered and resolved/worked around during development. decision: see decisions.log DEC-0002, DEC-0003, DEC-0004. next_action: EXP-0002 (ACC_WIDTH=24 comparison), then M2 (Processor Array, N_PROCESSORS sweep). EXP-0002 timestamp: 2026-09-05T12:03:12Z git_commit: 07a48e401f56d395f9d1a6e22f1b5ebc6b598e64 (+ uncommitted M1 work) session: v2-M1-neural-processor module: hardware/v2/rtl/neural_processor.v configuration: DATA_WIDTH=8, P_IN=8, ACC_WIDTH=24 (vs EXP-0001's ACC_WIDTH=32 baseline) -- requested explicitly by the user as a test variant ("come test nel perceptrone crea una versione con accumulatori a 24 bit anziche' a 32 bit"). reason: ACC_WIDTH is already fully parametric throughout neural_processor.v (no code duplication needed); 24 bits is a real, non-degenerate choice -- worst-case realistic accumulation (e.g. 256 INT8 inputs all at +-127/+-128) needs at most 23 signed bits (256 * 128 = 32768, |value| <= 2^22 for the largest single-tile- chain sums this project's configs reach), so 24 bits carries a legitimate 1-bit margin, not an arbitrary/unsafe truncation. command (sim, Verilator): same as EXP-0001 with a copy of tb_neural_processor.v with ACC_WIDTH localparam changed to 24 (V1's own neuron_parallel instantiated at ACC_WIDTH=24 too, for a true bit-exact comparison at the SAME width -- V1's ACC_WIDTH is also a module parameter, no V1 modification needed). command (synth): yosys -p "read_verilog hardware/v2/rtl/neural_processor.v; chparam -set ACC_WIDTH 24 neural_processor; synth_ecp5 -json hardware/v2/synthesis/neural_processor_p8_acc24/top.json -top neural_processor" command (timing): nextpnr-ecp5, same flags as EXP-0001, json from the ACC_WIDTH=24 build. result: SIMULATED: 7/7 tests PASS, bit-exact vs V1 at ACC_WIDTH=24 (no overflow in any test vector, as expected from the margin analysis above). SYNTHESIZED: 0 CHECK problems, 8 MULT18X18D (unchanged, multiplier width is DATA_WIDTH-driven not ACC_WIDTH-driven), 509 TRELLIS_FF (was 533 at ACC_WIDTH=32, -24 FF as expected for 8 fewer accumulator bits carried through ~3 pipeline-stage copies), 88 CCU2C (was 96), 49 LUT4 (was 55). POST-P&R: Fmax = 176.21 MHz (was 183.12 MHz at ACC_WIDTH=32) -- REAL measurement, both from nextpnr-ecp5. The 24-bit build is ~4% SLOWER despite fewer resources -- almost certainly placement noise (consistent with this project's established finding, hardware/v1/docs/WORKLOG.md "Timing closure", that seed-to-seed placement variance on this device dominates small logic-width differences), NOT attributed to a real architectural effect without a seed sweep to confirm. Reported as-measured, not overinterpreted -- see hardware/v2/logs/benchmark.log. errors: none. decision: ACC_WIDTH=32 remains the M1 default (matches V1 exactly for ongoing bit-exact comparisons); ACC_WIDTH=24 confirmed as a viable, correctly-functioning, slightly-smaller alternative, not adopted as default without a proper seed sweep (out of scope for this single comparison run). next_action: revisit ACC_WIDTH choice during M10 (Optimization) with a real seed sweep, not before. EXP-0003 timestamp: 2026-09-05T14:30:00Z git_commit: dc0b331 (+ uncommitted M2 work) session: v2-M2-processor-array module: hardware/v2/rtl/neural_processor_array.v configuration: N_PROCESSORS in {1,2,4,8}, P_IN=8, ACC_WIDTH=32 each action: M2 -- functional array + real N_PROCESSORS resource/timing sweep command (sim, Verilator): verilator --binary --timing -j 0 -Wno-fatal --top-module tb -o /tmp/vtb_array hardware/v2/rtl/neural_processor.v hardware/v2/rtl/neural_processor_array.v hardware/v2/sim/tb_neural_processor_array.v && /tmp/vtb_array command (synth/timing): see hardware/v2/synthesis/harness_n{1,2,4,8}/ (yosys.log, nextpnr.log) -- synthesized via hardware/v2/synthesis/harness_neural_processor_array.v, a synthesis-only timing harness (see its own header comment and errors.log ERR-0005 for why the array cannot be synthesized as a bare top-level module beyond N_PROCESSORS=1 without it). result: SIMULATED (N_PROCESSORS=4, tb_neural_processor_array.v): 7/7 tests PASS -- single-processor sanity, 4 processors launched the SAME cycle with different tile counts (finish at different times, proving true concurrency), and a staggered-start test (processor 1 launched mid-way through processor 0's 6-tile job, both complete correctly and independently, confirming §18/§34's "un processor bloccato non deve bloccare gli altri"). SYNTHESIZED (resource scaling, harness): perfectly linear in N_PROCESSORS, confirming no unintended resource sharing: N=1: LUT=59 FF=409 MULT18X18D=8 CCU2C=96 N=2: LUT=106 FF=786 MULT18X18D=16 CCU2C=192 N=4: LUT=207 FF=1540 MULT18X18D=32 CCU2C=384 N=8: LUT=374 FF=3048 MULT18X18D=64 CCU2C=768 0 CHECK problems in every configuration. POST-P&R (real nextpnr-ecp5, --45k --package CABGA381 --speed 8 --freq 80): Fmax PASS at 80MHz in every configuration: N=1: 159.11 MHz N=2: 149.59 MHz N=4: 151.01 MHz N=8: 134.70 MHz Fmax decreases gently with N (routing congestion), never close to failing the 80MHz target up to N=8. REAL RESOURCE CEILING FOUND (not assumed, measured via nextpnr's own device utilisation report): MULT18X18D usage is 22%/44%/88% of the LFE5U-45F's 72 total DSPs at N=2/4/8 respectively, while LUT4/FF usage stays under 6% even at N=8. **DSP (not LUT/FF/routing) is the first hard ceiling as N_PROCESSORS grows at P_IN=8** -- N_PROCESSORS=9 would already exceed the device's 72 MULT18X18D budget at P_IN=8, before accounting for any multipliers the rest of a real system (Memory Manager, PSRAM path, etc.) might need. See decisions.log DEC-0005 and benchmark.log. errors: ERR-0005 (toplevel-pin-count synthesis artifact, worked around with the timing harness -- see errors.log); a first harness attempt fed every processor and every MAC lane identical LFSR-derived data, which Yosys correctly (from pure logic-equivalence) collapsed via CSE down to 1 processor's worth of multipliers regardless of N -- fixed by giving each processor AND each of its P_IN MAC lanes a distinct bit-rotated data source, confirmed by the corrected, properly-linear MULT18X18D counts above. decision: see decisions.log DEC-0005 (DSP is the binding constraint, not LUT/FF -- informs how the N_PROCESSORS x P_IN trade-off should be explored going forward). next_action: M3 -- activation_buffer.v / weight_buffer.v / result_buffer.v. EXP-0004 timestamp: 2026-09-05T15:15:00Z git_commit: 3026dcd (+ uncommitted M3 work) session: v2-M3-buffers module: hardware/v2/rtl/activation_buffer.v, weight_buffer.v, result_buffer.v configuration: activation_buffer/result_buffer DEPTH in {4096, 256}; weight_buffer DEPTH (in tiles) in {512, 64}, P_IN=8, DATA_WIDTH=8 (TILE_WIDTH=64 bits) action: M3 -- three parametric dual-port BRAM-inferring buffers (Input/Weight/Result of the §12 data-plane diagram), reusing the proven inference idiom from hardware/v1/rtl/act_buffer.v (sync write, sync REGISTERED read, no reset on the read register). command (sim, Verilator): verilator --binary --timing -j 0 -Wno-fatal --top-module tb -o /tmp/vtb_buffers hardware/v2/rtl/ activation_buffer.v hardware/v2/rtl/weight_buffer.v hardware/v2/rtl/result_buffer.v hardware/v2/sim/tb_buffers.v && /tmp/vtb_buffers command (synth): yosys -p "read_verilog ; chparam -set DEPTH ; synth_ecp5 -json .../top.json -top " for each of the 6 (module, depth) combinations. command (timing): nextpnr-ecp5 --45k --package CABGA381 --speed 8 --freq 80 --lpf-allow-unconstrained, default-depth configs only. result: SIMULATED: 10/10 tests PASS -- write-then-read correctness incl. extreme INT8 (-128/127) round-tripping exactly, back-to-back writes to different addresses not disturbing earlier entries, and weight_buffer's full 64-bit TILE_WIDTH round-tripping (not just one lane). SYNTHESIZED (real BRAM mapping, not assumed): activation_buffer DEPTH=4096: 2 DP16KD, 37 LUT4, 30 FF activation_buffer DEPTH=256: 1 DP16KD, 21 LUT4, 26 FF weight_buffer DEPTH=512: 2 DP16KD, 88 LUT4, 139 FF weight_buffer DEPTH=64: 2 DP16KD, 73 LUT4, 136 FF result_buffer DEPTH=4096: 2 DP16KD, 37 LUT4, 30 FF result_buffer DEPTH=256: 1 DP16KD, 21 LUT4, 26 FF 0 CHECK problems in all 6 configurations -- every one correctly inferred DP16KD block RAM, none fell back to LUT-RAM. REAL, NON-OBVIOUS FINDING (§14: "non assumere che buffer piu' grandi siano automaticamente migliori"): shrinking weight_buffer's DEPTH 8x (512->64) did NOT reduce DP16KD usage at all (2->2) -- its 64-bit TILE_WIDTH (P_IN*DATA_WIDTH), not its depth, is what forces 2 physical block RAMs (a single DP16KD's usable port width in the density this needs is narrower than 64 bits). Depth-only buffer sizing decisions are the wrong lever for THIS buffer; width (P_IN) is. activation_buffer/result_buffer, being byte-wide, DO show the expected depth-proportional DP16KD count (2 -> 1). POST-P&R (default-depth configs): all PASS at 80MHz with large margin (287-367 MHz range, real place&route) -- these buffers are not a timing concern in isolation. errors: none. decision: keep DEPTH parametric as specified, but document (this entry + benchmark.log) that weight_buffer's real BRAM cost is driven by P_IN (its width), not DEPTH -- relevant when M4/M9 size these buffers against a real workload. next_action: M4 -- memory_manager.v + prefetch_engine.v (wires these three buffers + the array together, PSRAM backend reused unmodified from V1 per §15). EXP-0005 timestamp: 2026-09-05T16:00:00Z git_commit: 5f0d7f1 (+ uncommitted M4 work) session: v2-M4-memory-manager module: hardware/v2/rtl/memory_manager.v, prefetch_engine.v configuration: P_IN=8, ADDR_WIDTH=23, real V1 backend chain (int8_memory_access -> memory_interface -> psram_controller -> psram_model, ALL unmodified), real M1 neural_processor action: M4 -- end-to-end integration: memory_manager double-buffers prefetch of X/W tiles from PSRAM, feeds a real neural_processor, writes the computed result back to PSRAM. command (sim, Verilator): verilator --binary --timing -j 0 -Wno-fatal --top-module tb -o /tmp/vtb_mm hardware/v1/rtl/int8_memory_access.v hardware/v1/rtl/memory_interface.v hardware/v1/rtl/psram_controller.v hardware/v1/sim/psram_model.v hardware/v2/rtl/neural_processor.v hardware/v2/rtl/prefetch_engine.v hardware/v2/rtl/memory_manager.v hardware/v2/sim/tb_memory_manager.v && /tmp/vtb_mm command (synth): yosys -p "synth_ecp5 -json .../top.json -top memory_manager" hardware/v2/rtl/memory_manager.v hardware/v2/rtl/prefetch_engine.v (standalone, for real resource counts); harness_memory_manager.v (see errors.log ERR-0005 pattern) for real P&R Fmax, since the bare module exceeds the device's TRELLIS_IO budget as a top-level (same class of artifact as the Processor Array, not a logic limit). result: SIMULATED: 3/3 jobs PASS end-to-end -- 3-tile (bias=0, ACT_RELU, saturates to 127), 1-tile (no saturation, y=32), and 5-tile (steady-state double-buffer swap across more than 2 tiles, y=40) -- each verified by an INDEPENDENT PSRAM read-back of the written result byte (not just internal signal inspection), with "poison" bytes surrounding the real operand regions to catch any off-by-one addressing (none found). Cycle counts (real, PSRAM power-up already excluded): 3-tile job = 446 cycles, 1-tile job = 166 cycles, 5-tile job = 728 cycles. Roughly ~140-150 cycles/tile at this PSRAM timing (dominated by the real ~70ns TAA access latency modeled in psram_model.v, not by memory_manager's own control overhead) -- a real, measured number, not estimated. SYNTHESIZED (standalone, real resource count): 0 CHECK problems, 851 LUT4, 789 TRELLIS_FF, 108 CCU2C, 0 DSP (expected -- no multiplication in this module). POST-P&R (via harness, real Fmax): 165.86 MHz, PASS at 80MHz with large margin. errors: ERR-0006 (three real RTL bugs found and fixed during bring-up -- see errors.log for full detail: a missing single-in-flight- request discipline, a one-cycle pf_busy blind spot, and an off-by-one state mux for the write-back path). ERR-0005's pin-count artifact recurred for this module too (worked around the same way). decision: see decisions.log DEC-0006 (single prefetch engine + pending register is sufficient for this milestone's scope; a real backend arbiter is deferred until multiple processors/jobs actually need to share one memory_manager). next_action: M5 -- neural_director.v (first-free scheduling), wiring job dispatch to potentially multiple (memory_manager, neural_ processor) pairs instead of the single hardcoded pair tested here.