4acc669d43fd8a6682f91a1fa5cc1f45cf741aff
45
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4acc669d43 |
feat: real N=16 hybrid systolic top-level, synthesis-only pass (EXP-0091)
Adds n16_system_ddr3_top.v, directly adapted from n2_system_ddr3_top.v's own proven structure: same real MIG, spi_host_bridge_v3.v, flash_spi_ master.v, host_mem_bridge.v, all completely unmodified (confirms EXP-0090's zero-protocol-change finding holds at full N=16 scale). The real differences: neural_director_grouped.v replaces neural_director_ packed.v, 4x systolic_group.v replace 2x packed_slot.v, and the shared arbiter grows to a real 21-way NUM_REQ (4 group weight-fetch + 16 PE activation/writeback + 1 host_mem_bridge). Real synthesis-only result: 0 Errors, 0 Critical Warnings, 128 DSP48E1 of 240 (53.33%) -- an exact real match to docs/ARCHITECTURE_ANALYSIS.md S5.6's own original brainstorm DSP projection, now confirmed by real synthesis instead of estimated. Found and root-caused a real Vivado project quirk (not an RTL bug -- verified separately via a clean Icarus elaboration with stub modules for mig_7series_0/STARTUPE2): a fresh add_files + update_compile_order didn't make synth_design -top <newmodule> find the module. Fixed by explicitly setting the fileset's own top property before synth_design. CLAUDE.md updated with this as the real, confirmed procedure for adding any future brand-new top-level module. Honestly scoped: synthesis-only (connectivity/width correctness), NOT yet a real functional xsim test and NOT yet real P&R timing -- both real, disclosed next steps. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
bd1fb5dc14 |
feat: real Director extension for group dispatch + systolic_group.v P&R sanity check (EXP-0090)
Adds neural_director_grouped.v, a direct extension of neural_director_ packed.v's own already-proven 2-position pairing discipline to 8-position octets (matching systolic_group.v's fixed 4 PEs x 2 lanes). Real, deliberate finding: the host-facing SPI/WRITE_JOB submission protocol needs zero changes -- the host just submits 8 jobs sharing a weight base instead of 2, the same real pattern already required today. Real out-of-context synthesis of one systolic_group.v: 32 DSP48E1 (13.3%), confirming the original brainstorm's own DSP projection exactly. Found and fixed two real bugs: (1) a wraparound-arithmetic width bug in the octet index computation (same class already flagged for address math elsewhere in this project -- needs N+1 bits before the mod-reduce compare, not N); (2) a real, generalizable testbench race -- driving stimulus on the same clock edge the DUT samples on works fine with a natural gap between pulses (every prior testbench in this project has one) but silently double-registers data when called back-to-back with zero gap, confirmed via real signal tracing. Fixed with @(negedge clk) stimulus; CLAUDE.md's existing blocking/nonblocking testbench-race lesson extended to cover this new trigger. Verified via tb_neural_director_grouped.v: 4/4 PASS (octet dispatch + per-PE addressing, stall-not-mis-dispatch on a mismatched octet, queue wraparound). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
932aec2490 |
feat: real first step of 4x4 hybrid systolic architecture (EXP-0089)
Adds packed_pe.v (packed_slot.v's compute+activation-fetch+writeback subsystem, reusing ddr_prefetch_mgr.v/neural_processor_packed.v/ result_writeback.v completely unmodified, with its own private weight-fetch removed) and systolic_group.v (one real layer_prefetch_ ctrl.v+layer_weight_buffer.v+weight_tile_gather.v shared by 4x packed_pe.v via a real, barrier-synchronized broadcast bus). Real design choice confirmed with the user before writing any RTL (AskUserQuestion, concrete topology preview): shared-weight broadcast, not a literal PE-to-PE systolic shift register -- achieves the real, quantified rationale (4x reduction in redundant weight-fetch DDR3 traffic per group of 4 PEs) with much lower real risk than genuine inter-PE pipeline fill/drain. The real new design is the barrier: each PE's own tcnt is the join key against the group's broadcast tcnt, self-synchronizing regardless of which PE is momentarily ahead/behind (e.g. a real DDR3 row-switch stall on one PE's own activation fetch). Found and fixed a real bug during verification (not by inspection): the first full test run reported every result as undefined despite every control-flow signal tracing correctly -- root-caused via real signal tracing down to a 5-way test arbiter bus mis-sliced at the wrong slot offset (single-bit handshake buses happened to use a correct range and masked it from the control-flow trace; only the wide, byte-offset buses were wrong). Verified in isolation (tb_systolic_group.v, real Icarus xsim, real sdram_arbiter_n.v generalized to NUM_REQ=5 with zero changes): 8/8 PASS across 2 consecutive group jobs (exercising the barrier's own per-job reset path, not just cold start). Deliberately scoped to the isolated mechanism only, per this project's "one variable at a time" discipline -- Director/SPI job dispatch for group jobs, a real N=16 top-level, and real P&R are real, disclosed next steps, not done here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
7f9ece12dc |
docs: real P&R signoff for the result-writeback engine (EXP-0088)
Real in-context P&R confirms the result-writeback engine (EXP-0088) closes timing with essentially zero cost: WNS=+0.099962ns (vs EXP-0086's +0.095707ns), WHS=+0.036275ns, 0 failing endpoints. 6642 LUTs (+260 for the new engine), 16 DSP48E1 unchanged. This is now the current, trustworthy signoff, promoted over EXP-0086/0087's own pointers in both docs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
ccaf3ee059 |
feat: real result-writeback engine, removes the last hard N-scaling pin blocker (EXP-0088)
Adds result_writeback.v, one instance per packed_slot.v, writing each completed job's result directly into DDR3 at the job's own result_addr_a/b instead of driving literal top-level pins -- the same architectural shape as the weight-fetch path, in reverse. job_done now means "durably in DDR3", not "captured in a register only a pin could see". n2_system_ddr3_top.v's own s0_result_data_a/b, s1_result_data_a/b top-level package pins are removed (and the now-dangling XDC constraint for them), closing the real, hard scaling blocker docs/ARCHITECTURE_ ANALYSIS.md flagged since EXP-0074/0079 (8 bits x 2 lanes x N cores -> 256 pins at N=16). Addressing reuses the exact same JOB_ADDR_WIDTH->ctrl-bus-word truncation x_base_a/w_base already use (verified against act_tile_ fetch.v's/layer_prefetch_ctrl.v's own real code, not guessed). The host reads results back via the already-existing READ_MEM (0x02) SPI opcode -- no new protocol. A real EXP-0066-class bug (issuing ctrl_req before mem_grant) was caught and fixed before ever compiling, by re-deriving the design against act_tile_fetch.v's own proven S_MEMWAIT/S_GAP sequencing. Verified two ways: tb_packed_slot.v extended with a real DDR3 read-after-write check (9/9 PASS, confirms the write actually landed, not just that job_done pulsed); tb_n2_system_ddr3.v re-run via real xsim to confirm correct behavior under real 2-slot shared-bus arbitration (8/8 PASS, 0 errors, consistent timing with EXP-0087's own baseline for this workload). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
344e798ad5 |
feat: real DDRManager re-measurement at 32-bit channel + BOM/pinout docs (EXP-0087)
Re-measured ddr_prefetch_mgr.v's (EXP-0083) real benefit against the now-closed 32-bit DDR3 channel (EXP-0086), per this project's own standing plan. Real result: the 2.86% benefit measured at the old 16-bit channel is GONE at 32-bit (WITH: 100663.1335ns vs WITHOUT: 100656.6835ns -- a 0.0064% regression, statistically a wash). The wider channel's lower per-tile latency already absorbs the gap the look-ahead prefetch used to hide. Kept wired in for correctness/ timing-neutrality (real P&R already signs off with it included), but it's no longer a real performance win. Updated docs/ARCHITECTURE_ ANALYSIS.md and docs/PHYSICAL_REALIZATION.md accordingly. Found and fixed 3 real testbench/simulation-setup bugs along the way: - tb_n2_system_ddr3.v and tb_mig_native_adapter.v still had a stale CLKIN_PERIOD=2900 (the FAILED EXP-0084 clock period) instead of the current real, closed 3225ps (EXP-0086). - tb_n2_system_ddr3.v used SystemVerilog-only $signed(8'(...)) cast syntax, invalid for xvlog's default plain-Verilog mode -- fixed via an intermediate 8-bit reg. - Building a fresh sim_1 fileset needs the real MIG simulation dependency set added explicitly (mig_7series_0_mig.v is marked USED_IN_SIMULATION=0 in the project since testbenches bypass the public wrapper); verilog_define is a fileset-level property, not per-file, in this Vivado version. New measurement-only fork (not part of the real synthesis target, per fork-before-promote discipline): packed_slot_noprefetch.v + tb_n2_system_ddr3_noprefetch.v, reproducing the pre-EXP-0083 direct per-tile activation-fetch sequencing for a fair A/B baseline. Also adds docs/BOM.md and docs/PINOUT.md: a real component list (DDR3 x2, flash, FPGA already verified; clk_ref oscillator and an ESP32-S3- WROOM-1 module newly verified in-stock on LCSC; sys_clk oscillator flagged as needing a custom-programmed order, no off-the-shelf SKU at the required 310.077MHz) and a consolidated, board-layout-ready pinout extract of PHYSICAL_REALIZATION.md's own pin tables. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
678caa9bce |
docs: real timing closure for 32-bit DDR3 channel (EXP-0086)
Reverting Clock Period to 3225ps (keeping Data Width=32) closes real timing: WNS=+0.095707ns, WHS=+0.036275ns, 0 failing endpoints, all user-specified constraints met. This is the real final signoff for the 32-bit DDR3 channel widening effort (EXP-0083 through EXP-0086), replacing EXP-0083's 16-bit-era baseline as the project's current trustworthy number. Confirms the EXP-0084 root-cause analysis: the earlier -0.618ns failure was caused by a separate, stacked clock speedup, not the width change itself. Also found and fixed a second, wholesale real occurrence of the stale-import bug: the MIG wizard regeneration needed to revert Clock Period silently re-imported the entire v3 RTL tree (9 files) plus the top XDC back to pre-EXP-0084 stale copies, not just the file(s) the regeneration touched. CLAUDE.md's lesson extended accordingly. |
||
|
|
bdc821222f |
feat: real active-low data_ready_n sticky IRQ pin (EXP-0085)
User-requested hardware notification so the ESP32 can be interrupt-driven instead of polling STATUS in a loop. spi_host_bridge_v3.v: new job_out_done input (wired from neural_director_packed.v, already available at the top level) and new data_ready_n output. A sticky irq_pending register sets on job_out_done (latched, survives the pulse itself deasserting) and clears when the host completes a real STATUS (0x20) or REG_READ(0x02) transaction - reusing cs_rose, the same real transaction-complete event the module already relies on elsewhere, not a new mechanism. dir_error is ORed in live/combinational, not latched. SET has priority over CLEAR on the rare cycle both coincide. Real pin: D14, bank 15 (already 3.3V, alongside the SPI bus and sys_rst) - tentative, not yet a final board decision. Deliberately added after EXP-0084's own P&R iterations settled, so it didn't complicate that already-tight I/O/VCCO budget mid-fix. Its own real P&R verification is deferred to the next real P&R run (already needed to close EXP-0084's clock-period timing gap), not run separately against a config already known to fail timing for unrelated reasons. Real verification: tb_spi_host_bridge_v3.v extended with 10 new checks (idle state, sticky set, mid-transaction hold, real-acknowledge clear, unrelated-register non-acknowledge, dir_error live assert/clear). 49/49 PASS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
9dead54ebf |
feat: real 32-bit DDR3 channel widening - functionally complete, timing NOT yet closed (EXP-0084)
Real 32-bit DDR3 widening (2x MT41J128M16JT-125:K chips ganged in parallel, user's own MIG wizard session). Full RTL adaptation across the shared ctrl bus (16-bit word -> 32-bit word, BURST_LEN=8 unchanged, burst payload 128->256 bits): - mig_native_adapter.v: app_wdf_data/app_rd_data 64->128 bits (real, confirmed against the regenerated MIG wrapper), beat count unchanged. - act_tile_fetch.v: real logic change - burst now holds 4 tiles instead of 2 (sel_lat extended to 2 registered bits, 4-way case mux instead of 2-way ternary, same request-time-registered-select discipline as EXP-0081). Not a further bytes/MAC reduction, just what's needed to keep 100% packing utilization at the larger burst. - host_mem_bridge.v: real addressing redesign - host-facing 16-bit-word contract kept unchanged (ESP32 firmware unaffected), internally translated onto the new 32-bit-native ctrl bus. - sdram_arbiter_n.v, layer_prefetch_ctrl.v, packed_slot.v, ddr_prefetch_mgr.v, n2_system_ddr3_top.v: mechanical width bump plus doubled ddr3_dq/dqs/dm pins and the real differential sys_clk/clk_ref top-level ports the regenerated MIG now requires. New burst_mem_model32.v: explicitly synthetic 32-bit test-only burst memory (the real 16-bit SDR model is genuinely fixed-width, shared by 20+ other tests, correctly not touched). Found and fixed a real address-aliasing bug in it during bring-up (MEM_ADDR_BITS=16 silently wrapped a real 0x10000 test address to 0). Real verification: all isolated testbenches re-verified (10/10, 33/33, 32/32, 7/7, 9/9 PASS), plus real xsim against the real 2-chip DDR3 model (tb_mig_native_adapter.v 12/12 PASS, tb_n2_system_ddr3.v 8/8 PASS, both chips visibly returning different real data). Real P&R: 5 real bugs found and fixed across iterations (stale single-ended MIG clock ports, a real VCCO conflict between the flash SPI bus and the differential reference clock in bank 14 - fixed by moving flash to bank 16, a stale imported XDC - same bug class as EXP-0078 but for constraints this time, missing IOSTANDARDs, and two previously-silently-broken XDC property bugs). Route completes 100%, but real timing does NOT close: WNS -0.618ns, 213 failing endpoints. Honest root cause: the violation is inside neural_processor_packed.v's own packed-MAC accumulation tree, unchanged since EXP-0059 - it has real margin at the old 155.039MHz ui_clk but not at the new 172.414MHz the paired clock-period change produced. This is NOT caused by the 32-bit width change itself. Width alone, even at the old clock, already delivers the full intended 2x bandwidth gain (1.24 -> ~2.48 GB/s) - width and clock rate are separable levers. Current trustworthy timing signoff remains EXP-0083 (16-bit, +0.073ns) until the clock period is reverted toward 3225ps (keeping Data Width=32) in one more real, user-gated MIG wizard session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
376ccb6ee2 |
docs: capture two more exploratory scaling directions (BRAM cache, ESP32-side scheduling)
S5.6.1: opportunistic BRAM cache for activation tiles - exploits real, currently 0%-utilized Block RAM to catch whatever locality the workload happens to have, without committing to a specific reuse pattern the way the systolic direction does. No cache-invalidation problem given the current write-once-before-job protocol. S5.6.2: host-side (ESP32) job-queue reordering - a software-only "DDRManager" upstream of ddr_prefetch_mgr.v, grouping jobs with nearby DDR3 addresses before submission to reduce row-switch cost with zero RTL and zero timing-margin risk. Both marked exploratory, not decided, not built - same as S5.6's systolic direction. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
32d32f4c0e |
docs: capture exploratory 4x4 hybrid systolic scaling direction (not built)
Brainstorming session outcome: instead of flat N=16 independent DDR3 requesters, group cores into 4 weight-stationary systolic chains of 4 PEs each - reduces independent DDR3 contenders from 16 to 4 while preserving task-level parallelism across the 4 groups. Captured as docs/ARCHITECTURE_ANALYSIS.md S5.6, explicitly marked exploratory/not decided/not built - revisit after the 32-bit channel widening and real N=2/4/8 flat-core scaling tests produce real numbers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
fa327b75ca |
feat: DDRManager phase 1 - single-slot look-ahead activation prefetch (EXP-0083)
New ddr_prefetch_mgr.v wraps act_tile_fetch.v with a depth-2 ping-pong buffer, issuing the next tile's DDR3 fetch as soon as the fetch engine is free instead of waiting for packed_slot.v to finish consuming the current tile. Wired into packed_slot.v's tile loop (job-level start instead of per-tile req), simplifying the S_TILEWAIT join in the process (ddrpf_tile_valid is level-held, no separate act_seen latch needed). Verification: new tb_ddr_prefetch_mgr.v (25/25 PASS after fixing a real testbench polling race found via iteration-tagged tracing, not an RTL bug), tb_packed_slot.v re-run unmodified (9/9 PASS, bit-identical results), tb_n2_system_ddr3.v re-run via real xsim against real ddr3_model.sv (8/8 PASS). Real P&R: WNS +0.073ns (up from EXP-0082's +0.068ns), LUTs 5644, DSP48E1 16 unchanged, 0 failing endpoints. Honest result: real A/B on the actual DDR3 backend (same testbench, before/after) shows a real but modest 2.86% reduction in total simulated time - smaller than the original hypothesis suggested, because neural_processor_packed.v already accepts one operand per cycle, so the per-tile dead time being removed was already small relative to real DDR3 fetch latency. Docs updated to report this honestly rather than oversell it; the larger multi-slot DDRManager is deferred pending re-measurement against the (still pending, user-gated) 32-bit channel widening. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
cbd16dd727 |
docs: sync physical/architecture docs with EXP-0081/0082 reality, add 32-bit vs dual-channel analysis
- PHYSICAL_REALIZATION.md: replace stale "1 tile = 1 burst" layout description with the real EXP-0081/0082 "2 tiles = 1 burst" convention; add EXP-0082 signoff row and history table. - ARCHITECTURE_ANALYSIS.md: mark §5.1 (denser activation packing) DONE with real re-measured numbers (bandwidth ceiling fraction 25%->50%, WNS +0.030->+0.068ns); add §5.4, the real device-data-backed comparison of 32-bit single-channel widening vs a second independent DDR3 channel (decided: 32-bit widening, per real DQS/bank pin-conflict analysis); update scaling-path recommendation to reflect the user's final directive (widen channel -> build DDRManager -> N=2/4/8/16 tests, N=8 target, N=16 documentary). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
27cf5f36da |
docs: complete architecture analysis - DDR3 bandwidth is the real ceiling, not DSP count (EXP-0080)
Before building N=4/8/16 core scaling, did the requested full analysis. Real finding: using measured DDR3 throughput (1.24 GB/s, from the actual EXP-0079 JEDEC trace) against calculated compute-side need (4.96 GB/s for one core at peak DSP throughput, given the current activation memory layout's 2x byte overhead), the system is memory-bandwidth-bound already at N=1/N=2, not DSP-bound (only 6.67% DSP used). Scaling core count today would show no real throughput gain. docs/ARCHITECTURE_ANALYSIS.md: full module review + ranked interventions -- result-writeback engine (blocker), denser activation packing (highest-leverage bandwidth fix), then the user's own proposed DDRManager/orchestrator-prefetch idea (design-sketched, grounded in neural_director_packed.v's existing job queue, explicitly scoped as complementary to denser packing, not a substitute for it), THEN N-scaling with real per-N P&R signoff. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
43a12379a5 |
feat: MILESTONE - real activation-fetch engine, full N=2 system verified on real DDR3 (EXP-0079)
Closes the last major disclosed functional gap: packed_slot.v's activation data was read through a combinational stand-in since EXP-0062. New act_tile_fetch.v reads activation tiles directly from DDR3 (no on-chip buffering needed, unlike weights -- activation data has no reuse), sharing each slot's existing ctrl port with its own weight-prefetch engine. Real memory layout: one full BURST_LEN=8-word burst per tile, deliberately avoiding any runtime-indexed part-select given this project's thin P&R timing margin (EXP-0078). Verified at three levels: act_tile_fetch.v alone (6/6), packed_slot.v with real preloaded activation data (9/9), and the full N=2 system against real DDR3 via xsim (8/8, 0 errors) -- the first time this project's compute path has been verified end-to-end with real DDR3 for both weights and activations. Retired hardware/v3/rtl/n2_system_top.v and its testbench (pre-DDR3 SDR-placeholder era, fully superseded by n2_system_ddr3_top.v). Also: docs/PHYSICAL_REALIZATION.md (real pinout/parts/timing/protocol reference for the physical board) and CLAUDE.md (persistent project instructions for future Claude Code sessions). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC |
||
|
|
81a9619214 |
chore: remove root-level V1 duplicates, superseded by hardware/v1/ freeze
hardware/v1/ was created (
|
||
|
|
43abf28b5b |
V2.1.0-dev: SPI host bridge + clock/reset architecture (NOT release-ready)
STEP20 work toward the V2 hardware release gate. Adds real new RTL
implementing the three pieces the previous freeze (V2.0.0) explicitly
left open, plus real, disclosed verification findings. Does NOT
declare hardware release complete -- see below.
New RTL:
- spi_host_bridge.v: real SPI slave protocol engine (WRITE_JOB/
WRITE_MEM/READ_MEM/STATUS/RESET opcodes), replacing the 110-pin
reg_* testbench bus as the intended physical host interface.
Isolated regression 18/18 PASS (tb_spi_host_bridge.v); two real
MISO-timing bugs found and fixed during its own development (see
the module's header for the root-cause writeup).
- ecp5_pll_sys_clk.v: real, tool-generated (Project Trellis ecppll)
EHXPLLL wrapper, 16MHz oscillator -> 64MHz system clock, with a
declared (not fabricated) simulation-only PLL bypass.
- reset_sync.v: standard async-assert/sync-deassert reset bridge
gating on external POR and PLL lock.
- fpga_neural_v2_top.v: board-level top wiring the above around the
STEP19 compute+memory design's own already-frozen submodules
(zero modification to neural_processor.v, dependency_manager.v,
sdram_unified_backend.v, or any other previously-frozen file).
Real findings from this step's own re-verification (both logged in
full in hardware/v2/logs/errors.log):
- ERR-0024: the current Icarus Verilog v13.0 install (updated since
the last freeze) gives WRONG bit-exact results for the
already-committed STEP19 regression. Cross-checked against
Verilator per this project's own standing protocol (DEC-0004) --
the STEP19 baseline (single SDRAM, N=2/N=4, raw reg_* interface) IS
bit-exact correct, reconfirmed today, matching the historical cycle
counts exactly. Two provably-zero-behavior-change declaration-order
fixes were required just to get the current toolchain to elaborate
the already-shipped STEP19 files at all.
- ERR-0025: a real SPI-bridge protocol race (fixed) plus a SEPARATE,
real, UNRESOLVED defect -- two jobs dispatched through the real SPI
path with realistic pacing produce wrong compute results, even
though job registration itself is confirmed correct at the
handshake. Root cause not yet isolated. Committed as a known-failing
regression (tb_fpga_neural_v2_top_smoke.v) documenting the gap
honestly rather than hiding it.
Given ERR-0025 Part B is real and unresolved, synthesis/P&R of the new
board-level top was deliberately not attempted this round, and V2
hardware release is NOT declared complete. See decisions.log DEC-0036
and hardware/v2/docs/{CHIP_READINESS,OPEN_ITEMS}.md for the full,
itemized status.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
|
||
|
|
8e014d8d49 |
V2.0.0 hardware freeze - single SDRAM
FASE #1 hardware freeze for FPGA-Neural V2, N4/P8, single external SDRAM (Alliance Memory AS4C4M16SA-6TIN) serving weights, activations, and results through one physical sdram_controller.v instance. Removes the PSRAM dependency (hardware/v1/rtl/psram_controller.v + memory_interface.v) from the V2 physical path entirely -- V1 itself remains fully unmodified, the golden reference. New RTL: sdram_unified_backend.v (2-way W/AR arbitration over one SDRAM controller, real per-byte DQM write masking added to sdram_controller.v for correct single-byte result writes with no read-modify-write), nms_neural_multiprocessor_sdram_unified.v (the frozen top-level). Two real bugs found and fixed via full-system testing before being accepted (ERR-0023): a deadlock and an off-by-one data-shift bug in the new arbitration logic. Real results: N=4 and N=2 D-Stress bit-exact (256/256 neurons), 40 real AUTO REFRESH events interleaved with zero corruption, real Yosys+nextpnr-ecp5 synthesis/P&R for LFE5U-45F-8CABGA381 (149/245 TRELLIS_IO, a real 45-pin reduction from the prior dual-memory design). Timing is MARGINAL (1/8 P&R seeds >=80MHz), reported honestly rather than masked by the best seed. Real, sourced ball-level pinout for the SDRAM bus + clk/rst (39/149 signals, P&R-verified) using the official Lattice ECP5U-45 pinout CSV found on disk during this step's own pre-commit review -- corrects an earlier draft that wrongly assumed no real pinout data was available. Chip readiness: NO. Real, disclosed blockers remain (no physical host interface exists yet -- the RTL's own reg_* ports are a 110-pin raw test-harness bus; clock source/PLL decision; power/configuration component selection) -- see hardware/v2/docs/{HARDWARE_FREEZE, CHIP_READINESS,OPEN_ITEMS}.md for the complete, itemized status. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
5c9ec618d3 |
docs: publish refreshed V1 datasheet (EN/IT, Rev A1)
Adds the updated professional-template V1 datasheet PDFs to the tracked repo, matching the datasheet's own filename convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
dc0b331d3e |
feat(v2): scaffold hardware/v1 frozen baseline + M1 Neural Processor
Begins the V2 Neural Multiprocessor / Dataflow architecture per docs/v2-description.md, per explicit user request to freeze V1 and start V2 development, copying from V1 what's needed. Scaffold: - hardware/v1/: byte-exact, read-only copy of the current V1 codebase (rtl, testbenches, tools, constraints, a representative subset of synthesis results, and reference docs) -- verified identical via diff/cmp against the live top-level tree before being made filesystem-read-only. The live top-level tree is untouched and remains the project's "production" V1 (see hardware/v1/README.md and hardware/v2/logs/decisions.log DEC-0001 for why copy-not-move). - hardware/v2/: mandatory structure (rtl/sim/constraints/synthesis/ reports/scripts/logs/docs) plus the full logging system required by the spec (development/architecture/simulation/synthesis/timing/ benchmark/decisions/experiments/errors.log). M1 -- Neural Processor (hardware/v2/rtl/neural_processor.v): - 8-stage pipelined perceptron unit (P_IN=8): input align, 8 multipliers, 3-level adder tree, accumulator, bias+activation, INT8 saturation. Genuine 1-tile/cycle throughput, not just a wider combinational datapath. - 7-state FSM (NP_IDLE..NP_ERROR per docs/v2-description.md §6, with 4 baseline states merged into NP_WAIT_OPERANDS -- see decisions.log DEC-0002); valid/ready/data/last stream interfaces per §7. - Bit-exact vs the frozen hardware/v1/rtl/neuron_parallel.v + mac8.v + mac_unit.v: 7/7 tests pass (hardware/v2/sim/tb_neural_processor.v), covering regular/mixed-sign/extreme-INT8 vectors, both activations, a zero-idle-gap back-to-back-tiles throughput check, and an 8-tile job -- verified with Verilator (see below for why). - Real synthesis + place&route (Yosys + nextpnr-ecp5): 0 CHECK problems, Fmax 183.12 MHz at ACC_WIDTH=32 (PASS at 80MHz, ~3x V1's isolated PARALLEL=8 Fmax of 61.71 MHz) and 176.21 MHz at ACC_WIDTH=24 (a user-requested comparison experiment, also bit-exact-verified; see experiments.log EXP-0001/EXP-0002 and benchmark.log). Three real bugs found and resolved during M1 development (full diagnostic record in errors.log): - Two independent, reproducible Icarus Verilog v13.0 scheduling defects (ERR-0001, ERR-0002) that silently produced wrong simulation results for standard sequential Verilog -- confirmed via Verilator 5.050 giving correct results on the same minimal repros. Verilator is now the trusted simulator for hardware/v2/ (decisions.log DEC-0004); Icarus's affected protocol-violation check was removed from the RTL and deferred architecturally to the Neural Director (DEC-0003) rather than chased further. - One real RTL bug (ERR-0003): last0 wasn't gated like valid0, letting a "last tile" tag leak into the pipeline ahead of its actual valid tile on back-to-back jobs. Fixed and verified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
07a48e401f |
fix: close 7 zero-value/mid-run guard gaps found in re-certification campaign
Fixes all 7 bugs found in the FPGA-Neural re-certification campaign (docs/validation/bugs.md, CERTIFICATION.md), per campaign policy that fixes land as a commit separate from the analysis work (commits 313a199..77e74db): - BUG-005 (CRITICAL): layer_sequencer.v -- RUN_NETWORK(num_layers=0) ran through 256 fabricated layers reading arbitrary PSRAM data as descriptors. Now an immediate no-op. - BUG-007 (CRITICAL): spi_engine.v -- SET_NET_TYPE received mid-run remapped the arbiter mux and hung the in-progress engine. Now rejected while graph_busy/seq_busy, verified not to partially apply. - BUG-002 (MEDIA): neuron_parallel.v -- N_INPUTS=0 bypassed the elaboration-time guard, leaving x_bus/w_bus undriven. Guard extended to reject N_INPUTS==0. - BUG-003 (MEDIA): neuron_parallel.v -- n_inputs_real=0 at runtime had inconsistent behavior across repeated runs. Now an explicit early-out via the existing "finishing" completion path. - BUG-004 (BASSA): neuron_memory.v -- n_neurons_real=0 silently ignored the limit. Fixed at all three entry points into the vulnerable termination checks (STATE_READ_X, STATE_READ_W, and the X->W dispatch). - BUG-006 (BASSA): graph_engine.v -- num_neurons_graph=0 relied on an incidental guard rather than a real one. Now an explicit no-op. - BUG-001 (INFO): removed sim/top.v, confirmed dead code from the pre-INT8 Q8.8 era. Every bug-reproduction testbench is rewritten from observe-only to hard-assert the fixed behavior (sim/*_bug00[2-7]*_tb.v), verified individually and via a full regression (44 testbenches, 43 PASS, 0 FAIL/ERROR, 1 benchmark by design). Re-verified on the real toolchain (Yosys synth_ecp5 + nextpnr-ecp5): 0 constraint errors, Fmax 68.65 MHz (was 67.91 MHz, within known placement noise), critical path structurally unchanged (neuron_parallel/mac8 accumulator carry chain). Updates docs/validation/bugs.md and CERTIFICATION.md to reflect the resolved state, and docs/FPGA-NeuralNetwork-Engine.md + the LaTeX datasheet (IT/EN) with inline notes on each fixed edge case, closing the datasheet/RTL gap flagged in C.13 of the original certification. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
77e74db637 |
docs: final certification verdict for the re-certification campaign
Consolidates Fase 0 + C.1-C.14 + D into one verdict: the project is NOT certifiable without reservations as a whole. Core arithmetic datapath is solid (exhaustively certified where possible); control/ sequencing/arbitration is correct on the happy path (40 real tests pass via an independent regression harness). 7 real bugs found, all traceable to one systemic pattern (unguarded "real=0" boundary values plus one unguarded mid-run configuration write). 2 of 7 are CRITICAL (BUG-005, BUG-007), reachable via documented SPI opcodes under plausible conditions, with real PSRAM corruption or inference-hang risk. None were documented before this campaign. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
8d20a7bfbf |
docs: certify C.9-C.14 + cross-cutting analyses D
C.9 (pinout): certified citing this session's own F1-F7 work; also regenerated a real bitstream for the CURRENT full-system build (ecppack, 0 errors, real part header) rather than relying on an older pre-flash build's bitstream. C.10 (timing): certified citing Fmax re-measured at every structural change this session (54.58->75.30->73.88->66.68->67.91MHz), critical path re-verified identical at every re-synthesis. C.11 (toolchain): full RTL->Yosys->nextpnr-ecp5->ecppack flow re-run on the current build, 0 errors at every stage. Real-silicon behavior explicitly declared NOT certifiable (no physical hardware available). C.12 (netasm): certified citing 20/20 tests plus a real hardware round-trip already demonstrated earlier this session. C.13 (datasheet/RTL coherence): real, current discrepancy found and declared -- none of this campaign's 7 bugs (BUG-001-007) are yet mentioned in the datasheet or engine spec doc. Correct and expected (found after those docs were last updated), flagged as required follow-up after the campaign completes. C.14: page-mode PSRAM and the flash subsystem are actually COMPLETE, not "in progress" as the certification prompt assumed -- verified against real repo state. D (cross-cutting): CDC certified via C.7. Reset scheme found by exhaustive grep across all 20 RTL files to be synchronous-only everywhere (no posedge rst anywhere) -- a real fact, differs from the prompt's async-assert/sync-deassert phrasing, not a defect. FSM: the campaign's own BUG-002-007 ARE FSM defects, found and documented, but no exhaustive reachability analysis of every FSM was done (stated reserve). Width/overflow: 2 real cases found across the whole session. Lint: full-system Yosys check, one real warning (known PSRAM tristate), zero accidental latches confirmed signal-by-signal (25 explicit confirmations). Determinism: no real non-determinism found (the one apparent inconsistency, BUG-003, traced to genuinely different test stimuli between attempts, not unstable behavior for identical stimuli). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
f0a66363de |
test: certify spi_neuron_top mux/pins (C.8), find critical BUG-007
Legitimate dispatch mux and data_ready_n/irq_n pins certified via existing pre-session tests. BUG-007 (CRITICAL), confirmed end-to-end over real simulated SPI: SET_NET_TYPE has no check against graph_busy/seq_busy in rtl/spi_engine.v, and rtl/spi_neuron_top.v's arbiter Port C mux selects between graph_engine/layer_sequencer purely combinationally on the current net_type value -- not latched to whichever engine actually started the in-flight run. Started a valid graph RUN_NETWORK, sent SET_NET_TYPE(dense) immediately after (before completion): STATUS.busy gets stuck (30+ consecutive polls with no done/err, vs. ~12-25us normal completion) -- the graph engine is left waiting for a ram_ready that never arrives via the now-disconnected mux path. Also verified recovery: RESET during the hang brings the system back to a fully working state (a subsequent legitimate dense op completes normally) -- not a permanent lockup, but plain STATUS polling alone would never unstick without a host-side RESET fallback. Full regression: 40/40 real tests pass, 1 new observational test deterministically reproduces BUG-007 and verifies RESET recovery. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
95849c9002 |
docs: certify SPI slave/engine (C.7), confirm len=0 is properly guarded
Certified via existing extensive pre-session coverage (10 tests in spi_engine_tb.v, CDC ratio-variation test in spi_slave_tb.v, explicit unknown-opcode-as-NOP handling), re-confirmed passing in Phase 0. Targeted check given this campaign's recurring pattern (missing zero-value guards, BUG-002 through 006): WRITE_RAM/READ_RAM len=0 IS explicitly guarded (rtl/spi_engine.v:817, transitions to ST_IGNORE) -- confirms the project does not systematically lack these guards, the gap is specific to the modules already flagged. No new bugs found in this aspect. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
6db44efcfd |
test: certify graph_engine gather/guard (C.6), find related BUG-006
Gather/padding/src_id<out_id guard certified via existing solid pre-session tests (graph_engine_tb.v checks act_buffer contents via hierarchical reference, not just final output; graph_engine_guard_tb.v covers 4 adversarial cases incl. recovery). BUG-006 (LOW severity): num_neurons_graph=0 shares BUG-005's exact root cause (neuron_idx is a full 16-bit register, no guard), but graph_engine's existing per-edge src_id<out_id guard incidentally catches most garbage-data patterns fast (err at cycle 58 for a non-trivial test pattern, vs. layer_sequencer's 21761-cycle full run in BUG-005) -- not a designed protection for this case, so not closed as a non-issue, but lower severity given the observed practical risk. Not run to full 65536-iteration completion (impractical for this campaign's time budget) -- limitation stated explicitly. Full regression: 40/40 real tests pass, 1 new observational test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
f81d7fa1b3 |
test: certify layer_sequencer chain (C.5), find critical BUG-005
Layer chain / ping-pong / busy-done certified via the existing pre-session test, which already verifies the actual ping-pong buffer address used (not just the value) and correct busy/done timing. New finding, BUG-005 (CRITICAL): run_num_layers=0 has no guard at compile time or runtime, and unlike BUG-002's 1-bit group_index (which can never represent the wraparound value), layer_idx here is a full 8-bit register that naturally reaches it. Confirmed empirically with a minimal neuron_memory stub: RUN_NETWORK(0) runs through all 256 possible layer indices (21761 cycles), reading arbitrary PSRAM bytes far past the real descriptor table as if they were valid layer descriptors, running real neuron_memory passes on them, and writing results to ping-pong buffer addresses derived from that arbitrary data. More severe than BUG-002/003/004: reachable via a single documented SPI opcode (RUN_NETWORK), real PSRAM corruption risk rather than just a hang or wrong result. Root cause fully isolated, not just the symptom. Full regression: 40/40 real tests pass, 1 new observational test (no pass/fail by design) deterministically reproduces BUG-005. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
b528901510 |
test: certify mem_arbiter priority order (C.4)
Priority B>C>A>D confirmed correct with distinguishable per-port data (not just "someone got served" but "the right requester got its own data back") across 4 contention scenarios. D-alone case confirms low priority does not mean never granted. Found and fixed a real race in the test harness itself: blocking assignments withdrew loser requests in the same clock edge meant to grant the winner, racing the DUT's own synchronous block -- dut.owner never left SEL_NONE, every wait() blocked forever. Fixed by switching request-signal drives to non-blocking assignments throughout. Documented (not filed as a bug) that D can starve indefinitely under sustained continuous B contention -- standard behavior for a fixed-priority arbiter with no aging, and explicitly outside the header's own stated operating assumption (B/C temporally disjoint in normal operation). Flagged the header's "never starves or corrupts A/B/C" wording as ambiguous about whether it promises D's own progress. Full regression: 40/40 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
3dd75e9e0d |
test: certify memory subsystem addressing (C.3)
int8_memory_access.v byte<->word conversion and byte-lane selection: exhaustive 2048-address test + 6 real read/write round-trips through the FSM handshake. 2054/2054 checks, 0 mismatches, after fixing two bugs in the test harness itself (a same-timestep race reading a non-blocking update one iteration late, and a behavioral memory stub that ignored byte-lane enables on write) -- both documented as test-side, not RTL, issues. memory_interface.v and psram_controller.v not re-verified from scratch: cited against coverage already established/re-confirmed earlier in this same session (page-mode/tCEM against the ISSI datasheet, a real pre-existing power-up request-loss bug found and fixed), re-run clean via the Phase 0 regression harness rather than trusted from WORKLOG text alone. Full regression: 39/39 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
14c8d87194 |
test: certify runtime width early-termination (C.2), document BUG-003/004
n_inputs_real/n_neurons_real early termination for valid values is certified real: a "poison" region (data that would saturate the result if read past the claimed limit) confirms no over-read, cycle counts scale proportionally. n_inputs_real non-multiple-of-PARALLEL at runtime matches the documented silent-truncation risk exactly. n_inputs_real=0 / n_neurons_real=0 (BUG-003/004): confirmed incorrect behavior in every repetition, but the exact triggering mechanism was NOT fully isolated -- nearly-identical repeated tests produced different symptoms (clean hang vs. silently processing the full build width vs. a third cycle count matching neither). Reported in full, including the inconsistency itself, rather than picking the cleanest result. The two new permanent testbenches reflect this honestly: the solid early-termination checks are hard assertions, the n_*_real=0 probe is deliberately observe-only given the non-deterministic result. Full regression: 38/38 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
20b0b1f4c0 |
test: certify mac_unit/mac8 datapath (C.1), confirm real N_INPUTS=0 guard gap
mac_unit.v: exhaustive unit test (all 65536 (x,w) combinations at DATA_WIDTH=8, plus 486 boundary acc_in vectors) against an independent Python oracle (tools/validation/mac_oracle.py). 66022/66022 match, 0 reserves. mac8.v: first-ever dedicated unit test (previously only indirect coverage at whatever single PARALLEL neuron_parallel_tb.v happens to use). Verified at PARALLEL=2/8/32 with structural adversarial vectors (catches swapped/duplicated tree wiring), 300 random INT8 pairs per PARALLEL with realistic accumulating acc_in, and worst-case magnitude adversarial vectors. 939/939 match. Confirms BUG-002 (N_INPUTS=0 bypasses the N_INPUTS%PARALLEL elaboration guard) is real, on both simulation and real Yosys synthesis -- root cause: [DATA_WIDTH*N_INPUTS-1:0] becomes [-1:0] for N_INPUTS=0, which both tools treat as a genuine 2-bit undriven vector rather than collapsing to zero width. Includes a documented self-correction: the first verification attempt produced a false "hang" using an invalid one-shot late check of a single-cycle done pulse -- caught by reproducing the same false result on a known-good sanity config before trusting it. Full regression re-run clean after adding 3 new testbenches: 36/36 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
313a1994a4 |
docs: certification campaign Phase 0 -- real inventory, new regression harness
Builds tools/run_regression.py (no reproducible regression script existed before -- every prior "N testbenches, all pass" claim was assembled by hand). Resolves each testbench's dependencies by static analysis of instantiation sites, not from memory, then compiles+runs everything fresh. Independently re-verifies the 33-testbench regression clean (0 real failures) after fixing two false negatives in the harness itself (two tests are deliberate compile-time-failure negative tests, one file is a benchmark with no pass/fail verdict by design -- confirmed by reading each file's own header, not assumed). Findings recorded in docs/validation/: sim/top.v is dead code (references a removed FRAC_BITS parameter from the pre-INT8 Q8.8 era); mac_unit.v/ mac8.v have no dedicated unit testbench, only indirect coverage; the N_INPUTS%PARALLEL elaboration guard does not mathematically cover N_INPUTS=0 (open finding, not yet confirmed reachable -- BUG-002). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
b029e3d95a |
fix: make flash SPI bus electrically independent, drop USRMCLK/CCLK reuse (Phase F7)
The flash subsystem's SCLK previously reused the boot config-SPI's CCLK pad via the ECP5 USRMCLK primitive to save one pin. This made the "exclusive flash bus" claim misleading (SCLK still depended on the config engine's own pad electrically) and carried an unresolved verification gap (USRMCLKTS pad-enable timing never checked against the primary Lattice sysCONFIG Usage Guide). flash_sclk is now a genuine 4th ordinary GPIO pin (E3, bank 7), added purely additively to the real .lpf (git diff: one new line, no existing ball moved). The flash bus is now 4 fully independent wires (sclk/mosi/miso/cs_n), zero pins shared with any ECP5 config primitive -- confirmed by the full-system synthesis reporting USRMCLK 0/1 (0%) utilisation. All 33 project testbenches re-run clean after the port rename (no functional change, only sclk_sim -> sclk). Full-system real synthesis re-verified: 0 constraint errors, Fmax 67.91MHz (up slightly from 66.68MHz, same critical path, not a regression). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
97a21be240 |
feat: flash boot/persistence subsystem (SPI master, copy engine, CRC32 slot catalog)
Adds FPGA-exclusive access to the onboard W25Q128JV SPI NOR flash for weights/bias/network persistence, layered as spi_flash_master (raw SPI, USRMCLK-driven) -> flash_copy_engine (flash<->PSRAM streaming, erase- before-write, Page Program loop) -> flash_slot_manager (16-slot catalog with CRC32), exposed via 8 new SPI opcodes (0x40-0x47). Fixes two pre-existing bugs found during bring-up: a psram_controller.v request lost during power-up, and a one-cycle-pulse race in the PSRAM arbiter request handshake. Full simulation + real Yosys/nextpnr-ecp5 synthesis verification (0 errors, Fmax 66.68MHz) in WORKLOG.md and docs/FPGA-Neural-Flash-Subsystem-Verification.md. Also updates docs/pinout to reflect the 56-signal real .lpf (3 new flash pins) and documents the WRITE_RAM/READ_RAM host backpressure risk found while testing this subsystem. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
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 |
||
|
|
f6edc01613 |
docs: Phase 7 placement-seed sweep + hardware design document
Phase 7 (docs/FPGA-NeuralNetwork-Engine.md): re-ran nextpnr-ecp5 on the already-synthesized Phase 5 spi_neuron_top netlists (top.json reused, only placement re-seeded) at --seed 1/2/3 for both P8 and P2. Both land in a tight band regardless of seed (P8: 39.5-40.6 MHz, 2.6% spread; P2: 42.5-45.0 MHz, 5.8% spread) -- confirms the Phase 5 timing shortfall is a real structural bottleneck, not placement noise, unlike the much smaller same-tier benchmark design (<2% utilization, huge placer freedom, genuinely noisy). Corrected the earlier "pipeline the saturate stage" candidate fix, which targeted Phase 4's critical path and not the one Phase 5's logic actually shifted to; block RAM for x_mem/w_mem remains the leading candidate, not yet implemented. New docs/FPGA-Neural-Hardware-Design.md: draft hardware design doc for a board carrying the project's actual target device (LFE5U-45F-8BG381C) plus the parallel PSRAM rtl/psram_controller.v is written for. Covers: why not the basic-ecp5-pcb reference board (wrong package/speed grade, no RAM), a real I/O pin budget from Lattice's own CABGA381 pinout table, a researched PSRAM part (ISSI IS66WVE4M16EBLL-70BLI -- 70ns access matches the controller's timing assumption exactly, with a note on the byte/word address shift in int8_memory_access.v so the chip's top address line is correctly left as spare headroom, not a wiring error), clock (16 MHz, no PLL exists yet so CLK_FREQ_MHZ must match whatever oscillator is fitted), power/config reusing the reference board's proven circuitry and errata (config-SPI pin can't double as the application SPI interface), and a BOM/open-items list. 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. |
||
|
|
a2bd60e305 |
feat: complete Phase 4 SPI RTL (engine, arbiter, top) + real-RAM e2e test
Implements the rest of the SPI interface (docs §8.1) on top of spi_slave.v from the previous commit: - rtl/spi_engine.v: opcode FSM + register bank, all 8 opcodes (NOP, WRITE_RAM, READ_RAM, RESET, SET_BASE, START, STATUS, READ_OUTPUT, READ_CONFIG). tx_byte is driven combinationally from live state (not reactively on tx_byte_req), applying the prefetch-vs-consume contract documented on spi_slave.v. STATUS.done is a sticky, clear-on-read latch. RAM master port uses the same byte-level convention as neuron_memory.v's external mem_* port. - rtl/mem_arbiter.v: fixed-priority (neuron_memory > spi_engine) grant-and-forward arbiter sharing one byte-level memory port between spi_engine's WRITE_RAM/READ_RAM and neuron_memory's own X/W/bias reads during a run. - rtl/spi_neuron_top.v: full integration -- spi_slave -> spi_engine -> mem_arbiter -> a single shared int8_memory_access -> memory_interface -> psram_controller -> PSRAM pins. neuron_memory's rst is global rst OR'd with the RESET opcode's soft-reset pulse. The host has no direct electrical path to the RAM, only through this chain. Testing: - sim/spi_engine_tb.v: 10 tests (one per opcode + WRITE_RAM/READ_RAM, START idle-vs-busy, STATUS sticky/clear-on-read, extra-MOSI-bytes- ignored, back-to-back transactions) against a synthetic 2-cycle- latency RAM model, isolating the opcode FSM from PSRAM timing. Found and fixed two testbench-only bugs (RTL needed no change): the same delta-zero clock-edge race as spi_slave_tb.v (blocking `nm_done=1` landing on the same sim time as a posedge -- fixed via negedge-based pulsing) and a missing RAM sentinel initialization. - sim/spi_neuron_top_tb.v: end-to-end test against the **real** psram_model.v (not a mock) -- RESET/READ_CONFIG/WRITE_RAM/ READ_RAM/SET_BASE/START/STATUS/READ_OUTPUT all driven purely over simulated SPI. 3/3 scenarios (sum, saturation, ReLU) pass on the first attempt; confirms the arbiter and shared byte<->word bridge are correct against real PSRAM timing, not just a synthetic mock. Real-toolchain verification (Yosys + nextpnr-ecp5 + ecppack): spi_slave.v and spi_engine.v synthesize clean and comfortably clear 80 MHz in isolation (403 MHz / 191 MHz, no DSP usage). The full spi_neuron_top.v integration, however, does NOT meet 80 MHz (~52-56 MHz depending on PARALLEL) -- the critical path is entirely inside neuron_parallel.v's existing saturation comparator (no contribution from the new SPI/arbiter logic), but its routed delay is ~57% worse than in the isolated benchmark due to placement/ routing congestion once SPI + PSRAM logic shares the fabric with it, not resource exhaustion (2% DSP usage). Documented as a Phase 4/7 finding in docs/FPGA-NeuralNetwork-Engine.md -- a floorplanning/ pipelining problem for Phase 7, not a functional-correctness issue (verified independently in simulation against real PSRAM timing). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |
||
|
|
87efce3d9b |
docs: draft SPI protocol v1 for Phase 4 (opcodes, register map)
Phase 4 (SPI Interface) only had a high-level conceptual sequence (RESET/CONFIGURE/LOAD.../START/WAIT/READ) with no concrete opcodes, framing, or register map -- not enough to start RTL from. Added docs/FPGA-NeuralNetwork-Engine.md §8.1 with a concrete v1 draft: - SPI Mode 0, MSB-first, one opcode byte per CS-low transaction. - Explicit length field on WRITE_RAM/READ_RAM (chosen over CS-edge-delimited streaming: simpler controller, just a byte counter). - READ_CONFIG opcode exposing N_INPUTS/N_NEURONS/PARALLEL/ ADDR_WIDTH/DATA_WIDTH at runtime, so one host firmware build can target different bitstreams. - RESET kept as its own opcode (0x0F), distinct from NOP. - STATUS.done documented as required to be a STICKY, clear-on-read bit in the SPI register bank: neuron_memory.done is a one-cycle pulse that a slow SPI poll would almost certainly miss otherwise. Opcode values themselves are marked explicitly as draft/example, not frozen -- only the framing rules and the two decisions above are meant to stick going into Phase 4 RTL work. No RTL or testbench changes in this commit; design-only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |
||
|
|
661363f637 |
feat: extend neuron_memory to support N_NEURONS>1 (Phase 3)
neuron_memory.v only handled a single neuron. Added an N_NEURONS parameter (default 1, fully backward compatible) and a memory-bound neuron loop: X is read once (shared layer input), and for each neuron in turn W and bias are re-read from PSRAM and fed to a single, reused neuron_parallel instance -- no change to the validated compute datapath (neuron_parallel/mac8/mac_unit). Addressing follows layer.v's neuron-major convention: neuron n's weights live at w_base + n*N_INPUTS bytes, its bias at bias_addr + n. Output changed from a single `y` port to a packed `y_bus` (DATA_WIDTH*N_NEURONS bits, neuron-major), matching layer.v's y_bus. - rtl/neuron_memory.v: N_NEURONS parameter, neuron_index/ w_group_base/bias_group_addr tracking, y_reg[] array assembled into y_bus, STATE_WAIT_N now loops back to STATE_READ_W for the next neuron instead of finishing after one. - sim/neuron_memory_tb.v: updated to the new y_bus port (N_NEURONS=1 explicit); all 5 existing tests still pass unchanged, confirming backward compatibility. - sim/neuron_memory_multi_tb.v: new end-to-end test (full memory_interface + psram_controller + psram_model stack) with N_NEURONS=3, validating per-neuron addressing and a single done pulse at the end of the sequence (scale, larger value, ReLU). - Full regression re-run: all existing testbenches still pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |
||
|
|
1a6f0ba2ef |
fix: guard neuron_parallel against invalid N_INPUTS/PARALLEL combos
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 |
||
|
|
9b9859a104 |
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 |
||
|
|
16769eaa4b | working with memory |