test: add Phase 2 parameter sweep for neuron_parallel, update roadmap

Roadmap Phase 2 asks to validate N_INPUTS/N_NEURONS/PARALLEL
combinations, including non-exact-multiple configurations. Added
sim/parameter_sweep_tb.v with 5 configs (two exact-multiple sanity
checks, two non-exact-multiple, one degenerate PARALLEL>N_INPUTS),
using a cycle-count watchdog instead of a blocking wait so a hanging
config is reported rather than hanging the simulation.

Findings (RTL unchanged, core datapath left untouched):
- GROUPS = N_INPUTS / PARALLEL truncates: when N_INPUTS is not an
  exact multiple of PARALLEL, the remainder inputs are silently
  never summed (confirmed 30/8 -> 6 dropped, 20/16 -> 4 dropped).
- PARALLEL > N_INPUTS gives GROUPS=0, and the controller's
  group_index == GROUPS-1 terminal condition is never met: the
  neuron hangs forever (confirmed via watchdog timeout).

Documented both as findings under Phase 2 in
docs/FPGA-NeuralNetwork-Engine.md for follow-up in Phase 3/7.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
This commit is contained in:
2026-09-02 14:24:43 +02:00
co-authored by Claude Sonnet 5
parent 4b5cd4e558
commit 9b9859a104
5 changed files with 30779 additions and 0 deletions
+23
View File
@@ -47,3 +47,26 @@
- 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: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: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. - 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.
+25
View File
@@ -595,6 +595,31 @@ PARALLEL
including configurations where the number of inputs is not an exact multiple of the parallelism. including configurations where the number of inputs is not an exact multiple of the parallelism.
- [x] Exact-multiple sanity configs (32×8, 64×32)
- [x] Non-exact-multiple configs (30×8, 20×16)
- [x] Degenerate config, PARALLEL > N_INPUTS (4×8)
- [x] Sweep testbench: `sim/parameter_sweep_tb.v`
**Findings (current RTL behavior, not yet fixed):**
- `neuron_parallel.v` computes `GROUPS = N_INPUTS / PARALLEL` with
integer division. When `N_INPUTS` is **not** an exact multiple of
`PARALLEL`, only the first `GROUPS * PARALLEL` inputs are ever read
by the accumulator — the remainder is silently dropped (no error,
no warning). Confirmed for 30×8 → only 24 of 30 inputs summed, and
20×16 → only 16 of 20 inputs summed.
- If `PARALLEL > N_INPUTS`, `GROUPS = 0` and the controller's
`group_index == GROUPS-1` terminal condition is never satisfied:
the neuron enters `busy` and never asserts `done` (confirmed hang,
500-cycle watchdog in the sweep bench). This is a design
constraint (`PARALLEL` must not exceed `N_INPUTS`, and should
divide it exactly) that is not currently guarded in RTL.
- Action: either enforce `N_INPUTS % PARALLEL == 0` and
`PARALLEL <= N_INPUTS` at the caller/config level, or extend
`neuron_parallel.v` to handle a partial final group. Not addressed
in this phase — core datapath left untouched per current project
policy; tracked here for Phase 3/7.
## Phase 3 — Memory Architecture ## Phase 3 — Memory Architecture
Define: Define:
File diff suppressed because it is too large Load Diff
+11411
View File
File diff suppressed because one or more lines are too long
+428
View File
@@ -0,0 +1,428 @@
`timescale 1ns/1ps
// ================================================================
// PHASE 2 - PARAMETER SWEEP
//
// Roadmap requirement (docs/FPGA-NeuralNetwork-Engine.md, Phase 2):
// validate multiple combinations of N_INPUTS / N_NEURONS / PARALLEL,
// including configurations where N_INPUTS is NOT an exact multiple
// of PARALLEL.
//
// neuron_parallel.v computes:
// localparam GROUPS = N_INPUTS / PARALLEL;
// which is an INTEGER division. When N_INPUTS is not an exact
// multiple of PARALLEL, the remainder inputs are silently never
// read by the accumulator (GEN_TREE only ever selects the first
// GROUPS*PARALLEL inputs). This bench characterizes that behavior
// instead of assuming it does not exist, and uses a cycle-count
// watchdog (never a blocking `wait`) so a config that never
// asserts `done` is reported instead of hanging the simulation.
// ================================================================
module tb;
reg clk;
reg rst;
initial begin
clk = 0;
forever #5 clk = ~clk;
end
integer errors;
integer findings;
// ============================================================
// CONFIG A - baseline, exact multiple (sanity check)
// N_INPUTS=32 PARALLEL=8 -> GROUPS=4
// ============================================================
localparam A_DATA_WIDTH = 8;
localparam A_N_INPUTS = 32;
localparam A_PARALLEL = 8;
localparam A_ACC_WIDTH = 32;
reg start_a;
reg signed [A_DATA_WIDTH*A_N_INPUTS-1:0] x_bus_a;
reg signed [A_DATA_WIDTH*A_N_INPUTS-1:0] w_bus_a;
reg signed [A_DATA_WIDTH-1:0] bias_a;
wire signed [A_DATA_WIDTH-1:0] y_a;
wire busy_a, done_a;
neuron_parallel #(
.DATA_WIDTH(A_DATA_WIDTH),
.N_INPUTS(A_N_INPUTS),
.PARALLEL(A_PARALLEL),
.ACC_WIDTH(A_ACC_WIDTH)
) u_a (
.clk(clk), .rst(rst), .start(start_a),
.x_bus(x_bus_a), .w_bus(w_bus_a), .bias(bias_a),
.y(y_a), .busy(busy_a), .done(done_a)
);
// ============================================================
// CONFIG B - non-exact multiple
// N_INPUTS=30 PARALLEL=8 -> GROUPS=3 (24 inputs actually summed)
// ============================================================
localparam B_DATA_WIDTH = 8;
localparam B_N_INPUTS = 30;
localparam B_PARALLEL = 8;
localparam B_ACC_WIDTH = 32;
reg start_b;
reg signed [B_DATA_WIDTH*B_N_INPUTS-1:0] x_bus_b;
reg signed [B_DATA_WIDTH*B_N_INPUTS-1:0] w_bus_b;
reg signed [B_DATA_WIDTH-1:0] bias_b;
wire signed [B_DATA_WIDTH-1:0] y_b;
wire busy_b, done_b;
neuron_parallel #(
.DATA_WIDTH(B_DATA_WIDTH),
.N_INPUTS(B_N_INPUTS),
.PARALLEL(B_PARALLEL),
.ACC_WIDTH(B_ACC_WIDTH)
) u_b (
.clk(clk), .rst(rst), .start(start_b),
.x_bus(x_bus_b), .w_bus(w_bus_b), .bias(bias_b),
.y(y_b), .busy(busy_b), .done(done_b)
);
// ============================================================
// CONFIG C - non-exact multiple, different PARALLEL
// N_INPUTS=20 PARALLEL=16 -> GROUPS=1 (16 inputs actually summed)
// ============================================================
localparam C_DATA_WIDTH = 8;
localparam C_N_INPUTS = 20;
localparam C_PARALLEL = 16;
localparam C_ACC_WIDTH = 32;
reg start_c;
reg signed [C_DATA_WIDTH*C_N_INPUTS-1:0] x_bus_c;
reg signed [C_DATA_WIDTH*C_N_INPUTS-1:0] w_bus_c;
reg signed [C_DATA_WIDTH-1:0] bias_c;
wire signed [C_DATA_WIDTH-1:0] y_c;
wire busy_c, done_c;
neuron_parallel #(
.DATA_WIDTH(C_DATA_WIDTH),
.N_INPUTS(C_N_INPUTS),
.PARALLEL(C_PARALLEL),
.ACC_WIDTH(C_ACC_WIDTH)
) u_c (
.clk(clk), .rst(rst), .start(start_c),
.x_bus(x_bus_c), .w_bus(w_bus_c), .bias(bias_c),
.y(y_c), .busy(busy_c), .done(done_c)
);
// ============================================================
// CONFIG D - exact multiple, wide parallelism (sanity check)
// N_INPUTS=64 PARALLEL=32 -> GROUPS=2
// ============================================================
localparam D_DATA_WIDTH = 8;
localparam D_N_INPUTS = 64;
localparam D_PARALLEL = 32;
localparam D_ACC_WIDTH = 32;
reg start_d;
reg signed [D_DATA_WIDTH*D_N_INPUTS-1:0] x_bus_d;
reg signed [D_DATA_WIDTH*D_N_INPUTS-1:0] w_bus_d;
reg signed [D_DATA_WIDTH-1:0] bias_d;
wire signed [D_DATA_WIDTH-1:0] y_d;
wire busy_d, done_d;
neuron_parallel #(
.DATA_WIDTH(D_DATA_WIDTH),
.N_INPUTS(D_N_INPUTS),
.PARALLEL(D_PARALLEL),
.ACC_WIDTH(D_ACC_WIDTH)
) u_d (
.clk(clk), .rst(rst), .start(start_d),
.x_bus(x_bus_d), .w_bus(w_bus_d), .bias(bias_d),
.y(y_d), .busy(busy_d), .done(done_d)
);
// ============================================================
// CONFIG E - degenerate: PARALLEL > N_INPUTS
// N_INPUTS=4 PARALLEL=8 -> GROUPS=0
// Watchdog-guarded: expected to NOT complete (documents the
// constraint "PARALLEL must not exceed N_INPUTS").
// ============================================================
localparam E_DATA_WIDTH = 8;
localparam E_N_INPUTS = 4;
localparam E_PARALLEL = 8;
localparam E_ACC_WIDTH = 32;
reg start_e;
reg signed [E_DATA_WIDTH*E_N_INPUTS-1:0] x_bus_e;
reg signed [E_DATA_WIDTH*E_N_INPUTS-1:0] w_bus_e;
reg signed [E_DATA_WIDTH-1:0] bias_e;
wire signed [E_DATA_WIDTH-1:0] y_e;
wire busy_e, done_e;
neuron_parallel #(
.DATA_WIDTH(E_DATA_WIDTH),
.N_INPUTS(E_N_INPUTS),
.PARALLEL(E_PARALLEL),
.ACC_WIDTH(E_ACC_WIDTH)
) u_e (
.clk(clk), .rst(rst), .start(start_e),
.x_bus(x_bus_e), .w_bus(w_bus_e), .bias(bias_e),
.y(y_e), .busy(busy_e), .done(done_e)
);
// ============================================================
// MAIN
// ============================================================
integer max_cycles;
integer count;
reg timed_out;
initial begin
$dumpfile("sim/parameter_sweep.vcd");
$dumpvars(0, tb);
rst = 1;
errors = 0;
findings = 0;
max_cycles = 500;
start_a = 0; x_bus_a = 0; w_bus_a = 0; bias_a = 0;
start_b = 0; x_bus_b = 0; w_bus_b = 0; bias_b = 0;
start_c = 0; x_bus_c = 0; w_bus_c = 0; bias_c = 0;
start_d = 0; x_bus_d = 0; w_bus_d = 0; bias_d = 0;
start_e = 0; x_bus_e = 0; w_bus_e = 0; bias_e = 0;
repeat (2) @(posedge clk);
rst = 0;
$display("");
$display("========================================");
$display("PHASE 2 - PARAMETER SWEEP");
$display("========================================");
// --------------------------------------------------------
// CONFIG A: all x=1, all w=1, bias=0
// full sum = 32, exact multiple -> expect 32
// --------------------------------------------------------
for (count = 0; count < A_N_INPUTS; count = count + 1) begin
x_bus_a[count*A_DATA_WIDTH +: A_DATA_WIDTH] = 8'sd1;
w_bus_a[count*A_DATA_WIDTH +: A_DATA_WIDTH] = 8'sd1;
end
bias_a = 0;
@(posedge clk); start_a <= 1'b1;
@(posedge clk); start_a <= 1'b0;
timed_out = 1'b0;
count = 0;
while (!done_a && !timed_out) begin
@(posedge clk);
count = count + 1;
if (count > max_cycles) timed_out = 1'b1;
end
@(posedge clk);
$display("");
$display("CONFIG A: N_INPUTS=%0d PARALLEL=%0d (exact, GROUPS=%0d)",
A_N_INPUTS, A_PARALLEL, A_N_INPUTS/A_PARALLEL);
if (timed_out) begin
$display(" RESULT: TIMEOUT (did not assert done within %0d cycles)", max_cycles);
errors = errors + 1;
end else begin
$display(" y = %0d expected = 32", y_a);
if (y_a !== 8'sd32) begin
$display(" FAIL");
errors = errors + 1;
end else begin
$display(" PASS");
end
end
// --------------------------------------------------------
// CONFIG B: all x=1, all w=1, bias=0
// N_INPUTS=30, PARALLEL=8 -> GROUPS=3 -> only first 24
// inputs are actually summed by the current RTL.
// full-sum expectation would be 30; RTL-truncated
// expectation is 24. We check against the RTL-truncated
// value and flag the mismatch vs. the full sum as a
// documented finding (not a failure of this bench).
// --------------------------------------------------------
for (count = 0; count < B_N_INPUTS; count = count + 1) begin
x_bus_b[count*B_DATA_WIDTH +: B_DATA_WIDTH] = 8'sd1;
w_bus_b[count*B_DATA_WIDTH +: B_DATA_WIDTH] = 8'sd1;
end
bias_b = 0;
@(posedge clk); start_b <= 1'b1;
@(posedge clk); start_b <= 1'b0;
timed_out = 1'b0;
count = 0;
while (!done_b && !timed_out) begin
@(posedge clk);
count = count + 1;
if (count > max_cycles) timed_out = 1'b1;
end
@(posedge clk);
$display("");
$display("CONFIG B: N_INPUTS=%0d PARALLEL=%0d (NON-exact, GROUPS=%0d, %0d inputs actually read)",
B_N_INPUTS, B_PARALLEL, B_N_INPUTS/B_PARALLEL,
(B_N_INPUTS/B_PARALLEL)*B_PARALLEL);
if (timed_out) begin
$display(" RESULT: TIMEOUT (did not assert done within %0d cycles)", max_cycles);
errors = errors + 1;
end else begin
$display(" y = %0d RTL-truncated expected = 24 full-sum (NOT met) = 30", y_b);
if (y_b !== 8'sd24) begin
$display(" FAIL (unexpected value for current RTL behavior)");
errors = errors + 1;
end else begin
$display(" PASS (matches current RTL truncation behavior)");
end
if (y_b !== B_N_INPUTS[7:0]) begin
$display(" FINDING: last %0d input(s) are silently ignored (GROUPS = N_INPUTS/PARALLEL truncates)",
B_N_INPUTS - (B_N_INPUTS/B_PARALLEL)*B_PARALLEL);
findings = findings + 1;
end
end
// --------------------------------------------------------
// CONFIG C: same characterization, different sizes
// N_INPUTS=20, PARALLEL=16 -> GROUPS=1 -> only first 16 read
// --------------------------------------------------------
for (count = 0; count < C_N_INPUTS; count = count + 1) begin
x_bus_c[count*C_DATA_WIDTH +: C_DATA_WIDTH] = 8'sd1;
w_bus_c[count*C_DATA_WIDTH +: C_DATA_WIDTH] = 8'sd1;
end
bias_c = 0;
@(posedge clk); start_c <= 1'b1;
@(posedge clk); start_c <= 1'b0;
timed_out = 1'b0;
count = 0;
while (!done_c && !timed_out) begin
@(posedge clk);
count = count + 1;
if (count > max_cycles) timed_out = 1'b1;
end
@(posedge clk);
$display("");
$display("CONFIG C: N_INPUTS=%0d PARALLEL=%0d (NON-exact, GROUPS=%0d, %0d inputs actually read)",
C_N_INPUTS, C_PARALLEL, C_N_INPUTS/C_PARALLEL,
(C_N_INPUTS/C_PARALLEL)*C_PARALLEL);
if (timed_out) begin
$display(" RESULT: TIMEOUT (did not assert done within %0d cycles)", max_cycles);
errors = errors + 1;
end else begin
$display(" y = %0d RTL-truncated expected = 16 full-sum (NOT met) = 20", y_c);
if (y_c !== 8'sd16) begin
$display(" FAIL (unexpected value for current RTL behavior)");
errors = errors + 1;
end else begin
$display(" PASS (matches current RTL truncation behavior)");
end
if (y_c !== C_N_INPUTS[7:0]) begin
$display(" FINDING: last %0d input(s) are silently ignored (GROUPS = N_INPUTS/PARALLEL truncates)",
C_N_INPUTS - (C_N_INPUTS/C_PARALLEL)*C_PARALLEL);
findings = findings + 1;
end
end
// --------------------------------------------------------
// CONFIG D: all x=1, all w=1, bias=0
// full sum = 64, exact multiple -> expect 64
// --------------------------------------------------------
for (count = 0; count < D_N_INPUTS; count = count + 1) begin
x_bus_d[count*D_DATA_WIDTH +: D_DATA_WIDTH] = 8'sd1;
w_bus_d[count*D_DATA_WIDTH +: D_DATA_WIDTH] = 8'sd1;
end
bias_d = 0;
@(posedge clk); start_d <= 1'b1;
@(posedge clk); start_d <= 1'b0;
timed_out = 1'b0;
count = 0;
while (!done_d && !timed_out) begin
@(posedge clk);
count = count + 1;
if (count > max_cycles) timed_out = 1'b1;
end
@(posedge clk);
$display("");
$display("CONFIG D: N_INPUTS=%0d PARALLEL=%0d (exact, GROUPS=%0d)",
D_N_INPUTS, D_PARALLEL, D_N_INPUTS/D_PARALLEL);
if (timed_out) begin
$display(" RESULT: TIMEOUT (did not assert done within %0d cycles)", max_cycles);
errors = errors + 1;
end else begin
$display(" y = %0d expected = 64", y_d);
if (y_d !== 8'sd64) begin
$display(" FAIL");
errors = errors + 1;
end else begin
$display(" PASS");
end
end
// --------------------------------------------------------
// CONFIG E: degenerate PARALLEL > N_INPUTS -> GROUPS=0
// We EXPECT this to time out. If it ever completes, that
// is itself worth flagging (behavior changed).
// --------------------------------------------------------
for (count = 0; count < E_N_INPUTS; count = count + 1) begin
x_bus_e[count*E_DATA_WIDTH +: E_DATA_WIDTH] = 8'sd1;
w_bus_e[count*E_DATA_WIDTH +: E_DATA_WIDTH] = 8'sd1;
end
bias_e = 0;
@(posedge clk); start_e <= 1'b1;
@(posedge clk); start_e <= 1'b0;
timed_out = 1'b0;
count = 0;
while (!done_e && !timed_out) begin
@(posedge clk);
count = count + 1;
if (count > max_cycles) timed_out = 1'b1;
end
@(posedge clk);
$display("");
$display("CONFIG E: N_INPUTS=%0d PARALLEL=%0d (DEGENERATE, GROUPS=%0d)",
E_N_INPUTS, E_PARALLEL, E_N_INPUTS/E_PARALLEL);
if (timed_out) begin
$display(" RESULT: TIMEOUT as expected (done never asserted within %0d cycles)", max_cycles);
$display(" FINDING: PARALLEL > N_INPUTS (GROUPS=0) hangs neuron_parallel forever -- design constraint, not currently guarded in RTL");
findings = findings + 1;
end else begin
$display(" RESULT: completed with y=%0d (unexpected -- previously assumed to hang)", y_e);
errors = errors + 1;
end
$display("");
$display("========================================");
$display("PARAMETER SWEEP SUMMARY");
$display(" errors = %0d", errors);
$display(" findings = %0d (documented limitations, not bench failures)", findings);
if (errors == 0)
$display("PARAMETER SWEEP: PASSED (all configs behaved as characterized)");
else
$display("PARAMETER SWEEP: FAILED");
$display("========================================");
$display("");
$finish;
end
endmodule