Commit Graph
100 Commits
Author SHA1 Message Date
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
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
micheleandClaude Sonnet 5 49b25f6eb6 exp: layer_prefetch_ctrl.v, real synthesizable RTL for layer-weight-reuse prefetch, fixes a real address-truncation bug (EXP-0057b)
Built the real FSM version of EXP-0057's own task-based prefetch
pattern (bulk-sequential layer fetch via sdram_controller_openrow.v
into layer_weight_buffer.v), so it's an actual instantiable module,
not just a simulation convenience.

Found and fixed a real bug in the process: cur_fill_addr's own address
arithmetic bit-sliced BYTES_PER_BURST down to too few bits
(BYTES_PER_BURST[BIDXW-1:0]), silently truncating 16 to 0 -- every
burst's bytes landed at fill offset 0-15 instead of their real
position, overwriting each other (only each layer's last burst
survived). Root cause: misapplied a widening idiom used safely
elsewhere in this codebase to a case where the target width was
actually too small. Found via a standalone control-flow debug test
first, then tracing data once control-flow was cleared.

Verified: 8192/8192 bit-exact, 0 errors (was 512/8192 before the fix)
through the real controller + SDRAM model, 16 layers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 12:25:32 +02:00
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
micheleandClaude Sonnet 5 fce8ff2d66 exp: fork real board top (fpga_neural_v2_top_openrow.v) with open-row SDRAM backend, verified functionally identical
SPI+board-level smoke test: 11/11 PASS, matching the unmodified
production top exactly. 8-seed nextpnr-ecp5 P&R sweep (N=4, real
v2_board_top.lpf pins, 64MHz target): 8/8 PASS on both, open-row
variant has BETTER margin than baseline (worst 83.34 vs 76.80 MHz,
mean 90.40 vs 82.53 MHz) -- not just no regression, a real improvement.
Not yet promoted over the production fpga_neural_v2_top.v (that swap
is still pending an explicit go-ahead); this commit only adds the
verified fork + its own smoke test, additive only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 08:02:14 +02:00
micheleandClaude Sonnet 5 ee5a68f6e6 exp: SDRAM CDC bridge + open-row policy (EXP-0053/54/55) -- open-row is a real ~5% D-Stress win, CDC bridge measured net-negative once integrated
EXP-0053: sdram_cdc_bridge.v decouples the SDRAM clock (115.2MHz, real
value derived from the board's own existing PLL VCO=576MHz, verified
via ecppll) from the 64MHz compute domain. Isolated: 137/137 tests, 0
errors, but real measured speedup is only 1.095x (not the naive 1.8x
clock-ratio estimate) -- the CDC handshake's own synchronizer
round-trip is a fixed per-transaction tax.

EXP-0054: sdram_controller_openrow.v implements the page-hit/
keep-row-open optimization sdram_controller.v's own header had always
deferred. weight_prefetch_engine_wide.v's real production traffic is
strictly sequential per job and mostly stays within one SDRAM row --
closing/reopening it every tile (today's fixed auto-precharge policy)
wastes tRP+tRCD for no reason. Isolated: 154/154 tests, 0 errors, 0
protocol violations (including the new refresh-while-row-open hazard,
fixed via an explicit precharge-before-refresh path). Real measured
speedup on the actual sequential access pattern: 1.141x.

EXP-0055: composed both, then integrated into the real D-Stress
benchmark (N=4/N=8, 256/256 bit-exact in every config). Result:
open-row ALONE gives a real, consistent ~5% cycle-count improvement
(47445/47468 vs baseline 49927/49909). CDC alone is a real ~8%
REGRESSION. Combined is still a ~4% regression -- the CDC's fixed tax
is paid on every transaction regardless of row-hit, and real D-Stress
traffic interleaves weight-fetch/activation-result access far more
than the isolated same-row test exercised, so open-row's real saving
doesn't offset it. Decision: do not adopt the CDC approach; open-row
alone is the disclosed, real win worth considering for production
next, pending an explicit go-ahead (not applied to the real board top
in this commit -- all additive, existing production RTL untouched).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-16 07:07:57 +02:00
micheleandClaude Sonnet 5 03b5cbc25b exp: bank-interleaved SDRAM pipelining works in isolation, ~0.3% gain integrated (EXP-0052)
Follow-up to EXP-0051: built sdram_controller_pipelined.v, remapping
addr->bank to low-order bits (today's weight region always maps to bank
0) and adding a shadow-slot ACTIVATE lookahead so a different-bank
request can start its tRCD wait during the current transaction's tail.

Phase A (isolated tb_sdram_controller_pipelined.v, 38/38 bit-exact,
independently re-verified this session): mechanism works, saves exactly
2 cycles (tRCD) per different-bank back-to-back pair, matching the
theoretical ceiling derived before measuring (CAS_LATENCY+BURST_LEN are
serial on the shared data bus regardless of bank, so more than tRCD/tRP
was never on the table).

Phase B (integration, tb_nms_dstress_sdram_pipelined.v, independently
rebuilt/rerun): N=4 49760 cycles (-0.33% vs baseline), N=8 49755
(-0.31%) -- both 256/256 bit-exact. Root cause of the gap: the W port's
request/ready protocol is one-at-a-time, so a second, different-bank
request is essentially never already pending while the first is still
in flight, so the mechanism rarely triggers in the real system even
though it's correct when directly stimulated. Not integrated into
production; kept as additive reference for a possible future
arbiter/backend pipelined-dispatch rewrite (out of scope here, larger
and riskier).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHENedK76onD2Vtc2CMjej
2026-09-16 02:56:51 +02:00
micheleandClaude Sonnet 5 cc5db09f61 exp: dual-bank SDRAM sim shows W/AR split gives only ~9% cycle gain, not thrashing removal (EXP-0051)
Forked nms_neural_multiprocessor_sdram_unified.v + its D-Stress testbench
into a dual-bank variant (two independent sdram_unified_backend.v
instances, one for weight-fetch, one for activation+result) to test the
Fase-3 memory-bound hypothesis ahead of Phase 2. Simulation-only: the
real board (v2_board_top.lpf) still wires exactly one physical chip,
per STEP19's governing single-SDRAM mandate.

Result is honest but not the hoped-for one: splitting by traffic class
only cuts D-Stress cycles ~8-10% (N=4: 49927->45724, N=8: 49909->44980),
because the AR (activation+result) path was already lightly loaded
(~12% busy) even alone. The real ceiling is the weight-fetch channel
itself, which stays ~77-78% busy even with its own dedicated bank and
zero cross-traffic. Full writeup in experiments.log EXP-0051, including
the refined next-step options this suggests instead of a straight
2-bank board revision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHENedK76onD2Vtc2CMjej
2026-09-16 02:18:57 +02:00
micheleandClaude Sonnet 5 19ef54aea8 docs+synth: Phase 0 complete - N=8 baseline + toolchain discrepancy closed
N=8 baseline (EXP-0050, real fpga_neural_v2_top + v2_board_top.lpf, fresh
8-seed sweep): 8/8 PASS at 64MHz, worst 80.97MHz, mean 84.62MHz. DSP
64/72 (88.9%) confirms real N_SLOTS=8. This directly contradicts the
brief's own premise (N8 expected to collapse to ~31MHz) and the project's
historical numbers (3-5/8 pass, worst ~55-67MHz) despite bit-identical
RTL.

Investigated and closed the same discrepancy already flagged for N=4:
logs/experiments.log:2563 explicitly tags historical Fmax numbers as
measured on "Yosys 0.68+"; this session's toolchain is Yosys 0.69+59, a
nightly dated the same day as the session -- a confirmed ~59-commit
version gap. Critical-path inspection on both N=4 and N=8 confirms the
underlying RTL congestion the project diagnosed is real and still
present (same bottleneck locations as previously documented) -- the
newer toolchain just places it well enough to still clear 64MHz.

Decision: this session's numbers are adopted as the operative baseline
for Phase 1+ (see timing.log for the full writeup). Also found, not yet
applied: slot_mem_arbiter.v/slot_mem_arbiter_wide.v both still have the
runtime-indexed-crossbar anti-pattern already fixed in neural_director.v,
sitting right on the arbiter<->backend boundary the brief names -- a
concrete Phase 1 candidate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 21:44:10 +00:00
micheleandClaude Sonnet 5 685a4d6cfe docs+synth: Phase 0 baseline (N_SLOTS=4, real board top), fix stale LPF freq
New brief (N=8 timing closure, LFE5U-85F retarget, 4/8/16 x 1/2-bank SDRAM
sweep). Phase 0: no RTL changes, only measure the current baseline.

ERR-0030: constraints/v2_unified.lpf's FREQUENCY PORT "clk" was still 80MHz,
a leftover from the STEP19 freeze, never updated to the project's real
64MHz target -- fixed (LPF only, zero RTL/datapath effect).

ERR-0031 (bigger one): the first two synthesis attempts targeted
nms_neural_multiprocessor_sdram_unified.v, which is NOT the real
board-level top -- it's an obsolete wrapper only exercised by one
testbench now. The real target is fpga_neural_v2_top.v (adds the real
PLL, reset_sync, spi_host_bridge, and a second arbitration level), which
is what actually goes through synthesis+P&R for hardware. Re-targeted
correctly, matched against constraints/v2_board_top.lpf (all 17 ports
real-ball-assigned).

An N_SLOTS=8 P&R attempt against the WRONG (wrapper) target ran for 2h42m
without converging on a single seed; discarded rather than trusted.
N_SLOTS=8 baseline deferred by explicit user request until N_SLOTS=4 is
fully understood -- re-attempt against the correct fpga_neural_v2_top
target with an agreed time budget.

Result (EXP-0049, fresh 8-seed nextpnr-ecp5 P&R, real pins): N_SLOTS=4
8/8 PASS at 64MHz, worst-seed 81.20MHz, mean 91.05MHz. Higher than
DEC-0042's historical worst/best (64.55/77.21MHz) despite identical RTL --
disclosed, unresolved (leading hypothesis: nextpnr-ecp5 build/version
difference), adopted as the operative baseline for this session's
toolchain going forward. Full writeup in errors.log/timing.log/
experiments.log (EXP-0049).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 20:28:16 +00:00
micheleandClaude Sonnet 5 8b8ca239ca docs: consolidate all V2 datasheets into one current, complete document
The repository had accumulated multiple, contradictory "current state"
documents for V2 hardware: an old V1 IT/EN datasheet copy nested inside
hardware/v2/docs/datasheet/, a stray untracked duplicate at repo root
(docs/DatasheetLatex/), and a second, much older documentation track
(hardware/v2/docs/*.md: PRE_PCB_VERIFICATION.md, PRE_PCB_CLOSURE_4POINT.md,
MEMORY_UPGRADE_64MB_N8.md, and 10 more) describing an earlier PSRAM/
N_SLOTS<=2 milestone alongside the real, current SDRAM/N_SLOTS=4 board.
The LaTeX datasheet's own front matter (features/pinout cover pages) and
chapter 9 (benchmarks) were themselves still describing that obsolete
architecture, contradicting the real, current chapters 5/7/10 elsewhere
in the same document.

This commit:
- Flattens hardware/v2/docs/datasheet/files/docs/datasheet/v2-en/* up to
  hardware/v2/docs/datasheet/ (was 4 levels of redundant nesting).
- Removes the old V1 IT/EN LaTeX copies and the stray root-level
  duplicate entirely (recoverable from git history, not from disk).
- Preserves the real component reference PDFs (ECP5 eval board, ISSI
  PSRAM, programming cables) under datasheet/references/.
- Removes 13 superseded hardware/v2/docs/*.md status documents after
  folding every real, unique fact they contained into the datasheet:
  SPI max verified clock (12MHz, exact 12.8MHz CDC edge), SDRAM directed
  boundary test (21/21 PASS), 16MHz oscillator MPN (ECS-3225MV-160-BN-TR),
  and the real FPGA<->SDRAM ball mapping cross-check.
- Rewrites the datasheet's own front matter, ch.4 (parameters), ch.8
  (top-level module -- was documenting the wrong, non-physical top
  entirely), and ch.9 (benchmarks) to describe the current, real SDRAM/
  N_SLOTS=4 production board, while keeping the real PSRAM-era chapters
  as clearly-labeled history rather than deleting correctly-measured
  work.
- Fixes a title-page tikzpicture that was clipped off the page edge
  (pre-existing, unrelated to this change) by scaling it to fit.

Net: 85 files changed, -8814/+498 lines. hardware/v2/docs/ now contains
exactly one current datasheet plus FIRST_POWER_ON.md (a bring-up
runbook, not a duplicate spec).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-09 00:41:06 +02:00
micheleandClaude Sonnet 5 1efd63912f docs: polish V2 datasheet to professional third-person English
Removed session-log/conversational register leftover from the
interactive drafting process ("the user's own suggestion", "user
confirmed", "user-authored schematic", "user-requested optimizations",
etc.) and replaced with neutral, factual engineering-document
phrasing. No technical content changed -- attribution language only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 20:17:09 +02:00
micheleandClaude Sonnet 5 d4792beed7 docs: embed real KiCad schematic export in V2 datasheet, verify BOM from source
Exported the schematic (kicad-cli sch export pdf) and BOM (kicad-cli sch
export bom) directly from the real KiCad source instead of relying on
pasted screenshots/CSVs, then updated ch.10 accordingly:

- New "Real KiCad schematic capture" section embeds the two live sheets
  (FPGA, UnusedBank) as vector figures pulled straight from the project
  file, plus a note on the three sheet files not reachable from the
  root hierarchy.
- Boot-flash net-name mismatch finding confirmed resolved: FPGA_SPI_*
  labels now match exactly on both the flash chip and the ECP5's
  dedicated MSPI pins.
- FPGA grade fix (8BG381I -> 8BG381C) now verified directly in the
  source file's Value field, not just as a stated intent. Real package
  geometry added: 0.8mm pitch, 20x20 array, 17x17x1.76mm body.
- New minor finding: U2's footprint library folder is still named
  ..._8BG381I even though the Value field is corrected -- not
  board-affecting (identical physical footprint across grades), but
  flagged for a future rename.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 20:03:57 +02:00
micheleandClaude Sonnet 5 7eb02ba4bd docs: resolve FPGA grade to commercial (8BG381C), correct temp range data
User confirmed LFE5U-45F-8BG381C (commercial) is the intended part,
matching every other reference in this project. Also corrects this
chapter's own earlier temperature figures: commercial grade is real
TJ 0 to +85C, industrial is TJ -40 to +100C (an earlier draft of this
section had the industrial range wrong at -40/+85C). Same "-8" speed
grade in both -- the letter suffix only changes the characterized
temperature range. KiCad library still needs the part number itself
corrected from ...381I to ...381C.

Compiled clean (47 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:46:14 +02:00
micheleandClaude Sonnet 5 22e226a087 docs: resolve the +1V1 label mystery and close remaining schematic open items
User confirmed TLV62568 (U1) outputs 1.1V, explaining the +1V1 label
found near the VCCAUX ferrite (L2) during the schematic review: it
belongs to U1's own real output net, merely nearby on the page layout,
not routed through the ferrite. VCCAUX remains 2.5V as required by the
real Lattice datasheet.

All three schematic "open items" from the earlier review are now
resolved: TLV62568 EN (R3=499k, confirmed via BOM), the +1V1 label
(false alarm, explained above), and the JTAG pull-up array (confirmed
8 discrete 0402 parts via BOM, not a bussed package -- no polarity
limitation). Only the boot-flash net-name typo (FGPA/FPGA + SCLK/CLK)
remains as a real, open schematic fix, and the FPGA grade discrepancy
(8BG381I vs 8BG381C) remains to be confirmed.

Compiled clean (47 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:42:46 +02:00
micheleandClaude Sonnet 5 1e36853072 docs: clear CAS#/WE# false alarm, resolve TLV73325 EN wiring
User confirmed the real schematic has SDRAM CAS#=F7/WE#=F9 -- matches
this chapter exactly. The earlier review's "appear swapped" finding
was a misread of the schematic image, not a real error; moved from
"real findings" to a cleared checked-item note.

TLV73325's EN pin resolved: direct wire to +3.3V (VIN), always-enabled
-- no soft-start timing requirement like TLV62568's own R3 pull-up (a
plain LDO, no sequencing note in TI's own datasheet), and no dynamic
enable/disable control exists elsewhere in this design.

Remaining real, open finding: the boot-flash net-name mismatch
(FGPA/FPGA typo + SCLK/CLK) still needs a schematic fix.

Compiled clean (47 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:38:48 +02:00
micheleandClaude Sonnet 5 e2956d0377 docs: add real BOM, flag FPGA grade discrepancy (8BG381I vs 8BG381C)
Real KiCad-exported BOM added to the hardware chapter, cross-checked
against every component value this chapter already specifies -- most
match exactly (R1/R2 feedback divider, L1/L2, ferrite part number, all
three real ICs). One real discrepancy found: U2 is captured as
LFE5U-45F-8BG381I (industrial grade) while every other reference in
this project uses the C (commercial) grade part -- flagged, not
resolved, needs explicit confirmation.

Also resolves three earlier open items from the schematic review:
R3=499k confirms TLV62568's EN is populated; R5-R12 being 8 discrete
0402 resistors (not a multi-resistor array) confirms the JTAG
pull-up/down polarity concern doesn't apply; U5 confirms the
previously-missing 16MHz oscillator is now present.

Compiled clean (47 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:36:51 +02:00
micheleandClaude Sonnet 5 4cbbe5a29d docs: real KiCad schematic review + reserved PCB module section
Adds a dated review section recording today's actual schematic check
against every real ball assignment in this chapter: confirmed-correct
items, two real findings needing correction (boot-flash net-name
mismatch FGPA/FPGA typo + SCLK/CLK label mismatch that would leave the
flash electrically disconnected from the ECP5's config engine; SDRAM
CAS#/WE# appearing swapped vs. the verified F7/F9 assignment), and
open items not resolvable from the schematic image alone (regulator EN
pins, a stray +1V1 label near the VCCAUX ferrite, JTAG pull-up/down
array polarity).

Also reserves a "PCB module form factor" section (castellated-edge SMD
module, ~50x25mm) for the future physical layout.

Compiled clean (46 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:35:03 +02:00
micheleandClaude Sonnet 5 44be4a7d81 docs: reflect flash #1 removal and the N=4 8/8 timing recovery
Datasheet and pinouts.md updated to the current, real state after
today's session: flash #1 was implemented then removed (user's own
MHz-over-persistence priority call), the clock-closure table now
shows the post-revert, post-DEC-0042 numbers (N_SLOTS=4 @ 64MHz 8/8,
worst 64.55MHz/best 72.37MHz; N_SLOTS=8 deferred by explicit user
request), and the FPGA_DATA_READY any_pending formula is updated to
the real counter-based implementation (credited to the user's own
diagnosis) that fixed the last failing N=4 seed.

Compiled clean (45 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:20:57 +02:00
micheleandClaude Sonnet 5 481b5d223f perf: fix N=4@64MHz 7/8->8/8 by removing any_pending fan-out (user's idea)
Real critical-path trace after the flash #1 revert showed a NEW
bottleneck (neural_director.job_out_slot -> dependency_manager.
node_resolved/node_state, 76-84% routing) distinct from every prior
fix this session -- already a flat, parallel 64-way compare, not a
serial chain, so the established restructuring fix class doesn't
apply. Root cause: any_pending (added for FPGA_DATA_READY) reads
node_state[0:N_NODES-1] combinationally every cycle, adding real
fan-out onto the same congested signal.

User's own suggestion: replace the combinational scan with a
synchronous up/down counter. pending_count +1 on registration
acceptance, -1 on dispatch acceptance; any_pending = (pending_count
!= 0) -- mathematically identical (DEC-0008: nodes never reclaimed
mid-run) but reads one small register instead of scanning a 16-wide
array every cycle.

Verified: D-Stress N=4 bit-exact (49927 cycles, data_ready PASS).
Fresh 8-seed P&R: N_SLOTS=4 @ 64MHz now 8/8 PASS (was 7/8 after the
flash revert), worst seed1 64.55MHz, best seed0 72.37MHz.

See decisions.log DEC-0042.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 19:16:01 +02:00
michele ee708775c7 Revert "feat: integrate flash #1 (neural-network data) RTL into V2 top-level"
This reverts commit 59901a4905.
2026-09-07 19:02:44 +02:00
micheleandClaude Sonnet 5 59901a4905 feat: integrate flash #1 (neural-network data) RTL into V2 top-level
Closes the flash #1 RTL gap flagged in DEC-0041: real, unmodified V1
subsystem (flash_slot_manager.v/flash_copy_engine.v/spi_flash_master.v/
crc32.v) now instantiated in fpga_neural_v2_top.v, bridged to the AR
memory bus via a new flash_mem_adapter.v (byte<->word, matches
nms_memory_manager_stream_wide.v's own real masking convention), and
commandable over SPI via a new spi_host_bridge.v opcode (OP_FLASH_CMD,
0x30) using the same byte-counting idiom as OP_WRITE_JOB. Real balls
now in the LPF: flash_sclk=B2, flash_mosi=E2, flash_miso=F2,
flash_cs_n=F3.

New tb_flash_integration_smoke.v: real SPI-triggered OP_FLASH_READ_BLOCK
verified bit-exact (64/64 bytes) against a real V1 flash_model.v
instance, through the new adapter and the widened (2->3 port) host-arb
arbiter; WRITE_JOB regression confirms the new 3rd port doesn't disturb
existing traffic. Full existing regression re-run clean: D-Stress N=4/
N=8 (bit-exact + data_ready PASS), board-level smoke test (11/11),
isolated spi_host_bridge test (18/18).

Honest, disclosed finding: a full 8-seed P&R re-verification shows
N_SLOTS=4 @ 64MHz regressed from 8/8 to 3/8 PASS (worst 60.18MHz).
Root cause traced via the real critical-path report: the SAME
pre-existing arbiter-to-sdram-backend bottleneck already documented all
session, made worse by flash's added die-area placement pressure --
not a new path through the flash logic itself. N_SLOTS=8 essentially
unchanged (6/8, was 5/8). See decisions.log DEC-0042 for full detail
and open decision points.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 18:31:50 +02:00
micheleandClaude Sonnet 5 b4f33388a9 fix: add missing sdram_clk output port -- real chip never had a clock pin
Found while helping verify the user's own schematic: clk_sys (the PLL-
generated 64MHz system clock) was purely internal to
fpga_neural_v2_top.v -- never reached a physical output pin. The real
external SDRAM chip cannot function without its own CLK input driven
from the board; this was missing from every prior P&R run this
session, none of which ever routed a real clock to the SDRAM interface.

Added `sdram_clk` output port (driven directly by clk_sys), assigned
to J4 (bank 6, GR_PCLK6_0 -- a real clock-capable ball, confirmed free
via Trellis iodb.json). Verified via real synthesis (0 errors) and
nextpnr-ecp5 place&route: the pad-forwarded clock merges with the
existing internal clock net (as expected, both are the same clk_sys
signal), achieving 66.61 MHz post-route -- PASS at the 64 MHz target
(the pre-route estimate of 49.73 MHz was a placement-only pessimistic
number, superseded by the real post-route result). Smoke test
re-verified (tb_fpga_neural_v2_top_smoke.v, 11/11 PASS).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 17:55:23 +02:00
micheleandClaude Sonnet 5 abaaf21f56 docs: datasheet addendum for two-flash programming architecture + FPGA_DATA_READY
Adds a real, dated section to ch.10 (Hardware and board) covering the
two-independent-flash architecture (neural-network data vs. boot
bitstream), the ESP32<->ECP5 JTAG-only link, and the real CABGA381
ball assignments (JTAG/PROGRAMN/INITN/DONE/CFG[2:0]/MSPI dedicated
pins), matching decisions.log DEC-0041.

Also closes ch.7's (Host interface) own long-standing "still needed"
list: the physical transport (spi_host_bridge.v) and the completion-
notification path (FPGA_DATA_READY) it used to flag as missing are
both now real -- described with the same system-idle-detector formula
implemented in the RTL.

Compiled clean (44 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 13:11:12 +02:00
micheleandClaude Sonnet 5 7d6311bce3 feat: two-flash programming architecture, FPGA_DATA_READY, real JTAG/config pinout
Establishes the real ESP32<->ECP5 programming architecture: flash #1
(neural-network data, existing V1 subsystem, ball reserved not yet
wired into V2) stays separate from flash #2 (boot bitstream, MSPI
auto-boot, CFG[2:0]=[0,1,0]); ESP32 talks JTAG only (bit-banged, no
hardware JTAG-master peripheral on S3/C6), updating flash #2 through
the ECP5's own internal sysCONFIG-to-SPI bridge, never driving the
flash pins directly -- zero bus contention, confirmed against the real
Lattice hardware checklist and sysCONFIG user guide.

Adds real, verified ball assignments (official Lattice CABGA381 CSV +
Project Trellis iodb.json) for JTAG, PROGRAMN/INITN/DONE, CFG[2:0],
and the MSPI dedicated pins -- all written to docs/pinouts.md.

Implements FPGA_DATA_READY as real RTL: a system-idle detector
(dependency_manager's any_pending OR neural_director's !queue_empty OR
any active slot), sticky on the busy->idle edge, self-clearing on new
work -- not a per-neuron completion pulse, which was confirmed too
fine-grained. Bit-exact regression re-verified at N_SLOTS=4 and 8
(zero cycle-count change), new explicit data_ready assertion check
added to the D-Stress testbench (PASS both configs), and a fresh
Yosys+nextpnr-ecp5 placement check (0 errors, data_ready placed at G3).

Also fixes a real, independently-found bug while editing an adjacent
file: nms_neural_multiprocessor_sdram_unified.v's own sdram_a port was
still [11:0] (12 bits), stale from before the 64MB/13-bit memory
upgrade. Not exercised by the real board-level top (which wires SDRAM
directly, bypassing this wrapper) but WAS silently truncating A12 in
every D-Stress simulation this session, including today's earlier
ERR-0029 verification runs. Assessed impact: all D-Stress test
addresses used this session decode to rows under 4096 (bit 12 never
actually needed), so no false-positive PASS is believed to have
resulted -- but the full 64MB space was never actually exercised
through this wrapper. Fixed; re-verified bit-exact with identical
cycle counts.

See decisions.log DEC-0041 for full detail.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 12:46:00 +02:00
micheleandClaude Sonnet 5 81a9619214 chore: remove root-level V1 duplicates, superseded by hardware/v1/ freeze
hardware/v1/ was created (dc0b331) as a frozen snapshot of the V1
project that then lived at the repo root (rtl/, sim/, synth/, tools/,
docs/). Root received zero further commits to those files after the
freeze -- confirmed byte-identical to the hardware/v1/ copy for every
file removed here. Root was the "before", hardware/v1/ is the
curated, canonical "after".

Removed (all verified exact-hash duplicates of hardware/v1/ content):
  - rtl/ (20 files, 100% covered by hardware/v1/rtl/)
  - tools/{netasm,pinout,run_regression.py,flash_catalog,validation,
    fpga_benchmark.py} (19 files, 100% covered by hardware/v1/tools/;
    tools/neural_sim/ kept -- unique, post-freeze, no counterpart)
  - sim/*.v (47 testbenches, 100% covered by hardware/v1/sim/; the
    ~38 remaining sim/ entries are compiled binaries and .vcd
    waveform dumps, left as a separate cleanup decision)
  - synth/ecp5/{p2,p4,p8,post_fix_verify} (25 files, exact duplicates
    of hardware/v1/synthesis/; the other ~84 synth/ecp5/* experiment
    build directories are historical artifacts never carried into the
    freeze, left as a separate decision)
  - WORKLOG.md (duplicate of hardware/v1/docs/WORKLOG.md)
  - docs/{FPGA-Neural-Datapatch-Benchmark,FPGA-Neural-Hardware-Design,
    FPGA-NeuralNetwork-Engine}.md, docs/validation/*.md (18 files),
    docs/FPGA-Neural-Datasheet-{EN,IT}.pdf -- all exact duplicates of
    hardware/v1/docs/ content
  - hardware/v1/docs/DatasheetLatex/ (24 files) -- exact duplicate of
    hardware/v2/docs/datasheet/files/docs/datasheet/en/ (discovered
    during this audit; not the same DatasheetLatex already removed
    from hardware/v2/docs/ in an earlier commit)

Moved (genuine, unique, post-freeze V2 content -- not duplicated
anywhere, just living in the wrong/legacy root docs/ location):
  - docs/architecture/*.md -> hardware/v2/docs/architecture/
  - docs/pinouts.md, docs/FPGA_NEURAL_V2_DATASHEET.md,
    docs/FPGA_NEURAL_V2_SCHEMATIC.md,
    docs/FPGA-Neural-V2-Datasheet-EN.pdf -> hardware/v2/docs/

Left untouched (separate decisions, not part of this cleanup):
  - docs/FPGA-Neural-Flash-Subsystem-Verification.md, docs/
    v2-description.md -- orphaned root-only content, no duplicate
    found anywhere, but also not part of the reviewed plan
  - synth/ecp5/* experiment dirs and sim/*_sim + sim/*.vcd build
    artifacts -- not literal duplicates, flagged as candidates for a
    future, separate cleanup pass

Verified no functional breakage: grepped all remaining scripts/docs
for references to every removed path -- only prose/comment mentions
found, no executable imports or build-script paths broken.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 05:30:17 +02:00
micheleandClaude Sonnet 5 e1cefd13db docs: remove hardware/v2/docs/DatasheetLatex, superseded by datasheet/
Replaced by the just-versioned hardware/v2/docs/datasheet/ (the
project's real, maintained IT+EN LaTeX datasheet, now including the
2026-09-07 SDRAM upgrade addendum). DatasheetLatex/ was a separate,
differently-structured, stale LaTeX document living in the same
location -- removed to avoid two competing datasheets under
hardware/v2/docs. hardware/v1/docs/DatasheetLatex/ is untouched (V1
scope, not part of this request).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 05:10:58 +02:00
micheleandClaude Sonnet 5 ff49d3c75d docs: bring datasheet/ into the main repo under hardware/v2/docs
Was a separate, untracked directory (DataSheet/) outside the repo.
Renamed to lowercase and moved in as hardware/v2/docs/datasheet/, with
its own .gitignore for LaTeX build byproducts (compiled PDFs stay
tracked, .aux/.log/.toc/etc do not). Now versioned and shares this
repo's own remote instead of living untracked on disk.

Content: IT+EN LaTeX chapter sources, reference manufacturer PDFs, and
compiled datasheet PDFs including the 2026-09-07 SDRAM upgrade
addendum (AS4C32M16SB-7BIN part/pinout/timing) in the v2-en chapters.

Note: hardware/v2/docs/DatasheetLatex/ (and the v1 sibling) is a
separate, already-tracked, differently-structured LaTeX document that
predates this move -- left untouched, not merged, since its chapter
set and content differ and merging was not requested.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 05:09:14 +02:00
micheleandClaude Sonnet 5 f14224d0cd fix: ERR-0029 sdram_unified_backend weight-cache hit-index critical path
Replace the serial "last-match-wins" priority-scan hit-index encoder
with a flat one-hot compare + single-level priority encode, breaking
the serially-dependent PFUMX/OFX cascade real P&R showed dominating
the N_SLOTS=8 @ 64MHz critical path (55.84MHz worst, 4/8 seeds PASS).

Real nextpnr-ecp5 re-verification (32-run matrix, 4 configs x 8 seeds):
N_SLOTS=8 @ 64MHz improves to 5/8 PASS (worst 60.12MHz, up from
55.84MHz). N_SLOTS=4 @ 64MHz stays 8/8 PASS but with reduced worst-case
margin (WNS +0.605ns, down from +2.143ns) as the critical path
relocates to a different, previously-second-worst path in the same
module -- a real, disclosed trade-off, not hidden. 80MHz remains NO-GO
at both processor counts (re-confirmed on the fixed RTL).

Bit-exact, zero functional regression: isolated
tb_sdram_unified_backend.v (40/40) and full D-Stress N=4/N=8 (both
256/256 neurons bit-exact vs golden model).

See errors.log ERR-0029 and decisions.log DEC-0040 for full root-cause
writeup and MEMORY_UPGRADE_64MB_N8.md section 10 for the complete
measured data set and AS4C32M16SB-7BIN pinout tables.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 03:10:50 +02:00
micheleandClaude Sonnet 5 a20f1ecd73 docs: memory upgrade writeup (MEMORY_UPGRADE_64MB_N8.md) + supersede stale SDRAM docs
Adds the consolidated, authoritative record for the 64MB memory
upgrade and N_SLOTS=8 investigation (real datasheet family comparison,
RTL changes, both timing fixes with real P&R data, honest N=8 clock-
closure status, real DigiKey availability for the frozen part).

Marks HARDWARE_FREEZE.md/MEMORY_ARCHITECTURE.md/PRE_PCB_VERIFICATION.md/
PRE_PCB_CLOSURE_4POINT.md's own SDRAM-specific sections as superseded
(they describe the previous 8MB AS4C4M16SA-6TIN part) with pointers to
the new document, rather than rewriting each individually.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 00:21:05 +02:00
micheleandClaude Sonnet 5 8d83d97bde feat: SDRAM 8MB->64MB upgrade (AS4C32M16SB-7BIN) + N_SLOTS=8 support
Memory upgrade, at the user's own explicit request: Alliance Memory
AS4C4M16SA-6TIN (64Mbit/8MB) -> AS4C32M16SB-7BIN (512Mbit/64MB, 54-ball
TFBGA), the largest same-family SDR SDRAM Alliance Memory offers.
Real-datasheet-driven (whole AS4C4M16SA/AS4C8M16SA/AS4C16M16SA/
AS4C32M16SA family investigated): 13 row bits (was 12, one new FPGA
pin sdram_a[12]/ball F1), 10 column bits (was 8), real -7-grade AC
timing (tRCD/tRP improved to 15ns, tREFI halved to 7.8us for the
doubled row count). sdram_controller.v and sdram_model.v gained real
ROW_BITS/COL_BITS/BANK_BITS parameters (was hardcoded 12/8/2).

ADDR_WIDTH widened 23->26 bits across the live instantiation tree.
This required a real SPI protocol change (spi_host_bridge.v): a 26-bit
byte address no longer fits in 3 bytes -- every address field widened
3->4 bytes (WRITE_JOB 15->18 payload bytes, WRITE_MEM/READ_MEM header
5->6 bytes).

Found and fixed two real timing regressions via nextpnr-ecp5 P&R
(not assumed): neural_director.v's own runtime-indexed demux write
(ERR-0027, was silently synthesizing an extra MULT18X18D) and
nms_activation_fill_ctrl_v3.v's own linear N_SLOTS-wide max-scan
(ERR-0028, became dominant at N_SLOTS=8) -- both replaced with
constant-indexed/tree-based equivalents, bit-exact same behavior,
confirmed via full D-Stress N=2/4/8 regression (identical cycle
counts). N_SLOTS=4 now fully closes timing at 64MHz (8/8 seeds);
N_SLOTS=8 significantly improved but not yet fully reliable (5/8
seeds) -- honestly disclosed, not claimed complete.

Full regression re-verified: sdram_controller (461/461, 18 configs),
tb_sdram_boundary (21/21), D-Stress N=2/4/8 (bit-exact), spi_host_bridge
(18/18), board-level SPI smoke test (11/11), unified backend (40/40).

See hardware/v2/docs/MEMORY_UPGRADE_64MB_N8.md for the full
investigation, and errors.log/decisions.log (ERR-0027, ERR-0028,
DEC-0039) for the complete root-cause writeups.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-07 00:20:53 +02:00
micheleandClaude Sonnet 5 9b5d1055b8 feat: neural_sim Python golden functional reference simulator
Adds tools/neural_sim/, a NumPy-based reference implementation of the
FPGA-Neural V2 numeric model (INT8 in/weight, 32-bit wraparound
accumulation, ReLU+saturate out), derived directly from
hardware/v2/rtl/neural_processor.v (not assumed) and reusing
tools/validation/mac_oracle.py's own pre-existing, hand-verified
two's-complement primitives rather than duplicating them.

Provides: neuron/layer/network models, a logical memory model of the
real V2 SDRAM map (weights/activations/results), deterministic
test-vector generators (simple/signed/extremes/zero/random/D-Stress
256x128) with JSON golden-vector export, an FPGA-vs-Python bit-exact
comparison utility, four example networks, a CLI
(`python -m tools.neural_sim ...`), and a 96-test pytest suite (all
passing) covering signed-arithmetic edge cases (including a direct
32-bit wraparound proof), scalar-vs-vectorized neuron cross-checks,
layer/memory/vector/comparison tests.

This is a golden functional reference (bit-exact numeric result),
explicitly NOT a cycle-accurate FPGA simulator -- see
tools/neural_sim/README.md for the full scope statement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 19:51:33 +02:00
micheleandClaude Sonnet 5 c4763aab10 test: directed SDRAM boundary verification + SPI frequency sweep
PRE-PCB CLOSURE Point 1: adds tb_sdram_boundary.v, a directed (not
randomized) regression covering address 0/1/last/last-1, an explicit
row-boundary crossing, all 3 inter-bank boundary crossings, the real
V2 memory-map region boundaries (weights/activations/results), and
every DQM byte-mask combination with explicit read-after-write.
21/21 PASS at both 64MHz and 166MHz, zero bugs found.

PRE-PCB CLOSURE Point 2: adds tb_spi_freq_sweep.v, a reproducible SPI
bit-rate sweep against the real fpga_neural_v2_top (osc_clk driven at
the real 64MHz clk_sys rate via the SIM PLL bypass). Found and fixed a
race in the new test harness itself (a fixed-time wait before reading
a WRITE_MEM/READ_MEM response, too short whenever a periodic AUTO
REFRESH delayed the backend) -- not a spi_host_bridge.v defect,
confirmed against tb_spi_host_bridge.v's own isolated regression.
Determined the real, deterministic CDC margin: the synchronizer
requires >=5 system-clock cycles per SPI bit (exactly 64MHz/5 =
12.8MHz); recommends SPI_MAX_VERIFIED=12MHz with real margin below
that hard edge.

Full writeup: hardware/v2/docs/PRE_PCB_CLOSURE_4POINT.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 19:42:37 +02:00
micheleandClaude Sonnet 5 eb0b0f9170 docs: PRE-PCB VERIFICATION FREEZE report + config flash freeze
Adds hardware/v2/docs/PRE_PCB_VERIFICATION.md as the single
authoritative pre-schematic verification record: RTL/ERR-0025/
regression re-verification, clock/reset, synthesis/P&R re-audit,
setup/hold timing status, the real SDRAM datasheet-parameter audit
table (Alliance Memory AS4C4M16SA-6TIN Table 17), SPI host bridge
protocol documentation, FPGA power/pinout tables, first-board bring-up
and benchmark status, and cross-domain consistency audit.

Freezes the FPGA configuration flash: Winbond W25Q32JVSSIQ (32Mbit,
SOIC-8, 2.7-3.6V, standard SPI, ECP5 Master-SPI-boot compatible) --
this was the one section-11 item the governing mandate required not
be left OPEN.

Marks CHIP_READINESS.md/OPEN_ITEMS.md/PINOUT.md/CLOCK_ARCHITECTURE.md/
POWER_ARCHITECTURE.md/SCHEMATIC_READINESS.md as superseded (they
predate the SPI host bridge, PLL, and this session's SDRAM datasheet
audit) with pointers to the new consolidated document, rather than
rewriting each individually.

Updates the V2 LaTeX datasheet's status/roadmap chapter to reflect the
SDRAM datasheet audit and configuration-flash decisions, rebuilds the
PDF (clean compile, 16 pages).

Classification: PRE-PCB VERIFIED. Schematic and PCB implementation
remain user-owned and not started.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 18:32:40 +02:00
micheleandClaude Sonnet 5 8890b0abc2 fix: ERR-0026 SDRAM tMRD datasheet-unit mismatch at 64MHz
Real Alliance Memory AS4C4M16SA-6TIN datasheet (Rev 5.0, Table 17)
specifies tMRD as a fixed 2-tCK cycle count, not an ns value.
sdram_controller.v modeled it via ns_to_cycles(12), which rounded to
2 cycles by coincidence at every previously-tested frequency
(100/133/166MHz) but rounds to only 1 cycle at the real 64MHz board
target -- an under-provisioned one-time init sequence. Fixed by
hardcoding T_MRD=2, matching how CAS_LATENCY is already modeled.

Verified zero regression: full 9-config legacy sweep + a new
dedicated 64MHz config (461/461 PASS each), N=2/N=4 D-Stress
(identical cycle counts), board-level smoke test (11/11 PASS).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 18:27:36 +02:00
micheleandClaude Sonnet 5 d6376e8f2a V2 final synthesis/P&R sign-off + LaTeX datasheet port (NOT SILICON READY)
Real synthesis and place-and-route of the actual final board-level
top (fpga_neural_v2_top.v -- SPI bridge, real EHXPLLL, reset_sync,
compute+memory core), against a real, fully ball-assigned LPF. Also
ports the V2 datasheet to LaTeX using V1's own preamble/macros/
typography, and records a real (non-ESP32, honestly labeled) software
reference comparison.

Synthesis (Yosys, real run): 0 CHECK-pass problems, 38 unique warnings
(43 total), all matching this project's own previously-reviewed benign
set (neural_processor.v's known genvar multi-driver artifact, small-
array-to-register unrolling, the real SDRAM DQ tristate bus) -- no new
warnings from the SPI bridge, PLL, or reset synchronizer.
TRELLIS_FF=6322, TRELLIS_COMB=7084, MULT18X18D=32, EHXPLLL=1 (real PLL
confirmed present), DP16KD=0 (all small SRAMs -> distributed RAM).

Place-and-route (nextpnr-ecp5, real runs, 8 seeds, new
v2_board_top.lpf with all 44 top-level signals ball-assigned from the
official Lattice pinout CSV -- no placeholders): 8/8 PASS at 64MHz.
Worst 68.51MHz (seed 4), best 74.17MHz (seed 7), mean 71.16MHz. Zero
unrouted nets, zero placement/routing errors, TRELLIS_IO=44/245 (17%).
Critical path alternates between dependency_manager's own priority
encoder and sdram_unified_backend's own weight-cache hit-index logic,
matching this project's own prior documented timing investigations --
not a new defect.

New: hardware/v2/constraints/v2_board_top.lpf (final LPF, supersedes
v2_unified.lpf for the board-level top), hardware/v2/reports/
step_final_{synthesis,pnr_worst_seed4,timing}.* (raw evidence),
hardware/v2/docs/DatasheetLatex/ (V2 datasheet, real LaTeX build,
16 pages, visually inspected, ported from hardware/v1/docs/
DatasheetLatex/'s own preamble and macros).

Real, honestly-labeled software baseline: the D-Stress arithmetic
(256 neurons x 128 INT8 MACs) compiled and run on THIS development
machine (Apple M4, arm64, NOT an embedded target, NOT ESP32) --
1.28us/inference, included in the datasheet with an explicit
disclosure that no physical ESP32 hardware was available for a real
embedded-target comparison.

Remaining, disclosed, NOT-yet-closed items (this commit does NOT
claim silicon readiness): real KiCad schematic + ERC, PCB layout,
sourced BOM, real power current-budget estimate, SDRAM-datasheet-
parameter cross-check, configuration-flash selection, and (necessarily)
physical fabrication/bring-up. See hardware/v2/docs/DatasheetLatex/
chapters/08-status-roadmap.tex for the complete, itemized checklist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 17:50:53 +02:00
micheleandClaude Sonnet 5 68f3c5e403 fix(v2): resolve ERR-0025 Part B - SRAM read timing bug in weight/activation memory
Root-causes and fixes the real, disclosed defect left open at the end
of the previous STEP20 commit: the board-level SPI host interface
produced wrong compute results when jobs were dispatched with
realistic (widely time-separated) pacing, even though job registration
itself was already confirmed correct at the dependency_manager
handshake.

Root cause: nms_weight_packed.v and nms_activation_replicated.v both
used a REGISTERED SRAM read (rd_data_reg <= mem[addr], one full clock
of latency), but nms_memory_manager_stream_wide.v's own read-ahead
pipeline (its `rd_pending` bit) is designed around a COMBINATIONAL
read -- a request issued this cycle produces data already valid to
capture the very next cycle. A busy, multi-tile job (e.g. the STEP19
D-Stress regression, 16 tiles/neuron) never exposes the mismatch,
since its own weight/activation prefetch always runs far enough ahead
that any given tile has been sitting stable in the SRAM for many
cycles by the time it's actually consumed. An uncontested single-tile
job has zero such margin: its one tile's read fires on the exact edge
the data nominally becomes ready, landing squarely on the missing
cycle and permanently latching stale/zero data.

Fixed by making both SRAMs' reads combinational, with an explicit
same-cycle fill/read address-match bypass for the one hazard a plain
combinational read alone would still miss. No FSM, arbiter, or SDRAM
controller logic was touched.

Verified (Verilator, per this project's own standing DEC-0004
protocol):
- tb_fpga_neural_v2_top_smoke.v: 11/11 PASS -- single job, back-to-back
  jobs, a realistic ~85us-gap job pair, and a parametric sweep of
  inter-job gaps (100ns/5000ns/50000ns).
- STEP19 D-Stress N=2: 49788 cycles, 256/256 bit-exact -- identical
  cycle count to before this fix (zero regression).
- STEP19 D-Stress N=4: 49771 cycles, 256/256 bit-exact -- identical
  cycle count to before this fix (zero regression).
- tb_sdram_unified_backend.v (40/40) and tb_spi_host_bridge.v (18/18)
  reconfirmed unaffected.

The physical SPI host interface is now verified correct end-to-end.
Real synthesis/P&R of the board-level top (fpga_neural_v2_top.v) is
the deliberate next step, not yet performed this round.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 17:33:45 +02:00
michele 18bf038ed5 v1 datasheet latex source 2026-09-06 15:02:25 +02:00
micheleandClaude Sonnet 5 43abf28b5b V2.1.0-dev: SPI host bridge + clock/reset architecture (NOT release-ready)
STEP20 work toward the V2 hardware release gate. Adds real new RTL
implementing the three pieces the previous freeze (V2.0.0) explicitly
left open, plus real, disclosed verification findings. Does NOT
declare hardware release complete -- see below.

New RTL:
- spi_host_bridge.v: real SPI slave protocol engine (WRITE_JOB/
  WRITE_MEM/READ_MEM/STATUS/RESET opcodes), replacing the 110-pin
  reg_* testbench bus as the intended physical host interface.
  Isolated regression 18/18 PASS (tb_spi_host_bridge.v); two real
  MISO-timing bugs found and fixed during its own development (see
  the module's header for the root-cause writeup).
- ecp5_pll_sys_clk.v: real, tool-generated (Project Trellis ecppll)
  EHXPLLL wrapper, 16MHz oscillator -> 64MHz system clock, with a
  declared (not fabricated) simulation-only PLL bypass.
- reset_sync.v: standard async-assert/sync-deassert reset bridge
  gating on external POR and PLL lock.
- fpga_neural_v2_top.v: board-level top wiring the above around the
  STEP19 compute+memory design's own already-frozen submodules
  (zero modification to neural_processor.v, dependency_manager.v,
  sdram_unified_backend.v, or any other previously-frozen file).

Real findings from this step's own re-verification (both logged in
full in hardware/v2/logs/errors.log):
- ERR-0024: the current Icarus Verilog v13.0 install (updated since
  the last freeze) gives WRONG bit-exact results for the
  already-committed STEP19 regression. Cross-checked against
  Verilator per this project's own standing protocol (DEC-0004) --
  the STEP19 baseline (single SDRAM, N=2/N=4, raw reg_* interface) IS
  bit-exact correct, reconfirmed today, matching the historical cycle
  counts exactly. Two provably-zero-behavior-change declaration-order
  fixes were required just to get the current toolchain to elaborate
  the already-shipped STEP19 files at all.
- ERR-0025: a real SPI-bridge protocol race (fixed) plus a SEPARATE,
  real, UNRESOLVED defect -- two jobs dispatched through the real SPI
  path with realistic pacing produce wrong compute results, even
  though job registration itself is confirmed correct at the
  handshake. Root cause not yet isolated. Committed as a known-failing
  regression (tb_fpga_neural_v2_top_smoke.v) documenting the gap
  honestly rather than hiding it.

Given ERR-0025 Part B is real and unresolved, synthesis/P&R of the new
board-level top was deliberately not attempted this round, and V2
hardware release is NOT declared complete. See decisions.log DEC-0036
and hardware/v2/docs/{CHIP_READINESS,OPEN_ITEMS}.md for the full,
itemized status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 14:35:14 +02:00
micheleandClaude Sonnet 5 8e014d8d49 V2.0.0 hardware freeze - single SDRAM
FASE #1 hardware freeze for FPGA-Neural V2, N4/P8, single external
SDRAM (Alliance Memory AS4C4M16SA-6TIN) serving weights, activations,
and results through one physical sdram_controller.v instance. Removes
the PSRAM dependency (hardware/v1/rtl/psram_controller.v +
memory_interface.v) from the V2 physical path entirely -- V1 itself
remains fully unmodified, the golden reference.

New RTL: sdram_unified_backend.v (2-way W/AR arbitration over one
SDRAM controller, real per-byte DQM write masking added to
sdram_controller.v for correct single-byte result writes with no
read-modify-write), nms_neural_multiprocessor_sdram_unified.v (the
frozen top-level). Two real bugs found and fixed via full-system
testing before being accepted (ERR-0023): a deadlock and an off-by-one
data-shift bug in the new arbitration logic.

Real results: N=4 and N=2 D-Stress bit-exact (256/256 neurons), 40
real AUTO REFRESH events interleaved with zero corruption, real
Yosys+nextpnr-ecp5 synthesis/P&R for LFE5U-45F-8CABGA381 (149/245
TRELLIS_IO, a real 45-pin reduction from the prior dual-memory
design). Timing is MARGINAL (1/8 P&R seeds >=80MHz), reported honestly
rather than masked by the best seed.

Real, sourced ball-level pinout for the SDRAM bus + clk/rst (39/149
signals, P&R-verified) using the official Lattice ECP5U-45 pinout CSV
found on disk during this step's own pre-commit review -- corrects an
earlier draft that wrongly assumed no real pinout data was available.

Chip readiness: NO. Real, disclosed blockers remain (no physical host
interface exists yet -- the RTL's own reg_* ports are a 110-pin raw
test-harness bus; clock source/PLL decision; power/configuration
component selection) -- see hardware/v2/docs/{HARDWARE_FREEZE,
CHIP_READINESS,OPEN_ITEMS}.md for the complete, itemized status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-06 13:39:55 +02:00
micheleandClaude Sonnet 5 5c9ec618d3 docs: publish refreshed V1 datasheet (EN/IT, Rev A1)
Adds the updated professional-template V1 datasheet PDFs to the
tracked repo, matching the datasheet's own filename convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 21:59:02 +02:00
micheleandClaude Sonnet 5 63cac6a7e5 perf(v2): shared activation cache - further 1.66-2.00x real speedup (DEC-0016)
Implements optimization #2 from the final benchmark campaign's own
recommendation, on top of DEC-0015's word-level burst rewrite: a new
shared activation_cache.v module fetches a given activation (X)
vector from PSRAM once instead of once per neuron sharing it - the
exact redundant traffic pattern the dense-layer workloads in this
project's benchmark suite exhibit.

Each memory_manager's own prefetch_engine now fetches WEIGHTS only;
the activation half is requested from the shared cache instead
(single-tag, tile-granular, N_SLOTS request ports, its own real
word-level PSRAM backend via a new dedicated arbiter port).
dataflow_core.v/slot_mem_arbiter.v/neural_multiprocessor.v widened to
N_SLOTS+1 ports to arbitrate the cache's traffic alongside each
slot's weight traffic.

Two real bugs found and fixed during implementation (ERR-0010): a
target-bank/pending-bank race in memory_manager.v's activation-cache
wiring (the same bug class ERR-0006 already fixed once for
pf_target_bank - a later handoff's queued request can overwrite which
bank an earlier, still-in-flight request's ack applies to), and a
repeat of ERR-0009's N_SLOTS=1 zero-width replication bug in
activation_cache.v itself.

Real, measured results: the full final-benchmark campaign (24/24
workload/config combinations) re-verified bit-exact. D-Stress cycles
fall a further 1.66-2.00x on top of DEC-0015 (~4x combined vs the
original byte-level baseline). But the cache's real Fmax cost is much
steeper than DEC-0015's own: N_SLOTS=2 (the recommended default,
DEC-0014) drops from 133.58 to 87.72 MHz (-34%, margin over 80MHz
shrinks from +67% to +9.7%), and N_SLOTS=4 drops to 65.01 MHz - now
FAILING the 80MHz target it previously passed. Combined real
wall-clock speedup vs the original baseline: N=1 3.86x, N=2 2.45x
(both real net wins); N=4 is a real regression once its own now-failing
Fmax is honestly used, though N=4 was never the recommended
configuration.

N_SLOTS=2 remains the recommended default (DEC-0014 unaffected) with
a thinner but still real Fmax margin. Cache hit-detection pipelining
is flagged as concrete follow-up work if N_SLOTS>2 is ever needed with
the cache active - not attempted this round.

Logged: simulation/synthesis/timing/benchmark/decisions (DEC-0016)/
experiments (EXP-0016)/errors (ERR-0010)/development.log, ROADMAP.md
updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 20:59:14 +02:00
micheleandClaude Sonnet 5 e4a5540b6e perf(v2): word-level burst reads - 2.24-2.37x real wall-clock speedup (DEC-0015)
Implements optimization #1 from the final benchmark campaign's own
recommendation: exploit psram_controller.v's already-implemented
page-mode support (confirmed present by direct inspection) by
fetching multiple bytes per real backend transaction instead of one
at a time.

Root cause addressed: int8_memory_access.v (the byte-level backend
prefetch_engine.v originally sat on) already converts every 8-bit
logical request into a full 16-bit PSRAM word access internally
(mem_addr <= addr >> 1), discarding half of every word it already
paid for. prefetch_engine.v/memory_manager.v now speak
memory_interface.v's own 16-bit word protocol directly, bypassing
int8_memory_access.v entirely - which remains untouched, still frozen
V1 (§1/§34); V2 simply reuses the lower layer of the same frozen
chain instead of the byte-splitting layer on top of it, the same
"reuse what fits" precedent slot_mem_arbiter.v already set.
slot_mem_arbiter.v and neural_multiprocessor.v widened to match
(lb_n/ub_n added, master port wired directly to memory_interface.v).

Real, measured results: M4's own single-job testbench shows 49-56%
fewer cycles (166->84, 446->204, 728->322, all still bit-exact). The
full final-benchmark campaign (24/24 workload/config combinations)
re-verified bit-exact with D-Stress's real wall-clock time (cycles /
real POST-P&R Fmax) improving 2.24-2.37x across every N_SLOTS tested,
against a small real Fmax cost (unchanged at N=1, -6.2% at N=2, -1.2%
at N=4).

tb_neural_multiprocessor.v (M8) and tb_benchmark_suite.v (final
campaign) needed zero changes - both treat neural_multiprocessor.v as
a black box. Only tb_memory_manager.v (M4, rewired to skip
int8_memory_access.v) and tb_dataflow_core.v (M7, behavioral model
widened to word-level) needed updates.

The "real parallel scaling is flat beyond N_SLOTS=2" finding (DEC-0014)
still holds - this optimization made the shared PSRAM port more
efficient per transaction, not multi-ported - so N_SLOTS=2 remains
the recommended default.

Logged: simulation/synthesis/timing/benchmark/decisions (DEC-0015)/
experiments (EXP-0015)/development.log.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 20:35:19 +02:00
micheleandClaude Sonnet 5 3cdaeaee35 test(v2): final benchmark campaign - real end-to-end characterization (EXP-0014)
Post-M10, user-requested final benchmark campaign: 6 realistic
workloads (16-256 independent neurons in a shared-input dense-layer
shape, a random-seeded 2-layer network with real cross-node PSRAM
forwarding, and a 6-node 2-hop dependency diamond) x 4 concurrency
levels (N_SLOTS=1/2/4/8) through the real, full neural_multiprocessor
system (real V1 PSRAM chain, real slot_mem_arbiter). 24/24 runs PASS
bit-exact against a software golden model (11,520 individual neuron/
node checks, zero mismatches).

Three real bugs found and fixed during the campaign itself (ERR-0009):
1. neural_director.v (M5) had a real RTL bug at N_SLOTS=1
   ($clog2(1)=0 makes a replication expression illegal) - never
   caught because M5-M10 only ever tested N_SLOTS=2/4/8. Fixed with
   a width-agnostic '0 literal; M5's own testbench re-verified
   unaffected.
2/3. Two testbench sizing bugs in tb_benchmark_suite.v itself
   (psram_model DEPTH too small for the Large workload's address
   range; N_NODES too small for the Stress workload's node-id range,
   causing a real deadlock via node-id wraparound colliding with an
   already-DISPATCHED node - a real, honest consequence of DEC-0008's
   own "no node-slot reclamation" design choice).

Headline finding: real parallel scaling is essentially flat beyond
N_SLOTS=2 - the single shared PSRAM port saturates at ~91%
utilization regardless of slot count, so memory-bound workloads gain
only 1.05-1.06x real speedup from N=1 to N=8. Once real POST-P&R Fmax
degradation is also factored in, N_SLOTS=4 is measurably 21% SLOWER
in real wall-clock time than N_SLOTS=1 for the largest workload
tested. N_SLOTS=2 is recommended as the default (DEC-0014,
superseding DEC-0012's resource-only "N_SLOTS=8 ceiling" framing for
general use).

Full 21-section report (every number classified THEORETICAL/
SIMULATED/POST-P&R MEASURED/DERIVED, per the user's own
methodology requirements): hardware/v2/docs/benchmarks/
final-benchmark.md

Logged: simulation/synthesis/timing/benchmark/decisions (DEC-0014)/
experiments (EXP-0014)/errors (ERR-0009)/development.log,
ROADMAP.md updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 19:58:30 +02:00
micheleandClaude Sonnet 5 91bbbe2fe5 feat(v2): M10 Optimization - data-driven findings, completes the V2 roadmap
Final milestone of docs/v2-description.md's §33 roadmap, scoped
exactly to its own mandate: optimize only on data already gathered in
M1-M9, across the pipeline/P_IN/processor-count/scheduling/memory
axes - no speculative new features.

Three concrete, data-driven results:

1. N_SLOTS=8 (numero processor axis): real synthesis + nextpnr-ecp5
   P&R for dataflow_core at N_SLOTS=8, extending M7's N_SLOTS=2/4
   sweep to the real DSP ceiling DEC-0005 predicted. 92.63 MHz
   POST-P&R, PASS at 80MHz, DSP 64/72 (88.9%). DEC-0012 recommends
   N_SLOTS=8 as the practical ceiling for P_IN=8 on the
   LFE5U-45F-8BG381.

2. ACC_WIDTH 24 vs 32 (pipeline axis): a real 6-seed nextpnr-ecp5
   placement sweep (reusing already-synthesized netlists, no new
   synthesis needed) resolves EXP-0002's single-seed
   inconclusiveness. ACC_WIDTH=24 wins on both mean Fmax (+6.2%,
   180.71 vs 170.12 MHz) and seed-to-seed variance (~3.4x tighter),
   on top of its already-known resource advantage. DEC-0013
   recommends ACC_WIDTH=24 as the new default.

3. Stall %/utilization (scheduling/memory axes): testbench-only cycle
   counters added to tb_neural_multiprocessor.v (no RTL touched)
   close DEC-0011's deferred measurement gap with real data - shared
   PSRAM port 81.7% utilized, slot 0 95.2%, slot 1 65.2%, no
   conclusive evidence of harmful fixed-priority starvation at this
   scale.

The 10-milestone V2 roadmap (docs/v2-description.md §33) is now
complete end-to-end: real Verilator simulation, real Yosys synthesis,
real nextpnr-ecp5 place & route for every milestone, fully logged
(EXP-0001..EXP-0013, DEC-0001..DEC-0013, ERR-0001..ERR-0008) with no
invented results (§30) and V1 kept frozen and untouched throughout
(§1/§34).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 18:58:10 +02:00
micheleandClaude Sonnet 5 84794a3d25 docs(v2): M9 full benchmark - V1 vs V2 comparison table (§32)
Consolidates real, already-measured data from hardware/v1/ (frozen,
pre-certified) and V2's own M1-M8 logs into the §32-mandated
comparison table, on an apples-to-apples basis: both full systems
(V1's spi_neuron_top post_fix_verify vs V2's neural_multiprocessor
N_SLOTS=2), both PARALLEL=8/P_IN=8, both using the real unmodified V1
PSRAM backend.

Headline, all real measurements: V2 full-system Fmax 142.45 MHz
POST-P&R (PASS at 80MHz) vs V1's 68.65 MHz (FAIL at 80MHz); 166 vs 209
real simulated cycles for one neuron's 8-input dot product through the
same real PSRAM chain (2.6x wall-clock speedup); peak MAC/cycle 16
(N_SLOTS=2 concurrent slots, real contention already demonstrated in
EXP-0009) vs V1's 8 (single sequential core); lower LUT/FF despite V2
already including full dependency-graph scheduling that V1 has none
of.

9 of the table's 12 rows carry real sourced numbers; stall %/memory
utilization/processor utilization are reported as NOT MEASURED rather
than approximated (DEC-0011) - a real number needs dedicated
cycle-accounting instrumentation neither system has had built for it
yet, and approximating from partial data would violate §30's "no
invented results" rule. Deferred to M10, which needs exactly this
data to decide what to optimize.

No new RTL this milestone - pure data consolidation, logged as
EXP-0010.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 15:17:14 +02:00
micheleandClaude Sonnet 5 6cff2c8a7c feat(v2): M8 PSRAM integration - real V1 backend shared across concurrent slots
neural_multiprocessor.v wraps dataflow_core.v (M7, unmodified) around
the real, unmodified V1 PSRAM backend chain (int8_memory_access ->
memory_interface -> psram_controller), funneling N_SLOTS independent
Memory Backend Interface ports through a new generic N-port arbiter
(slot_mem_arbiter.v) inspired by (not copied from) V1's own
mem_arbiter.v.

Real concurrent-slot simulation immediately surfaced a genuine bug
(ERR-0008): memory_manager/prefetch_engine's byte-level backend
protocol is fire-and-forget (a single-cycle mem_req pulse with no
accept handshake) - correct for M4's direct 1:1 connection, but a
naive arbiter silently drops a pulse arriving while the shared bus is
owned by another slot, hanging that slot forever. Fixed with a
per-port pending-request latch, the same "queue, don't drop" idiom
already used by memory_manager's own pf_pending register (ERR-0006).

Verified (Verilator): 4/4 PASS with 2 slots genuinely contending for
one real PSRAM port (444 cycles). No regression on M4's own
testbench. Real synthesis + nextpnr-ecp5 P&R (no harness needed - real
PSRAM pins keep the top-level at 157 pins): 0 problems, Fmax 142.45
MHz, PASS at 80MHz.

Arbitration policy is fixed lowest-index priority, not fairness-
balanced (DEC-0010) - consistent with every other "simplest correct
policy first" scheduling choice in this roadmap, revisited only if
M9's real measurement shows starvation matters.

Logged: simulation/synthesis/timing/benchmark/decisions (DEC-0010)/
experiments (EXP-0009)/errors (ERR-0008)/development.log, ROADMAP.md
updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 15:13:11 +02:00
micheleandClaude Sonnet 5 77baa8fc16 feat(v2): M7 Dataflow Core - full M1-M6 integration, wake-up loop closed end-to-end
dataflow_core.v integrates dependency_manager (M6) -> neural_director
(M5) -> N_SLOTS x (memory_manager (M4) + neural_processor (M1)) for
the first time. A slot's completion (via neural_director's new
slot_node_id tracking, an additive port) feeds back as a
producer_done event to dependency_manager, waking up any node that
depended on it - closing the dataflow loop without external glue.

Verified end-to-end (Verilator) on a 3-node DAG: two independent
nodes plus a third depending on both, confirmed to dispatch only
after both genuinely complete via real neural_processor computation.
4/4 PASS.

Real synthesis + nextpnr-ecp5 P&R via a synthesis-only timing harness
(bare per-slot backend ports exceed the LFE5U-45F's TRELLIS_IO
budget, same pattern as ERR-0005): N_SLOTS=2 -> 165.15 MHz,
N_SLOTS=4 -> 133.19 MHz, both PASS at 80MHz, 0 synthesis problems.

Scope explicitly deferred to M8 (DEC-0009): M3's BRAM buffers not
wired in yet, per-slot Memory Backend Interface ports not arbitrated
to one shared PSRAM master yet - both need real measured data before
committing to a design, not guessed at here.

Logged: simulation/synthesis/timing/benchmark/decisions (DEC-0009)/
experiments (EXP-0008)/errors (ERR-0007, a Yosys chparam-ordering
build quirk, not an RTL bug)/development.log, ROADMAP.md updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 15:05:28 +02:00
micheleandClaude Sonnet 5 8af16d3a12 feat(v2): M6 Dependency Manager, multi-dependency wake-up
Implements M6: dependency_manager.v tracks a table of node
descriptors (node_id/state/required_dependencies/resolved_
dependencies/producer_ids -- §10's exact field list), incrementing a
waiting node's resolved count whenever one of its listed producers
completes, transitioning it to READY once resolved==required, and
dispatching ready nodes to the Neural Director (M5) one at a time via
a backpressure-safe valid/ready interface.

Verified with Verilator on a small hand-built DAG: node0/node1 have no
dependencies (dispatch immediately); node2 depends on BOTH node0 AND
node1 ("dipendenze multiple") and stays WAITING until both complete,
confirmed via an explicit negative check after only one resolves;
node3 depends on node0 ALONE, demonstrating a single producer
("node0") satisfying two different consumers' dependencies
("risultati condivisi... piu' consumer") -- node3 fully, node2
partially. 4/4 tests pass.

Scope for this milestone (decisions.log DEC-0008): dependency
COUNTING/readiness only, no direct producer-to-consumer value
forwarding (§11 frames that as a "quando possibile" optimization, not
a correctness requirement -- deferred until real bandwidth
measurements justify it) and no node-slot reclamation after dispatch
(not exercised by any scenario built so far).

Real synthesis: 0 CHECK problems, 763 LUT4/474 FF/0 DSP/0 CCU2C. Real
place&route (module fits the TRELLIS_IO budget as a bare top-level
this time, no harness needed): Fmax 155.30 MHz, PASS at 80MHz.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 14:51:02 +02:00
micheleandClaude Sonnet 5 2e4cedc761 feat(v2): M5 Neural Director, first-free job scheduling
Implements M5: neural_director.v dispatches job descriptors to
whichever of N_SLOTS (memory_manager, neural_processor) pairs is
currently free (first-free scheduling per §9's initial policy), with
a parametric-depth ready-queue FIFO for jobs arriving faster than
slots can absorb them.

Scope for this milestone (see decisions.log DEC-0007): a reduced
4-state FSM (DIR_IDLE/SCAN_READY/ALLOCATE/ERROR) rather than §9's full
8-state baseline -- dependency tracking, the waiting queue, and
wake-up are §10's explicit responsibility (Dependency Manager, M6, not
yet built), and slot-completion detection runs as an always-active
per-slot tracker rather than a dedicated FSM state, for the same
reason DEC-0002 already gave for the Neural Processor's own FSM
(gating concurrent per-unit progress behind one shared state kills
throughput).

Verified with Verilator (N_SLOTS=2, each slot backed by its own
independent behavioral memory rather than sharing V1's real PSRAM --
M4 already proved that path for one slot; this milestone's own concern
is scheduling across multiple slots): 4/4 tests pass -- 3 jobs
submitted to 2 slots (first two dispatch immediately, third correctly
queues until a slot frees), and a deliberate burst that forces the
ready queue to genuinely fill and recover.

Real synthesis: 0 CHECK problems, 382 LUT4/366 FF/4 CCU2C/0 DSP. Real
place&route (via a synthesis-only timing harness, same TRELLIS_IO
pin-budget reason as M2/M4): Fmax 250.50 MHz, PASS at 80MHz.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 14:46:32 +02:00
micheleandClaude Sonnet 5 175f697ae1 feat(v2): M4 Memory Manager + Prefetch Engine, real V1 PSRAM backend
Implements M4: memory_manager.v (arbitration/buffering/forwarding/
latency hiding/double buffering, §12) + prefetch_engine.v
(double-buffered tile fetch, §13), sitting on the REAL, UNMODIFIED V1
PSRAM backend chain (int8_memory_access.v -> memory_interface.v ->
psram_controller.v, per §15's explicit mandate not to touch the
controller).

Verified fully end-to-end with Verilator: real neural_processor (M1)
fed entirely by memory_manager, computing against PSRAM-resident X/W
tiles (double-buffered prefetch across up to 5 tiles) and writing its
result back to PSRAM -- checked via an independent PSRAM read-back,
with poison bytes around the operand regions to catch addressing
errors. 3/3 jobs pass (1/3/5-tile configurations).

Three real RTL bugs found and fixed during integration (full
diagnostic trail in errors.log ERR-0006): prefetch_engine had no
single-in-flight-request discipline, letting a queued request corrupt
the bank bookkeeping of a fetch already running; the fix's own
!pf_busy guard had a one-cycle blind spot (pf_busy lags pf_start by a
clock) that needed an explicit !pf_start term; and a state-based mux
for the shared backend port was off by one cycle, silently dropping
the PSRAM result write entirely.

Real synthesis: 0 CHECK problems, 851 LUT4/789 FF/108 CCU2C/0 DSP
(expected, no multiplication in this module). Real place&route (via a
synthesis-only timing harness, needed for the same TRELLIS_IO pin-
budget reason as M2's array): Fmax 165.86 MHz, PASS at 80MHz.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 14:39:29 +02:00
micheleandClaude Sonnet 5 5f0d7f101c feat(v2): M3 activation/weight/result buffers, real BRAM mapping
Implements M3: three parametric dual-port buffers for the §12
data-plane (Input/Weight/Result), reusing the proven BRAM-inference
idiom from the frozen hardware/v1/rtl/act_buffer.v (synchronous write,
synchronous REGISTERED read, no reset on the read register -- keeps
Yosys off the LUT-RAM path).

Verified with Verilator: 10/10 tests pass (write-then-read
correctness, extreme INT8 round-tripping, weight_buffer's full 64-bit
tile width round-tripping, undisturbed re-reads).

Real synthesis at two depths per module (6 configs total): 0 CHECK
problems, every configuration correctly infers DP16KD (never
LUT-RAM). Non-obvious real finding: weight_buffer's BRAM cost is
driven by its P_IN*DATA_WIDTH tile width, not its DEPTH -- an 8x depth
reduction (512->64) left DP16KD usage unchanged at 2, while
activation_buffer/result_buffer (byte-wide) scale as naively expected
(2->1). All default-depth configs PASS at 80MHz with large margin
(287-367 MHz) via real nextpnr-ecp5 place&route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 14:19:11 +02:00
micheleandClaude Sonnet 5 3026dcd997 feat(v2): M2 Neural Processor Array, N_PROCESSORS resource sweep
Implements M2 of the V2 roadmap: neural_processor_array.v instantiates
N_PROCESSORS independent neural_processor (M1) units, each with its
own dedicated point-to-point job/operand/result interface -- no shared
bus or mux at this level (arbitration is explicitly the Neural
Director's job, M5).

Verified with Verilator (tb_neural_processor_array.v, N_PROCESSORS=4):
7/7 tests pass, including a same-cycle 4-way concurrent launch with
different tile counts and a staggered-start test where a
later-launched, shorter job completes before an earlier-launched,
longer one -- confirming genuine independent concurrent execution
(§18/§34: a blocked/busy processor must not block the others).

Real resource/timing sweep for N_PROCESSORS in {1,2,4,8} (Yosys +
nextpnr-ecp5, real place&route): Fmax stays above the 80MHz target
throughout (159.11 -> 134.70 MHz), but MULT18X18D usage scales
linearly and reaches 88% of the LFE5U-45F's 72 DSPs at N=8 while
LUT/FF stay under 6% -- DSP, not LUT/FF/routing, is the first hard
ceiling on N_PROCESSORS at P_IN=8 (decisions.log DEC-0005). Measured
via a dedicated synthesis-only timing harness after the array's wide
per-processor buses were found to exhaust the device's TRELLIS_IO pin
budget as a bare top-level module beyond N=1 (errors.log ERR-0005) --
not a logic limit, an artifact of testing the array in isolation
before the Memory Manager/Director (M4/M5) exist to consume those
ports on-chip.

Full log trail (development/experiments/errors/decisions/simulation/
synthesis/timing/benchmark.log) in hardware/v2/logs/ per the project's
logging mandate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 14:15:11 +02:00
micheleandClaude Sonnet 5 dc0b331d3e feat(v2): scaffold hardware/v1 frozen baseline + M1 Neural Processor
Begins the V2 Neural Multiprocessor / Dataflow architecture per
docs/v2-description.md, per explicit user request to freeze V1 and
start V2 development, copying from V1 what's needed.

Scaffold:
- hardware/v1/: byte-exact, read-only copy of the current V1 codebase
  (rtl, testbenches, tools, constraints, a representative subset of
  synthesis results, and reference docs) -- verified identical via
  diff/cmp against the live top-level tree before being made
  filesystem-read-only. The live top-level tree is untouched and
  remains the project's "production" V1 (see hardware/v1/README.md
  and hardware/v2/logs/decisions.log DEC-0001 for why copy-not-move).
- hardware/v2/: mandatory structure (rtl/sim/constraints/synthesis/
  reports/scripts/logs/docs) plus the full logging system required by
  the spec (development/architecture/simulation/synthesis/timing/
  benchmark/decisions/experiments/errors.log).

M1 -- Neural Processor (hardware/v2/rtl/neural_processor.v):
- 8-stage pipelined perceptron unit (P_IN=8): input align, 8
  multipliers, 3-level adder tree, accumulator, bias+activation, INT8
  saturation. Genuine 1-tile/cycle throughput, not just a wider
  combinational datapath.
- 7-state FSM (NP_IDLE..NP_ERROR per docs/v2-description.md §6, with
  4 baseline states merged into NP_WAIT_OPERANDS -- see
  decisions.log DEC-0002); valid/ready/data/last stream interfaces
  per §7.
- Bit-exact vs the frozen hardware/v1/rtl/neuron_parallel.v + mac8.v
  + mac_unit.v: 7/7 tests pass (hardware/v2/sim/tb_neural_processor.v),
  covering regular/mixed-sign/extreme-INT8 vectors, both activations,
  a zero-idle-gap back-to-back-tiles throughput check, and an 8-tile
  job -- verified with Verilator (see below for why).
- Real synthesis + place&route (Yosys + nextpnr-ecp5): 0 CHECK
  problems, Fmax 183.12 MHz at ACC_WIDTH=32 (PASS at 80MHz, ~3x V1's
  isolated PARALLEL=8 Fmax of 61.71 MHz) and 176.21 MHz at ACC_WIDTH=24
  (a user-requested comparison experiment, also bit-exact-verified;
  see experiments.log EXP-0001/EXP-0002 and benchmark.log).

Three real bugs found and resolved during M1 development (full
diagnostic record in errors.log):
- Two independent, reproducible Icarus Verilog v13.0 scheduling
  defects (ERR-0001, ERR-0002) that silently produced wrong simulation
  results for standard sequential Verilog -- confirmed via Verilator
  5.050 giving correct results on the same minimal repros. Verilator
  is now the trusted simulator for hardware/v2/ (decisions.log
  DEC-0004); Icarus's affected protocol-violation check was removed
  from the RTL and deferred architecturally to the Neural Director
  (DEC-0003) rather than chased further.
- One real RTL bug (ERR-0003): last0 wasn't gated like valid0,
  letting a "last tile" tag leak into the pipeline ahead of its
  actual valid tile on back-to-back jobs. Fixed and verified.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-05 14:06:53 +02:00
micheleandClaude Sonnet 5 07a48e401f fix: close 7 zero-value/mid-run guard gaps found in re-certification campaign
Fixes all 7 bugs found in the FPGA-Neural re-certification campaign
(docs/validation/bugs.md, CERTIFICATION.md), per campaign policy that
fixes land as a commit separate from the analysis work (commits
313a199..77e74db):

- BUG-005 (CRITICAL): layer_sequencer.v -- RUN_NETWORK(num_layers=0)
  ran through 256 fabricated layers reading arbitrary PSRAM data as
  descriptors. Now an immediate no-op.
- BUG-007 (CRITICAL): spi_engine.v -- SET_NET_TYPE received mid-run
  remapped the arbiter mux and hung the in-progress engine. Now
  rejected while graph_busy/seq_busy, verified not to partially apply.
- BUG-002 (MEDIA): neuron_parallel.v -- N_INPUTS=0 bypassed the
  elaboration-time guard, leaving x_bus/w_bus undriven. Guard extended
  to reject N_INPUTS==0.
- BUG-003 (MEDIA): neuron_parallel.v -- n_inputs_real=0 at runtime had
  inconsistent behavior across repeated runs. Now an explicit early-out
  via the existing "finishing" completion path.
- BUG-004 (BASSA): neuron_memory.v -- n_neurons_real=0 silently ignored
  the limit. Fixed at all three entry points into the vulnerable
  termination checks (STATE_READ_X, STATE_READ_W, and the X->W
  dispatch).
- BUG-006 (BASSA): graph_engine.v -- num_neurons_graph=0 relied on an
  incidental guard rather than a real one. Now an explicit no-op.
- BUG-001 (INFO): removed sim/top.v, confirmed dead code from the
  pre-INT8 Q8.8 era.

Every bug-reproduction testbench is rewritten from observe-only to
hard-assert the fixed behavior (sim/*_bug00[2-7]*_tb.v), verified
individually and via a full regression (44 testbenches, 43 PASS, 0
FAIL/ERROR, 1 benchmark by design). Re-verified on the real toolchain
(Yosys synth_ecp5 + nextpnr-ecp5): 0 constraint errors, Fmax 68.65 MHz
(was 67.91 MHz, within known placement noise), critical path
structurally unchanged (neuron_parallel/mac8 accumulator carry chain).

Updates docs/validation/bugs.md and CERTIFICATION.md to reflect the
resolved state, and docs/FPGA-NeuralNetwork-Engine.md + the LaTeX
datasheet (IT/EN) with inline notes on each fixed edge case, closing
the datasheet/RTL gap flagged in C.13 of the original certification.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 20:29:05 +02:00
micheleandClaude Sonnet 5 77e74db637 docs: final certification verdict for the re-certification campaign
Consolidates Fase 0 + C.1-C.14 + D into one verdict: the project is
NOT certifiable without reservations as a whole. Core arithmetic
datapath is solid (exhaustively certified where possible); control/
sequencing/arbitration is correct on the happy path (40 real tests
pass via an independent regression harness). 7 real bugs found, all
traceable to one systemic pattern (unguarded "real=0" boundary values
plus one unguarded mid-run configuration write). 2 of 7 are CRITICAL
(BUG-005, BUG-007), reachable via documented SPI opcodes under
plausible conditions, with real PSRAM corruption or inference-hang
risk. None were documented before this campaign.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 19:59:58 +02:00
micheleandClaude Sonnet 5 8d20a7bfbf docs: certify C.9-C.14 + cross-cutting analyses D
C.9 (pinout): certified citing this session's own F1-F7 work; also
regenerated a real bitstream for the CURRENT full-system build
(ecppack, 0 errors, real part header) rather than relying on an older
pre-flash build's bitstream.

C.10 (timing): certified citing Fmax re-measured at every structural
change this session (54.58->75.30->73.88->66.68->67.91MHz), critical
path re-verified identical at every re-synthesis.

C.11 (toolchain): full RTL->Yosys->nextpnr-ecp5->ecppack flow re-run
on the current build, 0 errors at every stage. Real-silicon behavior
explicitly declared NOT certifiable (no physical hardware available).

C.12 (netasm): certified citing 20/20 tests plus a real hardware
round-trip already demonstrated earlier this session.

C.13 (datasheet/RTL coherence): real, current discrepancy found and
declared -- none of this campaign's 7 bugs (BUG-001-007) are yet
mentioned in the datasheet or engine spec doc. Correct and expected
(found after those docs were last updated), flagged as required
follow-up after the campaign completes.

C.14: page-mode PSRAM and the flash subsystem are actually COMPLETE,
not "in progress" as the certification prompt assumed -- verified
against real repo state.

D (cross-cutting): CDC certified via C.7. Reset scheme found by
exhaustive grep across all 20 RTL files to be synchronous-only
everywhere (no posedge rst anywhere) -- a real fact, differs from the
prompt's async-assert/sync-deassert phrasing, not a defect. FSM: the
campaign's own BUG-002-007 ARE FSM defects, found and documented, but
no exhaustive reachability analysis of every FSM was done (stated
reserve). Width/overflow: 2 real cases found across the whole session.
Lint: full-system Yosys check, one real warning (known PSRAM tristate),
zero accidental latches confirmed signal-by-signal (25 explicit
confirmations). Determinism: no real non-determinism found (the one
apparent inconsistency, BUG-003, traced to genuinely different test
stimuli between attempts, not unstable behavior for identical stimuli).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 18:53:58 +02:00
micheleandClaude Sonnet 5 f0a66363de test: certify spi_neuron_top mux/pins (C.8), find critical BUG-007
Legitimate dispatch mux and data_ready_n/irq_n pins certified via
existing pre-session tests.

BUG-007 (CRITICAL), confirmed end-to-end over real simulated SPI:
SET_NET_TYPE has no check against graph_busy/seq_busy in
rtl/spi_engine.v, and rtl/spi_neuron_top.v's arbiter Port C mux
selects between graph_engine/layer_sequencer purely combinationally on
the current net_type value -- not latched to whichever engine actually
started the in-flight run. Started a valid graph RUN_NETWORK, sent
SET_NET_TYPE(dense) immediately after (before completion): STATUS.busy
gets stuck (30+ consecutive polls with no done/err, vs. ~12-25us normal
completion) -- the graph engine is left waiting for a ram_ready that
never arrives via the now-disconnected mux path. Also verified
recovery: RESET during the hang brings the system back to a fully
working state (a subsequent legitimate dense op completes normally) --
not a permanent lockup, but plain STATUS polling alone would never
unstick without a host-side RESET fallback.

Full regression: 40/40 real tests pass, 1 new observational test
deterministically reproduces BUG-007 and verifies RESET recovery.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 18:50:06 +02:00
micheleandClaude Sonnet 5 95849c9002 docs: certify SPI slave/engine (C.7), confirm len=0 is properly guarded
Certified via existing extensive pre-session coverage (10 tests in
spi_engine_tb.v, CDC ratio-variation test in spi_slave_tb.v, explicit
unknown-opcode-as-NOP handling), re-confirmed passing in Phase 0.

Targeted check given this campaign's recurring pattern (missing
zero-value guards, BUG-002 through 006): WRITE_RAM/READ_RAM len=0 IS
explicitly guarded (rtl/spi_engine.v:817, transitions to ST_IGNORE) --
confirms the project does not systematically lack these guards, the
gap is specific to the modules already flagged.

No new bugs found in this aspect.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 14:54:56 +02:00
micheleandClaude Sonnet 5 6db44efcfd test: certify graph_engine gather/guard (C.6), find related BUG-006
Gather/padding/src_id<out_id guard certified via existing solid
pre-session tests (graph_engine_tb.v checks act_buffer contents via
hierarchical reference, not just final output; graph_engine_guard_tb.v
covers 4 adversarial cases incl. recovery).

BUG-006 (LOW severity): num_neurons_graph=0 shares BUG-005's exact
root cause (neuron_idx is a full 16-bit register, no guard), but
graph_engine's existing per-edge src_id<out_id guard incidentally
catches most garbage-data patterns fast (err at cycle 58 for a
non-trivial test pattern, vs. layer_sequencer's 21761-cycle full run
in BUG-005) -- not a designed protection for this case, so not closed
as a non-issue, but lower severity given the observed practical risk.
Not run to full 65536-iteration completion (impractical for this
campaign's time budget) -- limitation stated explicitly.

Full regression: 40/40 real tests pass, 1 new observational test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 14:52:52 +02:00
micheleandClaude Sonnet 5 f81d7fa1b3 test: certify layer_sequencer chain (C.5), find critical BUG-005
Layer chain / ping-pong / busy-done certified via the existing
pre-session test, which already verifies the actual ping-pong buffer
address used (not just the value) and correct busy/done timing.

New finding, BUG-005 (CRITICAL): run_num_layers=0 has no guard at
compile time or runtime, and unlike BUG-002's 1-bit group_index (which
can never represent the wraparound value), layer_idx here is a full
8-bit register that naturally reaches it. Confirmed empirically with a
minimal neuron_memory stub: RUN_NETWORK(0) runs through all 256
possible layer indices (21761 cycles), reading arbitrary PSRAM bytes
far past the real descriptor table as if they were valid layer
descriptors, running real neuron_memory passes on them, and writing
results to ping-pong buffer addresses derived from that arbitrary
data. More severe than BUG-002/003/004: reachable via a single
documented SPI opcode (RUN_NETWORK), real PSRAM corruption risk rather
than just a hang or wrong result. Root cause fully isolated, not just
the symptom.

Full regression: 40/40 real tests pass, 1 new observational test
(no pass/fail by design) deterministically reproduces BUG-005.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 14:47:41 +02:00
micheleandClaude Sonnet 5 b528901510 test: certify mem_arbiter priority order (C.4)
Priority B>C>A>D confirmed correct with distinguishable per-port data
(not just "someone got served" but "the right requester got its own
data back") across 4 contention scenarios. D-alone case confirms low
priority does not mean never granted.

Found and fixed a real race in the test harness itself: blocking
assignments withdrew loser requests in the same clock edge meant to
grant the winner, racing the DUT's own synchronous block -- dut.owner
never left SEL_NONE, every wait() blocked forever. Fixed by switching
request-signal drives to non-blocking assignments throughout.

Documented (not filed as a bug) that D can starve indefinitely under
sustained continuous B contention -- standard behavior for a
fixed-priority arbiter with no aging, and explicitly outside the
header's own stated operating assumption (B/C temporally disjoint in
normal operation). Flagged the header's "never starves or corrupts
A/B/C" wording as ambiguous about whether it promises D's own
progress.

Full regression: 40/40 real tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 14:41:44 +02:00
micheleandClaude Sonnet 5 3dd75e9e0d test: certify memory subsystem addressing (C.3)
int8_memory_access.v byte<->word conversion and byte-lane selection:
exhaustive 2048-address test + 6 real read/write round-trips through
the FSM handshake. 2054/2054 checks, 0 mismatches, after fixing two
bugs in the test harness itself (a same-timestep race reading a
non-blocking update one iteration late, and a behavioral memory stub
that ignored byte-lane enables on write) -- both documented as
test-side, not RTL, issues.

memory_interface.v and psram_controller.v not re-verified from
scratch: cited against coverage already established/re-confirmed
earlier in this same session (page-mode/tCEM against the ISSI
datasheet, a real pre-existing power-up request-loss bug found and
fixed), re-run clean via the Phase 0 regression harness rather than
trusted from WORKLOG text alone.

Full regression: 39/39 real tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 14:33:05 +02:00
micheleandClaude Sonnet 5 14c8d87194 test: certify runtime width early-termination (C.2), document BUG-003/004
n_inputs_real/n_neurons_real early termination for valid values is
certified real: a "poison" region (data that would saturate the result
if read past the claimed limit) confirms no over-read, cycle counts
scale proportionally. n_inputs_real non-multiple-of-PARALLEL at runtime
matches the documented silent-truncation risk exactly.

n_inputs_real=0 / n_neurons_real=0 (BUG-003/004): confirmed incorrect
behavior in every repetition, but the exact triggering mechanism was
NOT fully isolated -- nearly-identical repeated tests produced
different symptoms (clean hang vs. silently processing the full build
width vs. a third cycle count matching neither). Reported in full,
including the inconsistency itself, rather than picking the cleanest
result. The two new permanent testbenches reflect this honestly: the
solid early-termination checks are hard assertions, the n_*_real=0
probe is deliberately observe-only given the non-deterministic result.

Full regression: 38/38 real tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 14:27:14 +02:00
micheleandClaude Sonnet 5 20b0b1f4c0 test: certify mac_unit/mac8 datapath (C.1), confirm real N_INPUTS=0 guard gap
mac_unit.v: exhaustive unit test (all 65536 (x,w) combinations at
DATA_WIDTH=8, plus 486 boundary acc_in vectors) against an independent
Python oracle (tools/validation/mac_oracle.py). 66022/66022 match, 0
reserves.

mac8.v: first-ever dedicated unit test (previously only indirect
coverage at whatever single PARALLEL neuron_parallel_tb.v happens to
use). Verified at PARALLEL=2/8/32 with structural adversarial vectors
(catches swapped/duplicated tree wiring), 300 random INT8 pairs per
PARALLEL with realistic accumulating acc_in, and worst-case magnitude
adversarial vectors. 939/939 match.

Confirms BUG-002 (N_INPUTS=0 bypasses the N_INPUTS%PARALLEL elaboration
guard) is real, on both simulation and real Yosys synthesis -- root
cause: [DATA_WIDTH*N_INPUTS-1:0] becomes [-1:0] for N_INPUTS=0, which
both tools treat as a genuine 2-bit undriven vector rather than
collapsing to zero width. Includes a documented self-correction: the
first verification attempt produced a false "hang" using an invalid
one-shot late check of a single-cycle done pulse -- caught by
reproducing the same false result on a known-good sanity config before
trusting it.

Full regression re-run clean after adding 3 new testbenches: 36/36 real
tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 13:42:57 +02:00
micheleandClaude Sonnet 5 313a1994a4 docs: certification campaign Phase 0 -- real inventory, new regression harness
Builds tools/run_regression.py (no reproducible regression script existed
before -- every prior "N testbenches, all pass" claim was assembled by
hand). Resolves each testbench's dependencies by static analysis of
instantiation sites, not from memory, then compiles+runs everything fresh.

Independently re-verifies the 33-testbench regression clean (0 real
failures) after fixing two false negatives in the harness itself (two
tests are deliberate compile-time-failure negative tests, one file is a
benchmark with no pass/fail verdict by design -- confirmed by reading
each file's own header, not assumed).

Findings recorded in docs/validation/: sim/top.v is dead code (references
a removed FRAC_BITS parameter from the pre-INT8 Q8.8 era); mac_unit.v/
mac8.v have no dedicated unit testbench, only indirect coverage; the
N_INPUTS%PARALLEL elaboration guard does not mathematically cover
N_INPUTS=0 (open finding, not yet confirmed reachable -- BUG-002).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 13:26:11 +02:00
micheleandClaude Sonnet 5 b029e3d95a fix: make flash SPI bus electrically independent, drop USRMCLK/CCLK reuse (Phase F7)
The flash subsystem's SCLK previously reused the boot config-SPI's CCLK
pad via the ECP5 USRMCLK primitive to save one pin. This made the
"exclusive flash bus" claim misleading (SCLK still depended on the
config engine's own pad electrically) and carried an unresolved
verification gap (USRMCLKTS pad-enable timing never checked against
the primary Lattice sysCONFIG Usage Guide).

flash_sclk is now a genuine 4th ordinary GPIO pin (E3, bank 7), added
purely additively to the real .lpf (git diff: one new line, no existing
ball moved). The flash bus is now 4 fully independent wires
(sclk/mosi/miso/cs_n), zero pins shared with any ECP5 config primitive
-- confirmed by the full-system synthesis reporting USRMCLK 0/1 (0%)
utilisation.

All 33 project testbenches re-run clean after the port rename (no
functional change, only sclk_sim -> sclk). Full-system real synthesis
re-verified: 0 constraint errors, Fmax 67.91MHz (up slightly from
66.68MHz, same critical path, not a regression).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 10:16:31 +02:00
micheleandClaude Sonnet 5 97a21be240 feat: flash boot/persistence subsystem (SPI master, copy engine, CRC32 slot catalog)
Adds FPGA-exclusive access to the onboard W25Q128JV SPI NOR flash for
weights/bias/network persistence, layered as spi_flash_master (raw SPI,
USRMCLK-driven) -> flash_copy_engine (flash<->PSRAM streaming, erase-
before-write, Page Program loop) -> flash_slot_manager (16-slot catalog
with CRC32), exposed via 8 new SPI opcodes (0x40-0x47). Fixes two
pre-existing bugs found during bring-up: a psram_controller.v request
lost during power-up, and a one-cycle-pulse race in the PSRAM arbiter
request handshake. Full simulation + real Yosys/nextpnr-ecp5 synthesis
verification (0 errors, Fmax 66.68MHz) in WORKLOG.md and
docs/FPGA-Neural-Flash-Subsystem-Verification.md.

Also updates docs/pinout to reflect the 56-signal real .lpf (3 new
flash pins) and documents the WRITE_RAM/READ_RAM host backpressure
risk found while testing this subsystem.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
2026-09-04 08:37:19 +02:00
micheleandClaude Sonnet 5 55c827bedf feat: PSRAM page-mode reads + graph engine (Type #2) + real pinout/IRQ pins
PSRAM page-mode read burst support in psram_controller.v: enables the
ISSI IS66WVE4M16EBLL-70BLI's page mode via its configuration-register
software-access sequence at boot (disabled by default on the real
chip), then keeps CE#/OE# asserted after a read so a same-page
continuation only pays tAPA (20ns) instead of a full tAA (70ns)
random access, with automatic tCEM-safe session closing. Only a WRITE
closes the page -- byte-enable changes do not, since
int8_memory_access.v alternates them on nearly every access and an
early implementation attempt that treated them as a close condition
measured a real regression (53.25->61.25 cycles/edge) before being
corrected (53.25->37.53 cycles/edge, +42% gather bandwidth).
sim/psram_model.v gained independent tAPA/tAA and tCEM enforcement
(with a real Verilog same-timestep event-ordering race found and
fixed via a #0 sync) so the regression proves real timing compliance,
not just data correctness. New sim/psram_page_mode_tb.v; full 26-file
regression suite re-run clean. Real nextpnr-ecp5 Fmax re-measured on
the full spi_neuron_top system: 75.73MHz (P2, up from 55.59MHz) and
65.13MHz (P8) -- still under the 80MHz target but not regressed, with
the critical path confirmed (not assumed) to remain entirely inside
neuron_parallel's accumulate chain, never psram_controller.

Also includes this session's other already-validated work: the graph
engine (Type #2 sparse-graph network: act_buffer, graph_engine,
netasm host assembler), real CABGA381 pinout (.lpf, place&route
verified) and physical IRQ_N/DATA_READY_N pins, and Phase 7 timing
closure logs -- all previously uncommitted, documented in WORKLOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LH3jPeJ3eFMfF2v8SQhpkk
2026-09-03 17:12:05 +02:00
micheleandClaude Sonnet 5 7e2711fa27 feat: widen ADDR_WIDTH to 23 bits for full 8MB PSRAM addressing
Bumps ADDR_WIDTH's default from 22 to 23 bits across every RTL
module (neuron_memory, layer_sequencer, spi_engine, spi_neuron_top,
mem_arbiter, int8_memory_access, memory_interface, psram_controller,
memory_model) and every testbench that mirrors it, so the system's
byte-address space reaches the full 8 MiB the recommended PSRAM part
(ISSI IS66WVE4M16EBLL-70BLI, docs/FPGA-Neural-Hardware-Design.md §3)
actually provides -- previously only 4 MiB (half the chip) was
reachable, since int8_memory_access.v's byte->word address shift
(addr >> 1) turned the old 22-bit byte address into only 21 real word
bits, one short of the chip's real 22-bit word address (A0-A21). At
23 bits, that same shift lands exactly on all 22 chip address lines,
so the whole part is usable now instead of deferred to a future
widening.

Also fixes a stray 22'd11-sized literal in layer_sequencer.v's
descriptor-table address increment (numerically already safe via
Verilog's zero-extension, but now correctly unsized so it always
matches ADDR_WIDTH instead of silently assuming 22).

Updated docs/FPGA-NeuralNetwork-Engine.md's SPI protocol address-field
note (23 bits, top 1 reserved bit instead of 2) and
docs/FPGA-Neural-Hardware-Design.md's PSRAM section (the "chip has
one spare address line" framing is gone now that all 22 are wired
and used).

Full regression (all 11 ADDR_WIDTH-touching testbenches, plus a
Yosys elaboration check of spi_neuron_top with the new default and
no override) passes clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 21:00:46 +02:00
micheleandClaude Sonnet 5 f6edc01613 docs: Phase 7 placement-seed sweep + hardware design document
Phase 7 (docs/FPGA-NeuralNetwork-Engine.md): re-ran nextpnr-ecp5 on
the already-synthesized Phase 5 spi_neuron_top netlists (top.json
reused, only placement re-seeded) at --seed 1/2/3 for both P8 and P2.
Both land in a tight band regardless of seed (P8: 39.5-40.6 MHz,
2.6% spread; P2: 42.5-45.0 MHz, 5.8% spread) -- confirms the Phase 5
timing shortfall is a real structural bottleneck, not placement
noise, unlike the much smaller same-tier benchmark design (<2%
utilization, huge placer freedom, genuinely noisy). Corrected the
earlier "pipeline the saturate stage" candidate fix, which targeted
Phase 4's critical path and not the one Phase 5's logic actually
shifted to; block RAM for x_mem/w_mem remains the leading candidate,
not yet implemented.

New docs/FPGA-Neural-Hardware-Design.md: draft hardware design doc
for a board carrying the project's actual target device
(LFE5U-45F-8BG381C) plus the parallel PSRAM rtl/psram_controller.v
is written for. Covers: why not the basic-ecp5-pcb reference board
(wrong package/speed grade, no RAM), a real I/O pin budget from
Lattice's own CABGA381 pinout table, a researched PSRAM part
(ISSI IS66WVE4M16EBLL-70BLI -- 70ns access matches the controller's
timing assumption exactly, with a note on the byte/word address
shift in int8_memory_access.v so the chip's top address line is
correctly left as spare headroom, not a wiring error), clock
(16 MHz, no PLL exists yet so CLK_FREQ_MHZ must match whatever
oscillator is fitted), power/config reusing the reference board's
proven circuitry and errata (config-SPI pin can't double as the
application SPI interface), and a BOM/open-items list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 20:54:52 +02:00
micheleandClaude Sonnet 5 a918c3f1e9 feat: configurable activation functions + runtime-configurable network topology
Two related Phase 5 additions, both threaded the same way (a new
runtime field defaulting to the pre-existing behavior, settable
per-layer via the descriptor table or per-run via SET_BASE):

Configurable activation functions:
- neuron_parallel.v gains a 2-bit `activation` port (ACT_NONE =
  linear + two-sided INT8 saturate, ACT_RELU = the original
  hardwired behavior, kept as the default so every pre-existing
  caller/testbench is unaffected), threaded through neuron_memory.v.
- spi_engine.v: SET_BASE sel=6 (single-layer path); the descriptor
  table gains a 7th byte (multi-layer path).
- Verified in neuron_parallel_tb.v (negative pass-through + negative
  saturation to -128) and end-to-end in
  spi_neuron_top_runnetwork_tb.v (a real negative accumulator that
  ACT_RELU would clamp to 0 comes through unclamped under ACT_NONE,
  over real SPI/RAM).

Runtime network width (one bitstream, any topology up to its
build-time max, entirely host-configured over SPI):
- neuron_parallel.v gains n_inputs_real, bounding its MAC group loop
  (n_inputs_real/PARALLEL groups instead of the fixed build-time
  count). neuron_memory.v gains n_inputs_real/n_neurons_real,
  bounding its X/W RAM-read loop and its neuron loop. All default to
  the build-time max, so unconnected callers are unaffected.
  n_inputs_real must stay a multiple of PARALLEL (same constraint
  N_INPUTS itself is held to at elaboration time, now the caller's
  runtime responsibility).
- spi_engine.v: SET_BASE sel=7/8 (single-layer path); the descriptor
  table grows to 11 bytes/layer (+n_inputs_real +n_neurons_real,
  multi-layer path) -- layer_sequencer.v also now copies only
  n_neurons_real bytes into the ping-pong buffer, not the full
  build width.
- This is real early termination, not bookkeeping: no RAM
  zero-padding needed for the unused tail, and it measurably
  completes faster. neuron_parallel_tb.v TEST 7: 3 cycles vs 6 for a
  reduced-vs-full run, with garbage loaded into the skipped lanes to
  prove they're never read. neuron_memory_tb.v TEST 5: through the
  real PSRAM stack, 209 cycles vs 788. layer_sequencer_tb.v proves a
  reduced n_neurons_real shortens the ping-pong copy-out itself
  (bytes beyond the real count stay untouched, not just differing).

docs/FPGA-NeuralNetwork-Engine.md: §8.1 opcode/SET_BASE table, new
"Runtime network width" subsection, Phase 5 checklist, Current
Status table, and the "Core architectural principle" statement
updated to reflect that topology (not just trained parameters) is
now host-configured at runtime up to a build-time ceiling.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 20:18:24 +02:00
michele 1ddaa9adfd feat: real-toolchain Fmax check for Phase 5 spi_neuron_top
Yosys + nextpnr-ecp5 synthesis of the extended spi_neuron_top.v
(layer_sequencer + mux + arbiter Port C wired in), speed grade -8,
N_INPUTS=32/N_NEURONS=1: PARALLEL=8 -> 40.57 MHz FAIL, PARALLEL=2 ->
42.54 MHz FAIL. Both worse than Phase 4 alone (~52.58/~55.85 MHz for
the identical configs) -- the Phase 5 wiring cost real timing
headroom. Critical path also shifted from Phase 4's saturation-
comparator finding to neuron_memory's x_mem/w_mem LUT-RAM read-mux
tree feeding the accumulator. Full analysis and candidate next steps
(block RAM for x_mem/w_mem, pipelining, a seed sweep to separate
placement noise from a structural bottleneck) recorded under Phase 7
in docs/FPGA-NeuralNetwork-Engine.md.
2026-09-02 20:17:52 +02:00
michele cfd5e98a0e feat: add FPGA-Neural benchmark tooling + same-tier ECP5 P2/P4/P8 results
tools/fpga_benchmark.py: parametric Yosys + nextpnr-ecp5 benchmark
harness for the LFE5U-45F-8BG381 (speed grade -8, 80 MHz target),
sweeping PARALLEL over the neuron layer and parsing Fmax/LUT4/DFF/DSP
utilization out of the nextpnr report into JSON/CSV.

synth/ecp5/p2, p4, p8: real synthesis+PnR results backing the
same-price-tier FPGA comparison (P2: 87.88 MHz PASS, P4: 75.01 MHz
FAIL, P8: 147.62 MHz PASS -- non-monotonic, dominated by placement
noise since the whole design uses <2% of the device's LUT4 fabric at
every setting, and P8 notably maps to 0 DSP blocks vs 8/16 for P2/P4).

synth/ecp5/top.v: benchmark harness top-level, reworked to generate
deterministic non-constant X/weights/bias via `keep`-attributed
generate blocks so Yosys can't constant-fold the datapath away.

Also adds .gitignore for Python's __pycache__/*.pyc.
2026-09-02 19:48:03 +02:00
michele 233d6ff7fb feat: complete Phase 5 multi-layer network (RUN_NETWORK) + fix STATUS race
Wires the already-present layer_sequencer.v into the SPI stack:

- spi_engine.v: RUN_NETWORK opcode (0x23) + SET_BASE selectors for
  table_base/buf_a_base/buf_b_base; STATUS.busy/done extended to
  track the sequencer (seq_busy/seq_done) alongside neuron_memory
  directly, so done latches on the last layer only.
- spi_neuron_top.v: instantiates layer_sequencer, muxes
  neuron_memory's control inputs between it (while seq_busy) and
  spi_engine's direct-drive path (legacy single-layer mode), wires
  the sequencer's own RAM master to mem_arbiter's Port C.

Found and fixed a real race while writing the end-to-end test: STATUS's
sticky/clear-on-read done bit read its value live/combinationally
during transmission and cleared unconditionally on any STATUS read.
A done_event landing mid-transmission of a STATUS response byte could
be silently dropped -- the host would receive a stale byte while the
sticky bit was cleared regardless, hanging any host polling STATUS in
a loop. Present since Phase 4, not RUN_NETWORK-specific; only
surfaced under this test's continuous polling. Fixed by latching a
status_snapshot at opcode-accept time and gating the clear on what
was actually transmitted.

Tests: spi_engine_tb.v gains RUN_NETWORK/SET_BASE opcode tests (K/L);
new layer_sequencer_tb.v unit-tests the sequencer FSM directly
(descriptor table, ping-pong buffer addressing, byte-exact copy-out);
new spi_neuron_top_runnetwork_tb.v drives a real 2-layer network over
simulated SPI end to end (real neuron_memory + PSRAM, hand-computed
expected output) and confirms the legacy single-layer path still
works afterward. All existing testbenches still pass.
2026-09-02 19:47:36 +02:00
micheleandClaude Sonnet 5 a2bd60e305 feat: complete Phase 4 SPI RTL (engine, arbiter, top) + real-RAM e2e test
Implements the rest of the SPI interface (docs §8.1) on top of
spi_slave.v from the previous commit:

- rtl/spi_engine.v: opcode FSM + register bank, all 8 opcodes (NOP,
  WRITE_RAM, READ_RAM, RESET, SET_BASE, START, STATUS, READ_OUTPUT,
  READ_CONFIG). tx_byte is driven combinationally from live state
  (not reactively on tx_byte_req), applying the prefetch-vs-consume
  contract documented on spi_slave.v. STATUS.done is a sticky,
  clear-on-read latch. RAM master port uses the same byte-level
  convention as neuron_memory.v's external mem_* port.
- rtl/mem_arbiter.v: fixed-priority (neuron_memory > spi_engine)
  grant-and-forward arbiter sharing one byte-level memory port
  between spi_engine's WRITE_RAM/READ_RAM and neuron_memory's own
  X/W/bias reads during a run.
- rtl/spi_neuron_top.v: full integration -- spi_slave -> spi_engine
  -> mem_arbiter -> a single shared int8_memory_access ->
  memory_interface -> psram_controller -> PSRAM pins. neuron_memory's
  rst is global rst OR'd with the RESET opcode's soft-reset pulse.
  The host has no direct electrical path to the RAM, only through
  this chain.

Testing:
- sim/spi_engine_tb.v: 10 tests (one per opcode + WRITE_RAM/READ_RAM,
  START idle-vs-busy, STATUS sticky/clear-on-read, extra-MOSI-bytes-
  ignored, back-to-back transactions) against a synthetic 2-cycle-
  latency RAM model, isolating the opcode FSM from PSRAM timing.
  Found and fixed two testbench-only bugs (RTL needed no change):
  the same delta-zero clock-edge race as spi_slave_tb.v (blocking
  `nm_done=1` landing on the same sim time as a posedge -- fixed via
  negedge-based pulsing) and a missing RAM sentinel initialization.
- sim/spi_neuron_top_tb.v: end-to-end test against the **real**
  psram_model.v (not a mock) -- RESET/READ_CONFIG/WRITE_RAM/
  READ_RAM/SET_BASE/START/STATUS/READ_OUTPUT all driven purely over
  simulated SPI. 3/3 scenarios (sum, saturation, ReLU) pass on the
  first attempt; confirms the arbiter and shared byte<->word bridge
  are correct against real PSRAM timing, not just a synthetic mock.

Real-toolchain verification (Yosys + nextpnr-ecp5 + ecppack):
spi_slave.v and spi_engine.v synthesize clean and comfortably clear
80 MHz in isolation (403 MHz / 191 MHz, no DSP usage). The full
spi_neuron_top.v integration, however, does NOT meet 80 MHz
(~52-56 MHz depending on PARALLEL) -- the critical path is entirely
inside neuron_parallel.v's existing saturation comparator (no
contribution from the new SPI/arbiter logic), but its routed delay
is ~57% worse than in the isolated benchmark due to placement/
routing congestion once SPI + PSRAM logic shares the fabric with
it, not resource exhaustion (2% DSP usage). Documented as a Phase
4/7 finding in docs/FPGA-NeuralNetwork-Engine.md -- a floorplanning/
pipelining problem for Phase 7, not a functional-correctness issue
(verified independently in simulation against real PSRAM timing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 15:44:03 +02:00
micheleandClaude Sonnet 5 d716eb04dd feat: add spi_slave.v physical layer (Phase 4 SPI RTL, part 1/N)
First real RTL piece of the SPI interface (docs §8.1 protocol
draft): the physical layer only -- Mode 0 (CPOL=0, CPHA=0),
MSB-first, byte-level shift register with a 3-stage CDC synchronizer
for SCLK/MOSI/CS_N (the SPI master clock is asynchronous to the
FPGA system clock). Exposes rx_byte/rx_valid, tx_byte/tx_byte_req,
and cs_active/cs_start/cs_end to the (not yet written) protocol
engine.

Documented an important consumer contract on tx_byte_req: it is a
prefetch hint (fires once extra after the last byte of every
transaction, since the slave cannot know in advance whether the
master will keep clocking), not a "byte consumed" event -- a
consumer must advance any stateful pointer (e.g. a RAM read address)
on rx_valid instead, which fires exactly once per real byte
transferred.

sim/spi_slave_tb.v: bit-banged SPI master BFM (4 tests: single byte,
multi-byte in one CS period, back-to-back transactions, slower
SCLK). Two testbench-only bugs found and fixed during bring-up (RTL
itself needed no functional change beyond the tx_byte_req contract
comment): the BFM was advancing its tx queue on tx_byte_req instead
of rx_valid (see contract above), and inter-test reset pulses raced
against posedge clk (blocking `rst=1` landing on the same simulation
time as a clock edge) -- fixed by asserting/deasserting reset on
negedge clk instead.

Verified two ways: Icarus Verilog (4/4 tests pass) and the real
ECP5 toolchain used for prior benchmarks (Yosys 0.68 synth: 0
problems, 41 FF / 55 LUT4, no latches; nextpnr-ecp5 --45k --package
CABGA381 --speed 8 --freq 80: PASS, Fmax 403.23 MHz; ecppack:
bitstream generated with no errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 15:24:17 +02:00
micheleandClaude Sonnet 5 87efce3d9b docs: draft SPI protocol v1 for Phase 4 (opcodes, register map)
Phase 4 (SPI Interface) only had a high-level conceptual sequence
(RESET/CONFIGURE/LOAD.../START/WAIT/READ) with no concrete opcodes,
framing, or register map -- not enough to start RTL from. Added
docs/FPGA-NeuralNetwork-Engine.md §8.1 with a concrete v1 draft:

- SPI Mode 0, MSB-first, one opcode byte per CS-low transaction.
- Explicit length field on WRITE_RAM/READ_RAM (chosen over
  CS-edge-delimited streaming: simpler controller, just a byte
  counter).
- READ_CONFIG opcode exposing N_INPUTS/N_NEURONS/PARALLEL/
  ADDR_WIDTH/DATA_WIDTH at runtime, so one host firmware build can
  target different bitstreams.
- RESET kept as its own opcode (0x0F), distinct from NOP.
- STATUS.done documented as required to be a STICKY, clear-on-read
  bit in the SPI register bank: neuron_memory.done is a one-cycle
  pulse that a slow SPI poll would almost certainly miss otherwise.

Opcode values themselves are marked explicitly as draft/example,
not frozen -- only the framing rules and the two decisions above are
meant to stick going into Phase 4 RTL work.

No RTL or testbench changes in this commit; design-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 15:04:37 +02:00
micheleandClaude Sonnet 5 661363f637 feat: extend neuron_memory to support N_NEURONS>1 (Phase 3)
neuron_memory.v only handled a single neuron. Added an N_NEURONS
parameter (default 1, fully backward compatible) and a memory-bound
neuron loop: X is read once (shared layer input), and for each
neuron in turn W and bias are re-read from PSRAM and fed to a
single, reused neuron_parallel instance -- no change to the
validated compute datapath (neuron_parallel/mac8/mac_unit).

Addressing follows layer.v's neuron-major convention: neuron n's
weights live at w_base + n*N_INPUTS bytes, its bias at
bias_addr + n. Output changed from a single `y` port to a packed
`y_bus` (DATA_WIDTH*N_NEURONS bits, neuron-major), matching
layer.v's y_bus.

- rtl/neuron_memory.v: N_NEURONS parameter, neuron_index/
  w_group_base/bias_group_addr tracking, y_reg[] array assembled
  into y_bus, STATE_WAIT_N now loops back to STATE_READ_W for the
  next neuron instead of finishing after one.
- sim/neuron_memory_tb.v: updated to the new y_bus port
  (N_NEURONS=1 explicit); all 5 existing tests still pass unchanged,
  confirming backward compatibility.
- sim/neuron_memory_multi_tb.v: new end-to-end test (full
  memory_interface + psram_controller + psram_model stack) with
  N_NEURONS=3, validating per-neuron addressing and a single done
  pulse at the end of the sequence (scale, larger value, ReLU).
- Full regression re-run: all existing testbenches still pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 14:50:45 +02:00
micheleandClaude Sonnet 5 1a6f0ba2ef fix: guard neuron_parallel against invalid N_INPUTS/PARALLEL combos
Both Phase 2 findings (docs/FPGA-NeuralNetwork-Engine.md) shared one
root cause: GROUPS = N_INPUTS / PARALLEL is integer division. When
N_INPUTS is not an exact multiple of PARALLEL, the remainder inputs
were silently dropped from the accumulation (wrong result, no
error); when PARALLEL > N_INPUTS, GROUPS = 0 and the controller's
terminal condition was never met, hanging the neuron forever.

Added a single elaboration-time guard to rtl/neuron_parallel.v: a
`generate` block instantiates a deliberately undefined module when
N_INPUTS % PARALLEL != 0, forcing a hard failure in both simulation
and synthesis instead of a silent wrong answer or a deadlock. Valid
configurations are unaffected (the branch is never elaborated). The
validated datapath (mac8/mac_unit/accumulation/ReLU/saturation) is
untouched -- this is authorized as a scoped exception to the
"core is fixed, do not touch" project policy, for this guard only.

- sim/neuron_parallel_guard_negative_nonmultiple_tb.v and
  sim/neuron_parallel_guard_negative_degenerate_tb.v: negative tests
  that must fail to elaborate; verified both fail with the expected
  "Unknown module type" error.
- sim/parameter_sweep_tb.v: rewritten to valid-configs-only (the
  three configs that used to demonstrate truncation/hang no longer
  compile, by design); added PARALLEL=2 and PARALLEL=4 configs,
  the two best-performing values from
  docs/FPGA-Neural-Datapatch-Benchmark.md.
- Full regression re-run after the RTL change: all existing
  testbenches still pass unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
2026-09-02 14:41:49 +02:00