Files
FPGA-Neural/WORKLOG.md
T
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

167 lines
36 KiB
Markdown

# WORKLOG
## Fase: Setup sessione debug neuron_memory (2026-09-02)
- 2026-09-02T00:00 — [FASE 0] — Avviata sessione di debug su `neuron_memory`. Contesto fornito dall'utente: core di calcolo (neuron_parallel, mac8, mac_unit) considerato corretto e non da toccare; neuron_memory gestisce solo N_NEURONS=1; bug noto in sim/neuron_memory_tb.v su preload_vector/preload_weights (usavano `base + (k>>1)` invece di `(base>>1) + (k>>1)`).
- 2026-09-02T00:01 — [FASE 0] — `git status`: modifiche non committate su sim/neuron_memory.vcd, sim/neuron_memory_sim, sim/neuron_memory_tb.v (working tree su main).
- 2026-09-02T00:02 — [FASE 0] — `git diff sim/neuron_memory_tb.v`: confermato che il fix `(base >> 1) + (k >> 1)` è già presente nel working tree (non committato) sia in preload_vector che preload_weights. Aggiunto anche un nuovo task `preload_x_pattern` e un nuovo TEST 0 "PATTERN X=1..32" (somma 1..32 = 528, atteso saturato a 127).
- 2026-09-02T00:03 — [FASE 0] — Creato branch `fix/neuron-memory-debug` da main (non si lavora su main).
- 2026-09-02T00:04 — [FASE 0] — Letto rtl/neuron_memory.v per intero: FSM IDLE->READ_X->READ_W->READ_BIAS->START_N->WAIT_N. Nota: `access_mem_rdata` ricostruisce una parola a 16 bit da un singolo byte (`mem_rdata` esterno a 8 bit) usando `access_addr[0]` come selettore, per poi essere ri-decodificata dentro int8_memory_access con `addr_reg[0]` catturato alla request. Da verificare se il timing di `access_addr` (che avanza al prossimo indice nello stesso colpo di clock in cui `access_ready` viene consumato) possa disallinearsi da `addr_reg[0]` interno a int8_memory_access.
- 2026-09-02T00:05 — [FASE 0] — Letto rtl/int8_memory_access.v per intero: FSM IDLE/WAIT, converte addr byte->word (`addr>>1`), seleziona lb_n/ub_n in base a addr[0], estrae byte da mem_rdata in base a addr_reg[0] (registrato alla request, non al momento della ready) — quindi il rischio di disallineamento sopra ipotizzato non sussiste per l'estrazione interna a int8_memory_access, ma resta da verificare `access_mem_rdata` lato neuron_memory che usa `access_addr[0]` corrente (non registrato) nello stesso ciclo in cui int8_memory_access è ancora in STATE_WAIT.
- 2026-09-02T00:06 — [FASE 0] — Letto sim/neuron_memory_tb.v per intero: testbench end-to-end con memory_interface + psram_controller + psram_model reali (non mock), mux a singolo master (TB preload vs neuron_memory), 5 test (PATTERN, SUM, SATURATION, RELU, BIAS=10).
- 2026-09-02T00:10 — [FASE 1] — Compilato: `iverilog -g2012 -o sim/neuron_memory_sim rtl/*.v sim/psram_model.v sim/neuron_memory_tb.v`. Nessun errore/warning.
- 2026-09-02T00:11 — [FASE 1] — Eseguito: `vvp sim/neuron_memory_sim`. Risultato: TUTTI I 5 TEST PASSANO (PATTERN X=1..32 -> y=127 saturato correttamente; SUM=32 -> y=32; SATURATION -> y=127; RELU -> y=0; BIAS=10 -> y=42). Conferma che il fix `(base>>1)+(k>>1)` già presente nel working tree risolve correttamente l'indirizzamento byte->word nel preload e che neuron_memory.v (non modificato in questa sessione) è funzionalmente corretto per N_NEURONS=1 con questo pattern di test.
- 2026-09-02T00:12 — [FASE 1] — Analisi manuale del timing dell'handshake req/ready tra neuron_memory.v e int8_memory_access.v (in particolare `access_mem_rdata` che ricostruisce la word a 16 bit usando `access_addr[0]` corrente, non registrato): il redirect avviene un ciclo dopo che `access_req` diventa 1 quando `access_ready` è ancora alto, quindi `access_addr` non è stato ancora aggiornato al nuovo indice nel ciclo in cui `access_mem_rdata` viene consumato da int8_memory_access — nessuna race identificata. Confermato empiricamente anche dal test PATTERN X=1..32 che esercita 32 letture byte consecutive con parità alternata (indirizzi pari/dispari) e produce la somma corretta (528, satura a 127).
- 2026-09-02T00:15 — [FASE 2] — Eseguita regressione sugli altri testbench del repo per verificare che non ci fossero altri "test già eseguiti" rilevanti da esaminare:
- int8_memory_access_tb: PASS (tutti i sotto-test)
- memory_interface_tb: PASS
- psram_controller_tb: PASS (2048 transazioni stress + byte enable)
- int8_psram_integration_tb: PASS (incl. 2048 stress)
- parametric_tb (256x4, PARALLEL=32): PASS (N0..N3, incl. ReLU e saturazione)
- layer_tb: FALLISCE in elaborazione — `parameter FRAC_BITS not found in tb.dut`. Il testbench referenzia un parametro `FRAC_BITS` non più presente in rtl/layer.v (probabile debito tecnico da refactor precedente, indipendente da neuron_memory).
- neuron_parallel_tb: FALLISCE in elaborazione — stesso errore, `parameter FRAC_BITS not found in tb.dut`, riferito a rtl/neuron_parallel.v.
- NOTA: questi due fallimenti sono preesistenti e non toccano i file modificati in questa sessione (sim/neuron_memory_tb.v). Non risolti in questa sessione perché fuori scope dichiarato (debug neuron_memory); da segnalare all'utente.
- 2026-09-02T00:20 — [FASE 3] — CONCLUSIONE: il bug indirizzamento byte/word nei task preload_vector/preload_weights di sim/neuron_memory_tb.v era l'unico difetto riscontrato nel percorso neuron_memory; risulta già corretto nel working tree e validato con successo da tutti e 5 i test (incl. il nuovo test PATTERN aggiunto). Nessun bug residuo individuato in rtl/neuron_memory.v o rtl/int8_memory_access.v per il caso N_NEURONS=1. Procedo al commit del fix sul branch `fix/neuron-memory-debug`.
- 2026-09-02T00:21 — [FASE 4] — Committato (896f56c) il fix di sim/neuron_memory_tb.v + artefatti sim/neuron_memory_sim, sim/neuron_memory.vcd + WORKLOG.md sul branch fix/neuron-memory-debug. Ripristinati con `git checkout --` i .vcd di altri testbench (int8_memory_access, int8_psram_integration, memory_interface, parametric, psram_controller) rigenerati per errore durante la regressione, per non introdurre diff non pertinenti.
- 2026-09-02T00:25 — [FASE 5] — Contesto hardware fornito dall'utente durante la sessione (per riferimento futuro, non ancora usato in RTL/sim): FPGA target = Lattice ECP5 LFE5U-45F-8BG381C; PSRAM = ISSI IS66WVE4M16EBLL-70BLI; il controller esterno pilota il sistema FPGA+RAM via SPI, e accede alla RAM solo tramite la FPGA (mai in accesso diretto).
- 2026-09-02T00:26 — [FASE 6] — Richiesta utente: "sistema i due test e così chiudiamo" — riferito a sim/layer_tb.v e sim/neuron_parallel_tb.v, che fallivano in elaborazione con `parameter FRAC_BITS not found in tb.dut`.
- 2026-09-02T00:27 — [FASE 6] — Letto rtl/neuron_parallel.v per intero: DATA_WIDTH default 8, nessun parametro FRAC_BITS, ReLU (final_acc<=0 -> y=0) e saturazione positiva HARDCODED a `8'sd127` indipendentemente da DATA_WIDTH (nessuna saturazione negativa esplicita, solo ReLU). Confermato che il modulo non supporta più (e non ha mai realmente supportato in questa versione) il formato fixed-point Q8.8 a 16 bit assunto dai due testbench.
- 2026-09-02T00:28 — [FASE 6] — Letto rtl/layer.v per intero: parametrico, instanzia N_NEURONS x neuron_parallel con layout pesi neuron-major; nessun riferimento a FRAC_BITS.
- 2026-09-02T00:29 — [FASE 6] — Letto per intero sim/neuron_parallel_tb.v e sim/layer_tb.v (versioni obsolete, DATA_WIDTH=16, FRAC_BITS=8, valori Q8.8 via funzione q8_8(), saturazione attesa a ±32767): confermato che sono residui di una precedente architettura fixed-point a 16 bit, incompatibile con neuron_parallel.v attuale (core validato, da NON modificare).
- 2026-09-02T00:30 — [FASE 6] — Letto sim/parametric_tb.v (test già passante) come riferimento di stile/parametri corretti per l'architettura INT8 attuale (DATA_WIDTH=8, valori interi, saturazione a 127).
- 2026-09-02T00:35 — [FASE 6] — Riscritto interamente sim/neuron_parallel_tb.v: rimosso FRAC_BITS e funzione q8_8; parametri allineati ai default di rtl/neuron_parallel.v (DATA_WIDTH=8, N_INPUTS=32, PARALLEL=8, ACC_WIDTH=32); 4 test ridisegnati con valori interi INT8 mantenendo lo stesso intento dei test originali:
- TEST 1 DIVERSE VECTOR: x0=3,w0=2 / x1=4,w1=-1 / x2=2,w2=1 / bias=1 -> atteso 5.
- TEST 2 RELU: tutti i prodotti negativi (x=1,w=-1 su 32 input) -> atteso 0.
- TEST 3 POSITIVE SATURATION: x=100,w=2 su 32 input -> somma 6400, satura a 127.
- TEST 4 MIXED + NEGATIVE BIAS: 16 input pari (x=2,w=1) + 16 dispari (x=-1,w=1), bias=-16 -> somma esattamente 0 -> ReLU -> atteso 0.
- 2026-09-02T00:40 — [FASE 6] — Riscritto interamente sim/layer_tb.v: rimosso FRAC_BITS; parametri DATA_WIDTH=8, N_INPUTS=32, N_NEURONS=8, PARALLEL=8, ACC_WIDTH=32; input vettore tutti =1; 8 neuroni con scenari distinti (pattern ispirato a parametric_tb.v):
- N0 w=1,bias=0 -> 32
- N1 w=3,bias=0 -> 96
- N2 w=-1 -> ReLU -> 0
- N3 w=8 -> somma 256 -> satura 127
- N4 w=0,bias=+5 -> 5
- N5 w=0,bias=-5 -> ReLU -> 0
- N6 w=1,bias=-10 -> 32-10=22
- N7 primi 16 pesi=1 resto 0, bias=+5 -> 16+5=21 (verifica pattern di pesi sparso su più gruppi, PARALLEL=8 -> GROUPS=4)
- 2026-09-02T00:42 — [FASE 6] — Compilato ed eseguito `sim/neuron_parallel_tb.v`: `iverilog -g2012 -o <tmp> rtl/*.v sim/neuron_parallel_tb.v` + `vvp`. Risultato: TUTTI I 4 TEST PASSANO (TEST1=5, TEST2=0, TEST3=127, TEST4=0).
- 2026-09-02T00:43 — [FASE 6] — Compilato ed eseguito `sim/layer_tb.v`: `iverilog -g2012 -o <tmp> rtl/*.v sim/layer_tb.v` + `vvp`. Risultato: TUTTI GLI 8 NEURONI PASSANO (32,96,0,127,5,0,22,21), busy=0, done=1 a fine test.
- 2026-09-02T00:44 — [FASE 6] — Rigenerati gli artefatti tracciati in git `sim/neuron_sim` (da neuron_parallel_tb.v) e `sim/layer_sim` (da layer_tb.v) con i relativi `.vcd`, per coerenza con la convenzione del repo di committare i binari di simulazione insieme alle sorgenti.
- 2026-09-02T00:45 — [FASE 7] — Nessuna modifica a rtl/ in questa fase: root cause dei due fallimenti era esclusivamente nei testbench (riferimento a un parametro FRAC_BITS rimosso da tempo dall'RTL), non nel core di calcolo. Sessione di debug neuron_memory + fix dei due test residui considerata chiusa.
## Fase: Roadmap Fase 2 — Parameter Sweep (2026-09-02)
- 2026-09-02T01:00 — [FASE 8] — Utente: "passiamo a fase 1 (o successiva)". Chiarito (dopo scambio) che il riferimento è al roadmap ufficiale in docs/FPGA-NeuralNetwork-Engine.md (sezione 15, "Development Roadmap"), da seguire come percorso vincolante. Letta la roadmap: Fase 1 (Parametric Layer) risulta già completamente spuntata; Fase 2 (Parameter Sweep) non ancora affrontata: "Validate multiple combinations of N_INPUTS/N_NEURONS/PARALLEL, including configurations where the number of inputs is not an exact multiple of the parallelism."
- 2026-09-02T01:02 — [FASE 8] — Letto rtl/mac8.v per intero: PARALLEL deve essere potenza di 2 (adder tree binario con $clog2(PARALLEL) livelli). Nessun vincolo esplicito che N_INPUTS sia multiplo di PARALLEL.
- 2026-09-02T01:03 — [FASE 8] — Ri-analizzato rtl/neuron_parallel.v: `localparam GROUPS = N_INPUTS / PARALLEL;` è divisione INTERA. Ipotesi: se N_INPUTS non è multiplo esatto di PARALLEL, gli input residui (N_INPUTS - GROUPS*PARALLEL) non vengono mai letti dall'accumulatore (nessun errore/warning a compile o runtime). Ipotesi aggiuntiva: se PARALLEL > N_INPUTS, GROUPS=0 e la condizione di terminazione del controller (`group_index == GROUPS-1`) non è mai soddisfatta -> hang permanente (busy=1, done mai asserito).
- 2026-09-02T01:10 — [FASE 8] — Creato sim/parameter_sweep_tb.v: 5 istanze di neuron_parallel con configurazioni diverse (CONFIG A..E), watchdog a ciclo (max 500 cicli, nessun `wait` bloccante) per evitare hang reale della simulazione anche nel caso patologico:
- A: N_INPUTS=32 PARALLEL=8 (esatto, sanity check, atteso y=32)
- B: N_INPUTS=30 PARALLEL=8 (non esatto, GROUPS=3, atteso y=24 per troncamento RTL vs somma piena=30)
- C: N_INPUTS=20 PARALLEL=16 (non esatto, GROUPS=1, atteso y=16 per troncamento RTL vs somma piena=20)
- D: N_INPUTS=64 PARALLEL=32 (esatto, sanity check, atteso y=64)
- E: N_INPUTS=4 PARALLEL=8 (degenere, GROUPS=0, atteso TIMEOUT/hang)
- 2026-09-02T01:12 — [FASE 8] — Compilato: `iverilog -g2012 -o <tmp> rtl/*.v sim/parameter_sweep_tb.v`. Nessun errore (anche il part-select fuori range in CONFIG E, dovuto a x_bus/w_bus da 32 bit letti come blocchi da 64 bit dentro neuron_parallel, non causa errore di elaborazione in Icarus).
- 2026-09-02T01:13 — [FASE 8] — Eseguito `vvp`. RISULTATI (tutti confermano le ipotesi):
- CONFIG A: PASS, y=32.
- CONFIG B: y=24 (non 30) -> confermato troncamento silenzioso, 6 input su 30 ignorati.
- CONFIG C: y=16 (non 20) -> confermato troncamento silenzioso, 4 input su 20 ignorati.
- CONFIG D: PASS, y=64.
- CONFIG E: TIMEOUT confermato -> done mai asserito entro 500 cicli, hang reale per PARALLEL > N_INPUTS (GROUPS=0).
- Sweep complessivo: errors=0 (nessun comportamento imprevisto rispetto alle ipotesi), findings=3 (troncamento non-multiplo x2, hang degenerato x1).
- 2026-09-02T01:15 — [FASE 8] — Rigenerato artefatto tracciato sim/parameter_sweep_sim + sim/parameter_sweep.vcd per coerenza con la convenzione del repo.
- 2026-09-02T01:16 — [FASE 8] — Nessuna modifica a rtl/neuron_parallel.v in questa fase (core di calcolo non toccato, come da policy di progetto). I due limiti trovati (troncamento su non-multiplo, hang su PARALLEL>N_INPUTS) sono documentati come "Findings" nella Fase 2 della roadmap (docs/FPGA-NeuralNetwork-Engine.md) e proposti come azione futura per Fase 3/7, non risolti ora.
- 2026-09-02T01:18 — [FASE 8] — Aggiornato docs/FPGA-NeuralNetwork-Engine.md, sezione "## Phase 2 — Parameter Sweep": aggiunta checklist di completamento e sezione "Findings" con i due limiti documentati sopra.
## Fase: Fix dei findings di Fase 2 (guardia elaboration-time) (2026-09-02)
- 2026-09-02T02:00 — [FASE 9] — Utente chiede se i due finding di Fase 2 (troncamento su N_INPUTS non multiplo di PARALLEL; hang su PARALLEL>N_INPUTS) siano errori da correggere o si possano lasciare, segnalando che dai benchmark PARALLEL=2 dà i risultati migliori (PARALLEL=4 leggermente meno) — vedi docs/FPGA-Neural-Datapatch-Benchmark.md.
- 2026-09-02T02:02 — [FASE 9] — Letto docs/FPGA-Neural-Datapatch-Benchmark.md, tabella comparativa (§13, N_INPUTS=256, N_NEURONS=4): PARALLEL=16 FAIL (52.13 MHz), PARALLEL=8 FAIL (61.71 MHz), PARALLEL=4 FAIL (75.01 MHz), PARALLEL=2 PASS (87.88 MHz, unico sopra 80 MHz). Confermato che N_INPUTS e PARALLEL sono parametri Verilog fissati in fase di sintesi (non registri configurabili via SPI a runtime): il rischio dei due finding è quindi "errore di chi istanzia il modulo per un certo layer", non un rischio da campo/runtime.
- 2026-09-02T02:05 — [FASE 9] — Analisi di rischio comunicata all'utente: Finding 2 (hang, PARALLEL>N_INPUTS) trascurabile con PARALLEL reale=2/4 (richiederebbe un layer con 1-3 input, non realistico). Finding 1 (troncamento silenzioso) più concreto: con PARALLEL=2 basta un N_INPUTS dispari per perdere silenziosamente l'ultimo input, senza segnalazione — proposta una guardia a elaboration-time (nessuna modifica al datapath validato).
- 2026-09-02T02:10 — [FASE 9] — Proposta dettagliata verificata isolatamente PRIMA di toccare l'RTL: creato in scratchpad un modulo di test con blocco `generate` che istanzia un modulo indefinito `neuron_parallel_requires_N_INPUTS_multiple_of_PARALLEL` quando `N_INPUTS % PARALLEL != 0`. Compilato con Icarus (`iverilog -g2012`) sia con parametri validi (32/8, exit=0, nessun errore) sia invalidi (30/8, exit=1, errore "Unknown module type: neuron_parallel_requires_N_INPUTS_multiple_of_PARALLEL"). Idioma confermato portabile (standard Verilog generate + elaborazione modulo, non system task da simulazione tipo $error/$fatal che i tool di sintesi spesso ignorano).
- 2026-09-02T02:15 — [FASE 9] — Presentata la proposta all'utente (unica modifica prevista: blocco `generate` in rtl/neuron_parallel.v, nessun'altra riga toccata) e chiesta conferma esplicita prima di modificare il core "validato/non riscrivere" secondo la policy di progetto stabilita a inizio sessione.
- 2026-09-02T02:20 — [FASE 9] — Utente autorizza ESPLICITAMENTE e SOLO per questa modifica a toccare ciò che era dichiarato fisso ("core is correct, don't touch/rewrite"), con richiesta di annotazione dettagliata nel log (questa sezione).
- 2026-09-02T02:22 — [FASE 9] — MODIFICA A rtl/neuron_parallel.v: inserito blocco `generate` subito prima di `localparam GROUPS = N_INPUTS / PARALLEL;`:
```verilog
generate
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
endgenerate
```
Nessun'altra riga del file toccata. Il datapath di calcolo (mac8, accumulo, bias, ReLU, saturazione) è invariato. Per configurazioni valide (N_INPUTS%PARALLEL==0) il ramo generate non viene mai elaborato: zero impatto.
- 2026-09-02T02:25 — [FASE 9] — REGRESSIONE COMPLETA post-modifica, per verificare che nessuna configurazione valida esistente sia stata rotta. Compilato ed eseguito con `iverilog -g2012 -o <tmp> rtl/*.v [sim/psram_model.v] sim/<tb>.v` + `vvp`:
- int8_memory_access_tb: PASS (non usa neuron_parallel, invariato)
- layer_tb: PASS - ALL 8 NEURONS (N_INPUTS=32 PARALLEL=8, esatto)
- memory_interface_tb: PASS (non usa neuron_parallel, invariato)
- neuron_parallel_tb: PASS - ALL TESTS PASSED (N_INPUTS=32 PARALLEL=8, esatto)
- parametric_tb: PASS (N_INPUTS=256 PARALLEL=32, esatto)
- psram_controller_tb: PASS (non usa neuron_parallel, invariato)
- int8_psram_integration_tb: PASS (non usa neuron_parallel, invariato)
- neuron_memory_tb: PASS, tutti e 5 i test (N_INPUTS=32 PARALLEL=8, esatto)
Nessuna regressione: tutte le configurazioni preesistenti erano già multipli esatti, quindi il ramo `generate` della guardia non si attiva mai per esse.
- 2026-09-02T02:30 — [FASE 9] — Creati due test NEGATIVI dedicati (devono FALLIRE la compilazione per definizione, quello è il test):
- sim/neuron_parallel_guard_negative_nonmultiple_tb.v: istanzia neuron_parallel con N_INPUTS=30, PARALLEL=8 (il caso di troncamento del Finding 1).
- sim/neuron_parallel_guard_negative_degenerate_tb.v: istanzia neuron_parallel con N_INPUTS=4, PARALLEL=8 (il caso di hang/Finding 2).
Ogni file contiene nell'header il comando esatto di verifica e l'errore atteso, per poter essere ri-eseguito manualmente in futuro come regressione "negativa" (nessun binario/vcd generabile per questi, dato che l'elaborazione fallisce sempre by design).
- 2026-09-02T02:32 — [FASE 9] — Eseguita verifica di entrambi i test negativi: `iverilog -g2012 -o <tmp> rtl/*.v sim/neuron_parallel_guard_negative_nonmultiple_tb.v` -> exit=1, errore "Unknown module type: neuron_parallel_requires_N_INPUTS_multiple_of_PARALLEL" a rtl/neuron_parallel.v:45. Stesso esito identico per la variante degenerate_tb.v. Confermato: la guardia scatta per ENTRAMBI i finding con lo stesso, unico controllo (`N_INPUTS % PARALLEL != 0` intercetta sia il troncamento sia il caso degenere PARALLEL>N_INPUTS, perché quest'ultimo implica resto non nullo salvo N_INPUTS=0).
- 2026-09-02T02:40 — [FASE 9] — Riscritto sim/parameter_sweep_tb.v: rimosse le CONFIG B, C, E (non più compilabili per design, la loro copertura "negativa" è ora nei due file dedicati sopra). Aggiunte due nuove config valide allineate ai risultati reali del benchmark ECP5:
- CONFIG F: N_INPUTS=32 PARALLEL=2 (GROUPS=16) — configurazione a timing migliore (87.88 MHz, unica PASS a 80MHz nel benchmark).
- CONFIG G: N_INPUTS=32 PARALLEL=4 (GROUPS=8) — seconda scelta per timing.
Mantenute CONFIG A (32/8) e D (64/32) come sanity check di regressione. Rimosso il watchdog a ciclo (non più necessario: la guardia elimina la possibilità di hang per qualunque config che compili).
- 2026-09-02T02:42 — [FASE 9] — Compilato ed eseguito sim/parameter_sweep_tb.v aggiornato: TUTTE E 4 LE CONFIG PASSANO (A: y=32, D: y=64, F: y=32 con PARALLEL=2, G: y=32 con PARALLEL=4). Rigenerati gli artefatti tracciati sim/parameter_sweep_sim e sim/parameter_sweep.vcd.
- 2026-09-02T02:45 — [FASE 9] — Aggiornato docs/FPGA-NeuralNetwork-Engine.md, sezione "## Phase 2 — Parameter Sweep": la sezione "Findings" è stata aggiornata da "non ancora risolti" a "FIXED (2026-09-02)", con descrizione della guardia, riferimento ai due test negativi e al nuovo sweep positivo con PARALLEL=2/4.
- 2026-09-02T02:46 — [FASE 9] — CONCLUSIONE: entrambi i finding di Fase 2 sono ora chiusi tramite un'unica guardia a elaboration-time in rtl/neuron_parallel.v, verificata sia in positivo (nessuna regressione sulle config valide esistenti + nuove config F/G con PARALLEL=2/4) sia in negativo (entrambi i casi patologici falliscono ora la compilazione con un errore esplicito invece di produrre un risultato silenziosamente errato o un hang). Modifica autorizzata esplicitamente dall'utente in deroga alla policy "core non toccare", limitata a questo unico blocco generate.
## Fase: Merge/push in main + cleanup branch (2026-09-02)
- 2026-09-02T03:00 — [FASE 10] — Utente: "poi visto che questo è validato allinea il main." Eseguito `git checkout main` + `git merge --ff-only fix/neuron-memory-debug`: fast-forward pulito (16769ea..1a6f0ba), nessun conflitto, nessuna modifica al working tree oltre al merge. Riepilogati i 4 commit portati su main.
- 2026-09-02T03:05 — [FASE 10] — Utente: "fai pure" (autorizzazione a pushare). Eseguito `git push origin main`: push riuscito, origin/main allineato a 1a6f0ba.
- 2026-09-02T03:10 — [FASE 10] — Utente: "in locale elimina pure il branch neuron-memory-debug". Eseguito `git branch -d fix/neuron-memory-debug` (delete sicuro, branch già interamente mergiato in main, nessuna perdita di lavoro). Rimane solo `main` in locale.
## Fase: Fase 3 roadmap — neuron_memory multi-neurone (2026-09-02)
- 2026-09-02T03:20 — [FASE 11] — Utente: "poi prossimo punto". Consultato docs/FPGA-NeuralNetwork-Engine.md sezione 15: Fase 1 e Fase 2 chiuse/spuntate. Fase 3 (Memory Architecture) è il prossimo punto del roadmap, e coincide con il limite esplicitamente segnalato nel contesto iniziale della sessione: "neuron_memory è l'integrazione memoria→neurone. Attualmente gestisce solo N_NEURONS=1." Deciso di procedere con l'estensione multi-neurone di neuron_memory.v come primo task concreto di Fase 3.
- 2026-09-02T03:21 — [FASE 11] — Utente interviene a promemoria: "ricorda quanto ti ho incollato nelle istruzioni precise" — riconfermati i vincoli originali della sessione: NON toccare neuron_parallel/mac8/mac_unit (core validato — a parte la guardia già autorizzata in Fase 9, non riaperta qui), rispettare la convenzione indirizzi BYTE con conversione mem_addr<=addr>>1, rispettare il layout pesi NEURON-MAJOR di layer.v (weights_bus[n*N_INPUTS*DATA_WIDTH +: N_INPUTS*DATA_WIDTH], bias contigue per neurone), e mantenere il log dettagliato per ogni fase. neuron_memory.v stesso NON è nella lista dei moduli "non toccare" — è esplicitamente il modulo da estendere.
- 2026-09-02T03:25 — [FASE 11] — Letto integralmente rtl/neuron_memory.v (stato pre-modifica, invariato dall'inizio sessione): FSM STATE_IDLE->READ_X->READ_W->READ_BIAS->START_N->WAIT_N, un solo neuron_parallel instanziato, output singolo `y` a 8 bit, nessun parametro N_NEURONS.
- 2026-09-02T03:30 — [FASE 11] — DECISIONE DI PROGETTO: implementazione memory-bound (non compute-parallel). Un solo neuron_parallel viene riusato in sequenza per ciascun neurone: X viene letto UNA VOLTA (condiviso, è l'input del layer), mentre W e bias vengono ri-letti da PSRAM per ciascun neurone in turno, seguendo il layout neuron-major già usato da layer.v (pesi neurone n a w_base+n*N_INPUTS byte, bias neurone n a bias_addr+n byte). Motivazione: Fase 3 del roadmap è esplicitamente "Memory Architecture", quindi la sequenza memory-bound è la scelta naturale (throughput/parallelismo di calcolo sono oggetto della Fase 7 - Optimization); istanziare N_NEURONS neuron_parallel in parallelo (come fa layer.v) avrebbe richiesto anche N_NEURONS canali di memoria/prefetch, fuori scope per questo step.
- 2026-09-02T03:35 — [FASE 11] — MODIFICHE A rtl/neuron_memory.v (uniche modifiche di questa fase, nessun'altra riga toccata in nessun altro modulo core):
1. Aggiunto parametro `N_NEURONS = 1` (default 1 per retrocompatibilità totale).
2. Porta di output `output reg signed [7:0] y` sostituita con `output wire signed [DATA_WIDTH*N_NEURONS-1:0] y_bus` (packed, neuron-major, stessa convenzione di layer.v).
3. Aggiunti registri: `neuron_index` (larghezza $clog2(N_NEURONS), stesso pattern di GROUP_INDEX_WIDTH in neuron_parallel.v), `w_group_base` e `bias_group_addr` (ADDR_WIDTH bit, tracciano l'indirizzo base del neurone corrente), array `y_reg[0:N_NEURONS-1]` per l'output per-neurone.
4. Aggiunto blocco `generate` che assembla y_bus da y_reg[] (stesso pattern già usato per x_bus/w_bus da x_mem/w_mem).
5. STATE_IDLE: su `start`, inizializza neuron_index<=0, w_group_base<=w_base, bias_group_addr<=bias_addr (oltre alla logica preesistente di avvio lettura X).
6. STATE_READ_X: a fine lettura (index==N_INPUTS-1), l'indirizzo per iniziare READ_W ora usa `w_group_base` invece di `w_base` diretto (equivalenti per il neurone 0, ma necessario per generalizzare).
7. STATE_READ_W: indirizzi calcolati da `w_group_base` invece di `w_base`; a fine lettura pesi, transizione a READ_BIAS con indirizzo `bias_group_addr` invece di `bias_addr` diretto.
8. STATE_WAIT_N: riscritta. Al completamento del neurone corrente (`neuron_done`), salva `y_reg[neuron_index] <= neuron_y`. Se `neuron_index == N_NEURONS-1` (ultimo neurone del layer): busy<=0, done<=1, torna a IDLE (comportamento identico al precedente per N_NEURONS=1). Altrimenti: incrementa neuron_index, avanza w_group_base di N_INPUTS byte e bias_group_addr di 1 byte, azzera index, avvia una nuova richiesta di lettura pesi (access_addr<=w_group_base+N_INPUTS, cioè il nuovo base) e torna a STATE_READ_W per il neurone successivo — X in x_mem NON viene ricaricato (condiviso).
9. Reset: aggiunto reset esplicito di neuron_index, w_group_base, bias_group_addr, e di tutto l'array y_reg[] (tramite un `integer rst_i` e un for-loop, dato che y_reg è un array e non può essere azzerato con un singolo `<= 0`).
- 2026-09-02T03:40 — [FASE 11] — Verifica di correttezza degli indirizzi a mente prima della compilazione: per N_NEURONS=1 (default), neuron_index resta sempre 0, w_group_base=w_base e bias_group_addr=bias_addr per l'intera esecuzione -> comportamento bit-identico alla versione precedente. Per N_NEURONS>1, w_group_base/bias_group_addr vengono aggiornati SOLO nella transizione WAIT_N->READ_W (una volta per neurone, non per byte), quindi restano stabili per tutta la durata della lettura di quel neurone.
- 2026-09-02T03:42 — [FASE 11] — Compilazione di verifica: `iverilog -g2012 -o <tmp> rtl/*.v sim/psram_model.v sim/neuron_memory_tb.v` (tb esistente, non ancora aggiornato) -> ERRORE atteso: "port `y` is not a port of u_neuron" (il tb referenziava ancora la vecchia porta `y`).
- 2026-09-02T03:44 — [FASE 11] — Aggiornato sim/neuron_memory_tb.v: aggiunta esplicita `.N_NEURONS(1)` all'istanziazione (documenta l'intento, comportamento invariato essendo il default), e connessione porta cambiata da `.y(y)` a `.y_bus(y)` (il segnale interno del tb resta chiamato `y`, larghezza 8 bit compatibile con N_NEURONS=1). Nessun'altra riga del tb toccata.
- 2026-09-02T03:46 — [FASE 11] — Ricompilato ed eseguito sim/neuron_memory_tb.v: TUTTI E 5 I TEST PASSANO INVARIATI (PATTERN X=1..32=127, SUM=32, SATURATION=127, RELU=0, BIAS=10=42) — confermata retrocompatibilità totale per N_NEURONS=1.
- 2026-09-02T03:50 — [FASE 11] — Creato sim/neuron_memory_multi_tb.v: nuovo testbench dedicato, copia della struttura end-to-end di neuron_memory_tb.v (memory_interface + psram_controller + psram_model reali, mux a singolo master), che instanzia neuron_memory con N_NEURONS=3, N_INPUTS=32. Nuovi task: preload_x (X condiviso), preload_weights_n(base, n, value) (scrive i pesi del neurone n a w_base+n*32 byte), preload_bias_3(base, b0, b1, b2) (scrive le 3 bias contigue, impacchettando b0/b1 in una word e b2 nella successiva). Test case:
- X condiviso = 1 (32 elementi)
- Neurone 0: W=1, bias=0 -> atteso 32
- Neurone 1: W=2, bias=0 -> atteso 64
- Neurone 2: W=-1, bias=0 -> somma -32 -> ReLU -> atteso 0
Scelto per coprire: valore normale, valore più grande ma senza saturazione, e ReLU — validando in particolare che l'indirizzamento per-neurone (w_base+n*32, bias_addr+n) sia corretto e che `done` scatti una sola volta alla fine dell'intera sequenza (non per singolo neurone).
- 2026-09-02T03:55 — [FASE 11] — Compilato ed eseguito sim/neuron_memory_multi_tb.v: PASS AL PRIMO TENTATIVO. Neurone0=32 (atteso 32), Neurone1=64 (atteso 64), Neurone2=0 (atteso 0), busy=0 dopo done. Nessun bug di indirizzamento o di sequenza riscontrato.
- 2026-09-02T04:00 — [FASE 11] — REGRESSIONE COMPLETA finale: ricompilati ed eseguiti TUTTI i testbench del repo (int8_memory_access_tb, layer_tb, memory_interface_tb, neuron_parallel_tb, parametric_tb, psram_controller_tb, int8_psram_integration_tb, neuron_memory_tb, parameter_sweep_tb, neuron_memory_multi_tb) — TUTTI PASS. Ri-verificati anche i due test negativi della guardia Fase 9 (neuron_parallel_guard_negative_*): entrambi falliscono l'elaborazione come atteso, invariati (neuron_parallel.v non è stato toccato in questa fase).
- 2026-09-02T04:05 — [FASE 11] — Rigenerati gli artefatti tracciati sim/neuron_memory_sim + sim/neuron_memory.vcd (contenuto aggiornato dal nuovo N_NEURONS(1) esplicito) e creati i nuovi sim/neuron_memory_multi_sim + sim/neuron_memory_multi.vcd. Ripristinati con `git checkout --` i .vcd di testbench non pertinenti (int8_memory_access, int8_psram_integration, layer, memory_interface, neuron_parallel, parameter_sweep, parametric, psram_controller) rigenerati per effetto collaterale della regressione, per non introdurre diff non pertinenti.
- 2026-09-02T04:10 — [FASE 11] — Aggiornato docs/FPGA-NeuralNetwork-Engine.md, sezione "## Phase 3 — Memory Architecture": aggiunta checklist (single-neuron e multi-neuron chiusi; buffer intermedi multi-layer e analisi di banda esplicitamente rimandati rispettivamente a Fase 5 e Fase 7) e paragrafo descrittivo del design memory-bound scelto, con riferimento ai file di test.
- 2026-09-02T04:11 — [FASE 11] — CONCLUSIONE: neuron_memory.v supporta ora N_NEURONS>=1 in modo generico, validato end-to-end sia per il caso singolo-neurone (retrocompatibile, 5/5 test) sia multi-neurone (nuovo test dedicato, 3/3 neuroni corretti). Nessuna modifica al core di calcolo validato (neuron_parallel/mac8/mac_unit). Prossimo punto naturale di Fase 3 seguendo il roadmap: buffer di input/output e requisiti di banda, oppure passaggio a Fase 4 (interfaccia SPI) — da concordare con l'utente.
- 2026-09-02T04:15 — [FASE 11] — Merge/push: `git checkout main` + `git merge --ff-only feat/neuron-memory-multi-neuron` (fast-forward pulito 1a6f0ba..661363f) + `git push origin main` + `git branch -d feat/neuron-memory-multi-neuron`. main allineato e pushato, branch locale ripulito.
## Fase: Definizione protocollo SPI (Fase 4 roadmap, solo design) (2026-09-02)
- 2026-09-02T04:20 — [FASE 12] — Utente: "per interfaccia SPI occorre però che definiamo bene i protocolli e i comandi" — richiesta esplicita di FASE DI DESIGN prima di qualunque RTL per la Fase 4. Nessuna modifica a codice in questa fase.
- 2026-09-02T04:22 — [FASE 12] — Letto docs/FPGA-NeuralNetwork-Engine.md sezione "# 8. Host Interface" (già esistente): conteneva solo uno schema concettuale ad alto livello (RESET->CONFIGURE->LOAD PARAMS->LOAD WEIGHTS->LOAD BIASES->LOAD INPUT->START->WAIT DONE->READ OUTPUT) senza opcode, framing o register map concreti. Letta anche sezione "# 9. Dedicated FPGA RAM" per contesto su cosa la RAM deve contenere (pesi, bias, buffer input/output/intermedi, parametri rete).
- 2026-09-02T04:25 — [FASE 12] — Proposta iniziale (bozza v1) presentata all'utente: framing SPI byte-oriented con opcode da 1 byte, tabella comandi (NOP, WRITE_RAM, READ_RAM, SET_BASE, START, STATUS, READ_OUTPUT, READ_CONFIG) ancorata alle porte reali di rtl/neuron_memory.v (x_base/w_base/bias_addr come indirizzi BYTE a 22 bit, y_bus N_NEURONS*8 bit, N_INPUTS/N_NEURONS/PARALLEL fissati a sintesi).
- 2026-09-02T04:27 — [FASE 12] — Poste 2 domande di decisione architetturale via AskUserQuestion (non derivabili dal codice esistente, impattano la complessità del controller SPI e il firmware host):
1. WRITE_RAM/READ_RAM: lunghezza esplicita nel comando vs streaming fino a rilascio di CS -> utente ha scelto LUNGHEZZA ESPLICITA (campo len a 2 byte, il controller SPI usa un contatore invece di rilevare il fronte di CS a metà trasferimento).
2. Serve READ_CONFIG per esporre N_INPUTS/N_NEURONS/PARALLEL/ADDR_WIDTH a runtime, o il firmware host li assume fissi per bitstream -> utente ha scelto SÌ, READ_CONFIG (stesso firmware riusabile su bitstream diversi, coerente con l'obiettivo dichiarato nel doc di un protocollo indipendente dall'host).
- 2026-09-02T04:30 — [FASE 12] — Utente ha incollato la tabella comandi (identica a quella proposta) e chiesto esplicitamente: "terrei separato NOP con RESET" — richiesta di un opcode RESET dedicato, distinto da NOP (nella bozza iniziale RESET non era stato assegnato come opcode separato). Aggiunto opcode 0x0F = RESET: impulso di reset sincrono verso il motore di calcolo (neuron_memory) + pulizia del bit STATUS.done latched; NON cancella il contenuto della PSRAM (chiarito esplicitamente nello spec per evitare ambiguità).
- 2026-09-02T04:32 — [FASE 12] — Utente ha poi precisato: "questi sono esempi" — la tabella/i valori di opcode incollati sono da intendersi come ESEMPIO/bozza, non definitivi. Aggiornata la sezione nel doc da "defined/agreed" a "draft", specificando esplicitamente quali parti sono da considerarsi solide (framing MSB-first, lunghezza esplicita, STATUS.done sticky/clear-on-read, presenza di READ_CONFIG) vs quali sono ancora aperte a revisione (valori esatti degli opcode, set di comandi).
- 2026-09-02T04:35 — [FASE 12] — NOTA TECNICA IMPORTANTE identificata e documentata nello spec: in rtl/neuron_memory.v il segnale `done` è un impulso di UN SOLO CICLO di clock (asserito solo nello stato STATE_WAIT_N terminale, poi resettato al ciclo successivo dalla logica di default-pulse). Un host che fa polling via SPI (ordini di grandezza più lento del clock FPGA) lo perderebbe quasi certamente se il registro STATUS lo campionasse "al volo". Documentato che il register-bank SPI DEVE catturare `done` in un bit STICKY (latched sul fronte dell'impulso, azzerato alla lettura di STATUS o su RESET), non campionare il segnale raw — requisito di design per la futura implementazione RTL della Fase 4, non ancora implementato.
- 2026-09-02T04:40 — [FASE 12] — Scritta la spec completa in docs/FPGA-NeuralNetwork-Engine.md, nuova sezione "## 8.1 SPI Protocol v1 (draft, 2026-09-02)": fisico (SPI mode 0, MSB-first, single SPI, un comando per ciclo di CS), convenzione campi multi-byte big-endian, indirizzi a 3 byte (ADDR_WIDTH=22 bit + 2 bit riservati), tabella opcode completa (NOP 0x00, WRITE_RAM 0x01, READ_RAM 0x02, RESET 0x0F, SET_BASE 0x10, START 0x20, STATUS 0x21, READ_OUTPUT 0x22, READ_CONFIG 0x30), layout payload di READ_CONFIG (8 byte: ADDR_WIDTH, N_INPUTS, N_NEURONS, PARALLEL, DATA_WIDTH, versione protocollo), sessione di esempio end-to-end, elenco esplicito di ciò che resta fuori scope per v1 (Dual SPI, CRC/checksum, comandi di sequenziamento multi-layer -> rimandati a Fase 5).
- 2026-09-02T04:45 — [FASE 12] — Aggiornata la checklist della "## Phase 4 — SPI Interface" nel roadmap: spuntato "Protocol/opcode set drafted" con riferimento a §8.1; aggiunti come non ancora fatti: SPI controller RTL, register bank RTL, RAM access passthrough RTL, testbench dedicato (stile SPI master BFM + stack completo, sulla falsariga di neuron_memory_tb.v).
- 2026-09-02T04:46 — [FASE 12] — CONCLUSIONE: nessuna modifica a codice RTL/testbench in questa fase, solo documentazione di design (docs/FPGA-NeuralNetwork-Engine.md). Nessuna compilazione/simulazione necessaria. Prossimo step naturale: implementazione RTL del controller SPI/register-bank secondo questa spec, quando l'utente conferma che il draft è sufficientemente maturo (gli opcode restano volutamente aperti a revisione).