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:
+23
@@ -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: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.
|
||||
|
||||
@@ -595,6 +595,31 @@ PARALLEL
|
||||
|
||||
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
|
||||
|
||||
Define:
|
||||
|
||||
+18892
File diff suppressed because it is too large
Load Diff
Executable
+11411
File diff suppressed because one or more lines are too long
@@ -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
|
||||
Reference in New Issue
Block a user