feat(v2): M8 PSRAM integration - real V1 backend shared across concurrent slots
neural_multiprocessor.v wraps dataflow_core.v (M7, unmodified) around the real, unmodified V1 PSRAM backend chain (int8_memory_access -> memory_interface -> psram_controller), funneling N_SLOTS independent Memory Backend Interface ports through a new generic N-port arbiter (slot_mem_arbiter.v) inspired by (not copied from) V1's own mem_arbiter.v. Real concurrent-slot simulation immediately surfaced a genuine bug (ERR-0008): memory_manager/prefetch_engine's byte-level backend protocol is fire-and-forget (a single-cycle mem_req pulse with no accept handshake) - correct for M4's direct 1:1 connection, but a naive arbiter silently drops a pulse arriving while the shared bus is owned by another slot, hanging that slot forever. Fixed with a per-port pending-request latch, the same "queue, don't drop" idiom already used by memory_manager's own pf_pending register (ERR-0006). Verified (Verilator): 4/4 PASS with 2 slots genuinely contending for one real PSRAM port (444 cycles). No regression on M4's own testbench. Real synthesis + nextpnr-ecp5 P&R (no harness needed - real PSRAM pins keep the top-level at 157 pins): 0 problems, Fmax 142.45 MHz, PASS at 80MHz. Arbitration policy is fixed lowest-index priority, not fairness- balanced (DEC-0010) - consistent with every other "simplest correct policy first" scheduling choice in this roadmap, revisited only if M9's real measurement shows starvation matters. Logged: simulation/synthesis/timing/benchmark/decisions (DEC-0010)/ experiments (EXP-0009)/errors (ERR-0008)/development.log, ROADMAP.md updated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
@@ -103,3 +103,17 @@ uses 32/72 (44%), consistent with DEC-0005's finding that DSP, not
|
||||
LUT/FF, is the first resource to saturate as concurrency grows (M2's
|
||||
own N_PROCESSORS=8 measurement: 88%). BRAM=0 on both is expected --
|
||||
M3's buffers are not wired into dataflow_core yet (DEC-0009).
|
||||
|
||||
[2026-09-05] M8 Neural Multiprocessor top (real standalone synthesis +
|
||||
P&R, no harness needed -- real PSRAM pins keep the bare top-level
|
||||
pin count at 157, under the TRELLIS_IO budget)
|
||||
|
||||
| Module (config) | Fmax (POST-P&R) | LUT4 | CCU2C | FF | DSP | BRAM |
|
||||
|--------------------------------------|------------------|------|-------|------|-----|------|
|
||||
| neural_multiprocessor (N_SLOTS=2) | 142.45 MHz | 3145 | 388 | 3659 | 16 | 0 |
|
||||
|
||||
Compare to M7's dataflow_core alone (N_SLOTS=2): 165.15 MHz / LUT4=2127
|
||||
/ CCU2C=248 / FF=2505 / DSP=16. Adding the real V1 PSRAM chain +
|
||||
slot_mem_arbiter costs ~1000 LUT4/140 CCU2C/1150 FF and drops Fmax by
|
||||
~23 MHz (165.15 -> 142.45) -- both real, measured costs of real PSRAM
|
||||
integration, not assumed.
|
||||
|
||||
@@ -521,3 +521,64 @@ oversight.
|
||||
STATUS:
|
||||
ACCEPTED
|
||||
|
||||
DEC-0010
|
||||
|
||||
DATE: 2026-09-05
|
||||
|
||||
DECISION:
|
||||
slot_mem_arbiter.v (M8) arbitrates dataflow_core's N_SLOTS independent
|
||||
Memory Backend Interface ports down to the ONE real PSRAM port using
|
||||
FIXED, lowest-port-index priority (not round-robin/least-loaded/
|
||||
fair-share), with a per-port single-entry pending-request latch (see
|
||||
errors.log ERR-0008) so a fire-and-forget request pulse arriving
|
||||
during contention is queued, never dropped.
|
||||
|
||||
WHY:
|
||||
Fixed lowest-index priority is the same "first-found, simplest
|
||||
correct policy first" starting point already chosen for
|
||||
neural_director's first-free slot scheduling (decisions.log DEC-0007)
|
||||
and dependency_manager's first-ready dispatch -- consistent with this
|
||||
whole roadmap's own pattern of shipping the simplest policy that is
|
||||
provably correct, then revisiting fairness/throughput ONLY once real
|
||||
measured data (M9) shows it actually matters for a real workload.
|
||||
Under sustained heavy contention a low-index slot COULD in principle
|
||||
starve a higher-index one (an unfair, but not incorrect, outcome);
|
||||
this is an honestly-acknowledged limitation of a first cut, not an
|
||||
oversight. The pending-latch discipline (ERR-0008) is not a policy
|
||||
choice but a correctness requirement -- discovered empirically via
|
||||
real concurrent-slot simulation, not designed in from the start (an
|
||||
example of the mandate's own point, §22/§30: real measurement finds
|
||||
real problems that a purely theoretical design would not).
|
||||
|
||||
EVIDENCE:
|
||||
hardware/v2/sim/tb_neural_multiprocessor.v -- 4/4 PASS with N_SLOTS=2
|
||||
genuinely concurrent slots (node0/node1, no dependencies, dispatched
|
||||
back-to-back) contending for the one real PSRAM port through the real
|
||||
V1 backend chain; both complete correctly and node2 (depends on both)
|
||||
dispatches only once they genuinely do. No starvation observed in this
|
||||
small a test (2 slots, one short job each) -- a real starvation
|
||||
measurement would need a longer-running, higher-N_SLOTS workload,
|
||||
deferred to M9's own benchmark.
|
||||
|
||||
ALTERNATIVES:
|
||||
1. Round-robin or least-recently-served fairness now. Rejected: no
|
||||
measured evidence yet (M9 not run) that fixed-priority starvation
|
||||
is a real problem for the graph workloads this system targets --
|
||||
adding fairness logic before a measured need is speculative
|
||||
complexity, the same reasoning DEC-0007 already applied to
|
||||
neural_director's own scheduling policy.
|
||||
2. Give each slot its own dedicated PSRAM port (no arbitration at
|
||||
all). Rejected: real PSRAM hardware has exactly one physical port
|
||||
(the whole reason this module exists) -- not an option on real
|
||||
hardware, only in simulation.
|
||||
|
||||
RESULT:
|
||||
slot_mem_arbiter.v as implemented: fixed lowest-index priority,
|
||||
single-entry pending-request latch per port (mandatory for
|
||||
correctness, not a policy choice). Fairness/throughput-aware
|
||||
scheduling explicitly deferred to a future measurement-driven
|
||||
decision, not missing by oversight.
|
||||
|
||||
STATUS:
|
||||
ACCEPTED
|
||||
|
||||
|
||||
@@ -206,3 +206,27 @@ decision: see decisions.log DEC-0009 (M3 buffers not wired in yet, no
|
||||
next_action: M8 -- PSRAM integration. Wire the real (unmodified) V1
|
||||
PSRAM backend chain through dataflow_core end-to-end and measure/
|
||||
design whatever N_SLOTS>1 arbitration real contention requires.
|
||||
|
||||
[2026-09-05] M8 -- hardware/v2/rtl/neural_multiprocessor.v +
|
||||
hardware/v2/rtl/slot_mem_arbiter.v
|
||||
reason: roadmap M8 -- "Integrare il controller V1 senza modificarlo
|
||||
inizialmente. Misurare il comportamento reale." dataflow_core.v (M7,
|
||||
untouched) now shares the real, unmodified V1 PSRAM backend chain
|
||||
(int8_memory_access -> memory_interface -> psram_controller) across
|
||||
N_SLOTS genuinely concurrent memory_manager instances, via a new
|
||||
generic N-port arbiter inspired by (not copied from) V1's own
|
||||
mem_arbiter.v.
|
||||
result: real concurrent-slot simulation immediately surfaced a real
|
||||
bug (errors.log ERR-0008: the byte-level backend's fire-and-forget
|
||||
request pulse gets silently dropped by a naive arbiter under
|
||||
contention) -- fixed with a pending-request latch. After the fix:
|
||||
4/4 test PASS (2 concurrent slots genuinely contending for one real
|
||||
PSRAM port, 444 cycles). Real synthesis: 0 problems, LUT4=3145/
|
||||
CCU2C=388/FF=3659/DSP=16, real Fmax 142.45 MHz (PASS at 80MHz, no
|
||||
harness needed -- real PSRAM pins keep the top-level pin count at
|
||||
157). No regression on M4's own testbench.
|
||||
errors: see errors.log ERR-0008 (real RTL bug, found and fixed).
|
||||
decision: see decisions.log DEC-0010 (fixed-priority arbitration, no
|
||||
fairness yet -- deferred pending real measured need).
|
||||
next_action: M9 -- Full benchmark (§32): V1-vs-V2 comparison table,
|
||||
every number labeled THEORETICAL/SIMULATED/SYNTHESIZED/POST-P&R.
|
||||
|
||||
@@ -203,3 +203,54 @@ WORKAROUND: set the parameter on the TOP module being synthesized
|
||||
STATUS: WORKED AROUND. A build-script ordering detail, not a defect in
|
||||
dataflow_core.v or harness_dataflow_core.v themselves -- noted here
|
||||
so a future N_SLOTS sweep (M9/M10) does not re-trip over it.
|
||||
|
||||
ERR-0008 (real RTL bug in the first draft of hardware/v2/rtl/slot_mem_arbiter.v, FOUND AND FIXED)
|
||||
DATE: 2026-09-05
|
||||
MODULE: hardware/v2/rtl/slot_mem_arbiter.v (M8, new)
|
||||
SYMPTOM: hardware/v2/sim/tb_neural_multiprocessor.v -- node0 (slot 0)
|
||||
completes correctly (result=48), but node1 (slot 1, dispatched
|
||||
concurrently to node0) never completes; its byte-level backend
|
||||
request appears to simply vanish, and the slot hangs forever
|
||||
(watchdog timeout at 20000 cycles, result stays 0).
|
||||
ROOT CAUSE: memory_manager.v/prefetch_engine.v's own byte-level
|
||||
backend protocol (mem_req/mem_wr/mem_addr/mem_wdata/mem_rdata/
|
||||
mem_ready) is FIRE-AND-FORGET: mem_req is asserted for exactly ONE
|
||||
clock cycle per byte transaction, with no separate "request
|
||||
accepted" acknowledgment -- only `mem_ready` (transaction
|
||||
COMPLETION) exists. M4's own testbench (tb_memory_manager.v) never
|
||||
exposed this because it connects exactly ONE memory_manager directly
|
||||
to int8_memory_access, which is always idle and therefore always
|
||||
able to accept that single pulse the instant it fires. The first
|
||||
arbiter draft only granted a port while its s_req was LIVE that same
|
||||
cycle -- if slot 1's one-cycle pulse arrived on a cycle where the
|
||||
arbiter was already owned by slot 0, the pulse was gone the very
|
||||
next cycle with no record of it ever having happened, and slot 1's
|
||||
prefetch_engine sat in ST_READ_X/ST_READ_W waiting forever for a
|
||||
mem_ready that could never arrive (its request never reached
|
||||
int8_memory_access at all).
|
||||
DIAGNOSIS METHOD: ran the M8 testbench, observed node0 (whichever slot
|
||||
the Director happened to grant the shared bus to first) complete
|
||||
while node1 (the other, concurrently-dispatched slot) hung; traced
|
||||
the fire-and-forget nature of mem_req directly in
|
||||
prefetch_engine.v's own state machine (`mem_req <= 1'b1;` appearing
|
||||
only inside single-cycle state-transition branches, unconditionally
|
||||
cleared to 0 every other cycle) -- confirmed the arbiter's naive
|
||||
"grant only while req is live" logic could not possibly catch a
|
||||
pulse arriving during contention.
|
||||
FIX: every incoming s_req pulse is now LATCHED into a per-port
|
||||
`pending` register (capturing wr/addr/wdata the same cycle),
|
||||
regardless of arbiter state -- the same single-entry "queue, don't
|
||||
drop the request" idiom already used by memory_manager's own
|
||||
pf_pending register (ERR-0006 fix #1). Grants are drawn from
|
||||
`pending`, never from a live s_req directly. This adds a uniform
|
||||
minimum 1-cycle latency to every byte transaction (a real, honestly
|
||||
measured cost of sharing one PSRAM port across N_SLOTS -- see
|
||||
timing.log/benchmark.log EXP-0009), but never drops a request
|
||||
regardless of contention.
|
||||
VERIFICATION: hardware/v2/sim/tb_neural_multiprocessor.v -- 4/4 PASS
|
||||
after the fix (444 cycles end-to-end, vs the buggy draft's 20000-
|
||||
cycle watchdog timeout). hardware/v2/sim/tb_memory_manager.v (M4,
|
||||
untouched) re-run unchanged -- still 3/3 PASS, confirming the fix is
|
||||
entirely contained inside the new arbiter module.
|
||||
STATUS: FIXED, verified end-to-end with the real (unmodified) V1
|
||||
PSRAM backend chain and real concurrent multi-slot contention.
|
||||
|
||||
@@ -401,3 +401,35 @@ next_action: M8 -- PSRAM integration. Wire the real (unmodified) V1
|
||||
psram_controller) end-to-end through dataflow_core, and design/
|
||||
measure whatever N_SLOTS>1 arbitration across ONE physical PSRAM
|
||||
port actually requires.
|
||||
|
||||
[2026-09-05] EXP-0009 -- hardware/v2/rtl/neural_multiprocessor.v (M8,
|
||||
real PSRAM integration)
|
||||
test: hardware/v2/sim/tb_neural_multiprocessor.v -- same 3-node DAG as
|
||||
EXP-0008, now routed through the REAL, UNMODIFIED V1 PSRAM backend
|
||||
chain shared across N_SLOTS=2 genuinely concurrent memory_manager
|
||||
instances via the new slot_mem_arbiter.v
|
||||
simulator: Verilator 5.050 (--binary --timing)
|
||||
PASS/FAIL:
|
||||
SIMULATED: 4/4 PASS after fixing a real dropped-request bug in the
|
||||
arbiter's first draft (errors.log ERR-0008) -- 444 cycles
|
||||
end-to-end. hardware/v2/sim/tb_memory_manager.v (M4, untouched)
|
||||
re-confirmed 3/3 PASS, no regression.
|
||||
SYNTHESIZED (real standalone top-level, no harness needed -- 157
|
||||
port bits, real PSRAM pins keep it under the TRELLIS_IO budget):
|
||||
0 CHECK problems, LUT4=3145, CCU2C=388, TRELLIS_FF=3659,
|
||||
MULT18X18D=16, DP16KD=0.
|
||||
POST-P&R (real): Fmax=142.45 MHz -- PASS at 80MHz.
|
||||
errors: one real RTL bug (errors.log ERR-0008) -- the first arbiter
|
||||
draft silently dropped a request pulse arriving during contention;
|
||||
fixed with a per-port pending-request latch, the same "queue, don't
|
||||
drop" idiom already used by memory_manager's own pf_pending register
|
||||
(ERR-0006).
|
||||
decision: see decisions.log DEC-0010 (fixed lowest-index priority
|
||||
arbitration, not fairness-balanced -- consistent with every other
|
||||
scheduling policy chosen so far in this roadmap; revisit only if
|
||||
M9's real measurement shows starvation actually matters).
|
||||
next_action: M9 -- Full benchmark. Produce the V1-vs-V2 comparison
|
||||
table mandated by §32 (Fmax, LUT, FF, DSP, BRAM, MAC/cycle,
|
||||
cycles/neuron, neurons/s, stall %, memory/processor utilization,
|
||||
effective MAC/s), each number labeled THEORETICAL/SIMULATED/
|
||||
SYNTHESIZED/POST-P&R per §30.
|
||||
|
||||
@@ -75,3 +75,23 @@ PASS/FAIL: 4/4 PASS -- node0=48, node1=8 (both real neural_processor
|
||||
dependency-manager-to-director wake-up loop closes correctly
|
||||
end-to-end with real hardware in between, not just in isolation
|
||||
(M6's own testbench already proved the wake-up logic alone)
|
||||
|
||||
[2026-09-05] EXP-0009 -- hardware/v2/sim/tb_neural_multiprocessor.v
|
||||
test: same 3-node DAG as EXP-0008 (node0/node1 independent, node2
|
||||
depends on both), routed through neural_multiprocessor.v (M8): the
|
||||
REAL, UNMODIFIED V1 PSRAM backend chain (int8_memory_access ->
|
||||
memory_interface -> psram_controller -> psram_model) shared across
|
||||
N_SLOTS=2 genuinely concurrent memory_manager instances via the new
|
||||
slot_mem_arbiter.v -- node0 and node1 are registered back-to-back
|
||||
with no dependencies, so both dispatch to their slots essentially
|
||||
simultaneously and genuinely contend for the one real PSRAM port.
|
||||
simulator: Verilator 5.050 (--binary --timing)
|
||||
PASS/FAIL: 4/4 PASS after fixing a real dropped-request bug in the
|
||||
first arbiter draft (errors.log ERR-0008) -- node0=48, node1=8
|
||||
(concurrent, real PSRAM, real arbitration), node2=40 dispatched only
|
||||
after both genuinely completed. 444 cycles end-to-end (vs 20000-cycle
|
||||
watchdog timeout with the buggy first draft, where node1's request
|
||||
was silently dropped and its slot hung forever).
|
||||
regression: hardware/v2/sim/tb_memory_manager.v (M4) re-run unchanged
|
||||
(no M4 file touched) -- still 3/3 PASS, identical cycle counts
|
||||
(446/166/728), confirming slot_mem_arbiter.v is purely additive.
|
||||
|
||||
@@ -63,3 +63,16 @@ CHECK: 0 problems on both configs (same 32 benign "multiple conflicting
|
||||
deliberately NOT instantiated inside dataflow_core yet (decisions.log
|
||||
DEC-0009). DSP scales exactly 8/slot (matches P_IN=8, consistent with
|
||||
every prior per-processor DSP measurement since M1/M2).
|
||||
|
||||
[2026-09-05] EXP-0009 -- neural_multiprocessor (M8, N_SLOTS=2, real
|
||||
standalone top-level synthesis -- no timing harness needed: real PSRAM
|
||||
pins replace dataflow_core's wide per-slot arrays, total 157 port bits,
|
||||
well under the LFE5U-45F-8BG381's ~245 TRELLIS_IO budget)
|
||||
LUT4=3145 CCU2C=388 TRELLIS_FF=3659 MULT18X18D=16 DP16KD=0
|
||||
TRELLIS_DPR16X4=45 (small LUT-based distributed RAM, inferred from
|
||||
neural_director's shallow QUEUE_DEPTH-entry job queue -- not BRAM,
|
||||
same primitive class already seen in M7's own harness stat)
|
||||
$_TBUF_=16 (tri-state buffers for the bidirectional psram_dq bus,
|
||||
from V1's own unmodified psram_controller.v)
|
||||
CHECK: 0 problems (same 32 benign "multiple conflicting drivers for
|
||||
...neural_processor.\gi" warnings documented since EXP-0001).
|
||||
|
||||
@@ -65,3 +65,8 @@ Fmax drops as N_SLOTS grows (more concurrent memory_manager+
|
||||
neural_processor instances competing for the same routing fabric
|
||||
around the shared neural_director/dependency_manager hub) -- both
|
||||
configs still clear the 80MHz target with real margin.
|
||||
|
||||
[2026-09-05] EXP-0009 -- neural_multiprocessor (M8, N_SLOTS=2, real
|
||||
standalone synthesis, no harness needed), real nextpnr-ecp5 --45k
|
||||
--package CABGA381 --speed 8 --freq 80 --lpf-allow-unconstrained
|
||||
Fmax: 142.45 MHz -- PASS at 80MHz (real place&route measurement)
|
||||
|
||||
Reference in New Issue
Block a user