test: certify spi_neuron_top mux/pins (C.8), find critical BUG-007

Legitimate dispatch mux and data_ready_n/irq_n pins certified via
existing pre-session tests.

BUG-007 (CRITICAL), confirmed end-to-end over real simulated SPI:
SET_NET_TYPE has no check against graph_busy/seq_busy in
rtl/spi_engine.v, and rtl/spi_neuron_top.v's arbiter Port C mux
selects between graph_engine/layer_sequencer purely combinationally on
the current net_type value -- not latched to whichever engine actually
started the in-flight run. Started a valid graph RUN_NETWORK, sent
SET_NET_TYPE(dense) immediately after (before completion): STATUS.busy
gets stuck (30+ consecutive polls with no done/err, vs. ~12-25us normal
completion) -- the graph engine is left waiting for a ram_ready that
never arrives via the now-disconnected mux path. Also verified
recovery: RESET during the hang brings the system back to a fully
working state (a subsequent legitimate dense op completes normally) --
not a permanent lockup, but plain STATUS polling alone would never
unstick without a host-side RESET fallback.

Full regression: 40/40 real tests pass, 1 new observational test
deterministically reproduces BUG-007 and verifies RESET recovery.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
2026-09-04 18:50:06 +02:00
co-authored by Claude Sonnet 5
parent 95849c9002
commit f0a66363de
22 changed files with 832338 additions and 17 deletions
+22
View File
@@ -1371,3 +1371,25 @@ integrazione nel top level (F5), verifica consolidata e misure reali (F6).
- **Deliverable**: `docs/validation/07-spi.md`. - **Deliverable**: `docs/validation/07-spi.md`.
- **Prossimo passo**: C.8 (top-level `spi_neuron_top` — mux `seq_busy`, reset soft, pin - **Prossimo passo**: C.8 (top-level `spi_neuron_top` — mux `seq_busy`, reset soft, pin
`data_ready_n`/`irq_n`). `data_ready_n`/`irq_n`).
## Campagna di ri-certificazione — C.8: Top-level (2026-09-04)
- **Mux dispatch legittimo e pin `data_ready_n`/`irq_n` certificati** citando test
pre-esistenti (`spi_neuron_top_runnetwork_tb.v`, `spi_neuron_top_irq_tb.v`).
- **BUG-007 (CRITICO) trovato e confermato end-to-end su SPI reale**: `SET_NET_TYPE` non
ha alcun controllo su `graph_busy`/`seq_busy` (`rtl/spi_engine.v`), e il mux della Porta C
dell'arbitro sceglie tra `graph_engine`/`layer_sequencer` in modo puramente
combinazionale sul valore corrente di `net_type` — non agganciato a quale motore ha
avviato il run. Verificato: avviato un `RUN_NETWORK` grafo valido, inviato
immediatamente `SET_NET_TYPE(dense)` prima del completamento → **`STATUS.busy` resta
bloccato** (30+ poll consecutivi senza `done`/`err`, contro i ~12-25µs normali per quel
grafo) — il motore grafo resta in attesa di un `ram_ready` che non arriva più tramite il
mux ormai scollegato. **Verificato anche il recupero**: un `RESET` durante l'hang
riporta il sistema a uno stato pienamente funzionante (operazione dense successiva
completata normalmente) — non un blocco permanente, ma il polling da solo non si
sbloccherebbe mai senza un `RESET` di ripiego lato host.
- **Regressione completa**: 40/40 test reali PASS invariati, 1 nuovo test osservazionale
(riproduce BUG-007 in modo deterministico e verifica il recupero via RESET).
- **Deliverable**: `docs/validation/08-top-level.md`, `bugs.md` con BUG-007.
- **Prossimo passo**: C.9 (pinout/.lpf — già ampiamente verificato in sessioni precedenti,
citazione + eventuale riverifica mirata).
+67
View File
@@ -0,0 +1,67 @@
# C.8 — Top-level (`spi_neuron_top.v`)
Data: 2026-09-04.
---
## 8.1 Mux `seq_busy`/dispatch legittimo, pin `data_ready_n`/`irq_n` — CERTIFICATO
Il meccanismo di mux `mux_nm_*` (che decide se `neuron_memory` è pilotato da
`layer_sequencer` durante un `RUN_NETWORK` o direttamente da `spi_engine` per un `START`
manuale) è già coperto da `sim/spi_neuron_top_runnetwork_tb.v` (un `START` a singolo layer
funziona ancora correttamente dopo un `RUN_NETWORK` precedente — "mux sanity"). I pin
`data_ready_n`/`irq_n` sono coperti da 4 test dedicati in `sim/spi_neuron_top_irq_tb.v`
(idle, run valido, run non valido, `RESET` pulisce `irq_n`). Entrambi pre-esistenti,
riverificati PASS in Fase 0.
**Verdetto: CERTIFICATO** per questi aspetti.
---
## 8.2 `SET_NET_TYPE` durante un run in corso — BUG-007 CONFERMATO END-TO-END, CRITICO
**Analisi strutturale**: il mux della Porta C dell'arbitro (righe 394-397) sceglie tra
`graph_engine` e `layer_sequencer` in modo **puramente combinazionale** sul valore corrente
di `net_type`. `rtl/spi_engine.v` accetta `SET_NET_TYPE` **incondizionatamente**, senza
alcun controllo su `graph_busy`/`seq_busy`. Il commento "mutually exclusive by
construction" (riga 390) copre solo l'AVVIO simultaneo dei due motori, non una scrittura
di `net_type` che arriva a metà di un run già avviato.
**Verificato end-to-end su SPI reale** (`sim/spi_neuron_top_bug007_mid_run_net_type_tb.v`,
stesso grafo valido già certificato in `spi_neuron_top_graph_tb.v`, stesse routine SPI
provate):
```
--- starting graph RUN_NETWORK, then immediately SET_NET_TYPE(dense) before it completes ---
after 30 polls: last_status=0x01 (bit0=busy) -- expected 0x01 stuck if the hang reproduces
RESULT: HANG CONFIRMED -- STATUS.busy stuck, no done/err after 30 polls (vs. ~12-25us normal completion time for this graph)
--- recovery check: RESET, then a legitimate legacy dense START ---
RECOVERY RESULT: RESET DOES recover the system -- a subsequent legitimate dense op completed normally (status=0x02 after 2 polls)
```
`STATUS.busy` resta bloccato dopo un `SET_NET_TYPE` inviato subito dopo un `RUN_NETWORK` in
modalità grafo, per un tempo enormemente superiore al normale completamento di quel grafo
(~2.35ms osservati in un run più lungo, vs ~12-25µs normali) — un hang reale, non un
rallentamento. Le transazioni SPI stesse continuano a funzionare (il `SET_NET_TYPE`
avversariale e i successivi poll di `STATUS` completano regolarmente); è specificamente il
motore grafo a restare bloccato, in attesa di un `ram_ready` che non arriva più tramite il
percorso del mux ormai scollegato.
**Recupero verificato**: un `RESET` durante l'hang riporta il sistema a uno stato
pienamente funzionante (una successiva operazione dense legittima completa normalmente).
Non è un blocco permanente — ma senza un `RESET` di ripiego lato host, il polling da solo
non si sbloccherebbe mai.
**Verdetto: NON CERTIFICATO.** Vedi `docs/validation/bugs.md` BUG-007 — severità CRITICA
insieme a BUG-005, per raggiungibilità diretta con due soli opcode SPI documentati in
sequenza ravvicinata, uno scenario host plausibile.
---
## 8.3 Verdetto complessivo C.8
| Sotto-aspetto | Verdetto |
|---|---|
| Mux `seq_busy` per dispatch legittimo | **CERTIFICATO** |
| Pin `data_ready_n`/`irq_n` | **CERTIFICATO** |
| `SET_NET_TYPE` durante un run in corso | **NON CERTIFICATO** — BUG-007 (CRITICO, confermato end-to-end, recupero via RESET verificato) |
+42
View File
@@ -165,6 +165,48 @@ funzionale pratico), **INFO** (non un bug: gap di copertura, ambiguità document
- **Stato**: **APERTO**, severità inferiore a BUG-005 per la protezione incidentale - **Stato**: **APERTO**, severità inferiore a BUG-005 per la protezione incidentale
osservata, non pienamente verificato su ogni pattern di dati possibile. osservata, non pienamente verificato su ogni pattern di dati possibile.
### 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`, stato `ST_SET_NET_TYPE`, accetta
`net_type <= rx_byte` **incondizionatamente** su qualunque `rx_valid`, senza alcun
controllo su `graph_busy`/`seq_busy`. `rtl/spi_neuron_top.v` (righe 394-397) instrada la
Porta C dell'arbitro tra `graph_engine` e `layer_sequencer` in modo **puramente
combinazionale** sul valore CORRENTE di `net_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 di `net_type` che arriva a
metà di un run già avviato.
- **Confermato end-to-end su SPI reale** (non solo per ispezione): avviato un
`RUN_NETWORK` in modalità grafo (lo stesso grafo valido già certificato in
`spi_neuron_top_graph_tb.v`), poi immediatamente — prima che completi — inviato
`SET_NET_TYPE(dense)` via SPI. Risultato: **`STATUS.busy` resta 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. il
`SET_NET_TYPE` avversariale) completano regolarmente; è specificamente il motore grafo
a restare bloccato in attesa di un `ram_ready` che 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_NETWORK` seguito da
`SET_NET_TYPE` prima 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 — solo
`STATUS.busy` che non si abbassa mai).
- **Recupero verificato**: un `RESET` inviato 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 di `STATUS` senza un timeout e un
`RESET` di ripiego resterebbe bloccato indefinitamente comunque, dato che l'hardware non
segnala mai da solo che qualcosa è andato storto.
- **Fix proposto** (non applicato — analisi separata dalla correzione): in
`rtl/spi_engine.v`, rifiutare/accodare `SET_NET_TYPE` mentre `graph_busy||seq_busy` è
asserto, oppure latchare `net_type` in `spi_neuron_top.v` solo all'avvio di un run
(non renderlo immediatamente combinazionale sul mux dell'arbitro).
- **Stato**: **APERTO, confermato end-to-end, causa isolata con certezza, recupero via
RESET verificato.**
--- ---
## Risolti ## Risolti
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:41 2026 Fri Sep 4 18:48:52 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:45 2026 Fri Sep 4 18:48:56 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:45 2026 Fri Sep 4 18:48:56 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:45 2026 Fri Sep 4 18:48:56 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:45 2026 Fri Sep 4 18:48:57 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:54 2026 Fri Sep 4 18:49:05 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:50 2026 Fri Sep 4 18:49:01 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:54 2026 Fri Sep 4 18:49:05 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:54 2026 Fri Sep 4 18:49:05 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:54 2026 Fri Sep 4 18:49:05 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:58 2026 Fri Sep 4 18:49:09 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:59 2026 Fri Sep 4 18:49:10 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:51:59 2026 Fri Sep 4 18:49:10 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:52:25 2026 Fri Sep 4 18:49:41 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,311 @@
`timescale 1ns/1ps
// ================================================================
// C.8 certification: does SET_NET_TYPE mid-run corrupt an in-flight
// RUN_NETWORK?
//
// Code-inspection finding: rtl/spi_engine.v's ST_SET_NET_TYPE state
// (around line 961) accepts `net_type <= rx_byte` unconditionally on
// any rx_valid -- no check against graph_busy or seq_busy anywhere.
// rtl/spi_neuron_top.v's arbiter Port C mux (lines 394-397) selects
// between graph_engine's and layer_sequencer's ram_req/rdata/ready
// signals PURELY combinationally on the CURRENT value of `net_type`
// -- not latched to "whichever engine started this run". The
// header comment at line 390 calls the two engines "mutually
// exclusive by construction", but that construction only prevents
// both engines from being STARTED at once -- it says nothing about a
// net_type write arriving mid-run.
//
// Hypothesis: starting a graph RUN_NETWORK, then sending
// SET_NET_TYPE(dense) before it completes, re-routes Port C away from
// graph_engine's in-flight memory transaction mid-flight -- graph_engine
// would be left waiting for a ram_ready that can never arrive via its
// now-disconnected mux path (permanent hang, STATUS.busy stuck,
// STATUS.done never sets), while the freshly-selected dense path sees
// spurious traffic not meant for it.
//
// Full end-to-end setup identical to the proven, passing
// sim/spi_neuron_top_graph_tb.v (same graph, same addresses, same SPI
// BFM tasks) -- only the test sequence differs, so any failure here is
// attributable to the net_type switch, not to a setup difference from
// the already-certified happy path.
// ================================================================
module tb;
localparam ADDR_WIDTH = 23;
localparam DATA_WIDTH = 8;
localparam N_INPUTS = 4;
localparam N_NEURONS = 4;
localparam PARALLEL = 2;
localparam ACC_WIDTH = 32;
localparam MEM_DATA_WIDTH = 16;
localparam N_LAYERS = 4;
localparam GRAPH_MAX_CONN = 4;
localparam GRAPH_N_TOTAL = 4096;
localparam CLK_PERIOD = 12.5; // 80 MHz
reg clk;
reg rst;
initial begin
clk = 1'b0;
forever #(CLK_PERIOD / 2.0) clk = ~clk;
end
reg sclk;
reg mosi;
wire miso;
reg cs_n;
wire [ADDR_WIDTH-1:0] psram_a;
wire [MEM_DATA_WIDTH-1:0] psram_dq;
wire psram_ce_n, psram_oe_n, psram_we_n, psram_lb_n, psram_ub_n, psram_zz_n;
spi_neuron_top #(
.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH),
.N_INPUTS(N_INPUTS), .N_NEURONS(N_NEURONS), .PARALLEL(PARALLEL), .ACC_WIDTH(ACC_WIDTH),
.MEM_DATA_WIDTH(MEM_DATA_WIDTH), .CLK_FREQ_MHZ(80), .N_LAYERS(N_LAYERS),
.GRAPH_MAX_CONN(GRAPH_MAX_CONN), .GRAPH_N_TOTAL(GRAPH_N_TOTAL)
) dut (
.clk(clk), .rst(rst),
.sclk(sclk), .mosi(mosi), .miso(miso), .cs_n(cs_n),
.psram_a(psram_a), .psram_dq(psram_dq),
.psram_ce_n(psram_ce_n), .psram_oe_n(psram_oe_n), .psram_we_n(psram_we_n),
.psram_lb_n(psram_lb_n), .psram_ub_n(psram_ub_n), .psram_zz_n(psram_zz_n)
);
psram_model #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(MEM_DATA_WIDTH), .DEPTH(16384)) u_psram (
.clk(clk), .a(psram_a), .dq(psram_dq),
.ce_n(psram_ce_n), .oe_n(psram_oe_n), .we_n(psram_we_n),
.lb_n(psram_lb_n), .ub_n(psram_ub_n), .zz_n(psram_zz_n)
);
task clk_wait; input integer n; integer k; begin for (k=0;k<n;k=k+1) @(posedge clk); end endtask
task spi_begin;
input integer half_bit_cycles;
begin cs_n=1'b1; sclk=1'b0; mosi=1'b0; clk_wait(half_bit_cycles*2); cs_n=1'b0; clk_wait(half_bit_cycles*2); end
endtask
task spi_end;
input integer half_bit_cycles;
begin clk_wait(half_bit_cycles*2); cs_n=1'b1; clk_wait(half_bit_cycles*2); end
endtask
task spi_xfer_byte;
input [7:0] tx; input integer half_bit_cycles; output [7:0] rx;
integer i; reg [7:0] rx_acc;
begin
rx_acc = 8'h00;
for (i=7;i>=0;i=i-1) begin
mosi=tx[i]; clk_wait(half_bit_cycles);
sclk=1'b1; rx_acc[i]=miso; clk_wait(half_bit_cycles);
sclk=1'b0; clk_wait(half_bit_cycles);
end
rx = rx_acc;
end
endtask
localparam HB_RAM = 40;
localparam HB_REG = 8;
reg [7:0] rx_tmp;
integer errors, errors_before, poll_count;
reg signed [7:0] payload [0:31];
reg signed [7:0] readback [0:31];
task do_reset; begin spi_begin(HB_REG); spi_xfer_byte(8'h0F, HB_REG, rx_tmp); spi_end(HB_REG); end endtask
task set_net_type;
input [7:0] t;
begin spi_begin(HB_REG); spi_xfer_byte(8'h11, HB_REG, rx_tmp); spi_xfer_byte(t, HB_REG, rx_tmp); spi_end(HB_REG); end
endtask
task set_base;
input [7:0] sel; input [ADDR_WIDTH-1:0] addr;
begin
spi_begin(HB_REG); spi_xfer_byte(8'h10, HB_REG, rx_tmp); spi_xfer_byte(sel, HB_REG, rx_tmp);
spi_xfer_byte(addr[23:16], HB_REG, rx_tmp); spi_xfer_byte(addr[15:8], HB_REG, rx_tmp); spi_xfer_byte(addr[7:0], HB_REG, rx_tmp);
spi_end(HB_REG);
end
endtask
task write_ram_bytes;
input [ADDR_WIDTH-1:0] addr; input integer len; integer k;
begin
spi_begin(HB_RAM); spi_xfer_byte(8'h01, HB_RAM, rx_tmp);
spi_xfer_byte(addr[23:16], HB_RAM, rx_tmp); spi_xfer_byte(addr[15:8], HB_RAM, rx_tmp); spi_xfer_byte(addr[7:0], HB_RAM, rx_tmp);
spi_xfer_byte(len[15:8], HB_RAM, rx_tmp); spi_xfer_byte(len[7:0], HB_RAM, rx_tmp);
for (k=0;k<len;k=k+1) spi_xfer_byte(payload[k], HB_RAM, rx_tmp);
spi_end(HB_RAM);
end
endtask
task read_ram_bytes;
input [ADDR_WIDTH-1:0] addr; input integer len; integer k;
begin
spi_begin(HB_RAM); spi_xfer_byte(8'h02, HB_RAM, rx_tmp);
spi_xfer_byte(addr[23:16], HB_RAM, rx_tmp); spi_xfer_byte(addr[15:8], HB_RAM, rx_tmp); spi_xfer_byte(addr[7:0], HB_RAM, rx_tmp);
spi_xfer_byte(len[15:8], HB_RAM, rx_tmp); spi_xfer_byte(len[7:0], HB_RAM, rx_tmp);
for (k=0;k<len;k=k+1) spi_xfer_byte(8'h00, HB_RAM, readback[k]);
spi_end(HB_RAM);
end
endtask
task read_status;
output [7:0] status;
begin spi_begin(HB_REG); spi_xfer_byte(8'h21, HB_REG, rx_tmp); spi_xfer_byte(8'h00, HB_REG, status); spi_end(HB_REG); end
endtask
task run_network;
input [7:0] payload_byte;
begin spi_begin(HB_REG); spi_xfer_byte(8'h23, HB_REG, rx_tmp); spi_xfer_byte(payload_byte, HB_REG, rx_tmp); spi_end(HB_REG); end
endtask
reg [7:0] last_status;
task wait_done_or_err;
begin
poll_count = 0; last_status = 8'h00;
while (!last_status[1] && !last_status[2] && poll_count < 2000) begin
clk_wait(20); read_status(last_status); poll_count = poll_count + 1;
if (poll_count <= 5 || poll_count % 200 == 0)
$display(" poll_count=%0d t=%0t last_status=0x%02x", poll_count, $time, last_status);
end
end
endtask
task write_graph_desc;
input [ADDR_WIDTH-1:0] base; input [23:0] conn_ptr; input [15:0] n_conn;
input [15:0] out_id; input [7:0] activation; input [7:0] bias;
begin
payload[0]=conn_ptr[23:16]; payload[1]=conn_ptr[15:8]; payload[2]=conn_ptr[7:0];
payload[3]=n_conn[15:8]; payload[4]=n_conn[7:0];
payload[5]=out_id[15:8]; payload[6]=out_id[7:0];
payload[7]=activation; payload[8]=bias; payload[9]=8'h00; payload[10]=8'h00;
write_ram_bytes(base, 11);
end
endtask
task write_edge;
input [ADDR_WIDTH-1:0] base; input [15:0] src_id; input [7:0] weight;
begin payload[0]=src_id[15:8]; payload[1]=src_id[7:0]; payload[2]=weight; payload[3]=8'h00; write_ram_bytes(base,4); end
endtask
localparam [ADDR_WIDTH-1:0] X_BASE = 22'h000000;
localparam [ADDR_WIDTH-1:0] TABLE_BASE = 22'h000100;
localparam [ADDR_WIDTH-1:0] N4_EDGES = 22'h000200;
localparam [ADDR_WIDTH-1:0] N5_EDGES = 22'h000210;
localparam [ADDR_WIDTH-1:0] OUT_BASE = 22'h000300;
localparam ACT_NONE = 8'h00;
localparam ACT_RELU = 8'h01;
initial begin
$dumpfile("sim/spi_neuron_top_bug007.vcd");
$dumpvars(0, tb);
rst = 1'b1; cs_n = 1'b1; sclk = 1'b0; mosi = 1'b0; errors = 0;
repeat(5) @(posedge clk);
rst = 1'b0;
wait (dut.u_psram_ctrl.state == dut.u_psram_ctrl.STATE_IDLE);
$display("");
$display("========================================");
$display("BUG-007 PROBE: SET_NET_TYPE mid-RUN_NETWORK");
$display("========================================");
do_reset;
// Same valid graph as the certified sim/spi_neuron_top_graph_tb.v TEST 1.
payload[0]=8'sd10; payload[1]=8'sd1; payload[2]=8'sd4; payload[3]=8'sd0;
write_ram_bytes(X_BASE, 4);
write_graph_desc(TABLE_BASE + 0*11, N4_EDGES, 16'd2, 16'd4, ACT_RELU, 8'sd2);
write_graph_desc(TABLE_BASE + 1*11, N5_EDGES, 16'd2, 16'd5, ACT_NONE, 8'sd0);
write_edge(N4_EDGES + 0*4, 16'd0, 8'sd5);
write_edge(N4_EDGES + 1*4, 16'd1, -8'sd3);
write_edge(N5_EDGES + 0*4, 16'd4, 8'sd2);
write_edge(N5_EDGES + 1*4, 16'd2, 8'sd7);
set_net_type(8'h02); // NET_TYPE_GRAPH
set_base(8'h00, X_BASE);
set_base(8'h03, TABLE_BASE);
set_base(8'h04, OUT_BASE);
set_base(8'h07, 24'h000004);
set_base(8'h09, 24'h000002);
set_base(8'h0A, 24'h000001);
$display("--- starting graph RUN_NETWORK, then immediately SET_NET_TYPE(dense) before it completes ---");
$display("t=%0t before run_network", $time);
run_network(8'h00);
$display("t=%0t after run_network, before set_net_type", $time);
// Do NOT wait for done/err -- immediately issue the adversarial
// net_type switch while graph_engine should still be busy.
set_net_type(8'h01); // NET_TYPE_DENSE, mid-flight
$display("t=%0t after mid-flight set_net_type, before short confirm-hang poll ---", $time);
// 30 polls (~150 cycles' worth of clk_wait plus SPI overhead,
// roughly 20-30us of simulated time) is already several times
// longer than this exact graph normally takes to complete
// (~12-25us, per the certified sim/spi_neuron_top_graph_tb.v) --
// enough to confirm the hang without waiting for the full
// 2000-poll budget.
poll_count = 0; last_status = 8'h00;
while (!last_status[1] && !last_status[2] && poll_count < 30) begin
clk_wait(20); read_status(last_status); poll_count = poll_count + 1;
end
$display("after 30 polls: last_status=0x%02x (bit0=busy) -- expected 0x01 stuck if the hang reproduces", last_status);
if (!last_status[1] && !last_status[2]) begin
$display("RESULT: HANG CONFIRMED -- STATUS.busy stuck, no done/err after 30 polls (vs. ~12-25us normal completion time for this graph)");
// --------------------------------------------------------
// Recovery check: does RESET bring the system back to a
// usable state, or is this a permanent lockup requiring a
// power cycle? Not assumed either way -- checked directly
// with a subsequent legitimate legacy dense operation.
// --------------------------------------------------------
$display("--- recovery check: RESET, then a legitimate legacy dense START ---");
do_reset;
payload[0]=8'sd1; payload[1]=8'sd2; payload[2]=8'sd3; payload[3]=8'sd4;
write_ram_bytes(X_BASE, 4);
payload[0]=8'sd1; payload[1]=8'sd1; payload[2]=8'sd1; payload[3]=8'sd1;
write_ram_bytes(22'h000400, 4); // W_BASE, single neuron n0=[1,1,1,1]
payload[0]=8'sd0;
write_ram_bytes(22'h000420, 1); // BIAS_ADDR
set_base(8'h00, X_BASE);
set_base(8'h01, 22'h000400);
set_base(8'h02, 22'h000420);
spi_begin(HB_REG); spi_xfer_byte(8'h20, HB_REG, rx_tmp); spi_end(HB_REG); // START
poll_count = 0; last_status = 8'h00;
while (!last_status[1] && poll_count < 200) begin
clk_wait(20); read_status(last_status); poll_count = poll_count + 1;
end
if (!last_status[1]) begin
$display("RECOVERY RESULT: FAILED -- legitimate dense START never completed after RESET (status=0x%02x, poll_count=%0d) -- the hang from BUG-007 is NOT cleanly recoverable via RESET alone", last_status, poll_count);
end else begin
read_ram_bytes(22'h000000, 1); // harmless if this doesn't match READ_OUTPUT semantics -- just probing responsiveness
$display("RECOVERY RESULT: RESET DOES recover the system -- a subsequent legitimate dense op completed normally (status=0x%02x after %0d polls)", last_status, poll_count);
end
end else begin
$display("RESULT: UNEXPECTED -- done or err latched within 30 polls (status=0x%02x) -- the hang did NOT reproduce this run. Re-examine before assuming BUG-007 is fixed or was a fluke.", last_status);
end
$finish;
end
initial begin
#4000000;
$display("SAFETY TIMEOUT after 4ms -- something past the confirm-hang/recovery-check sequence did not complete in time");
$finish;
end
endmodule
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:52:10 2026 Fri Sep 4 18:49:25 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:52:20 2026 Fri Sep 4 18:49:36 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog
+1 -1
View File
@@ -1,5 +1,5 @@
$date $date
Fri Sep 4 14:52:26 2026 Fri Sep 4 18:49:42 2026
$end $end
$version $version
Icarus Verilog Icarus Verilog