Commit Graph
133 Commits
Author SHA1 Message Date
micheleandClaude Sonnet 5 fc0130e2e2 docs: LaTeX chapters for hardware, chip architecture, and tests/timing
Three chapters (docs/latex/), sourced from the real project docs
(BOM.md, PHYSICAL_REALIZATION.md, ARCHITECTURE_ANALYSIS.md,
experiments.log) -- no invented numbers:

- hardware.tex: board components, DDR3/FPGA/flash/ESP32, real pinout,
  SPI protocol, boot procedure.
- architecture.tex: packed INT8 MAC core, N=8 hybrid systolic
  architecture (shared-weight broadcast), hierarchical arbiter,
  result writeback, why N=8 is the DDR3-bandwidth-bound sweet spot.
- tests_timing.tex: verification methodology, real functional test
  results table, full real P&R signoff history (N=2 through N=16),
  the definitive N=8 signoff (WNS=0.000ns), and an honestly-caveated
  ESP32-S3 comparison (real historical ECP5 measurement vs. the
  superseded, never-verified Artix-7 projection).

main.tex ties the three together as report chapters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 08:54:25 +02:00
micheleandClaude Sonnet 5 264950923b feat: N=8 hybrid systolic promoted to the real, definitive deployment target (EXP-0096)
New n8_system_ddr3_top.v: a real, permanent, named top-level (not a
build-time -generic override), byte-for-byte the same RTL as
n16_system_ddr3_top.v with N_GROUPS defaulting to 2. Real, full P&R
under this file's own name reproduces EXP-0095's own generic-override
result exactly: WNS=0.000ns, WHS=+0.017ns, 0 failing setup endpoints,
64 DSP48E1/26.7%, 12535 LUTs/19.77%.

New tb_n8_system_ddr3.v (real DDR3-model methodology, adapted from
tb_n16_system_ddr3.v, M=16 positions covering every one of the 2
groups x 4 PEs x 2 lanes exactly once): real functional xsim, 16/16
PASS, 0 errors -- closes the real functional-verification gap this
specific N previously had.

N=8 is now BOTH functionally verified AND timing-closed under its own
permanent name -- the real, definitive deployment target. N=2 kept as
a documented, valid fallback; N=16 kept as documented, functionally-
verified-but-not-timing-closed future work, not abandoned.

docs/PHYSICAL_REALIZATION.md, docs/ARCHITECTURE_ANALYSIS.md,
docs/PINOUT.md updated to reflect N=8 as the current real signoff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 08:29:04 +02:00
micheleandClaude Sonnet 5 6d06404428 feat: real N=4/N=8 timing curve -- N=8 REALLY CLOSES (0 failing endpoints, EXP-0095)
Fixed a real, previously-untested N_GROUPS=1 edge case in
neural_director_grouped.v (bare $clog2(N_GROUPS) invalid for
N_GROUPS=1, same class of bug sdram_arbiter_n.v's own SELW guard
already handles -- applied the same fix here and in
n16_system_ddr3_top.v's own job_out_group_w wire).

Real synth_design -generic N_GROUPS=<n> P&R sweep (same RTL/arbiter as
EXP-0094's real N=16 result):
  N=4  (N_GROUPS=1): WNS=-0.005ns, 2 failing endpoints
  N=8  (N_GROUPS=2): WNS=0.000ns, 0 failing endpoints -- REALLY CLOSED
  N=16 (N_GROUPS=4): WNS=-0.338ns, 60 failing endpoints (EXP-0094)

N=8 is a real, new, closed P&R signoff -- 8x N=2's parallelism with
the same already-verified systolic RTL. Answers the user's own
question directly: the design does not fail until somewhere between
N=8 and N=16, not at N=4 or N=8.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 07:56:19 +02:00
micheleandClaude Sonnet 5 67ccb420d6 docs: real P&R directive tuning closes most of remaining N=16 timing gap (EXP-0094 cont.)
Same RTL, real Vivado strategy directives (Explore/ExtraNetDelay_high/
AggressiveExplore, zero RTL risk): WNS -0.646ns -> -0.338ns, TNS
-97.5ns -> -5.3ns, failing endpoints 771 -> 60. Remaining bottleneck
confirmed intrinsic (84% logic delay, DSP48E1->CARRY4 path inside
neural_processor_packed.v, recurring per-PE) -- unlikely to shrink
further via P&R strategy alone.

Cumulative: two safe fixes (hierarchical arbiter + directive tuning)
close ~89% of the original TNS gap and ~63% of WNS, neither touching
neural_processor_packed.v. Deliberate stop: closing the rest needs
that shared, load-bearing module touched -- a larger, more careful
step (verify against both N=2 and N=16) documented as next_action,
not attempted without further direction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 02:39:45 +02:00
micheleandClaude Sonnet 5 9e1f16db47 feat: real hierarchical 2-level arbiter, real N=16 timing WNS -0.913ns -> -0.646ns (EXP-0094)
sdram_arbiter_hier.v: fixes EXP-0093's own real, traced P&R timing
failure (flat 21-way req_wdata mux, route-delay-dominated). Reuses
sdram_arbiter_n.v unmodified, twice: 4 leaf instances (NUM_REQ=5, one
per group) + 1 top instance (NUM_REQ=5: 4 groups + host, host
bypassed/unpipelined), one real pipeline register stage between
levels. Isolated verification (tb_sdram_arbiter_hier.v): 23/23 PASS.

Two real bugs found and fixed via signal tracing: a testbench helper
not waiting for grant before firing req, and a genuine RTL lost-pulse
bug at the leaf-to-top boundary (a transient one-shot request could be
dropped if the top level was busy with a different group) -- fixed
with a sticky per-group pending_req_r latch.

Wired into n16_system_ddr3_top.v (drop-in). Real, full P&R re-run:
WNS improved -0.913ns -> -0.646ns, TNS -690ns -> -97.5ns, failing
endpoints 3021 -> 771 -- substantial, measured improvement, confirming
the arbiter was correctly root-caused (bottleneck moved elsewhere:
neural_processor_packed.v's own already-thin-margin MAC datapath,
eroded by N=16's higher overall congestion). Functional xsim still
32/32 PASS. Timing not yet fully closed -- real next steps documented,
not yet attempted without further direction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 02:25:44 +02:00
micheleandClaude Sonnet 5 50c940a41d docs: real, full P&R for N=16 hybrid systolic system -- timing NOT met (EXP-0093)
Real place_design/route_design against XC7A100T-CSG324-2: post-route
utilization holds (128 DSP48E1/53.33%, matching EXP-0091's projection),
but real timing fails on clk_pll_i (155.039MHz): WNS=-0.913ns,
WHS=+0.029ns, 3021 failing setup endpoints. Root-caused to
sdram_arbiter_n.v's req_wdata mux, now a real 20/21-way select (up
from 3-way at N=2) feeding mig_native_adapter.v's wdata_lat_reg.

N=16 is functionally correct (EXP-0092) but not yet timing-closed --
honestly not ready for real hardware at the target clock. N=2
(EXP-0088) remains the trustworthy, deployable signoff. Real options
for closing timing documented as next_action, not yet attempted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 00:41:17 +02:00
micheleandClaude Sonnet 5 996dda3415 feat: real functional xsim verification of N=16 hybrid systolic system (EXP-0092)
tb_n16_system_ddr3.v, adapted from tb_n2_system_ddr3.v's own real DDR3-
model methodology (real mig_7series_0_mig, real 2-chip ddr3_model.sv,
real Vivado xsim). Submits 32 positions across all 4 groups of
n16_system_ddr3_top.v's own real neural_director_grouped.v + 4x
systolic_group.v + 20-way arbiter. 32/32 PASS, 0 errors.

This confirms EXP-0091's synthesis-only result (0 errors, 128
DSP48E1/53.33%) reflected real functional correctness, not just
connectivity -- the grouped Director's octet dispatch, the new
arbiter's slot map, and the shared-weight-broadcast barrier all work
correctly wired together at full N=16 scale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 00:31:02 +02:00
micheleandClaude Sonnet 5 4acc669d43 feat: real N=16 hybrid systolic top-level, synthesis-only pass (EXP-0091)
Adds n16_system_ddr3_top.v, directly adapted from n2_system_ddr3_top.v's
own proven structure: same real MIG, spi_host_bridge_v3.v, flash_spi_
master.v, host_mem_bridge.v, all completely unmodified (confirms
EXP-0090's zero-protocol-change finding holds at full N=16 scale). The
real differences: neural_director_grouped.v replaces neural_director_
packed.v, 4x systolic_group.v replace 2x packed_slot.v, and the shared
arbiter grows to a real 21-way NUM_REQ (4 group weight-fetch + 16 PE
activation/writeback + 1 host_mem_bridge).

Real synthesis-only result: 0 Errors, 0 Critical Warnings, 128 DSP48E1
of 240 (53.33%) -- an exact real match to docs/ARCHITECTURE_ANALYSIS.md
S5.6's own original brainstorm DSP projection, now confirmed by real
synthesis instead of estimated.

Found and root-caused a real Vivado project quirk (not an RTL bug --
verified separately via a clean Icarus elaboration with stub modules
for mig_7series_0/STARTUPE2): a fresh add_files + update_compile_order
didn't make synth_design -top <newmodule> find the module. Fixed by
explicitly setting the fileset's own top property before synth_design.
CLAUDE.md updated with this as the real, confirmed procedure for adding
any future brand-new top-level module.

Honestly scoped: synthesis-only (connectivity/width correctness), NOT
yet a real functional xsim test and NOT yet real P&R timing -- both
real, disclosed next steps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 00:12:12 +02:00
micheleandClaude Sonnet 5 bd1fb5dc14 feat: real Director extension for group dispatch + systolic_group.v P&R sanity check (EXP-0090)
Adds neural_director_grouped.v, a direct extension of neural_director_
packed.v's own already-proven 2-position pairing discipline to 8-position
octets (matching systolic_group.v's fixed 4 PEs x 2 lanes). Real,
deliberate finding: the host-facing SPI/WRITE_JOB submission protocol
needs zero changes -- the host just submits 8 jobs sharing a weight base
instead of 2, the same real pattern already required today.

Real out-of-context synthesis of one systolic_group.v: 32 DSP48E1
(13.3%), confirming the original brainstorm's own DSP projection exactly.

Found and fixed two real bugs: (1) a wraparound-arithmetic width bug in
the octet index computation (same class already flagged for address
math elsewhere in this project -- needs N+1 bits before the mod-reduce
compare, not N); (2) a real, generalizable testbench race -- driving
stimulus on the same clock edge the DUT samples on works fine with a
natural gap between pulses (every prior testbench in this project has
one) but silently double-registers data when called back-to-back with
zero gap, confirmed via real signal tracing. Fixed with @(negedge clk)
stimulus; CLAUDE.md's existing blocking/nonblocking testbench-race
lesson extended to cover this new trigger.

Verified via tb_neural_director_grouped.v: 4/4 PASS (octet dispatch +
per-PE addressing, stall-not-mis-dispatch on a mismatched octet, queue
wraparound).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-21 00:05:16 +02:00
micheleandClaude Sonnet 5 932aec2490 feat: real first step of 4x4 hybrid systolic architecture (EXP-0089)
Adds packed_pe.v (packed_slot.v's compute+activation-fetch+writeback
subsystem, reusing ddr_prefetch_mgr.v/neural_processor_packed.v/
result_writeback.v completely unmodified, with its own private
weight-fetch removed) and systolic_group.v (one real layer_prefetch_
ctrl.v+layer_weight_buffer.v+weight_tile_gather.v shared by 4x
packed_pe.v via a real, barrier-synchronized broadcast bus).

Real design choice confirmed with the user before writing any RTL
(AskUserQuestion, concrete topology preview): shared-weight broadcast,
not a literal PE-to-PE systolic shift register -- achieves the real,
quantified rationale (4x reduction in redundant weight-fetch DDR3
traffic per group of 4 PEs) with much lower real risk than genuine
inter-PE pipeline fill/drain.

The real new design is the barrier: each PE's own tcnt is the join key
against the group's broadcast tcnt, self-synchronizing regardless of
which PE is momentarily ahead/behind (e.g. a real DDR3 row-switch
stall on one PE's own activation fetch). Found and fixed a real bug
during verification (not by inspection): the first full test run
reported every result as undefined despite every control-flow signal
tracing correctly -- root-caused via real signal tracing down to a
5-way test arbiter bus mis-sliced at the wrong slot offset (single-bit
handshake buses happened to use a correct range and masked it from the
control-flow trace; only the wide, byte-offset buses were wrong).

Verified in isolation (tb_systolic_group.v, real Icarus xsim, real
sdram_arbiter_n.v generalized to NUM_REQ=5 with zero changes): 8/8
PASS across 2 consecutive group jobs (exercising the barrier's own
per-job reset path, not just cold start).

Deliberately scoped to the isolated mechanism only, per this project's
"one variable at a time" discipline -- Director/SPI job dispatch for
group jobs, a real N=16 top-level, and real P&R are real, disclosed
next steps, not done here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 22:54:52 +02:00
micheleandClaude Sonnet 5 7f9ece12dc docs: real P&R signoff for the result-writeback engine (EXP-0088)
Real in-context P&R confirms the result-writeback engine (EXP-0088)
closes timing with essentially zero cost: WNS=+0.099962ns (vs
EXP-0086's +0.095707ns), WHS=+0.036275ns, 0 failing endpoints. 6642
LUTs (+260 for the new engine), 16 DSP48E1 unchanged. This is now the
current, trustworthy signoff, promoted over EXP-0086/0087's own
pointers in both docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 19:27:00 +02:00
micheleandClaude Sonnet 5 ccaf3ee059 feat: real result-writeback engine, removes the last hard N-scaling pin blocker (EXP-0088)
Adds result_writeback.v, one instance per packed_slot.v, writing each
completed job's result directly into DDR3 at the job's own
result_addr_a/b instead of driving literal top-level pins -- the same
architectural shape as the weight-fetch path, in reverse. job_done now
means "durably in DDR3", not "captured in a register only a pin could
see". n2_system_ddr3_top.v's own s0_result_data_a/b, s1_result_data_a/b
top-level package pins are removed (and the now-dangling XDC constraint
for them), closing the real, hard scaling blocker docs/ARCHITECTURE_
ANALYSIS.md flagged since EXP-0074/0079 (8 bits x 2 lanes x N cores ->
256 pins at N=16).

Addressing reuses the exact same JOB_ADDR_WIDTH->ctrl-bus-word
truncation x_base_a/w_base already use (verified against act_tile_
fetch.v's/layer_prefetch_ctrl.v's own real code, not guessed). The host
reads results back via the already-existing READ_MEM (0x02) SPI opcode
-- no new protocol. A real EXP-0066-class bug (issuing ctrl_req before
mem_grant) was caught and fixed before ever compiling, by re-deriving
the design against act_tile_fetch.v's own proven S_MEMWAIT/S_GAP
sequencing.

Verified two ways: tb_packed_slot.v extended with a real DDR3
read-after-write check (9/9 PASS, confirms the write actually landed,
not just that job_done pulsed); tb_n2_system_ddr3.v re-run via real
xsim to confirm correct behavior under real 2-slot shared-bus
arbitration (8/8 PASS, 0 errors, consistent timing with EXP-0087's own
baseline for this workload).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 19:18:57 +02:00
micheleandClaude Sonnet 5 344e798ad5 feat: real DDRManager re-measurement at 32-bit channel + BOM/pinout docs (EXP-0087)
Re-measured ddr_prefetch_mgr.v's (EXP-0083) real benefit against the
now-closed 32-bit DDR3 channel (EXP-0086), per this project's own
standing plan. Real result: the 2.86% benefit measured at the old
16-bit channel is GONE at 32-bit (WITH: 100663.1335ns vs WITHOUT:
100656.6835ns -- a 0.0064% regression, statistically a wash). The
wider channel's lower per-tile latency already absorbs the gap the
look-ahead prefetch used to hide. Kept wired in for correctness/
timing-neutrality (real P&R already signs off with it included), but
it's no longer a real performance win. Updated docs/ARCHITECTURE_
ANALYSIS.md and docs/PHYSICAL_REALIZATION.md accordingly.

Found and fixed 3 real testbench/simulation-setup bugs along the way:
- tb_n2_system_ddr3.v and tb_mig_native_adapter.v still had a stale
  CLKIN_PERIOD=2900 (the FAILED EXP-0084 clock period) instead of the
  current real, closed 3225ps (EXP-0086).
- tb_n2_system_ddr3.v used SystemVerilog-only $signed(8'(...)) cast
  syntax, invalid for xvlog's default plain-Verilog mode -- fixed via
  an intermediate 8-bit reg.
- Building a fresh sim_1 fileset needs the real MIG simulation
  dependency set added explicitly (mig_7series_0_mig.v is marked
  USED_IN_SIMULATION=0 in the project since testbenches bypass the
  public wrapper); verilog_define is a fileset-level property, not
  per-file, in this Vivado version.

New measurement-only fork (not part of the real synthesis target, per
fork-before-promote discipline): packed_slot_noprefetch.v +
tb_n2_system_ddr3_noprefetch.v, reproducing the pre-EXP-0083 direct
per-tile activation-fetch sequencing for a fair A/B baseline.

Also adds docs/BOM.md and docs/PINOUT.md: a real component list (DDR3
x2, flash, FPGA already verified; clk_ref oscillator and an ESP32-S3-
WROOM-1 module newly verified in-stock on LCSC; sys_clk oscillator
flagged as needing a custom-programmed order, no off-the-shelf SKU at
the required 310.077MHz) and a consolidated, board-layout-ready pinout
extract of PHYSICAL_REALIZATION.md's own pin tables.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 18:37:41 +02:00
michele 678caa9bce docs: real timing closure for 32-bit DDR3 channel (EXP-0086)
Reverting Clock Period to 3225ps (keeping Data Width=32) closes real
timing: WNS=+0.095707ns, WHS=+0.036275ns, 0 failing endpoints, all
user-specified constraints met. This is the real final signoff for the
32-bit DDR3 channel widening effort (EXP-0083 through EXP-0086),
replacing EXP-0083's 16-bit-era baseline as the project's current
trustworthy number. Confirms the EXP-0084 root-cause analysis: the
earlier -0.618ns failure was caused by a separate, stacked clock
speedup, not the width change itself.

Also found and fixed a second, wholesale real occurrence of the
stale-import bug: the MIG wizard regeneration needed to revert Clock
Period silently re-imported the entire v3 RTL tree (9 files) plus the
top XDC back to pre-EXP-0084 stale copies, not just the file(s) the
regeneration touched. CLAUDE.md's lesson extended accordingly.
2026-09-20 18:05:30 +02:00
micheleandClaude Sonnet 5 bdc821222f feat: real active-low data_ready_n sticky IRQ pin (EXP-0085)
User-requested hardware notification so the ESP32 can be
interrupt-driven instead of polling STATUS in a loop.

spi_host_bridge_v3.v: new job_out_done input (wired from
neural_director_packed.v, already available at the top level) and new
data_ready_n output. A sticky irq_pending register sets on job_out_done
(latched, survives the pulse itself deasserting) and clears when the
host completes a real STATUS (0x20) or REG_READ(0x02) transaction -
reusing cs_rose, the same real transaction-complete event the module
already relies on elsewhere, not a new mechanism. dir_error is ORed in
live/combinational, not latched. SET has priority over CLEAR on the
rare cycle both coincide.

Real pin: D14, bank 15 (already 3.3V, alongside the SPI bus and
sys_rst) - tentative, not yet a final board decision. Deliberately
added after EXP-0084's own P&R iterations settled, so it didn't
complicate that already-tight I/O/VCCO budget mid-fix. Its own real
P&R verification is deferred to the next real P&R run (already needed
to close EXP-0084's clock-period timing gap), not run separately
against a config already known to fail timing for unrelated reasons.

Real verification: tb_spi_host_bridge_v3.v extended with 10 new checks
(idle state, sticky set, mid-transaction hold, real-acknowledge clear,
unrelated-register non-acknowledge, dir_error live assert/clear).
49/49 PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 16:33:46 +02:00
micheleandClaude Sonnet 5 9dead54ebf feat: real 32-bit DDR3 channel widening - functionally complete, timing NOT yet closed (EXP-0084)
Real 32-bit DDR3 widening (2x MT41J128M16JT-125:K chips ganged in
parallel, user's own MIG wizard session). Full RTL adaptation across
the shared ctrl bus (16-bit word -> 32-bit word, BURST_LEN=8 unchanged,
burst payload 128->256 bits):

- mig_native_adapter.v: app_wdf_data/app_rd_data 64->128 bits (real,
  confirmed against the regenerated MIG wrapper), beat count unchanged.
- act_tile_fetch.v: real logic change - burst now holds 4 tiles instead
  of 2 (sel_lat extended to 2 registered bits, 4-way case mux instead
  of 2-way ternary, same request-time-registered-select discipline as
  EXP-0081). Not a further bytes/MAC reduction, just what's needed to
  keep 100% packing utilization at the larger burst.
- host_mem_bridge.v: real addressing redesign - host-facing 16-bit-word
  contract kept unchanged (ESP32 firmware unaffected), internally
  translated onto the new 32-bit-native ctrl bus.
- sdram_arbiter_n.v, layer_prefetch_ctrl.v, packed_slot.v,
  ddr_prefetch_mgr.v, n2_system_ddr3_top.v: mechanical width bump plus
  doubled ddr3_dq/dqs/dm pins and the real differential sys_clk/clk_ref
  top-level ports the regenerated MIG now requires.

New burst_mem_model32.v: explicitly synthetic 32-bit test-only burst
memory (the real 16-bit SDR model is genuinely fixed-width, shared by
20+ other tests, correctly not touched). Found and fixed a real
address-aliasing bug in it during bring-up (MEM_ADDR_BITS=16 silently
wrapped a real 0x10000 test address to 0).

Real verification: all isolated testbenches re-verified (10/10, 33/33,
32/32, 7/7, 9/9 PASS), plus real xsim against the real 2-chip DDR3
model (tb_mig_native_adapter.v 12/12 PASS, tb_n2_system_ddr3.v 8/8
PASS, both chips visibly returning different real data).

Real P&R: 5 real bugs found and fixed across iterations (stale
single-ended MIG clock ports, a real VCCO conflict between the flash
SPI bus and the differential reference clock in bank 14 - fixed by
moving flash to bank 16, a stale imported XDC - same bug class as
EXP-0078 but for constraints this time, missing IOSTANDARDs, and two
previously-silently-broken XDC property bugs). Route completes 100%,
but real timing does NOT close: WNS -0.618ns, 213 failing endpoints.

Honest root cause: the violation is inside neural_processor_packed.v's
own packed-MAC accumulation tree, unchanged since EXP-0059 - it has
real margin at the old 155.039MHz ui_clk but not at the new 172.414MHz
the paired clock-period change produced. This is NOT caused by the
32-bit width change itself. Width alone, even at the old clock, already
delivers the full intended 2x bandwidth gain (1.24 -> ~2.48 GB/s) -
width and clock rate are separable levers. Current trustworthy timing
signoff remains EXP-0083 (16-bit, +0.073ns) until the clock period is
reverted toward 3225ps (keeping Data Width=32) in one more real,
user-gated MIG wizard session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 16:28:44 +02:00
micheleandClaude Sonnet 5 376ccb6ee2 docs: capture two more exploratory scaling directions (BRAM cache, ESP32-side scheduling)
S5.6.1: opportunistic BRAM cache for activation tiles - exploits real,
currently 0%-utilized Block RAM to catch whatever locality the workload
happens to have, without committing to a specific reuse pattern the way
the systolic direction does. No cache-invalidation problem given the
current write-once-before-job protocol.

S5.6.2: host-side (ESP32) job-queue reordering - a software-only
"DDRManager" upstream of ddr_prefetch_mgr.v, grouping jobs with nearby
DDR3 addresses before submission to reduce row-switch cost with zero
RTL and zero timing-margin risk. Both marked exploratory, not decided,
not built - same as S5.6's systolic direction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 13:22:17 +02:00
micheleandClaude Sonnet 5 32d32f4c0e docs: capture exploratory 4x4 hybrid systolic scaling direction (not built)
Brainstorming session outcome: instead of flat N=16 independent DDR3
requesters, group cores into 4 weight-stationary systolic chains of 4
PEs each - reduces independent DDR3 contenders from 16 to 4 while
preserving task-level parallelism across the 4 groups. Captured as
docs/ARCHITECTURE_ANALYSIS.md S5.6, explicitly marked exploratory/not
decided/not built - revisit after the 32-bit channel widening and real
N=2/4/8 flat-core scaling tests produce real numbers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 13:18:46 +02:00
micheleandClaude Sonnet 5 fa327b75ca feat: DDRManager phase 1 - single-slot look-ahead activation prefetch (EXP-0083)
New ddr_prefetch_mgr.v wraps act_tile_fetch.v with a depth-2 ping-pong
buffer, issuing the next tile's DDR3 fetch as soon as the fetch engine
is free instead of waiting for packed_slot.v to finish consuming the
current tile. Wired into packed_slot.v's tile loop (job-level start
instead of per-tile req), simplifying the S_TILEWAIT join in the process
(ddrpf_tile_valid is level-held, no separate act_seen latch needed).

Verification: new tb_ddr_prefetch_mgr.v (25/25 PASS after fixing a real
testbench polling race found via iteration-tagged tracing, not an RTL
bug), tb_packed_slot.v re-run unmodified (9/9 PASS, bit-identical
results), tb_n2_system_ddr3.v re-run via real xsim against real
ddr3_model.sv (8/8 PASS). Real P&R: WNS +0.073ns (up from EXP-0082's
+0.068ns), LUTs 5644, DSP48E1 16 unchanged, 0 failing endpoints.

Honest result: real A/B on the actual DDR3 backend (same testbench,
before/after) shows a real but modest 2.86% reduction in total
simulated time - smaller than the original hypothesis suggested, because
neural_processor_packed.v already accepts one operand per cycle, so the
per-tile dead time being removed was already small relative to real DDR3
fetch latency. Docs updated to report this honestly rather than oversell
it; the larger multi-slot DDRManager is deferred pending re-measurement
against the (still pending, user-gated) 32-bit channel widening.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 10:59:45 +02:00
micheleandClaude Sonnet 5 cbd16dd727 docs: sync physical/architecture docs with EXP-0081/0082 reality, add 32-bit vs dual-channel analysis
- PHYSICAL_REALIZATION.md: replace stale "1 tile = 1 burst" layout
  description with the real EXP-0081/0082 "2 tiles = 1 burst" convention;
  add EXP-0082 signoff row and history table.
- ARCHITECTURE_ANALYSIS.md: mark §5.1 (denser activation packing) DONE with
  real re-measured numbers (bandwidth ceiling fraction 25%->50%, WNS
  +0.030->+0.068ns); add §5.4, the real device-data-backed comparison of
  32-bit single-channel widening vs a second independent DDR3 channel
  (decided: 32-bit widening, per real DQS/bank pin-conflict analysis);
  update scaling-path recommendation to reflect the user's final directive
  (widen channel -> build DDRManager -> N=2/4/8/16 tests, N=8 target, N=16
  documentary).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 10:07:16 +02:00
micheleandClaude Sonnet 5 e6768623d6 docs: confirm denser activation packing is real-timing-safe, margin improved (EXP-0082)
Real P&R: WNS +0.068ns (up from +0.030ns), 0 failing endpoints, 5437
LUTs, 16 DSP48E1. Confirms EXP-0081's "register the select bit at
request time" design genuinely kept the fix off the critical path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 10:01:58 +02:00
micheleandClaude Sonnet 5 8ad04987de feat: denser activation packing, real bandwidth ceiling doubled (EXP-0081)
Implements the highest-leverage fix from EXP-0080's bottleneck
analysis: act_tile_fetch.v now packs 2 consecutive tiles per DDR3
burst (even tile low 64 bits, odd tile high 64 bits) instead of 1
tile per burst, halving real DDR3 bytes-per-useful-byte. Timing-safe
by construction: the tile-index select bit is registered at request
time, long before the real DDR3 round-trip completes, never racing
the arriving read data (unlike the runtime part-select pattern
EXP-0079 deliberately avoided).

Re-verified at all 3 levels (isolated engine 8/8, packed_slot.v 9/9
with bit-identical results to EXP-0079, full N=2 system on real DDR3
8/8) -- the real JEDEC trace now shows no half-burst padding, direct
confirmation the fix works in practice, not just in theory.

Also: real device data gathered on this package's I/O bank layout
(only 5 banks total, 14/15/16/34/35) informing the next bandwidth step
(32-bit-wide single controller recommended over a second independent
channel, given the pin/logic cost comparison).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 09:55:16 +02:00
micheleandClaude Sonnet 5 27cf5f36da docs: complete architecture analysis - DDR3 bandwidth is the real ceiling, not DSP count (EXP-0080)
Before building N=4/8/16 core scaling, did the requested full
analysis. Real finding: using measured DDR3 throughput (1.24 GB/s,
from the actual EXP-0079 JEDEC trace) against calculated compute-side
need (4.96 GB/s for one core at peak DSP throughput, given the
current activation memory layout's 2x byte overhead), the system is
memory-bandwidth-bound already at N=1/N=2, not DSP-bound (only 6.67%
DSP used). Scaling core count today would show no real throughput
gain.

docs/ARCHITECTURE_ANALYSIS.md: full module review + ranked
interventions -- result-writeback engine (blocker), denser activation
packing (highest-leverage bandwidth fix), then the user's own proposed
DDRManager/orchestrator-prefetch idea (design-sketched, grounded in
neural_director_packed.v's existing job queue, explicitly scoped as
complementary to denser packing, not a substitute for it), THEN
N-scaling with real per-N P&R signoff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 09:44:10 +02:00
micheleandClaude Sonnet 5 43a12379a5 feat: MILESTONE - real activation-fetch engine, full N=2 system verified on real DDR3 (EXP-0079)
Closes the last major disclosed functional gap: packed_slot.v's
activation data was read through a combinational stand-in since
EXP-0062. New act_tile_fetch.v reads activation tiles directly from
DDR3 (no on-chip buffering needed, unlike weights -- activation data
has no reuse), sharing each slot's existing ctrl port with its own
weight-prefetch engine. Real memory layout: one full BURST_LEN=8-word
burst per tile, deliberately avoiding any runtime-indexed part-select
given this project's thin P&R timing margin (EXP-0078).

Verified at three levels: act_tile_fetch.v alone (6/6), packed_slot.v
with real preloaded activation data (9/9), and the full N=2 system
against real DDR3 via xsim (8/8, 0 errors) -- the first time this
project's compute path has been verified end-to-end with real DDR3
for both weights and activations.

Retired hardware/v3/rtl/n2_system_top.v and its testbench (pre-DDR3
SDR-placeholder era, fully superseded by n2_system_ddr3_top.v).

Also: docs/PHYSICAL_REALIZATION.md (real pinout/parts/timing/protocol
reference for the physical board) and CLAUDE.md (persistent project
instructions for future Claude Code sessions).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 08:13:15 +02:00
micheleandClaude Sonnet 5 78577dde59 docs: log real Vivado stale-source bug + final flash-bridge P&R signoff (EXP-0078)
Found and fixed a real Vivado project-management bug: two source
files (n2_system_ddr3_top.v, spi_host_bridge_v3.v) had stale imported
copies (plus a duplicate at a second path) that silently kept being
used in synth+impl despite live edits, making the first "flash bridge
included" P&R run silently synthesize the OLD design (STARTUPE2
Used=0/1 gave it away). Fixed by removing the stale/duplicate entries
and re-adding both files as direct (non-copied) references.

Final real P&R: STARTUPE2 Used=1/1 (confirms the flash bridge is
genuinely placed/routed), timing still closes but margin is now
thinner and real: WNS +0.013ns, WHS +0.032ns, 0 failing endpoints.
5213 LUTs, 16 DSP48E1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 07:24:33 +02:00
micheleandClaude Sonnet 5 a4c080da83 feat: config-flash passthrough bridge via STARTUPE2, real board-exclusive flash access (EXP-0077)
Implements the user's board architecture: config flash wired
exclusively to the FPGA, host (ESP32) reaches it only through the
FPGA. flash_spi_master.v is a plain byte-wide SPI master using
STARTUPE2 to reclaim CCLK after configuration (the real, Xilinx-
documented "indirect SPI flash programming" technique, UG470 p94-96).
New opcode 0x40 FLASH_XFER in spi_host_bridge_v3.v relays bytes
byte-for-byte between host and the physical flash bus -- the host
decides the exact SPI NOR command sequence (verified against the real
W25Q32JV datasheet), this RTL knows nothing about flash semantics.

Found and fixed two real bugs during verification: a byte-assembly
off-by-one in flash_spi_master.v, and a genuine protocol-latency bug
in the FLASH_XFER opcode's response timing (needed 2 trailing margin
bytes, not 1 -- the internal flash transfer doesn't start until the
triggering byte finishes, so 1 byte of margin isn't enough). 39/39
tests pass end to end (host SPI -> bridge -> flash_spi_master ->
behavioral flash model).

Wired into n2_system_ddr3_top.v with real pin constraints (flash_mosi
=K17/flash_miso=K18/flash_cs_n=L13, the same pins reserved-but-unused
in EXP-0075) and BITSTREAM.CONFIG.PERSIST=FALSE made explicit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-19 21:45:17 +02:00
micheleandClaude Sonnet 5 fd6cc7a2fa docs: log final re-verified real P&R signoff after SPI fix + reg file (EXP-0076)
Confirms EXP-0075's fixes (SPI physical-layer bug, register file,
real pin constraints) don't regress timing: WNS +0.056ns (slightly
better than EXP-0074's +0.040ns), 0 failing endpoints, 5173 LUTs,
16 DSP48E1. User confirmed board plan: both JTAG and Master SPI boot
(Winbond W25Q32JVSSIQ config flash, verified in-stock on LCSC) will
be present on the custom PCB.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-19 19:22:32 +02:00
micheleandClaude Sonnet 5 ca17765fe3 fix: real SPI MISO bit-corruption bug found+fixed; add register file + pin plan (EXP-0075)
Found a real, previously-masked bug in spi_host_bridge_v3.v's physical
layer (inherited unchanged from V1/V2): the bit_count==0 MISO bypass
corrupts the last bit of any multi-byte response whose value happens
to end in a 1 -- every prior test's response data coincidentally
ended in 0, hiding it until the new DEVICE_ID register (0x...01)
exposed it via a real bit-exact mismatch. Fixed by removing the
bypass (verified unnecessary for this protocol's actual usage).

Added REG_WRITE/REG_READ opcodes (0x30/0x31) and a register file
(DEVICE_ID/CONTROL/STATUS/N_SLOTS) for general device control beyond
job submission, per explicit user request. Full regression: 38/38
PASS, including new cases specifically targeting the bit-corruption
bug for both REG_READ and READ_MEM.

New hardware/v3/constraints/n2_system_ddr3_top.xdc: reserves the
FPGA's dedicated Master-SPI config-flash pins (found colliding with
auto-placed design ports in the real routed checkpoint) and assigns
the neural-processor management SPI to real, verified-free, edge-
adjacent pins on xc7a100tcsg324-2.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-19 18:31:51 +02:00
micheleandClaude Sonnet 5 835ae881dc feat: MILESTONE - first real in-context P&R, timing closes at 310MHz DDR3 (EXP-0074)
n2_system_ddr3_top.v: the first synthesizable top wiring the real
mig_7series_0 DDR3 controller (public wrapper, real calibration) +
mig_native_adapter.v + sdram_arbiter_n.v (3-way: 2 packed_slot + host
raw-access) + neural_director_packed.v + spi_host_bridge_v3.v.

Real Vivado in-context synth+impl against the actual MIG-generated
XDC (pin locations, DDR3 timing exceptions) on xc7a100tcsg324-2:
route_design 100%, all timing constraints met (WNS +0.040ns, WHS
+0.048ns, 0 failing endpoints), 310.078MHz DDR3 PHY clock / 155.039MHz
compute domain, 5140 LUTs / 5952 regs / 16 DSP48E1 / 0 BRAM.

Fixed three real issues found getting here: a SystemVerilog literal
synth_design can't parse, MIG stub port mismatch (calib_tap_* isn't
exposed in this IP config), and a genuine design mistake -- exposing
packed_slot.v's activation-fetch stand-in ports as literal top-level
pins demanded ~360 I/O against the package's 324 total. Made that
interface internal (stub-driven) instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-19 17:47:40 +02:00
micheleandClaude Sonnet 5 6caea54b8e fix: neural_director_packed.v SV literal for synth; exonerate it from a testbench race (EXP-0073)
Replaced three uses of the SystemVerilog '0 self-sizing literal with
explicit-width zero-fill so the file synthesizes under Vivado's
synth_design (which has no -sv equivalent in this flow), needed while
adding this module to the real in-context P&R project.

Re-running its isolated regression after that edit surfaced 3/8
failures. Root-caused via git stash (reproduces on the untouched
committed file, not caused by this edit) and a DUT-internal $display:
tb_neural_director_packed.v's own submit_job task drove DUT inputs
with blocking assignment across two separate @(posedge clk) waits,
racing the DUT's own always block under Icarus and causing a spurious
duplicate enqueue. Fixed by switching to nonblocking assignment
(race-free by construction). neural_director_packed.v itself was
correct all along - 8/8 tests pass after the testbench fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-19 13:37:25 +02:00
micheleandClaude Sonnet 5 562cf91f1e feat: spi_host_bridge_v3.v, SPI opcode re-audit against real V3 RTL (EXP-0072)
Forked from V2's spi_host_bridge.v after finding two real protocol
mismatches: WRITE_JOB carried dependency-manager fields (required/
producer_ids) that neural_director_packed.v's job_in_* port doesn't
have (no dependency manager exists in V3 -- dropped, disclosed, not
silently ignored), and WRITE_MEM/READ_MEM assumed a word-granularity
host-arb port V3 never had (now wired through host_mem_bridge.v,
EXP-0071). Physical SPI layer carried over unchanged.

Verified standalone: 18/18 tests, 0 errors, including a case
exercising the narrower 25-bit MEM_ADDR_WIDTH's own top bit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 08:56:15 +02:00
micheleandClaude Sonnet 5 786464ee21 feat: host_mem_bridge.v, word<->burst translator for host DDR3 access (EXP-0071)
Closes part of the gap found re-auditing spi_host_bridge.v against V3:
V3 had no host raw-memory-access path into DDR3 at all. This module
translates single-16-bit-word req/wr/addr/wdata/lb_n/ub_n transactions
(spi_host_bridge.v's own WRITE_MEM/READ_MEM shape) into BURST_LEN=8
transactions on the shared arbiter, using the project's existing
DQM-style partial-burst masking technique.

Verified standalone against the SDR SDRAM placeholder: 16/16 tests,
0 errors, including cross-word-corruption checks on every burst
offset. Not yet wired into the N=2 system or driven by real SPI
opcode decode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 08:53:23 +02:00
micheleandClaude Sonnet 5 598feb975b feat: first genuine N=2 multi-core system verified against real DDR3 (EXP-0070)
Swapped mig_native_adapter.v + sdram_arbiter_n.v into the full N=2
system (neural_director_packed.v + 2x packed_slot.v), replacing the
SDR SDRAM placeholder used since EXP-0057. Verified against the real
Vivado-generated ddr3_model.sv end-to-end: 8/8 positions bit-exact
against golden model, 0 errors, real JEDEC command traffic observed.

This is the first fully real V3 system-level correctness result:
real packed DSP cores + real weight-reuse scheduling + real N-way
arbitration + real DDR3 timing, all verified together.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 08:51:43 +02:00
micheleandClaude Sonnet 5 afff0c4f02 test: fix tb_sdram_arbiter_n.v hang, root-caused as testbench bug (EXP-0069)
TEST2 fired all 3 simulated requesters' one-shot req pulse
unconditionally, not waiting for each one's own grant -- an
unrealistic stimulus that doesn't match packed_slot.v's real
S_MEMWAIT usage (wait for grant, then fire). Rewrote with parallel
fork branches, each waiting for its own req_grant first, still
exercising the real simultaneous-activation contention case.

7/7 PASS, 0 errors. sdram_arbiter_n.v is now genuinely verified, not
just written.

Full writeup in hardware/v2/logs/experiments.log EXP-0069.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 08:44:46 +02:00
micheleandClaude Sonnet 5 e25e4a1506 feat: real DDR3 memory path verified against MIG's own ddr3_model.sv (EXP-0068)
New hardware/v3/rtl/mig_native_adapter.v: adapts this project's
established req/wr/addr/wdata/wmask->rdata/ready/busy contract to the
real MIG 7-series native app interface (app_addr/app_cmd/app_en,
app_wdf_data/app_wdf_mask/app_wdf_wren/app_wdf_end, app_rd_data/
app_rd_data_valid/app_rd_data_end), derived from this project's own
real generated mig_7series_0.v port widths, not assumed. Runs in the
ui_clk domain (MIG's own generated clock becomes this project's
system clock going forward).

Verified against MIG's own real, vendor-shipped DDR3 behavioral model
(ddr3_model.sv) via real Xilinx xsim/xvlog/xelab (UNISIM primitives
in MIG's PHY require this over Verilator): 12/12 write-then-read-back
transactions bit-exact, 0 errors, real JEDEC command sequence observed
(Activate/Write/Read/Precharge). Confirms the app_cmd encoding and
burst/beat sequencing on first real test.

Also adds hardware/v3/rtl/sdram_arbiter_n.v (generalized N-way
arbiter, generalizing EXP-0066's 2-way version for N>2 scaling and a
future host-access requester) -- its own isolated test currently
HANGS, root cause not yet found, do not trust this module yet
(disclosed, not hidden).

Full writeup in hardware/v2/logs/experiments.log EXP-0068.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 08:43:22 +02:00
micheleandClaude Sonnet 5 0589620b58 exp: real P&R of the N=2 multi-core system, first genuine system Fmax (EXP-0067)
New hardware/v3/rtl/n2_system_top.v: synthesis wrapper around the
EXP-0066-verified system (neural_director_packed.v + arbiter + real
SDRAM controller + 2 real packed_slot.v instances).

Real Vivado post-route: 16/240 DSP48E1, WNS -2.570ns @ 200MHz ->
Fmax ~132.1MHz -- only -2.1% vs the isolated single core (134.9MHz,
EXP-0059). Unlike V2/ECP5 (where the real full-system Fmax was
measurably lower than the isolated-core number), this Director+
arbiter architecture shows no comparable penalty at N=2, real
confirmation (not projection) that the earlier ~55-85x-over-ESP32-S3
estimate's key assumption holds at this scale.

Full writeup in hardware/v2/logs/experiments.log EXP-0067.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 00:21:01 +02:00
micheleandClaude Sonnet 5 09fbf01ea5 feat: first genuine N=2 multi-core system, two real bugs found+fixed (EXP-0066)
New sdram_slot_arbiter2.v + tb_np_director_n2_system.v: real
neural_director_packed.v dispatching to 2 real packed_slot.v
instances sharing one real SDRAM controller. Jobs submitted one at a
time through the Director's own producer interface -- the Director's
own scheduling decisions determine slot assignment here, unlike every
prior V3 test.

Bug 1 (real, structural): the arbiter's first design registered its
grant one cycle late; layer_prefetch_ctrl.v's ctrl_req is a one-shot
pulse with no retry (every prior use wired it directly to a
controller, never behind arbitration), so a slot's first request
could be silently lost, hanging it forever. Fixed with a new
S_MEMWAIT state in packed_slot.v (wait for a combinational mem_grant
before ever pulsing layer_prefetch_ctrl's start) and a combinational-
first grant in the arbiter.

Bug 2 (testbench): node_id used a stray bit-slice (li[15:8]) instead
of a real multiply, making every layer produce the same node_ids and
silently checking results against the wrong layer's golden value.
Fixed.

Result: 12/12 PASS, 0 errors, real concurrent execution across both
slots (slot 0: positions {0,1,4,5,8,9}, slot 1: {2,3,6,7,10,11}).

Also noted (user correction): the SDR SDRAM controller used
throughout this memory path is a declared placeholder -- the real
target is DDR3 on a custom XC7A100T board, not yet built.

Full writeup in hardware/v2/logs/experiments.log EXP-0066.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 00:16:47 +02:00
micheleandClaude Sonnet 5 124a0dbca0 feat: packed_slot.v, real per-slot sequencer FSM (EXP-0065)
Promotes EXP-0062's own procedural testbench sequence (prefetch ->
buffer swap -> per-tile gather -> operand streaming -> result
capture) into real synthesizable RTL, wrapping layer_prefetch_ctrl.v
-> layer_weight_buffer.v -> weight_tile_gather.v ->
neural_processor_packed.v behind a 9-state FSM matching
neural_director_packed.v's own per-slot contract.

First run: 4/9 failed, deterministic. Root-caused (not a sequencer
bug): the testbench's own w_base computation wrongly treated it as a
byte address needing *2 conversion; layer_prefetch_ctrl.v expects a
word address directly, and packed_slot.v already passes it through
unconverted to match. Fixed the testbench.

Re-verified: 9/9 PASS, 0 errors, bit-exact results and correct
node_id/result_addr passthrough, entirely self-sequenced (no
testbench-side procedural driving of the sub-modules).

Full writeup in hardware/v2/logs/experiments.log EXP-0065.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-17 00:02:35 +02:00
micheleandClaude Sonnet 5 71600096f9 feat: neural_director_packed.v, job-pairing scheduler for packed cores (EXP-0064)
Forked from neural_director.v (M5): dispatches PAIRS of queued jobs
(sharing w_base+n_tiles) to packed-core slots instead of one job per
slot, matching neural_processor_packed.v's A/B job structure. If the
two oldest queue entries don't share w_base/n_tiles, the Director
stalls (never mis-pairs) -- a disclosed scope limitation, not hidden.

Isolated testbench with behavioral per-slot stubs (same DEC-0007 scope
decision as tb_neural_director.v). First run: 3/7 tests failed --
investigated each, root-caused as testbench timing bugs (checking
dispatch state before the Director's own FSM had caught up, and a
held-too-long job_in_valid making push counts ambiguous), not Director
bugs. Fixed the testbench, re-verified: 8/8 PASS, 0 errors.

Full writeup in hardware/v2/logs/experiments.log EXP-0064.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 23:52:19 +02:00
micheleandClaude Sonnet 5 5afa6a7477 exp: real P&R of verified weight-reuse memory path + packed core (EXP-0063)
New hardware/v3/rtl/np_packed_weight_reuse_top.v: flat structural
synthesis wrapper around the EXP-0062-verified module chain (real SDR
SDRAM controller -> layer_prefetch_ctrl.v -> layer_weight_buffer.v ->
weight_tile_gather.v -> neural_processor_packed.v).

Real Vivado post-route: 8/240 DSP48E1 (unchanged, memory path uses
zero DSPs), WNS -2.502ns @ 200MHz -> Fmax ~133.3MHz, only -1.2% vs
the isolated single core (134.9MHz, EXP-0059). Real memory-path
control logic adds negligible Fmax cost at this scale.

Full writeup in hardware/v2/logs/experiments.log EXP-0063.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 23:44:24 +02:00
micheleandClaude Sonnet 5 9ca180a787 test: first real end-to-end weight-reuse -> packed-core integration, bug found+fixed (EXP-0062)
New hardware/v3/sim/tb_np_packed_layer_reuse.v: real SDR SDRAM ->
layer_prefetch_ctrl.v -> layer_weight_buffer.v -> weight_tile_gather.v
-> neural_processor_packed.v, ALL real synthesizable RTL (unlike
EXP-0058, which still had a testbench-only gather step).

First run: 15/16 PASS, 1 FAIL. Root-caused (not re-run away): a
testbench handshake bug, not a DUT bug -- operand_valid was held one
extra clock edge after each accepted tile, double-consuming stale
data every tile on every pair. 15 of 16 "passed" only because this
test's saturating outputs happened to clamp to the same value whether
or not the accumulator was inflated -- disclosed as a real methodology
risk, not swept under the rug. Fixed by dropping operand_valid the
same delta the handshake is observed.

Re-verified after the fix: 16/16 PASS, 0 errors, bit-exact against an
independent golden model, through the complete real RTL path.

Full writeup in hardware/v2/logs/experiments.log EXP-0062.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 23:41:35 +02:00
micheleandClaude Sonnet 5 5c127fb069 feat: weight_tile_gather.v, real synthesizable byte-to-tile adapter (EXP-0061)
Closes the gap EXP-0058 left testbench-only: assembling P_IN
sequential layer_weight_buffer.v byte reads into one weight_data
tile bus, as real RTL instead of a testbench driver task. Avoids the
runtime-indexed-part-select anti-pattern already found and fixed once
in neural_director.v (ERR-0027-class Fmax collapse) by using a fixed
shift-concat instead.

Verified in isolation against a real, unmodified layer_weight_buffer.v:
37/37 tests, 0 errors, bit-exact across sequential, back-to-back, and
non-sequential/repeated (real reuse-position-style) access patterns.

Full writeup in hardware/v2/logs/experiments.log EXP-0061.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 23:34:09 +02:00
micheleandClaude Sonnet 5 94b63705be exp: N=8 packed-core array real P&R, isolates placement density from interconnect (EXP-0060)
New hardware/v3/rtl/np_packed_array.v: flat array of 8 unmodified
neural_processor_packed.v instances, fully independent I/O, zero
shared arbiter/Director logic -- isolates exactly one variable
(DSP/placement density) from EXP-0059's single-core baseline.

Real Vivado post-route: 64/240 DSP48E1 (26.67%), WNS -2.592ns @
200MHz -> Fmax ~131.7MHz, only -2.4% vs the single-core 134.9MHz.
Placement density alone is NOT the main driver of the ECP5-era gap
between isolated-core and full-system Fmax -- narrows the question
for the still-unbuilt real Director/arbiter/memory integration.

Full writeup in hardware/v2/logs/experiments.log EXP-0060.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 23:29:54 +02:00
micheleandClaude Sonnet 5 34b15cc0a7 exp: first real Vivado synthesis+P&R on XC7A100T for V3 DSP-packed core (EXP-0059)
Real Vivado 2026.1 run (not estimated) confirms the DSP48 packing
survives actual Xilinx synthesis: mac2_dsp_packed.v uses exactly 1
DSP48E1, and the full neural_processor_packed.v pipeline uses 8
DSP48E1/240 for 2 jobs -- half the DSP of two separate V2 cores for
the same work.

Post-route (real place_design+route_design, not synthesis-only):
WNS -2.414ns @ 200MHz -> Fmax ~134.9MHz, within 0.5% of the
post-synthesis-only estimate. This is the isolated compute core,
out-of-context -- not yet a real N-core system number, flagged as
such in the log entry.

Full writeup, including the two real toolchain fixes needed to get
Vivado running on this machine (CRLF line endings in installLibs.sh,
missing libncurses.so.5 on Ubuntu 26.04), in
hardware/v2/logs/experiments.log EXP-0059.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 23:18:18 +02:00
michele 9851930613 docs: extract V2 datasheet into submodule 2026-09-16 23:03:43 +02:00
micheleandClaude Sonnet 5 1cbe7b85d5 v3: real Artix-7 compute core -- DSP48 packing verified, 2 real bugs found and fixed
New hardware/v3/ (Artix-7 port, branch v3-artix7): the compute engine
that makes the 100x-vs-ESP32 target theoretically reachable on
XC7A100T's 240 DSP48E1 budget.

mac2_dsp_packed.v: packs 2 INT8 MACs sharing one resident weight into
a single DSP48-shaped 25x18 multiply, exploiting this project's own
weight-stationary reuse pattern (layer_weight_buffer.v, EXP-0057/0058)
where one weight is genuinely multiplied against many different
activations. Verified exhaustively: 16,777,216/16,777,216
(weight,x0,x1) combinations, 0 errors.

Two real bugs found and fixed during that verification (both purely
arithmetic/RTL, not toolchain-related):
1. An off-by-one in a declared wire width caused Verilog's part-select
   unsigned-by-default rule to corrupt sign extension on the upper
   product field -- ~50% of vectors failed.
2. After fixing (1), still ~50% failed: concatenating two independently
   sign-extended fields ({sext(x1,9), sext(x0,16)}) is NOT equivalent
   to the real arithmetic sum x1*2^16+x0 whenever the lower field is
   negative (its own two's-complement encoding "bleeds" an extra 2^16
   into the concatenated value). Fixed by building the packed operand
   with an explicit arithmetic shift-and-add instead of concatenation.

neural_processor_packed.v: full port of hardware/v2/rtl/
neural_processor.v's pipeline (same stage count/structure), doubled on
the accumulator/bias/activation/saturation side to process two
weight-reuse positions per weight-tile stream. Verified against TWO
real hardware/v2/rtl/neural_processor.v instances (job A / job B, same
shared weight, independent activations) -- 18/18 PASS, 0 errors,
covering the functional sweep, INT8 extremes, and back-to-back jobs.

A third real bug found in the process (in the new testbench, not the
RTL): clearing operand_valid/tile_last in the same simulation delta as
the handshake edge that should register tile_last=1 races against the
DUTs' own FSM evaluation of that same edge -- the same pulse-clearing
race class found three times already today in hardware/v2/sim (EXP-0058
and its follow-up commits). Fixed the same way: hold the pulse past the
edge with a real time delay (#1) before clearing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 19:48:46 +02:00
micheleandClaude Sonnet 5 ff5908f25f docs: log DEC-0009 -- project v22 paused, hard DSP resource ceiling on ECP5
Real nextpnr-ecp5 resource data shows the ECP5 family (45F: 72
MULT18X18D, 85F: 156) cannot reach the 20x-1000x speedup targets
discussed today with this or any redesigned architecture -- N=16 was
already near the 85F's absolute physical ceiling of 19 cores. User
made an informed decision to pause the project rather than chase a
target this hardware family cannot physically deliver.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
v22-final
2026-09-16 15:38:39 +02:00
micheleandClaude Sonnet 5 3416b8d3cf exp: fix pulse-race hang in tb_layer_reuse_vs_zero_reuse.v (EXP-0058 follow-up); real remaining bug still open, 7.16x NOT yet re-verified
Same class of testbench-vs-DUT scheduling race documented in EXP-0058
(tb_layer_prefetch_ctrl.v, tb_neural_processor_layer_reuse.v) was also
present here on ctrl_req/fill_done/consume_done: clearing a one-cycle
pulse on the very next @(posedge clk) let the clear land in the same
active-region pass as the edge the DUT needed to sample it at, so the
pulse could be silently missed. Confirmed via direct state tracing:
sdram_controller_openrow.v sat in S_IDLE with busy=0 forever after the
first burst, never latching req_pending for the second -- this is why
the file hung indefinitely rather than completing. Fixed with the same
#1-before-clear hardening as the other files.

Honesty note: fixing this hang exposed a SECOND, still-unfixed bug in
prefetch_layer's own fill_addr sequencing (real data-correctness
failures once the run actually completes, not just a hang) -- so
EXP-0057's own headline "7.16x real measured speedup" number is NOT
re-verified by this commit and should not be treated as confirmed. Not
pursued further -- see decisions.log for why (project paused).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 15:38:12 +02:00
micheleandClaude Sonnet 5 80c89fa10d exp: real end-to-end weight-reuse integration with neural_processor.v (EXP-0058)
New tb_neural_processor_layer_reuse.v wires the real SDRAM controller,
layer_prefetch_ctrl.v and layer_weight_buffer.v into a real
neural_processor.v compute engine: one resident filter is fetched once
and reused across 8 independent jobs per layer, verified bit-exact
against an independent golden dot-product model (32/32 PASS).

Also found and fixed a real testbench-vs-DUT scheduling race present in
tb_layer_prefetch_ctrl.v (and hardened in the new file): clearing a
one-cycle control pulse on the very next clock edge lands the clear in
the same active-region pass as the edge a receiving module's own
synchronous logic reads it at, so the pulse can be silently missed
depending on implementation-defined process ordering. This had been
silently preventing tb_layer_prefetch_ctrl.v's own claimed 8192/8192
result from ever actually being observed; fixed by holding the pulse
past the edge with a real time delay before clearing, and the
8192/8192 result is now genuinely reproducible (5/5 consecutive runs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 14:26:09 +02:00
micheleandClaude Sonnet 5 ca94083366 prod: promote EXP-0056 fixes into production RTL (dependency_manager.v, nms_activation_fill_ctrl_v3.v)
Per explicit instruction: when a bug/fix found in an experimental fork
also applies to the production file it was forked from, apply it
there too, not just in the fork.

dependency_manager.v: swaps in priority_encoder_lsb.v for the
first_ready_idx scan (was: serial O(N_NODES) for-loop). Bit-exact
equivalent, correctness-neutral by construction.

nms_activation_fill_ctrl_v3.v: adds the missing N_SLOTS==16 balanced
max-tree case (was: silently falling back to the slow flat scan for
any N_SLOTS not in {1,2,4,8}) -- this was the real cause of N_SLOTS=16
failing timing closure (23-24MHz vs 64MHz target), fixed to 71.01MHz
PASS in the experimental fork.

Verified on the REAL, unmodified production top (fpga_neural_v2_top.v,
N_SLOTS=4 default): tb_dependency_manager.v 4/4 PASS, board smoke test
11/11 PASS, D-Stress N=4 total_cycles=49927 (bit-exact, IDENTICAL to
the pre-fix baseline -- zero functional regression, as expected from a
pure combinational-depth change). Real nextpnr-ecp5 P&R (LFE5U-45F,
seed 1): 97.36MHz, PASS at 64MHz -- BETTER margin than the pre-fix
baseline's own 76.80-88.25MHz seed range, not just neutral.

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