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
This commit is contained in:
2026-09-21 00:05:16 +02:00
co-authored by Claude Sonnet 5
parent 932aec2490
commit bd1fb5dc14
5 changed files with 627 additions and 8 deletions
+92
View File
@@ -6079,3 +6079,95 @@ verified, with its own real P&R signoff. (4) Revisit the flat N=2/4/8/16
core-count scaling tests (deferred by the user's own explicit
reprioritization this session) once there's a real basis for comparing
flat vs. grouped scaling with real numbers from both.
EXP-0090 -- real second step of the 4x4 hybrid systolic architecture:
Director extension for group-level job dispatch, plus a real out-of-
context P&R sanity check for systolic_group.v (2026-09-21, continuing
the user's own explicit reprioritization: "Ok procedi ad implementare
quel che manca" -- proceed to implement what's missing)
CONTEXT: EXP-0089 built and verified the isolated systolic_group.v
mechanism (4 PEs sharing one broadcast weight fetch). Two real, disclosed
gaps remained before any top-level integration: (a) no real area/timing
data point for the new module, (b) no way to dispatch a group-level job
-- neural_director_packed.v only knows how to pair 2 queue entries for a
flat packed_slot.v, not 8 for a systolic_group.v.
PART 1 -- real out-of-context synthesis, systolic_group.v (one group,
4 PEs), xc7a100tcsg324-2: **32 DSP48E1** (240 available, 13.3%), 3533
LUTs, 0 Block RAM. This is a REAL confirmation of the original
brainstorm's own quantified rationale (docs/ARCHITECTURE_ANALYSIS.md
S5.6: "16 cores x 8 DSP/core = 128/240") -- one group of 4 PEs at 8
DSP/PE = 32 DSP exactly matches 4 PEs x 8 DSP/PE, and scaling to the
full 4-group (16-PE) design would be 4x32=128/240 (53%), exactly the
projected figure. Real, not just a projection anymore, for at least
the per-group DSP cost (timing not meaningful out-of-context, no clock
buffer -- real P&R timing requires real system integration first, per
this project's own standing practice).
PART 2 -- new module `neural_director_grouped.v`, a real, direct
extension of neural_director_packed.v's own already-proven pairing
discipline (NOT a redesign): dispatches the 8 OLDEST queue entries
together (GROUP_SIZE=8, matching systolic_group.v's own fixed 4 PEs x
2 lanes) instead of 2, requiring all 8 to share w_base/n_tiles -- same
real reasoning, same real "stall visibly, never silently mis-dispatch"
standard. REAL, DELIBERATE NON-CHANGE: the host-facing job_in_*
submission interface is byte-for-byte identical to today's -- the ESP32/
SPI protocol (spi_host_bridge_v3.v's WRITE_JOB opcode) needs ZERO real
changes; the host just submits 8 jobs sharing a w_base instead of 2, the
same real submission pattern already required today, just wider. This
was confirmed as a genuine simplification of the original integration
plan, not an oversight.
REAL BUG FOUND AND FIXED DURING DESIGN (before compiling): the initial
draft's own q_head/q_idx wraparound arithmetic computed
`q_head + qk[...]` at only Q_ADDR_WIDTH bits before comparing against
QUEUE_DEPTH -- silently wrong for the same real reason a naive `base+
tcnt` sum was flagged unsafe elsewhere in this project (EXP-0088's own
addressing note): the addition needs Q_ADDR_WIDTH+1 bits to represent a
real carry-out BEFORE the mod-reduction compare, or the comparison
against QUEUE_DEPTH silently uses an already-wrapped (wrong) sum. Fixed
by widening the intermediate sum by 1 bit before comparing/subtracting.
REAL BUG FOUND AND FIXED DURING VERIFICATION (a significant, real,
generalizable testbench-discipline finding, not just a one-off): the
first full test run showed queue entries being silently duplicated --
every logical `submit_job` push registered as TWO real, identical
writes into consecutive queue slots (confirmed via real signal tracing
of q_tail/q_count/job_in_x_base, not guessed). Root cause: the test's
own stimulus-driving task pulsed `job_in_valid` on `@(posedge clk)` --
the SAME edge the DUT's own always block samples on -- and was called
BACK-TO-BACK with zero real simulated gap (a tight 8-iteration
submission loop, unlike every OTHER testbench in this project, which
always has a natural gap via a `while(!done)`-style poll between
pulses). This is the SAME underlying race family CLAUDE.md's own
existing "blocking vs nonblocking stimulus" lesson already covers, but
a real, previously-unseen TRIGGER for it (a tight back-to-back pulse
loop with no natural gap) -- CLAUDE.md's lesson extended accordingly.
Fixed by driving stimulus changes on `@(negedge clk)` instead of
`@(posedge clk)`, guaranteeing they can never race the DUT's own
posedge sampling regardless of call tightness.
VERIFICATION: new `tb_neural_director_grouped.v`, real Icarus xsim,
tests: (1) real octet dispatch with correct per-PE x_base_a/b
assignment (position pairs 0/1->PE0, 2/3->PE1, 4/5->PE2, 6/7->PE3); (2)
a second, different-w_base octet dispatches correctly to a freed group;
(3) a real mismatched w_base among the 8 oldest entries correctly
STALLS (no dispatch, matching this Director's own disclosed real
design -- confirmed there is no in-band recovery from a real submitter
mistake like neural_director_packed.v already has for pairs, a real
reset is the only way to clear it); (4) real queue wraparound across
the QUEUE_DEPTH=16 boundary. **4/4 PASS, 0 errors, ALL TESTS PASSED.**
DECISION: real, verified second step. Group-level job dispatch is now
provably correct in isolation. Still not done (real, disclosed, next):
a real N=16 top-level module wiring 4x systolic_group.v +
neural_director_grouped.v + a real, appropriately-sized arbiter (4
group weight-fetch requesters + 16 per-PE activation/writeback
requesters + host_mem_bridge.v = 21) + the existing, unmodified
spi_host_bridge_v3.v (no changes needed, per Part 2's own real finding)
+ mig_native_adapter.v, and real, in-context P&R for that whole system.
next_action: build the real N=16 top-level, verify it end-to-end (real
xsim against the real DDR3 model, matching this project's own
established multi-level verification discipline), then real P&R.