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
10 KiB
Fase 0 — Inventario reale della repo
Data: 2026-09-04. Metodo: lettura diretta dei file, non delle descrizioni in WORKLOG.md o nei datasheet. Ogni claim qui sotto è verificato con un comando citato, rieseguibile.
0.1 Struttura dei file
| Area | File | Righe totali |
|---|---|---|
rtl/*.v |
20 file | 7016 |
sim/*_tb.v (testbench) |
33 file | 12934 |
sim/*.v non-tb (modelli/benchmark) |
4 file: flash_model.v, psram_model.v, flash_latency_bench.v, top.v |
— |
tools/ |
fpga_benchmark.py, netasm/ (assembler+parser+cli+frames, con test suite propria), pinout/gen_lpf.py, flash_catalog/oracle.py, run_regression.py (nuovo, questa fase) |
— |
docs/ |
4 documenti .md + 2 PDF |
— |
Elenco RTL completo: act_buffer.v, crc32.v, flash_copy_engine.v,
flash_slot_manager.v, graph_engine.v, int8_memory_access.v, layer_sequencer.v,
layer.v, mac_unit.v, mac8.v, mem_arbiter.v, memory_interface.v, memory_model.v,
neuron_memory.v, neuron_parallel.v, psram_controller.v, spi_engine.v,
spi_flash_master.v, spi_neuron_top.v, spi_slave.v.
Discrepanza doc↔codice: conteggio testbench
Il prompt di questa campagna cita "la regressione dichiarata (22 testbench)". Il conteggio
reale via find sim -name "*_tb.v" | wc -l è 33 (più 1 file benchmark mal nominato con
suffisso _tb.v, vedi §0.3) — il numero 22 è superato da fasi successive del progetto
(sottosistema flash e Tipo #2 grafo, aggiunti dopo). Non è un errore nel senso di un bug: è
un documento/prompt che descrive uno stato precedente. Il numero corrente verificato è 33.
0.2 Codice morto/orfano trovato
sim/top.v — DEAD, non compila contro l'RTL corrente
$ iverilog -g2012 -o /tmp/topcheck.out rtl/neuron_parallel.v rtl/mac8.v rtl/mac_unit.v sim/top.v
sim/top.v:17: error: parameter `FRAC_BITS` not found in `top.dut`.
2 error(s) during elaboration.
sim/top.v istanzia neuron_parallel con .DATA_WIDTH(16) e .FRAC_BITS(8) — un residuo
della versione a virgola fissa Q8.8 del progetto, prima che fosse convertito a INT8 puro
(coerente con WORKLOG.md, Fase 6: "rimosso FRAC_BITS e funzione q8_8"). Il modulo
neuron_parallel.v corrente non ha più un parametro FRAC_BITS. Il file è tracciato in git
(git log --oneline -- sim/top.v → d4ae241 test: validate parametric 32x4 layer with parallelism 8, un commit storico) ma non è referenziato da nessun testbench, script, o
tool di questo repo — non partecipa alla regressione, non compila. Non toccato in questa
fase (nessuna modifica, per policy §E del prompt di certificazione — la rimozione, se
voluta, è una decisione separata dall'analisi).
Nessun altro modulo RTL orfano
Ogni file in rtl/*.v è raggiungibile da almeno un testbench (direttamente o
transitivamente) — vedi matrice §0.4. Nessun modulo istanziato da zero testbench e da
nessun altro modulo RTL.
Due file "memory model" distinti — non un bug, ma nome ambiguo
rtl/memory_model.v (interfaccia generica req/wr/addr/wdata/rdata con READ_LATENCY
parametrico, usato solo da sim/memory_interface_tb.v per isolare memory_interface.v dal
timing reale della PSRAM) è un modulo diverso da sim/psram_model.v (interfaccia
pin-accurate ce_n/oe_n/we_n/lb_n/ub_n, usato da praticamente tutti i test di integrazione
reali). Non è un bug — sono stub di fedeltà diversa per scopi diversi — ma il nome simile
(memory_model vs psram_model) e la collocazione di uno stub-solo-per-test dentro rtl/
anziché sim/ è una scelta organizzativa che vale la pena segnalare per chi legge la repo
la prima volta.
0.3 Convenzione di naming inconsistente: benchmark con suffisso _tb.v
sim/graph_engine_bandwidth_tb.v ha il suffisso _tb.v (come i 33 testbench veri) ma è in
realtà un benchmark — stampa numeri misurati (edges/sec, bandwidth), non ha verdetto
PASS/FAIL, per progetto (stesso stile dichiarato di sim/flash_latency_bench.v, che invece
non ha il suffisso _tb.v e quindi non viene raccolto insieme ai testbench veri da un
comando generico find sim -name "*_tb.v"). Questa incoerenza di naming ha causato una
classificazione errata al primo giro del regression runner (§0.5) — corretta dopo aver letto
l'intento dichiarato nell'header del file, non assumendolo.
0.4 Matrice modulo → testbench (istanziazione diretta)
Costruita via analisi statica delle istanziazioni (^\s*modulo\s+(#\(|nomeistanza\s*\()),
non a memoria.
| Modulo RTL | Testbench che lo istanziano direttamente |
|---|---|
act_buffer |
act_buffer_tb |
crc32_byte (in crc32.v) |
crc32_tb |
flash_copy_engine |
flash_copy_engine_{erase,load,save}_tb |
flash_slot_manager |
flash_slot_manager_tb, flash_slot_manager_raw_tb |
graph_engine |
graph_engine_tb, graph_engine_guard_tb, graph_engine_bandwidth_tb |
int8_memory_access |
10 testbench (tutti quelli con path PSRAM reale) |
layer_sequencer |
layer_sequencer_tb |
layer |
layer_tb, parametric_tb |
mac_unit |
nessuno — 0 istanziazioni dirette in sim/ |
mac8 |
nessuno — 0 istanziazioni dirette in sim/ |
mem_arbiter |
4 testbench (i 4 test flash con path PSRAM) |
memory_interface |
12 testbench |
memory_model |
memory_interface_tb (solo questo) |
neuron_memory |
neuron_memory_tb, neuron_memory_multi_tb |
neuron_parallel |
5 testbench (incl. i 2 negativi, §0.5) |
psram_controller |
13 testbench |
spi_engine |
spi_engine_tb |
spi_flash_master |
spi_flash_master_tb |
spi_neuron_top |
5 testbench (_tb, _graph_tb, _irq_tb, _runnetwork_tb, _flash_tb) |
spi_slave |
spi_slave_tb, spi_engine_tb |
Finding da riportare in C.1 (Datapath aritmetico)
mac_unit.v e mac8.v non hanno un testbench unitario dedicato. Sono esercitati solo
indirettamente, come sotto-componenti di neuron_parallel nei test di livello superiore
(neuron_parallel_tb, neuron_parallel_saturation_bounds_tb, ecc.). Questo significa che
un comportamento scorretto isolato di mac_unit/mac8 (es. estensione di segno errata sul
prodotto INT8×INT8, prima dell'accumulo) sarebbe rilevabile solo se si propaga fino
all'uscita finale del layer con un pattern di input che lo renda visibile — non c'è un
oracolo che verifichi mac_unit da solo. Non certificabile come "coperto" fino a C.1.
0.5 Regressione: eseguita da zero con harness nuovo, non fidandosi del WORKLOG
Non esisteva alcuno script di regressione riproducibile nella repo prima di questa fase
— ogni precedente affermazione "N testbench, tutti PASS" in WORKLOG.md è stata prodotta
assemblando a mano la lista file iverilog per ciascun test, mai da un harness unico
rieseguibile. Questo è di per sé un gap reale (nessuna prova automatizzata, riproducibile,
del claim di regressione) — colmato creando tools/run_regression.py: risolve le
dipendenze di ogni testbench per analisi statica delle istanziazioni (non a memoria/elenco
scritto a mano), compila con iverilog -g2012 ed esegue con vvp, classifica il risultato.
Primo run: 2 falsi negativi e 1 "sconosciuto" — non erano bug, erano un blind spot del mio stesso harness, corretto leggendo il codice sorgente dei test incriminati (non assumendo):
neuron_parallel_guard_negative_{degenerate,nonmultiple}_tb.vsono test negativi dichiarati: il loro header dice esplicitamente "This file must FAIL TO COMPILE/ELABORATE. That failure is the test" — verificano che il guardN_INPUTS % PARALLEL != 0dirtl/neuron_parallel.v:71-72blocchi l'elaborazione istanziando un modulo inesistente (neuron_parallel_requires_N_INPUTS_multiple_of_PARALLEL) quando la condizione è violata. Il fallimento di compilazione è il PASS.graph_engine_bandwidth_tb.vè un benchmark (§0.3), nessun verdetto per progetto.
Corretto l'harness (whitelist esplicita per questi 3 casi, letta dal codice sorgente stesso dei test, non inventata) e rilanciato:
$ python3 tools/run_regression.py
TOTAL: 34 PASS: 33 FAIL/ERROR: 0 OTHER/UNKNOWN: 1
33/33 testbench reali PASS, 0 regressioni, 1 benchmark eseguito correttamente senza verdetto (per progetto). Il claim del WORKLOG ("tutti i testbench passano") è confermato vero da un run indipendente e da zero — non solo creduto sulla parola.
tools/netasm/tests/test_netasm.py (20 test) verificato separatamente, anch'esso da zero:
20/20 PASS, invariato.
0.6 Osservazione preliminare, da verificare formalmente in C.1
Leggendo rtl/neuron_parallel.v:70-73, il guard elaboration-time è un solo controllo:
if (N_INPUTS % PARALLEL != 0) begin : PARAMETER_ERROR_N_INPUTS_NOT_MULTIPLE_OF_PARALLEL
neuron_parallel_requires_N_INPUTS_multiple_of_PARALLEL invalid_parameter_combination();
end
Il commento del progetto dice che questo guard copre sia "N_INPUTS non multiplo di
PARALLEL" sia il caso degenere "PARALLEL > N_INPUTS" (con GROUPS=0, hang documentato).
Ma matematicamente: se N_INPUTS == 0, allora N_INPUTS % PARALLEL == 0 per qualunque
PARALLEL != 0 — il guard non scatta, eppure GROUPS = 0/PARALLEL = 0, la stessa
condizione di hang che il guard dichiara di prevenire. Non ancora verificato se
N_INPUTS=0 sia un caso raggiungibile/rilevante nella pratica (nessun layer con zero
ingressi ha senso semantico, ma nessun controllo esplicito lo esclude) — portato come
finding aperto da chiudere formalmente in C.1 con un test avversariale dedicato e un
oracolo indipendente, non certificato né come bug né come non-bug qui.
0.7 Artefatti fuori dal codice sorgente (non toccati)
docs/FPGANeuralDatasheet.pdfedocs/FPGANeuralDatasheetEN.pdf: comparsi come file non tracciati, non generati da alcun processo di build di questo repo (il datasheet LaTeX vive inDataSheet/, una directory separata non versionata — vedi memoria di progetto). Probabile sottoprodotto del meccanismo di invio file usato in questa sessione. Non fanno parte della fonte di verità RTL/documentazione; non modificati né cancellati (non è una decisione di questa fase).FPGA-Neural/(progetto KiCad): non tracciato per policy di progetto pre-esistente (vedi memoria), non toccato.
0.8 Prossimi passi
Procedo con gli aspetti C.1–C.14 uno alla volta, ciascuno con: analisi statica, test
avversari con oracolo indipendente (§A.1/A.3), verdetto tracciato. Il finding aperto di
§0.6 (N_INPUTS=0) va chiuso in C.1. mac_unit/mac8 senza test unitario (§0.4) va colmato
in C.1 prima di poter certificare il datapath aritmetico.