Bumps ADDR_WIDTH's default from 22 to 23 bits across every RTL
module (neuron_memory, layer_sequencer, spi_engine, spi_neuron_top,
mem_arbiter, int8_memory_access, memory_interface, psram_controller,
memory_model) and every testbench that mirrors it, so the system's
byte-address space reaches the full 8 MiB the recommended PSRAM part
(ISSI IS66WVE4M16EBLL-70BLI, docs/FPGA-Neural-Hardware-Design.md §3)
actually provides -- previously only 4 MiB (half the chip) was
reachable, since int8_memory_access.v's byte->word address shift
(addr >> 1) turned the old 22-bit byte address into only 21 real word
bits, one short of the chip's real 22-bit word address (A0-A21). At
23 bits, that same shift lands exactly on all 22 chip address lines,
so the whole part is usable now instead of deferred to a future
widening.
Also fixes a stray 22'd11-sized literal in layer_sequencer.v's
descriptor-table address increment (numerically already safe via
Verilog's zero-extension, but now correctly unsized so it always
matches ADDR_WIDTH instead of silently assuming 22).
Updated docs/FPGA-NeuralNetwork-Engine.md's SPI protocol address-field
note (23 bits, top 1 reserved bit instead of 2) and
docs/FPGA-Neural-Hardware-Design.md's PSRAM section (the "chip has
one spare address line" framing is gone now that all 22 are wired
and used).
Full regression (all 11 ADDR_WIDTH-touching testbenches, plus a
Yosys elaboration check of spi_neuron_top with the new default and
no override) passes clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
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 Phase 2 findings (docs/FPGA-NeuralNetwork-Engine.md) shared one
root cause: GROUPS = N_INPUTS / PARALLEL is integer division. When
N_INPUTS is not an exact multiple of PARALLEL, the remainder inputs
were silently dropped from the accumulation (wrong result, no
error); when PARALLEL > N_INPUTS, GROUPS = 0 and the controller's
terminal condition was never met, hanging the neuron forever.
Added a single elaboration-time guard to rtl/neuron_parallel.v: a
`generate` block instantiates a deliberately undefined module when
N_INPUTS % PARALLEL != 0, forcing a hard failure in both simulation
and synthesis instead of a silent wrong answer or a deadlock. Valid
configurations are unaffected (the branch is never elaborated). The
validated datapath (mac8/mac_unit/accumulation/ReLU/saturation) is
untouched -- this is authorized as a scoped exception to the
"core is fixed, do not touch" project policy, for this guard only.
- sim/neuron_parallel_guard_negative_nonmultiple_tb.v and
sim/neuron_parallel_guard_negative_degenerate_tb.v: negative tests
that must fail to elaborate; verified both fail with the expected
"Unknown module type" error.
- sim/parameter_sweep_tb.v: rewritten to valid-configs-only (the
three configs that used to demonstrate truncation/hang no longer
compile, by design); added PARALLEL=2 and PARALLEL=4 configs,
the two best-performing values from
docs/FPGA-Neural-Datapatch-Benchmark.md.
- Full regression re-run after the RTL change: all existing
testbenches still pass unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt
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