diff --git a/WORKLOG.md b/WORKLOG.md index 5907424..2e41c25 100644 --- a/WORKLOG.md +++ b/WORKLOG.md @@ -1337,3 +1337,21 @@ integrazione nel top level (F5), verifica consolidata e misure reali (F6). BUG-005. - **Prossimo passo**: C.6 (motore grafo — `graph_engine`, `act_buffer`, guard `src_id=out_id` auto-riferimento, +`out_id>=N_TOTAL`, `n_conn_padded==0`, percorso di recovery dopo un `err`) — entrambi +pre-esistenti, riverificati PASS in Fase 0. Copertura solida su happy-path e sui casi +avversari già identificati dal progetto. + +**Verdetto: CERTIFICATO** per questi aspetti (copertura pre-esistente adeguata). + +--- + +## 6.2 `num_neurons_graph=0` — stessa causa radice di BUG-005, ma protezione incidentale diversa + +**Analisi strutturale**: `neuron_idx` (`rtl/graph_engine.v:159`) è un registro a 16 bit +PIENI, e la condizione di terminazione (righe 527/561) +`neuron_idx==num_neurons_graph-16'd1` per `num_neurons_graph=0` avvolge a `65535` — un +valore che il contatore RAGGIUNGE naturalmente, stessa struttura esatta di BUG-005 +(`layer_idx`). Stessa causa radice: nessun guard su `num_neurons_graph`, né a compile-time +né a runtime. + +**Verificato empiricamente, con una riserva esplicita**: `sim/graph_engine_bug006_zero_neurons_probe_tb.v`, +finestra di osservazione limitata a 5000 cicli (**non fatto girare fino a completamento +reale** — fino a 65536 iterazioni con la logica di gather di questo modulo, più costosa per +iterazione del semplice dispatch di `layer_sequencer`, sarebbe stato impraticabile per il +budget di tempo di questa campagna; dichiarato come limite esplicito, non nascosto). + +``` +RESULT: err fired at cycle 58 (neuron_idx=0) -- the src_id=out_id` o `out_id>=N_TOTAL` → `err`, §6.1) che **non è stato progettato per +proteggere da `num_neurons_graph=0`** ma **lo cattura come effetto collaterale**: con un +pattern di dati "spazzatura" non banale (non tutto a zero, un pattern a rampa), il guard +esistente ha fermato l'esecuzione dopo sole 58 cicli, al primissimo neurone fasullo letto, +molto prima di avvicinarsi alle 65536 iterazioni possibili. `layer_sequencer.v` **non ha +alcun guard equivalente** — da qui la severità molto più alta di BUG-005. + +**Non è una garanzia**: questo test usa UN pattern di dati specifico. Non è stato +dimostrato che OGNI possibile contenuto PSRAM causi un arresto altrettanto rapido — esiste +in linea di principio un pattern di dati "sfortunato" che rispetti `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: `err` a 58 cicli, non 65536. + `layer_sequencer.v` non 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, vedi + `docs/validation/06-graph-engine.md` §6.2). +- **Impatto pratico**: basso ma non nullo — la protezione osservata è incidentale, non + garantita per ogni possibile contenuto PSRAM. Il buco strutturale è reale. +- **Stato**: **APERTO**, severità inferiore a BUG-005 per la protezione incidentale + osservata, non pienamente verificato su ogni pattern di dati possibile. + --- ## Risolti diff --git a/sim/graph_engine_bug006_zero_neurons_probe_tb.v b/sim/graph_engine_bug006_zero_neurons_probe_tb.v new file mode 100644 index 0000000..f2c1f86 --- /dev/null +++ b/sim/graph_engine_bug006_zero_neurons_probe_tb.v @@ -0,0 +1,96 @@ +`timescale 1ns/1ps + +// ================================================================ +// C.6 probe (NOT a certified bug entry by itself -- see file header +// note below and docs/validation/06-graph-engine.md): does +// num_neurons_graph=0 reproduce the same class of issue as BUG-005 +// (rtl/layer_sequencer.v)? +// +// Structural analysis: rtl/graph_engine.v's neuron_idx (line 159) is +// a full 16-bit register, and the termination check +// `neuron_idx == num_neurons_graph-16'd1` (lines 527/561) wraps to +// 65535 for num_neurons_graph=0 -- a value neuron_idx CAN naturally +// reach, structurally identical to BUG-005's layer_idx pattern. This +// probe checks empirically what actually happens within a BOUNDED +// window (a full 65536-iteration run was not attempted -- would take +// far longer per iteration than layer_sequencer's simpler dispatch, +// impractical for this campaign's effort budget; see docs/validation/ +// 06-graph-engine.md for the honesty note about this limitation). +// ================================================================ + +module tb; + + localparam ADDR_WIDTH = 23; + localparam DATA_WIDTH = 8; + localparam ACC_WIDTH = 32; + localparam PARALLEL = 4; + localparam MAX_CONN = 8; + localparam N_TOTAL = 4096; + + reg clk, rst; + reg run_start; + wire busy, done, err; + reg [ADDR_WIDTH-1:0] x_base, table_base, out_base; + reg [15:0] n_inputs_graph, num_neurons_graph, n_out; + + wire ram_req, ram_wr; + wire [ADDR_WIDTH-1:0] ram_addr; + wire signed [7:0] ram_wdata; + reg signed [7:0] ram_rdata; + reg ram_ready; + + graph_engine #( + .ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH), .ACC_WIDTH(ACC_WIDTH), + .PARALLEL(PARALLEL), .MAX_CONN(MAX_CONN), .N_TOTAL(N_TOTAL) + ) dut ( + .clk(clk), .rst(rst), + .run_start(run_start), .busy(busy), .done(done), .err(err), + .x_base(x_base), .table_base(table_base), .out_base(out_base), + .n_inputs_graph(n_inputs_graph), .num_neurons_graph(num_neurons_graph), .n_out(n_out), + .ram_req(ram_req), .ram_wr(ram_wr), .ram_addr(ram_addr), .ram_wdata(ram_wdata), + .ram_rdata(ram_rdata), .ram_ready(ram_ready) + ); + + initial begin clk = 0; forever #5 clk = ~clk; end + + // Non-trivial (not all-zero) "garbage" pattern: a repeating ramp, + // deliberately NOT chosen to make the src_id