Files
FPGA-Neural/docs/validation/00-inventario.md
T
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

204 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.v` sono **test negativi
dichiarati**: il loro header dice esplicitamente "This file must FAIL TO
COMPILE/ELABORATE. That failure is the test" — verificano che il guard
`N_INPUTS % PARALLEL != 0` di `rtl/neuron_parallel.v:71-72` blocchi 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:
```verilog
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.pdf` e `docs/FPGANeuralDatasheetEN.pdf`: comparsi come file non
tracciati, non generati da alcun processo di build di questo repo (il datasheet LaTeX vive
in `DataSheet/`, 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.1C.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.