Begins the V2 Neural Multiprocessor / Dataflow architecture per docs/v2-description.md, per explicit user request to freeze V1 and start V2 development, copying from V1 what's needed. Scaffold: - hardware/v1/: byte-exact, read-only copy of the current V1 codebase (rtl, testbenches, tools, constraints, a representative subset of synthesis results, and reference docs) -- verified identical via diff/cmp against the live top-level tree before being made filesystem-read-only. The live top-level tree is untouched and remains the project's "production" V1 (see hardware/v1/README.md and hardware/v2/logs/decisions.log DEC-0001 for why copy-not-move). - hardware/v2/: mandatory structure (rtl/sim/constraints/synthesis/ reports/scripts/logs/docs) plus the full logging system required by the spec (development/architecture/simulation/synthesis/timing/ benchmark/decisions/experiments/errors.log). M1 -- Neural Processor (hardware/v2/rtl/neural_processor.v): - 8-stage pipelined perceptron unit (P_IN=8): input align, 8 multipliers, 3-level adder tree, accumulator, bias+activation, INT8 saturation. Genuine 1-tile/cycle throughput, not just a wider combinational datapath. - 7-state FSM (NP_IDLE..NP_ERROR per docs/v2-description.md §6, with 4 baseline states merged into NP_WAIT_OPERANDS -- see decisions.log DEC-0002); valid/ready/data/last stream interfaces per §7. - Bit-exact vs the frozen hardware/v1/rtl/neuron_parallel.v + mac8.v + mac_unit.v: 7/7 tests pass (hardware/v2/sim/tb_neural_processor.v), covering regular/mixed-sign/extreme-INT8 vectors, both activations, a zero-idle-gap back-to-back-tiles throughput check, and an 8-tile job -- verified with Verilator (see below for why). - Real synthesis + place&route (Yosys + nextpnr-ecp5): 0 CHECK problems, Fmax 183.12 MHz at ACC_WIDTH=32 (PASS at 80MHz, ~3x V1's isolated PARALLEL=8 Fmax of 61.71 MHz) and 176.21 MHz at ACC_WIDTH=24 (a user-requested comparison experiment, also bit-exact-verified; see experiments.log EXP-0001/EXP-0002 and benchmark.log). Three real bugs found and resolved during M1 development (full diagnostic record in errors.log): - Two independent, reproducible Icarus Verilog v13.0 scheduling defects (ERR-0001, ERR-0002) that silently produced wrong simulation results for standard sequential Verilog -- confirmed via Verilator 5.050 giving correct results on the same minimal repros. Verilator is now the trusted simulator for hardware/v2/ (decisions.log DEC-0004); Icarus's affected protocol-violation check was removed from the RTL and deferred architecturally to the Neural Director (DEC-0003) rather than chased further. - One real RTL bug (ERR-0003): last0 wasn't gated like valid0, letting a "last tile" tag leak into the pipeline ahead of its actual valid tile on back-to-back jobs. Fixed and verified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
24 KiB
Registro bug — campagna di ri-certificazione FPGA-Neural
Formato per ogni voce: severità, sintomo, causa radice, evidenza (file:riga / comando/log citabile), stato, test di regressione che lo blocca (se risolto) o che lo riprodurrebbe (se aperto). Aggiornato incrementalmente man mano che avanzano gli aspetti C.1–C.14.
Severità: CRITICA (corrompe dati/hang in scenari raggiungibili), MEDIA (comportamento scorretto in casi limite plausibili ma rari), BASSA (difetto reale ma senza impatto funzionale pratico), INFO (non un bug: gap di copertura, ambiguità documentale/naming).
Aperti
(nessuno — tutti i bug della campagna sono stati corretti e verificati, vedi "Risolti" sotto)
Risolti
BUG-001 (INFO) — sim/top.v non compila contro l'RTL corrente
- Sintomo:
iverilogfallisce conparameter FRAC_BITS not found in top.dut. - Causa radice:
sim/top.vè un residuo della versione Q8.8 a virgola fissa del progetto, mai aggiornato dopo la conversione a INT8 puro (Fase 6, vedidocs/validation/00-inventario.md§0.2). - Evidenza:
iverilog -g2012 -o /tmp/topcheck.out rtl/neuron_parallel.v rtl/mac8.v rtl/mac_unit.v sim/top.v→ 2 errori di elaborazione. - Impatto: nessuno sulla regressione (il file non è referenziato da alcun testbench o tool) — era dead code, non un difetto funzionale del design.
- Fix applicato: file rimosso (
git rm sim/top.v) — confermato non referenziato da alcun testbench o tool (tools/run_regression.pylo esclude esplicitamente dal proprio elenco sorgenti anche prima della rimozione). - Stato: RISOLTO — file eliminato, nessun test di regressione necessario (non c'era comportamento da preservare).
BUG-002 (MEDIA, CONFERMATO su sim + sintesi reale) — N_INPUTS=0 bypassa il guard, start viene silenziosamente ignorato
- Sintomo confermato (non più un'ipotesi — vedi
docs/validation/01-datapath.md§1.4 per la narrativa completa, incl. un falso positivo iniziale nella mia stessa metodologia di test, corretto e ridocumentato per trasparenza): conneuron_parallel #(.N_INPUTS(0), .PARALLEL(P)), il guard elaboration-time (rtl/neuron_parallel.v:71,if (N_INPUTS % PARALLEL != 0)) non scatta (0 % P == 0per ogniP), il modulo elabora con successo (sia in simulazione Icarus sia in sintesi reale Yosys, 0 problemi CHECK). A runtime:startviene accettato mabusynon si alza mai edonenon pulsa mai — non l'hang "busy resta alto per sempre" descritto nel commento originale del guard (righe 55-58), un sintomo diverso, osservato per la prima volta in questa campagna. - Causa radice, confermata (non più ipotesi):
x_bus/w_bussono dichiarati[DATA_WIDTH*N_INPUTS-1:0], che perN_INPUTS=0diventa[-1:0]— un range che non collassa a larghezza zero: sia Icarus sia Yosys lo trattano come un vettore reale a 2 bit (larghezza = |MSB-LSB|+1 = 2), lasciato non pilotato. Confermato dai warning di Yosys:Wire ...x_bus[1] is used but has no driver(×2, per x_bus e w_bus). - Evidenza:
iverilog -g2012 -o /tmp/n0proper.out rtl/neuron_parallel.v rtl/mac8.v rtl/mac_unit.v sim/neuron_parallel_bug002_n_inputs_zero_tb.v && vvp /tmp/n0proper.out→ conferma il sintomo, ogni volta.yosys -p "synth_ecp5 -json /tmp/n0.json -top n0_synth_wrap" rtl/neuron_parallel.v rtl/mac8.v rtl/mac_unit.v <wrapper>→ 0 problemi CHECK, 4 warning "no driver" su x_bus/w_bus[1:0].- Test di regressione permanente:
sim/neuron_parallel_bug002_n_inputs_zero_tb.v.
- Impatto pratico:
N_INPUTSè un parametro Verilog fissato in fase di sintesi (non un registro configurabile via SPI a runtime) — per essere raggiunto, qualcuno deve deliberatamente istanziare il modulo conN_INPUTS=0, cosa che non ha senso semantico per un layer reale. Rischio quindi basso in pratica (nessun percorso runtime/host-controllato può innescarlo), ma è un buco reale e confermato nella protezione, non solo teorico. - Fix applicato (
rtl/neuron_parallel.v, guard di elaborazione): esteso aif (N_INPUTS == 0 || N_INPUTS % PARALLEL != 0)—N_INPUTS=0ora fa fallire l'elaborazione con lo stesso erroreUnknown module type: neuron_parallel_requires_N_INPUTS_multiple_of_PARALLELdei due test negativi già esistenti, invece di elaborare con successo e produrrestartsilenziosamente ignorato. - Test di regressione:
sim/neuron_parallel_bug002_n_inputs_zero_tb.v, riscritto per asserire il fallimento di compilazione (stesso pattern dineuron_parallel_guard_negative_*_tb.v), aggiunto aEXPECTED_COMPILE_FAILintools/run_regression.py. Verificato:iverilog -g2012 -o /tmp/out rtl/neuron_parallel.v sim/neuron_parallel_bug002_n_inputs_zero_tb.v→ errore di elaborazione atteso, exit code 3. - Stato: RISOLTO, verificato.
BUG-003 (MEDIA, CONFERMATO ma NON pienamente caratterizzato) — n_inputs_real=0 a runtime, comportamento incoerente tra ripetizioni
- Sintomo: con
N_INPUTS=32, PARALLEL=8validi a compile-time (nessun problema di larghezza[-1:0], a differenza di BUG-002), impostandon_inputs_real=0a runtime (lo stesso percorso raggiungibile dall'host viaSET_BASE sel=7) il comportamento osservato varia tra ripetizioni quasi identiche dello stesso test: a voltestartviene accettato mabusy/donenon si muovono mai più (hang), a volte l'operazione completa normalmente ma processa l'INTERA larghezza di build invece di zero elementi (limite ignorato silenziosamente, stessa classe di BUG-004). Vedidocs/validation/ 02-runtime-width.md§2.3 per la registrazione completa di ogni singola ripetizione e dei suoi risultati, riportati senza scartare quelli "scomodi". - Causa radice: non isolata con certezza entro il tempo ragionevole per questa
campagna. Analisi aritmetica plausibile (non confermata come spiegazione completa): per
questa build
GROUP_INDEX_WIDTH=2bit, quindigroups_real[1:0]-1pergroups_real=0avvolge al valore 3 (raggiungibile da un contatore a 2 bit, a differenza del contatore a 1 bit di BUG-002) — spiegherebbe l'esito "limite ignorato" come esito aritmeticamente atteso, ma non spiega perché in alcune ripetizioni compaia invece un hang vero. Esclusi esplicitamente: propagazione di X in simulazione (verificato inizializzando ogni registro prima di qualunque reset, il comportamento non cambia), e una dipendenza semplice dall'ordine delle chiamate (una sequenza valida→zero non blocca; una sequenza zero→zero→zero blocca dalla seconda chiamata in poi, non dalla prima — non un pattern semplice "prima volta sicura, poi no"). - Impatto pratico: come BUG-002,
n_inputs_real=0non ha senso semantico per una rete reale, ma a differenza di BUG-002 questo valore è raggiungibile a runtime da un host via SPI senza bisogno di una nuova sintesi — un host con un bug che calcola erroneamenten_inputs_real=0per un caso limite (es. un layer con zero neuroni in una topologia degenere) potrebbe innescarlo, con un esito imprevedibile tra hang e risultato silenziosamente sbagliato. - Fix applicato (
rtl/neuron_parallel.v, dentroif (start && !busy)):finishing <= (n_inputs_real == 16'h0);(erafinishing <= 0;) — riusa il percorso di completamento "finishing" già corretto ed esistente nel modulo invece di introdurre nuova logica per il caso degenere, stessa convenzione già usata altrove nel progetto. Per zero input reali il risultato matematico èy = activation(bias): conbias=0e ACT_RELU nel test di regressione,expect_y=0. - Nota onestà: il meccanismo esatto per cui il comportamento pre-fix variava tra ripetizioni (hang vs. risultato sbagliato silenzioso) non è stato isolato bit-per-bit neppure in fase di correzione — il fix è un early-out esplicito che bypassa l'intero percorso ambiguo, verificato corretto e deterministico sul nuovo comportamento, non una spiegazione a posteriori del vecchio meccanismo.
- Test di regressione:
sim/neuron_parallel_bug003_n_inputs_real_zero_tb.vTEST 4, riscritto da osservazione ad asserzione hard (done entro 8 cicli,y===0). Verificato:n_inputs_real=0completa in 1 ciclo,y=0— TUTTI I TEST PASSED (incluse le TEST 1-3 di non-regressione sulla regione "poison"). - Stato: RISOLTO, verificato (il meccanismo esatto del comportamento PRE-fix resta non isolato per intero, per trasparenza, ma non è più rilevante: il nuovo percorso è deterministico e verificato indipendentemente).
BUG-004 (MEDIA, CONFERMATO scorretto, NON pienamente caratterizzato) — n_neurons_real=0 non blocca, ma non fa nemmeno quello che ci si aspetterebbe in modo coerente
- Sintomo: a
rtl/neuron_memory.v, conn_neurons_real=0, l'operazione completa sempre normalmente (mai un hang, a differenza di BUG-002/003) — ma il numero di cicli impiegato non è coerente tra build diverse: perN_NEURONS=2(NEURON_INDEX_WIDTH=1bit) impiega esattamente lo stesso numero di cicli din_neurons_real=2(114=114, suggerendo che il limite venga ignorato e processi tutto), mentre perN_NEURONS=3(NEURON_INDEX_WIDTH=2bit) impiega 196 cicli — più della build completa a 3 neuroni (155), un terzo valore che non corrisponde né a "zero neuroni" né a "tutti i neuroni". In ogni caso testato: nessun errore, nessun timeout — un host che chiede zero neuroni riceve sempre un completamento dall'aspetto normale ma su un conteggio/dato diverso da quanto richiesto, e il conteggio esatto varia conN_NEURONS. - Causa radice: non isolata bit-per-bit (a differenza di BUG-002). Ipotesi coerente con
BUG-003: l'aritmetica di avvolgimento (
neuron_index == n_neurons_real[W-1:0]-1) pern_neurons_real=0produce un valore di terminazione che, per coincidenza di larghezza, corrisponde al conteggio pieno invece che a "termina subito". Vedidocs/validation/02-runtime-width.md§2.5. - Impatto pratico: come BUG-002/003, richiede che l'host imposti deliberatamente (o per
bug proprio)
n_neurons_real=0— non raggiungibile da un input esterno arbitrario, ma raggiungibile da un bug nel software host senza bisogno di ricompilare il bitstream. - Fix applicato (
rtl/neuron_memory.v, tre punti coordinati, non uno solo): il pattern vulnerabile esisteva in TRE punti distinti, non uno — scoperto durante la correzione stessa (un primo tentativo di guard solo al dispatchSTATE_IDLE→STATE_READ_Xè stato riconosciuto insufficiente perché copriva un solo dei tre punti d'ingresso nello stato vulnerabileSTATE_READ_W, che viene rientrato indipendentemente una volta per neurone nel loop). Fix corretto (single-point-of-truth sulle CONDIZIONI di terminazione, non sui punti di dispatch):STATE_READ_XeSTATE_READ_Wguadagnano entrambe un prefisson_inputs_real == 16'h0 ||/n_neurons_real == 16'h0 ||sulla propria condizione di terminazione, e il punto di transizione X→W guadagna un ramo esplicitoif (n_neurons_real == 16'h0) begin busy<=0; done<=1; state<=STATE_IDLE; end. - Test di regressione:
sim/neuron_memory_bug004_n_neurons_real_zero_tb.vTEST 4, riscritto per asseriredoneentro il baseline a piena larghezza (cyc_full, stabilito dinamicamente da TEST 3). Verificato:n_neurons_real=0completa in 32 cicli contro 155 per il build completo a 3 neuroni (X viene ancora letto una volta, condiviso tra neuroni, ma nessun calcolo per-neurone viene eseguito) — TUTTI I TEST PASSED. - Stato: RISOLTO, verificato (stessa nota di onestà di BUG-003: il meccanismo esatto
del comportamento PRE-fix — 114 vs 196 cicli a seconda di
N_NEURONS— non è stato isolato bit-per-bit, ma il nuovo percorso è deterministico e verificato indipendentemente).
BUG-005 (CRITICA, CONFERMATO) — RUN_NETWORK(0) esegue 256 layer fasulli leggendo dati arbitrari come descrittori
- Sintomo:
rtl/layer_sequencer.vdocumentarun_num_layerscome "1..N_LAYERS" ma non esiste alcun guard, né a tempo di elaborazione né a runtime, che lo imponga.layer_idx(riga 121) è un registro a 8 bit PIENO (non ristretto come ilgroup_indexa 1 bit di BUG-002) — perrun_num_layers=0, la condizione di terminazionelayer_idx == num_layers_reg-1(riga 303) avvolge alayer_idx==255, un valore che il contatore RAGGIUNGE naturalmente contando da 0. Risultato confermato empiricamente:RUN_NETWORK(0)non si blocca — esegue tutti e 256 gli indici di layer possibili (21761 cicli in simulazione) prima di terminare, ciascuno leggendo 11 byte di "descrittore" datable_base + layer_idx×11— ben oltre la vera tabella descrittori (dimensionata per il build reale, tipicamente poche decine di byte) — e interpretando dati PSRAM arbitrari (pesi, altri dati di rete, o memoria non inizializzata) come indirizzi/parametri di layer validi, eseguendo run reali dineuron_memorycon quei parametri e scrivendo i risultati nei buffer ping-pong a indirizzi derivati da quei dati arbitrari. - Causa radice: nessun guard su
run_num_layers, né a tempo di elaborazione (come invece esiste perN_INPUTS%PARALLELinneuron_parallel.v) né a runtime (come invece esiste, sia pure incompleto, pern_inputs_real/n_neurons_real, BUG-003/004). - Evidenza:
sim/layer_sequencer_bug005_zero_layers_tb.v—iverilog -g2012 -o /tmp/ls0.out rtl/layer_sequencer.v sim/layer_sequencer_bug005_zero_layers_tb.v && vvp /tmp/ls0.out→dut.layer_idxtermina a 255, non a 0. - Impatto pratico: più severo di BUG-002/003/004 — raggiungibile con un singolo
opcode SPI documentato (
RUN_NETWORK,num_layers=0) senza bisogno di ricompilare il bitstream né di impostare un valore "runtime" degenere in un percorso secondario; il rischio non è solo un risultato sbagliato o un hang, ma scritture reali in PSRAM a indirizzi non controllati, derivati da dati che non erano mai stati pensati per essere interpretati come indirizzi. - Fix applicato (
rtl/layer_sequencer.v,ST_IDLE):run_num_layers==0è ora un no-op esplicito e immediato —seq_donepulsa senza mai entrare inST_READ_DESC, stessa convenzione già usata daspi_engine.vperWRITE_RAM/READ_RAMconlen==0(accetta il comando, non fa nulla, nessun errore riportato). - Test di regressione:
sim/layer_sequencer_bug005_zero_layers_tb.v, riscritto da osservazione ad asserzione hard (seq_done entro 5 cicli,layer_idx===0). Verificato:run_num_layers=0completa in 1 ciclo conlayer_idxrimasto a 0 (era 21761 cicli,layer_idxterminato a 255, prima del fix) — PASS. - Stato: RISOLTO, verificato.
BUG-006 (BASSA, stessa causa radice di BUG-005, protezione incidentale) — num_neurons_graph=0 in graph_engine.v
- Sintomo/causa radice: identica struttura a BUG-005 —
neuron_idx(rtl/graph_engine.v:159) è un registro a 16 bit pieni,num_neurons_graph=0fa avvolgere la condizione di terminazione a un valore (65535) che il contatore raggiunge naturalmente. Nessun guard esplicito sunum_neurons_graph. - Differenza da BUG-005:
graph_engineha già un guard runtime per-edge (src_id>=out_id/out_id>=N_TOTAL→err) che, come effetto collaterale non progettato per questo scopo, cattura la maggior parte dei pattern di dati spazzatura molto rapidamente — verificato con un pattern non banale:erra 58 cicli, non 65536.layer_sequencer.vnon ha alcuna protezione equivalente. - Evidenza:
sim/graph_engine_bug006_zero_neurons_probe_tb.v— finestra di 5000 cicli, non fatto girare a completamento (limite dichiarato, vedidocs/validation/06-graph-engine.md§6.2). - Impatto pratico: basso ma non nullo — la protezione PRE-fix era incidentale, non garantita per ogni possibile contenuto PSRAM. Il buco strutturale era reale.
- Fix applicato (
rtl/graph_engine.v,ST_COPY_IN_WAIT, alla transizione di fine copia input):num_neurons_graph==0è ora un no-op esplicito e immediato —donepulsa subito dopo il completamento della copia input, senza mai entrare inST_DESC_RD/il loop descrittori, stessa convenzione del fix BUG-005. - Test di regressione:
sim/graph_engine_bug006_zero_neurons_probe_tb.v, riscritto da osservazione a asserzione hard (doneentro 30 cicli, nessunerr,neuron_idx===0). Verificato:num_neurons_graph=0completa in 13 cicli senzaerr,neuron_idxrimasto a 0 (era 58 cicli tramite l'intercettazione incidentale del guard src_id/out_id, prima del fix) — PASS. - Stato: RISOLTO, verificato.
BUG-007 (CRITICA, CONFERMATO end-to-end via SPI reale) — SET_NET_TYPE durante un RUN_NETWORK in corso blocca permanentemente il motore in esecuzione
- Sintomo:
rtl/spi_engine.v, statoST_SET_NET_TYPE, accettanet_type <= rx_byteincondizionatamente su qualunquerx_valid, senza alcun controllo sugraph_busy/seq_busy.rtl/spi_neuron_top.v(righe 394-397) instrada la Porta C dell'arbitro tragraph_engineelayer_sequencerin modo puramente combinazionale sul valore CORRENTE dinet_type— non agganciato a quale motore ha effettivamente avviato il run in corso. Il commento alla riga 390 dichiara i due motori "mutually exclusive by construction", ma quella costruzione impedisce solo che entrambi vengano avviati insieme — non dice nulla su una scrittura dinet_typeche arriva a metà di un run già avviato. - Confermato end-to-end su SPI reale (non solo per ispezione): avviato un
RUN_NETWORKin modalità grafo (lo stesso grafo valido già certificato inspi_neuron_top_graph_tb.v), poi immediatamente — prima che completi — inviatoSET_NET_TYPE(dense)via SPI. Risultato:STATUS.busyresta bloccato a 1 per 400+ letture consecutive, ~2.35ms di tempo simulato (contro i ~12-25µs normali per quel grafo) — un hang permanente, non un rallentamento. Le transazioni SPI stesse (incl. ilSET_NET_TYPEavversariale) completano regolarmente; è specificamente il motore grafo a restare bloccato in attesa di unram_readyche non arriverà mai più tramite il percorso ormai scollegato dal mux. - Evidenza:
sim/spi_neuron_top_bug007_mid_run_net_type_tb.v— riproduce l'hang in modo deterministico e ripetibile su SPI reale (non solo un accesso interno). - Impatto pratico: il più severo finora insieme a BUG-005 — raggiungibile con due
soli opcode SPI documentati emessi in sequenza ravvicinata (
RUN_NETWORKseguito daSET_NET_TYPEprima del completamento), uno scenario host plausibile (es. un host che prepara la configurazione per il prossimo run senza attendere la fine del precedente, o una race a livello applicativo tra due richieste). Blocca l'inferenza in corso finché l'host non se ne accorge (nessun timeout hardware, nessun errore riportato — soloSTATUS.busyche non si abbassa mai). - Recupero verificato: un
RESETinviato durante l'hang riporta il sistema a uno stato pienamente funzionante — verificato con una successiva operazione dense legittima completata normalmente (2 cicli di polling, esito corretto). Non è un blocco permanente, ma un host che si limita a fare polling diSTATUSsenza un timeout e unRESETdi ripiego resterebbe bloccato indefinitamente comunque, dato che l'hardware non segnala mai da solo che qualcosa è andato storto. - Fix applicato (
rtl/spi_engine.v,ST_SET_NET_TYPE):net_type <= rx_byteora condizionato aif (!graph_busy && !seq_busy)— la scrittura viene silenziosamente rifiutata (comando accettato via SPI come prima, ma senza effetto) mentre un run è in corso, invece di rimappare il mux dell'arbitro a metà esecuzione. - Test di regressione:
sim/spi_neuron_top_bug007_mid_run_net_type_tb.v, riscritto per asserire end-to-end su SPI reale sia (a) che il run in corso completi normalmente nonostante loSET_NET_TYPEavversariale a metà esecuzione, sia (b) che la scrittura sia stata VERAMENTE rifiutata e non parzialmente applicata (una successivaRUN_NETWORKsenza re-inviareSET_NET_TYPE(graph)completa comunque correttamente). Verificato: il run grafo completa conout_base[0]=126dopo 1 solo polling nonostante loSET_NET_TYPE(dense)avversariale;net_typeconfermato ancoraGRAPHinternamente — PASS su entrambi i controlli. - Stato: RISOLTO, verificato end-to-end su SPI reale.
Verifica post-fix su entrambi i piani (§A.4)
Dopo l'applicazione di tutti e 7 i fix (RTL: rtl/neuron_parallel.v, rtl/neuron_memory.v,
rtl/layer_sequencer.v, rtl/graph_engine.v, rtl/spi_engine.v; rimozione:
sim/top.v):
- Regressione Icarus completa (
python3 tools/run_regression.py): 44 testbench, 43 PASS, 0 FAIL/ERROR, 1 BENCHMARK (nessun verdetto per progetto, invariato). Nessuna regressione sui 37 test già certificati pre-fix. - Sintesi reale (
yosys synth_ecp5, sistema completospi_neuron_topcon sottosistema flash, PARALLEL=8, stessi file/comando già validati in Fase 15/F7 di WORKLOG.md): 0 problemi CHECK, 1 warning atteso/preesistente (tri-state limitato inpsram_controller.v, invariato). TRELLIS_FF: 4900 (era 4855 — +45, coerente con la nuova logica di guard/controllo introdotta dai fix, nessuna crescita anomala). - Place&route reale (
nextpnr-ecp5 --45k --package CABGA381 --speed 8 --freq 80 --lpf synth/ecp5/spi_neuron_top.lpf): 0 errori di vincolo, 0 pin non vincolati, "Program finished normally". Fmax: 68.65 MHz (era 67.91 MHz — leggermente meglio, entro il rumore di piazzamento già documentato in Fase 7/WORKLOG.md, non una regressione). Percorso critico verificato esplicitamente strutturalmente identico a prima del fix:u_neuron_memory.u_neuron.group_index→u_mac8→ catena di riporto CCU2C dell'accumulatore (rtl/mac8.v) — nessun modulo toccato dai fix (che sono tutti aggiunte al percorso di controllo, non al datapath MAC/accumulatore) compare nel percorso critico. Margine sull'oscillatore reale 16MHz: 4.29× (invariato). Log:synth/ecp5/post_fix_verify/yosys.log,synth/ecp5/post_fix_verify/nextpnr.log.
Non-bug (falsi positivi trovati e chiusi durante l'analisi)
Voci che sono sembrate anomalie a un primo controllo automatico ma si sono rivelate corrette per progetto una volta letto il codice/intento — riportate per trasparenza sul processo, non perché siano difetti.
neuron_parallel_guard_negative_{degenerate,nonmultiple}_tb.v"falliscono a compilare": comportamento corretto e intenzionale (test negativi, la mancata compilazione è il PASS). Vedidocs/validation/00-inventario.md§0.5.graph_engine_bandwidth_tb.v"nessun verdetto PASS/FAIL": è un benchmark per progetto, non un test di correttezza. Vedi §0.3/§0.5 dell'inventario.- C.1 (falso "hang" iniziale,
neuron_parallelconfig nota-buona): controllo tardivo e singolo didone(impulso di un solo ciclo) in uno script bespoke — non un problema dell'RTL. Vedidocs/validation/01-datapath.md§1.4. - C.2 (falso "hang" per
n_inputs_real=17): stesso tipo di errore in un secondo script bespoke diverso da quello già provato — corretto riusando lo schema affidabile. Vedidocs/validation/02-runtime-width.md§2.2. - C.3 (falsi mismatch su 2048 controlli + un falso fallimento di round-trip): nel nuovo
sim/int8_memory_access_bytelane_tb.v, un controllo dei segnali nello stesso passo di simulazione del loro aggiornamento non-bloccante (leggeva il valore dell'iterazione precedente), e uno stub di memoria comportamentale che ignorava le byte-lanemem_lb_n/mem_ub_ndurante la scrittura. Entrambi difetti della testbench, non dell'RTL — vedidocs/validation/03-memoria.md§3.1. - C.4 (
mem_arbitermai concedeva nulla nella mia prima testbench): assegnazioni bloccanti per ritirare le richieste dei "perdenti" nello stesso fronte di clock che doveva concedere la richiesta — race reale con il blocco sincrono del DUT. Diagnosticato condut.ownermai uscito daSEL_NONE. Corretto passando ad assegnazioni non bloccanti. Vedidocs/validation/04-arbiter.md§4.1.