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
189 lines
44 KiB
Markdown
189 lines
44 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).
|
|
|
|
## Fase: Implementazione RTL SPI - livello fisico (Fase 4 RTL) (2026-09-02)
|
|
|
|
- 2026-09-02T05:00 — [FASE 13] — Utente conferma priorità: matrice di sequenziamento layer-to-layer (Fase 5) rimandata; procedere ORA con l'implementazione RTL vera dell'SPI (Fase 4), "fatta bene". Successivamente chiarito con "#4" = implementazione hardware COMPLETA di SPI, testata in tutte le sue sfaccettature (non solo happy path).
|
|
- 2026-09-02T05:02 — [FASE 13] — Piano comunicato: (1) spi_slave.v livello fisico + tb dedicato, (2) spi_engine.v FSM opcode + register bank, (3) arbitro bus condiviso spi_engine/neuron_memory, (4) top-level integrazione, (5) tb end-to-end con master SPI simulato che esegue l'intera sessione della spec. Iniziato dal punto più a rischio (CDC tra SCLK del master e clock di sistema).
|
|
- 2026-09-02T05:05 — [FASE 13] — Creato rtl/spi_slave.v: livello fisico SPI Mode 0 (CPOL=0,CPHA=0), MSB-first. Sincronizzatori a doppio flip-flop (3 stadi) per SCLK/MOSI/CS_N in ingresso dal dominio di clock del master (asincrono). Edge-detect su segnali sincronizzati. Shift register a 8 bit per rx (campiona MOSI su fronte di salita SCLK sincronizzato) e tx (aggiorna MISO su fronte di discesa SCLK sincronizzato). Segnali cs_start/cs_end (impulsi) e cs_active (livello) per delimitare le transazioni. Rimossa durante la scrittura una riga placeholder/dead-code lasciata per errore (wire sclk_rising ridondante).
|
|
- 2026-09-02T05:10 — [FASE 13] — Creato sim/spi_slave_tb.v: master SPI simulato bit-banged (Mode 0, MSB-first) con 4 test: TEST1 singolo byte (rx_byte/rx_valid + readback MISO), TEST2 multi-byte in una sola sessione CS, TEST3 transazioni back-to-back separate, TEST4 SCLK più lento (verifica indipendenza dal rapporto SCLK/clk).
|
|
- 2026-09-02T05:12 — [FASE 13] — Prima esecuzione: TEST1 e TEST3 PASS, TEST2 e TEST4 FAIL su mismatch MISO (sequenza byte ricevuta dal master shiftata, es. atteso "de ad be ef" ottenuto "ad be ef 00").
|
|
- 2026-09-02T05:15 — [FASE 13] — Prima ipotesi (poi rivelatasi solo parzialmente corretta): margine di temporizzazione insufficiente nel BFM del testbench, basato su ritardi `#ns` non allineati al clock di sistema. Riscritto il BFM per usare conteggi di cicli di `clk` (task `clk_wait`) invece di `#ns` assoluti, con margini generosi (8 e 20 cicli per mezzo-bit, ben oltre la latenza di sincronizzazione CDC di ~4 cicli). Stesso esito di fallimento anche dopo questa riscrittura -> la causa non era (solo) di margine.
|
|
- 2026-09-02T05:20 — [FASE 13] — Aggiunta strumentazione di debug temporanea (blocco `always @(posedge clk)` con `$display`/poi `$strobe` che traccia tx_byte_req, sclk_rise/fall, cs_fell, rx_valid, bit_count, tx_shift, miso, tx_byte, tx_queue_idx). Individuata la causa reale: `tx_queue_idx` (nel testbench) veniva avanzato su OGNI impulso `tx_byte_req`, ma il DUT genera legittimamente un impulso "phantom" extra dopo l'ultimo bit di ogni transazione (non può sapere in anticipo se il master continuerà a inviare altri byte prima di rilasciare CS, quindi pre-carica comunque il prossimo byte per garantire la validità di MISO). Questo consumava un elemento di troppo dalla coda del testbench ad ogni transazione.
|
|
- 2026-09-02T05:25 — [FASE 13] — FIX 1 (contratto tx_byte_req): documentato esplicitamente in rtl/spi_slave.v (commento sul segnale `tx_byte_req`) che è un hint di prefetch, NON un evento "byte consumato" — un consumer non deve usarlo per avanzare un puntatore stateful (es. indirizzo di lettura RAM), pena un avanzamento di un byte in eccesso ad ogni transazione. Il segnale corretto per avanzare un puntatore è `rx_valid`, che scatta esattamente una volta per ogni byte REALMENTE trasferito (mai un impulso extra, essendo guidato dal conteggio di fronti SCLK realmente avvenuti). Aggiornato sim/spi_slave_tb.v: `tx_queue_idx` ora avanza su `rx_valid` invece che su `tx_byte_req`.
|
|
- 2026-09-02T05:28 — [FASE 13] — Rieseguito dopo il fix 1: STESSO fallimento identico (miso ancora shiftato). Approfondito ulteriormente con debug tracing esteso dall'inizio simulazione: scoperto che `tx_queue_idx` non veniva MAI azzerato dai reset intermedi tra un test e l'altro (`rst=1'b1; @(posedge clk); rst=1'b0; @(posedge clk);`), nonostante la logica `if(rst) tx_queue_idx<=0;` fosse corretta.
|
|
- 2026-09-02T05:32 — [FASE 13] — CAUSA REALE identificata: race classica a "ritardo zero" sul fronte di clock. L'assegnazione blocking `rst=1'b1;` nel testbench avveniva, per la cadenza temporale usata (nessun `#delay` tra istruzioni, solo `@(posedge clk)`), esattamente nello stesso istante di simulazione di un fronte di salita del clock — rendendo non deterministico se i vari blocchi `always @(posedge clk)` (nel testbench e nel DUT) vedessero il vecchio o il nuovo valore di `rst` per quel fronte. Risultato osservato: il reset "non prendeva mai" per l'indice della coda, che restava sporco da un test al successivo.
|
|
- 2026-09-02T05:35 — [FASE 13] — FIX 2 (eliminazione della race): sostituiti i 3 reset rapidi inter-test da `rst=1'b1; @(posedge clk); rst=1'b0; @(posedge clk);` a `@(negedge clk); rst=1'b1; @(negedge clk); rst=1'b0; @(posedge clk);` — assegnando rst sul FRONTE DI DISCESA del clock, garantendo per costruzione che sia già stabile e senza ambiguità al successivo fronte di salita.
|
|
- 2026-09-02T05:38 — [FASE 13] — Rieseguito dopo il fix 2: TUTTI E 4 I TEST PASSANO (TEST1 PASS, TEST2 miso=de ad be ef come atteso, TEST3 PASS, TEST4 rx=0x96/miso=0x5a come atteso). Rimossa tutta la strumentazione di debug temporanea (blocco always/$strobe, reg debug_on e relativi toggle) per lasciare il testbench pulito.
|
|
- 2026-09-02T05:40 — [FASE 13] — Ricompilato ed eseguito sim/spi_slave_tb.v pulito (senza debug): confermato TUTTI E 4 I TEST PASSANO, esito identico.
|
|
- 2026-09-02T05:42 — [FASE 13] — LEZIONE APPRESA (rilevante per ogni futuro testbench in questo repo che usa sequenziamento `@(posedge clk)` senza `#delay` espliciti): assegnare segnali di controllo come `rst` con blocking assignment immediatamente prima di un `@(posedge clk)` rischia una race a delta-zero se il tempo di simulazione corrente coincide esattamente con un fronte di clock. Pattern sicuro: assegnare su `@(negedge clk)` quando si vuole garantire che il valore sia stabile e non ambiguo al successivo fronte di salita.
|
|
- 2026-09-02T05:45 — [FASE 13] — VERIFICA CON TOOLCHAIN FPGA REALE (richiesta esplicita dell'utente: "hai verificato con il simulatore della FPGA specifica?"): fino a questo punto la verifica era stata fatta solo con Icarus Verilog (simulatore RTL generico, verifica solo correttezza logica/funzionale, non timing né sintetizzabilità specifica del vendor). Individuato il toolchain reale già presente in ambiente (stesso usato per i benchmark in docs/FPGA-Neural-Datapatch-Benchmark.md, Appendice A): Yosys 0.68+post (/opt/homebrew/bin/yosys), nextpnr-ecp5 0.11.1 (/tmp/nextpnr/build/nextpnr-ecp5), ecppack/Project Trellis (/opt/homebrew/bin/ecppack).
|
|
- 2026-09-02T05:48 — [FASE 13] — Eseguita sintesi reale: `yosys -p "synth_ecp5 -json spi_slave.json -top spi_slave" rtl/spi_slave.v`. Risultato: 0 problemi rilevati dal CHECK pass, nessun latch inferito, 41 TRELLIS_FF, 55 LUT4, 21 PFUMX, 10 L6MUX21 — footprint coerente con un semplice shift register SPI.
|
|
- 2026-09-02T05:50 — [FASE 13] — Eseguito place&route reale: `nextpnr-ecp5 --45k --package CABGA381 --speed 8 --json spi_slave.json --lpf-allow-unconstrained --freq 80 --textcfg spi_slave.config` (stessi parametri usati nei benchmark esistenti, target LFE5U-45F-8BG381C). Risultato: **Max frequency 403.23 MHz, PASS al target 80 MHz** (ampio margine, atteso per un modulo così piccolo). Nessun errore di routing, "Program finished normally".
|
|
- 2026-09-02T05:52 — [FASE 13] — Generato bitstream reale: `ecppack spi_slave.config spi_slave.bit` -> completato senza errori (file da ~1 MB, dimensione plausibile per ECP5-45F). Confermata l'intera catena di implementazione (Verilog -> sintesi -> place&route -> bitstream) funzionante per questo modulo, non solo la simulazione comportamentale.
|
|
- 2026-09-02T05:53 — [FASE 13] — CONCLUSIONE: rtl/spi_slave.v verificato sia funzionalmente (Icarus, 4/4 test) sia a livello di implementazione reale sul target FPGA dichiarato nel progetto (Yosys+nextpnr-ecp5+ecppack, PASS a 80MHz con margine ampio). Il bug trovato durante il debug era interamente nel testbench (due bug distinti: contratto tx_byte_req mal interpretato + race di reset a delta-zero), NON nel DUT stesso — rtl/spi_slave.v non ha richiesto modifiche funzionali, solo l'aggiunta del commento di contratto su tx_byte_req. Prossimo step: rtl/spi_engine.v (FSM opcode + register bank).
|