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
322 lines
13 KiB
Plaintext
322 lines
13 KiB
Plaintext
# V2 decisions log -- formato DEC-XXXX, mai sovrascritto (vedi README.md)
|
|
|
|
DEC-0001
|
|
|
|
DATE: 2026-09-05
|
|
|
|
DECISION:
|
|
Congelare V1 come copia separata in hardware/v1/ (sola lettura a livello
|
|
filesystem) invece di spostare (git mv) l'albero top-level esistente
|
|
(rtl/, sim/, synth/, tools/) dentro hardware/v1/.
|
|
|
|
WHY:
|
|
docs/v2-description.md §1/§34 impone una "struttura obbligatoria"
|
|
hardware/v1/ + hardware/v2/ e vieta di modificare/degradare V1. Due
|
|
strade possibili: (a) spostare fisicamente rtl/sim/synth/tools
|
|
nell'albero hardware/v1/, oppure (b) copiarli lasciando l'albero
|
|
top-level esattamente come e' oggi. Lo spostamento romperebbe tutti i
|
|
riferimenti a percorso in WORKLOG.md, docs/validation/*.md,
|
|
docs/FPGA-NeuralNetwork-Engine.md (centinaia di citazioni tipo
|
|
"rtl/neuron_parallel.v:127") e negli script (tools/run_regression.py
|
|
resta funzionante per costruzione relativa, ma altri riferimenti
|
|
documentali no) -- un costo reale senza benefico funzionale, dato che
|
|
l'obiettivo della regola e' *non perdere/alterare* V1, non *dove* vive
|
|
fisicamente. La copia raggiunge lo stesso obiettivo (baseline
|
|
funzionale/numerica/bit-exact per V2, mai modificabile) senza il
|
|
rischio di rompere la cronologia documentale esistente.
|
|
|
|
EVIDENCE:
|
|
- `diff -rq rtl/ hardware/v1/rtl/` e confronto file-per-file su
|
|
sim/*.v: 0 differenze (copia bit-esatta verificata, non assunta).
|
|
- hardware/v1/ reso sola lettura (`chmod -R a-w`) subito dopo la copia,
|
|
prima di qualunque lavoro V2.
|
|
|
|
ALTERNATIVES:
|
|
- git mv dell'intero albero rtl/sim/synth/tools sotto hardware/v1/,
|
|
poi aggiornamento di tutti i riferimenti di percorso nella
|
|
documentazione. Scartata: costo/rischio alto, beneficio nullo
|
|
rispetto all'obiettivo dichiarato della regola.
|
|
- Symlink hardware/v1/ -> ../../rtl ecc. Scartata: non garantisce
|
|
l'immutabilita' (un simlink non protegge dalla modifica dell'originale
|
|
ed e' fragile rispetto a `chmod -R a-w`).
|
|
|
|
RESULT:
|
|
hardware/v1/ creato come copia bit-esatta e sola-lettura. L'albero
|
|
top-level del repository resta la "produzione" V1 corrente, invariata,
|
|
usata anche dal resto del progetto (WORKLOG.md, docs/) esattamente come
|
|
prima di questa sessione.
|
|
|
|
STATUS:
|
|
ACCEPTED
|
|
|
|
DEC-0002
|
|
|
|
DATE: 2026-09-05
|
|
|
|
DECISION:
|
|
Collapse §6's baseline Neural Processor FSM states NP_LOAD_TILE,
|
|
NP_MAC, NP_ACCUM, NP_NEXT_TILE into the single NP_WAIT_OPERANDS state
|
|
in the actual implementation (hardware/v2/rtl/neural_processor.v),
|
|
rather than implementing them as four separate one-cycle-gated states.
|
|
|
|
WHY:
|
|
§5 explicitly states the internal datapath must be pipelined and the
|
|
goal is throughput, not minimal latency -- accepting a new P_IN-wide
|
|
tile every cycle. Gating tile acceptance behind four sequential FSM
|
|
states (each holding for exactly one cycle per tile) would recreate a
|
|
non-pipelined, one-tile-per-4-cycles controller, directly
|
|
contradicting §5/§34 ("ottimizzare il throughput effettivo"). The four
|
|
states from §6's baseline list describe what the ORIGINAL (pre-M1)
|
|
sketch assumed before the pipeline design in §5 was fully worked out;
|
|
once the datapath is genuinely pipelined, tile acceptance becomes a
|
|
single steady-state condition (operand_valid && operand_ready), and
|
|
per-tile progress is tracked by the valid/last tags flowing through
|
|
the pipeline registers, not by the outer FSM.
|
|
|
|
EVIDENCE:
|
|
EXP-0001 (bit-exact vs V1, 7/7 tests incl. a deliberate zero-idle-gap
|
|
back-to-back-tiles case, TEST 5 in tb_neural_processor.v) -- confirms
|
|
tiles are genuinely accepted one per cycle with no outer-FSM stall
|
|
between them.
|
|
|
|
ALTERNATIVES:
|
|
Literal 11-state FSM per §6's baseline list, with LOAD_TILE/MAC/
|
|
ACCUM/NEXT_TILE each a real one-cycle state gating acceptance.
|
|
Rejected: would cap throughput at 1 tile per 4 cycles, defeating the
|
|
pipeline's own purpose.
|
|
|
|
RESULT:
|
|
7-state FSM (NP_IDLE, NP_LOAD_JOB, NP_WAIT_OPERANDS, NP_FINISH,
|
|
NP_WRITE_RESULT, NP_DONE, NP_ERROR) implemented and verified.
|
|
|
|
STATUS:
|
|
ACCEPTED
|
|
|
|
---
|
|
|
|
DEC-0003
|
|
|
|
DATE: 2026-09-05
|
|
|
|
DECISION:
|
|
Remove the operand-arrival protocol-violation guard from
|
|
neural_processor.v (the check that would raise NP_ERROR if
|
|
operand_valid arrived while the processor could not consume it) rather
|
|
than continue debugging it. Defer this responsibility to the Neural
|
|
Director (M5).
|
|
|
|
WHY:
|
|
The guard's own evaluation triggered ERR-0002 (docs/v2-description.md
|
|
mandate §25-29 requires documenting this, not hiding it) -- a
|
|
reproducible Icarus Verilog v13.0 bug where the guard's condition
|
|
evaluated true despite operand_valid being independently confirmed 0.
|
|
Root cause was bisected down to a minimal FSM transition unrelated to
|
|
this specific expression (see errors.log ERR-0002), meaning the bug is
|
|
in the toolchain's scheduling, not fixable by rewording the condition.
|
|
Architecturally, a standalone Neural Processor policing its OWN
|
|
issuer's protocol is also arguably the wrong owner of that
|
|
responsibility: per §34's own division of labor ("Il Director gestisce
|
|
WHAT deve essere eseguito"), arbitrating/validating operand issuance
|
|
across possibly-multiple Neural Processors is the Director's job, not
|
|
each processor's.
|
|
|
|
EVIDENCE:
|
|
ERR-0002 (errors.log) -- the guard, and several simplified variants of
|
|
it, all misevaluated under Icarus v13.0; disabling it entirely (and
|
|
only it) restored correct behavior in every case, confirmed via
|
|
Verilator that the underlying pipeline logic was already correct.
|
|
|
|
ALTERNATIVES:
|
|
1. Keep chasing the exact Icarus root cause. Rejected for this
|
|
session: already bisected to a toolchain-level scheduling issue
|
|
independent of this specific code, further chasing would not
|
|
change the architectural need for this check to live in the
|
|
Director eventually anyway.
|
|
2. Reimplement the same check with different Verilog phrasing.
|
|
Rejected: multiple independent phrasings all reproduced the bug.
|
|
|
|
RESULT:
|
|
NP_ERROR is now reachable only via the `default:` case branch (a
|
|
genuine np_state encoding corruption) -- a real safety net, just not
|
|
exercised by operand-arrival timing. The corresponding negative test
|
|
(TEST 7) was removed from tb_neural_processor.v; the scenario is
|
|
deferred to M5's testbench (tb_neural_director.v), where the Director
|
|
is the actual issuer under test.
|
|
|
|
STATUS:
|
|
ACCEPTED
|
|
|
|
---
|
|
|
|
DEC-0004
|
|
|
|
DATE: 2026-09-05
|
|
|
|
DECISION:
|
|
Adopt Verilator 5.050 (`verilator --binary --timing`) as the primary/
|
|
trusted simulator for hardware/v2/ testbenches going forward, in
|
|
addition to (not instead of) Icarus Verilog. Cross-check any Icarus
|
|
result that looks anomalous against Verilator before concluding it is
|
|
an RTL bug.
|
|
|
|
WHY:
|
|
ERR-0001/ERR-0002 (errors.log) are two independently-reproduced Icarus
|
|
Verilog v13.0 defects that produced WRONG simulation results (not
|
|
compile errors) for straightforward, standard sequential Verilog, with
|
|
no workaround available at the RTL/testbench level for ERR-0002 short
|
|
of removing the affected logic. Verilator gave the CORRECT result for
|
|
every one of these repros. §30's rule against invented results cuts
|
|
both ways: a simulator that silently gives a WRONG "measured" result is
|
|
just as dangerous as inventing one outright -- cross-checking against
|
|
a second, architecturally different simulator (Verilator compiles to
|
|
C++, Icarus interprets bytecode -- unlikely to share the same
|
|
scheduling bug) is now mandatory whenever a hardware/v2/ testbench
|
|
shows unexpected behavior.
|
|
|
|
EVIDENCE:
|
|
- Minimal FSM repro (`if (go) st<=B;`, no tasks, no other logic):
|
|
Icarus v13.0 fails to transition on specific testbench edge-count
|
|
parities; Verilator 5.050 gives the correct result every time.
|
|
- Full hardware/v2/sim/tb_neural_processor.v: Icarus v13.0 hangs/
|
|
misbehaves even after every known-real RTL bug (ERR-0003) was fixed;
|
|
the SAME unmodified file under Verilator gives 7/7 PASS, bit-exact
|
|
vs the frozen V1 reference.
|
|
|
|
ALTERNATIVES:
|
|
1. Downgrade Icarus to an older release. Rejected: no older bottle was
|
|
cached on this machine (`brew list --versions icarus-verilog` shows
|
|
only 13.0) and fetching a specific historical formula version was
|
|
not attempted this session (time-boxed decision, revisit if it
|
|
becomes a recurring blocker).
|
|
2. Keep using only Icarus and manually work around each new defect as
|
|
found. Rejected: not sustainable across the dozens of testbenches
|
|
the full V2 roadmap requires (§20).
|
|
|
|
RESULT:
|
|
Verilator installed (`brew install verilator`, 5.050). hardware/v2/
|
|
testbenches are compiled/run with both simulators when convenient;
|
|
Verilator's result is authoritative when the two disagree, and any
|
|
such disagreement is logged here / in errors.log, not silently
|
|
resolved by picking whichever answer looks more convenient.
|
|
|
|
STATUS:
|
|
ACCEPTED
|
|
|
|
---
|
|
|
|
DEC-0005
|
|
|
|
DATE: 2026-09-05
|
|
|
|
DECISION:
|
|
Treat DSP (MULT18X18D) budget, not LUT/FF/routing, as the primary
|
|
constraint when exploring the N_PROCESSORS x P_IN trade-off space
|
|
(§16) going forward.
|
|
|
|
WHY:
|
|
Real place&route measurement (EXP-0003) shows MULT18X18D usage
|
|
scaling linearly and reaching 88% of the LFE5U-45F-8BG381's 72 total
|
|
DSPs at N_PROCESSORS=8, P_IN=8 -- while LUT4/FF usage stays under 6%
|
|
at the SAME configuration and Fmax is still comfortably above the
|
|
80MHz target (134.70 MHz). This means the naive "just add more
|
|
processors" scaling (§8/§16) hits a hard DSP ceiling around
|
|
N_PROCESSORS=9 at P_IN=8, long before LUT/FF/routing/timing become
|
|
relevant -- the opposite of what LUT/FF utilization alone would
|
|
suggest if read in isolation.
|
|
|
|
EVIDENCE:
|
|
experiments.log EXP-0003 -- MULT18X18D 8/16/32/64 (11%/22%/44%/88% of
|
|
72) at N_PROCESSORS 1/2/4/8, LUT4 under 6% throughout, Fmax PASS at
|
|
80MHz throughout (159.11/149.59/151.01/134.70 MHz).
|
|
|
|
ALTERNATIVES:
|
|
Assume LUT/FF/routing congestion would be the limiting factor (the
|
|
naive expectation for "more parallel copies of a datapath"). Rejected
|
|
by direct measurement, not assumed -- §16 explicitly requires
|
|
choosing the final configuration "sulla base del throughput effettivo
|
|
... non dell'utilizzo massimo delle risorse", and knowing WHICH
|
|
resource binds first is a prerequisite for that.
|
|
|
|
RESULT:
|
|
Future N_PROCESSORS x P_IN sweeps (§16, deferred to a dedicated
|
|
scripts/sweep/ run per §31) should budget MULT18X18D count explicitly
|
|
(N_PROCESSORS * P_IN <= ~72, minus whatever the rest of the real
|
|
system needs once M4/PSRAM integration lands) rather than only
|
|
tracking LUT/FF. A smaller P_IN with more N_PROCESSORS (or vice versa)
|
|
is a live trade-off worth exploring precisely because of this ceiling,
|
|
not merely a stylistic choice.
|
|
|
|
STATUS:
|
|
ACCEPTED
|
|
|
|
---
|
|
|
|
DEC-0006
|
|
|
|
DATE: 2026-09-05
|
|
|
|
DECISION:
|
|
memory_manager.v (M4) uses a SINGLE prefetch_engine instance,
|
|
retargeted per bank via a depth-1 pending-request register, rather
|
|
than multiple engines or a general request queue. The result
|
|
write-back (one byte per job, after the last tile) shares the same
|
|
backend port via a simple state-based mux, not a general arbiter --
|
|
because prefetch and write-back are temporally disjoint by
|
|
construction (the write only happens after prefetch_engine has
|
|
nothing left to fetch for that job).
|
|
|
|
WHY:
|
|
§13's double-buffering strategy needs at most ONE fetch "in flight"
|
|
and at most ONE fetch "queued" at any time for a SINGLE Neural
|
|
Processor consuming tiles sequentially (proven by construction: a new
|
|
prefetch is only ever queued on a tile handoff, and at most one
|
|
handoff can be pending completion of the previous prefetch before the
|
|
next one is even requested). A general multi-entry queue or a second
|
|
engine would add complexity with no present benefit. Likewise,
|
|
because this Memory Manager currently serves exactly one Neural
|
|
Processor and one job at a time, no concurrent second requester can
|
|
ever contend for the backend port with prefetch reads -- a real
|
|
mem_arbiter-style arbiter (as V1 uses for ITS OWN multi-master case)
|
|
is deferred until a scenario that actually needs it exists (multiple
|
|
Neural Processors or overlapping jobs sharing one memory_manager,
|
|
not yet built).
|
|
|
|
EVIDENCE:
|
|
errors.log ERR-0006 -- the single-entry pending register, once
|
|
correctly gated (see ERR-0006 items 1-2), handled 1-tile, 3-tile, and
|
|
5-tile jobs correctly with no queue overflow in
|
|
hardware/v2/sim/tb_memory_manager.v.
|
|
|
|
ALTERNATIVES:
|
|
1. Multiple prefetch_engine instances (one per bank), letting both
|
|
banks fetch fully in parallel. Rejected for M4: doubles DSP-free
|
|
logic for a benefit only realized when compute-tile time is
|
|
SHORTER than 2x fetch-tile time for a single engine -- not yet
|
|
measured to be the case (§22, deferred to M9), and the single-
|
|
engine design already fully hides fetch latency behind neural_
|
|
processor's own per-tile compute time in the cases tested (see
|
|
experiments.log EXP-0005 cycle counts).
|
|
2. General N-entry FIFO for pending requests. Rejected: no scenario
|
|
in the current single-processor, single-job design can ever
|
|
generate more than one pending request before the in-flight one
|
|
completes -- an N-entry queue would be complexity with no
|
|
reachable use.
|
|
3. Reuse V1's mem_arbiter.v as-is for the prefetch-vs-writeback
|
|
sharing. Rejected: mem_arbiter.v's four ports are hardcoded to
|
|
specific V1 module names/priorities (§1 already established this
|
|
pattern in DEC-0001 for the broader V1-freeze decision) -- and
|
|
prefetch/write-back are provably never simultaneous here anyway,
|
|
so even a generic 2-port arbiter would be unexercised complexity.
|
|
|
|
RESULT:
|
|
memory_manager.v as implemented. A NOTED, NOT-YET-OPTIMIZED
|
|
characteristic (documented in the module's own header comment): the
|
|
bank-swap-and-check control path costs a minimum 1 idle cycle per
|
|
tile handoff even when the next bank was already prefetched in time,
|
|
unlike neural_processor.v's own zero-gap tile acceptance -- left for
|
|
M10 (Optimization) to revisit using real stall-percentage data (§22)
|
|
rather than optimized blindly now.
|
|
|
|
STATUS:
|
|
ACCEPTED
|