55c827bedf7cc3b7203e895512c650d089fe9255
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
55c827bedf |
feat: PSRAM page-mode reads + graph engine (Type #2) + real pinout/IRQ pins
PSRAM page-mode read burst support in psram_controller.v: enables the ISSI IS66WVE4M16EBLL-70BLI's page mode via its configuration-register software-access sequence at boot (disabled by default on the real chip), then keeps CE#/OE# asserted after a read so a same-page continuation only pays tAPA (20ns) instead of a full tAA (70ns) random access, with automatic tCEM-safe session closing. Only a WRITE closes the page -- byte-enable changes do not, since int8_memory_access.v alternates them on nearly every access and an early implementation attempt that treated them as a close condition measured a real regression (53.25->61.25 cycles/edge) before being corrected (53.25->37.53 cycles/edge, +42% gather bandwidth). sim/psram_model.v gained independent tAPA/tAA and tCEM enforcement (with a real Verilog same-timestep event-ordering race found and fixed via a #0 sync) so the regression proves real timing compliance, not just data correctness. New sim/psram_page_mode_tb.v; full 26-file regression suite re-run clean. Real nextpnr-ecp5 Fmax re-measured on the full spi_neuron_top system: 75.73MHz (P2, up from 55.59MHz) and 65.13MHz (P8) -- still under the 80MHz target but not regressed, with the critical path confirmed (not assumed) to remain entirely inside neuron_parallel's accumulate chain, never psram_controller. Also includes this session's other already-validated work: the graph engine (Type #2 sparse-graph network: act_buffer, graph_engine, netasm host assembler), real CABGA381 pinout (.lpf, place&route verified) and physical IRQ_N/DATA_READY_N pins, and Phase 7 timing closure logs -- all previously uncommitted, documented in WORKLOG.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LH3jPeJ3eFMfF2v8SQhpkk |
||
|
|
7e2711fa27 |
feat: widen ADDR_WIDTH to 23 bits for full 8MB PSRAM addressing
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 |
||
|
|
a918c3f1e9 |
feat: configurable activation functions + runtime-configurable network topology
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 |
||
|
|
233d6ff7fb |
feat: complete Phase 5 multi-layer network (RUN_NETWORK) + fix STATUS race
Wires the already-present layer_sequencer.v into the SPI stack: - spi_engine.v: RUN_NETWORK opcode (0x23) + SET_BASE selectors for table_base/buf_a_base/buf_b_base; STATUS.busy/done extended to track the sequencer (seq_busy/seq_done) alongside neuron_memory directly, so done latches on the last layer only. - spi_neuron_top.v: instantiates layer_sequencer, muxes neuron_memory's control inputs between it (while seq_busy) and spi_engine's direct-drive path (legacy single-layer mode), wires the sequencer's own RAM master to mem_arbiter's Port C. Found and fixed a real race while writing the end-to-end test: STATUS's sticky/clear-on-read done bit read its value live/combinationally during transmission and cleared unconditionally on any STATUS read. A done_event landing mid-transmission of a STATUS response byte could be silently dropped -- the host would receive a stale byte while the sticky bit was cleared regardless, hanging any host polling STATUS in a loop. Present since Phase 4, not RUN_NETWORK-specific; only surfaced under this test's continuous polling. Fixed by latching a status_snapshot at opcode-accept time and gating the clear on what was actually transmitted. Tests: spi_engine_tb.v gains RUN_NETWORK/SET_BASE opcode tests (K/L); new layer_sequencer_tb.v unit-tests the sequencer FSM directly (descriptor table, ping-pong buffer addressing, byte-exact copy-out); new spi_neuron_top_runnetwork_tb.v drives a real 2-layer network over simulated SPI end to end (real neuron_memory + PSRAM, hand-computed expected output) and confirms the legacy single-layer path still works afterward. All existing testbenches still pass. |
||
|
|
d716eb04dd |
feat: add spi_slave.v physical layer (Phase 4 SPI RTL, part 1/N)
First real RTL piece of the SPI interface (docs §8.1 protocol draft): the physical layer only -- Mode 0 (CPOL=0, CPHA=0), MSB-first, byte-level shift register with a 3-stage CDC synchronizer for SCLK/MOSI/CS_N (the SPI master clock is asynchronous to the FPGA system clock). Exposes rx_byte/rx_valid, tx_byte/tx_byte_req, and cs_active/cs_start/cs_end to the (not yet written) protocol engine. Documented an important consumer contract on tx_byte_req: it is a prefetch hint (fires once extra after the last byte of every transaction, since the slave cannot know in advance whether the master will keep clocking), not a "byte consumed" event -- a consumer must advance any stateful pointer (e.g. a RAM read address) on rx_valid instead, which fires exactly once per real byte transferred. sim/spi_slave_tb.v: bit-banged SPI master BFM (4 tests: single byte, multi-byte in one CS period, back-to-back transactions, slower SCLK). Two testbench-only bugs found and fixed during bring-up (RTL itself needed no functional change beyond the tx_byte_req contract comment): the BFM was advancing its tx queue on tx_byte_req instead of rx_valid (see contract above), and inter-test reset pulses raced against posedge clk (blocking `rst=1` landing on the same simulation time as a clock edge) -- fixed by asserting/deasserting reset on negedge clk instead. Verified two ways: Icarus Verilog (4/4 tests pass) and the real ECP5 toolchain used for prior benchmarks (Yosys 0.68 synth: 0 problems, 41 FF / 55 LUT4, no latches; nextpnr-ecp5 --45k --package CABGA381 --speed 8 --freq 80: PASS, Fmax 403.23 MHz; ecppack: bitstream generated with no errors). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |