Two related Phase 5 additions, both threaded the same way (a new
runtime field defaulting to the pre-existing behavior, settable
per-layer via the descriptor table or per-run via SET_BASE):
Configurable activation functions:
- neuron_parallel.v gains a 2-bit `activation` port (ACT_NONE =
linear + two-sided INT8 saturate, ACT_RELU = the original
hardwired behavior, kept as the default so every pre-existing
caller/testbench is unaffected), threaded through neuron_memory.v.
- spi_engine.v: SET_BASE sel=6 (single-layer path); the descriptor
table gains a 7th byte (multi-layer path).
- Verified in neuron_parallel_tb.v (negative pass-through + negative
saturation to -128) and end-to-end in
spi_neuron_top_runnetwork_tb.v (a real negative accumulator that
ACT_RELU would clamp to 0 comes through unclamped under ACT_NONE,
over real SPI/RAM).
Runtime network width (one bitstream, any topology up to its
build-time max, entirely host-configured over SPI):
- neuron_parallel.v gains n_inputs_real, bounding its MAC group loop
(n_inputs_real/PARALLEL groups instead of the fixed build-time
count). neuron_memory.v gains n_inputs_real/n_neurons_real,
bounding its X/W RAM-read loop and its neuron loop. All default to
the build-time max, so unconnected callers are unaffected.
n_inputs_real must stay a multiple of PARALLEL (same constraint
N_INPUTS itself is held to at elaboration time, now the caller's
runtime responsibility).
- spi_engine.v: SET_BASE sel=7/8 (single-layer path); the descriptor
table grows to 11 bytes/layer (+n_inputs_real +n_neurons_real,
multi-layer path) -- layer_sequencer.v also now copies only
n_neurons_real bytes into the ping-pong buffer, not the full
build width.
- This is real early termination, not bookkeeping: no RAM
zero-padding needed for the unused tail, and it measurably
completes faster. neuron_parallel_tb.v TEST 7: 3 cycles vs 6 for a
reduced-vs-full run, with garbage loaded into the skipped lanes to
prove they're never read. neuron_memory_tb.v TEST 5: through the
real PSRAM stack, 209 cycles vs 788. layer_sequencer_tb.v proves a
reduced n_neurons_real shortens the ping-pong copy-out itself
(bytes beyond the real count stay untouched, not just differing).
docs/FPGA-NeuralNetwork-Engine.md: §8.1 opcode/SET_BASE table, new
"Runtime network width" subsection, Phase 5 checklist, Current
Status table, and the "Core architectural principle" statement
updated to reflect that topology (not just trained parameters) is
now host-configured at runtime up to a build-time ceiling.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
Both testbenches instantiated their DUTs with a FRAC_BITS parameter and
Q8.8 fixed-point 16-bit values, which no longer exist in rtl/neuron_parallel.v
(now plain INT8, DATA_WIDTH=8, hardcoded +127 saturation, ReLU-only clamp).
This made both tests fail elaboration ("parameter FRAC_BITS not found").
Rewrote both benches with integer INT8 stimuli and expectations matching
the current core (no RTL changes): neuron_parallel_tb covers a mixed
vector, ReLU, positive saturation, and mixed values with a boundary
negative bias; layer_tb covers 8 neurons exercising scale, ReLU,
saturation, bias-only, and a sparse weight pattern across groups.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt