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
This commit is contained in:
2026-09-20 19:18:57 +02:00
co-authored by Claude Sonnet 5
parent 344e798ad5
commit ccaf3ee059
8 changed files with 534 additions and 51 deletions
+105
View File
@@ -5813,3 +5813,108 @@ now-diminished DDR3-latency lever this experiment just showed has
little room left to give at N=2 -- worth real-measuring its own benefit
carefully before investing further RTL effort, rather than assuming
EXP-0083's original optimistic framing still applies.
EXP-0088 -- real result-writeback engine: the last hard N-scaling
blocker removed (2026-09-20, user's own explicit reprioritization:
"riordiniamo le priorita ... BRAM ci pensiamo dopo. Fai la parte
realmente mancante prima, il RESULT-WRITEBACK e poi implementa la 4x4
sistolica")
CONTEXT: docs/ARCHITECTURE_ANALYSIS.md S4.6/S5.3 flagged this since
packed_slot.v's own original header (unchanged through EXP-0079):
result_data_a/b/result_node_id_a/b were literal top-level PACKAGE PINS
on n2_system_ddr3_top.v (s0_result_data_a/b, s1_result_data_a/b) --
fine at N=2 (4 pins), the exact same class of scaling mistake already
caught once for activation data (EXP-0074: ~360 pins nearly exceeded
the whole package's I/O budget) -- at N=16 this port alone would need
8 bits x 2 lanes x 16 cores = 256 pins, a real, hard blocker.
DESIGN: new module `result_writeback.v`, one instance per
`packed_slot.v` (matching how layer_prefetch_ctrl.v/act_tile_fetch.v/
ddr_prefetch_mgr.v are already one-per-slot, NOT a new arbiter-
requester count as N scales -- each slot still contributes exactly one
ctrl_req to the shared arbiter, now locally 3-way-muxed instead of 2).
On job completion (S_RESULT), packed_slot.v pulses `wb_start`; the new
S_WRITEBACK state holds `job_done` back until `wb_done` fires -- job_done
now means "the result is durably in DDR3", not "captured in a register
only a literal top-level pin could see".
REAL ADDRESSING (verified against act_tile_fetch.v's/layer_prefetch_
ctrl.v's own real address-computation code, not guessed, since getting
this wrong would be a silent correctness bug, not just a performance
one): result_addr_a/b arrive in packed_slot.v's own JOB_ADDR_WIDTH=26-bit
convention. The low ADDR_WIDTH=25 bits (dropping the unused top/MSB
headroom bit) are used DIRECTLY as a ctrl-bus-native 32-bit-word
address -- the exact same address space x_base_a/w_base already live
in (confirmed: `x_base_a_lat[ADDR_WIDTH-2:0]` feeds act_tile_fetch.v's
own ctrl_addr computation directly, same truncation). ONE full 32-bit
ctrl-word is written per lane: {node_id[15:0], 8'h00, result_data[7:0]}
(low 16 bits = zero-extended 8-bit result value, high 16 bits =
node_id). The host reads results back via the ALREADY-EXISTING
READ_MEM (0x02) SPI opcode -- no new protocol needed. Real, disclosed
host-firmware implication (not yet built, same class of gap as this
project's other disclosed firmware work, e.g. JTAG bit-banging):
reading a result back needs `mem_addr = result_addr[24:0]*2` for the
value and `mem_addr = result_addr[24:0]*2 + 1` for node_id (2 host
reads per lane), since READ_MEM's own mem_addr is 16-bit-word-granular
while this engine writes a native 32-bit ctrl-word -- the same real
halving host_mem_bridge.v's own header already discloses for the debug
raw-access path (EXP-0084).
SHARED-BUS DISCIPLINE (mirrors act_tile_fetch.v's own real, proven
pattern, not reinvented -- a REAL bug was caught and fixed during
design, not just asserted correct): the first draft omitted `mem_grant`
entirely and issued ctrl_req unconditionally, exactly the class of bug
EXP-0066 already documented (an early/blind request on a shared,
arbitrated bus can lose the request permanently) -- caught by re-
deriving the design against act_tile_fetch.v's own real S_MEMWAIT
sequencing before ever compiling it, not found by simulation. Fixed:
real S_MEMWAIT (wait for mem_grant before issuing ctrl_req) and a real
S_GAP state (wait for !ctrl_busy between lane A's write and lane B's
own, since mig_native_adapter.v's own busy stays asserted one cycle
past ctrl_ready -- act_tile_fetch.v's own header already established
this). wmask polarity matches host_mem_bridge.v's own real, already-
working convention exactly (0 = write this byte, 1 = masked).
REAL SCALING FIX: 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 IOSTANDARD constraint for them removed too) --
packed_slot.v still exposes result_data_a/b/etc. as plain output ports
(unchanged, for debug/testbench visibility), but nothing wires them to
literal FPGA pins any more, at any N.
VERIFICATION (two levels, same discipline as every other real change
in this project):
1. tb_packed_slot.v -- extended with a new real read-after-write
check (`verify_writeback` task): after each job's job_done, the
testbench independently reads back the EXACT DDR3 location
result_writeback.v should have written (via burst_mem_model32.v)
and confirms BOTH the result value AND node_id match -- not just
that job_done eventually pulsed. **9/9 PASS, 0 errors**, real
Icarus xsim (iverilog -g2012).
2. tb_n2_system_ddr3.v -- real xsim (Vivado, real ddr3_model.sv,
real mig_7series_0_mig, real 2-slot sdram_arbiter_n.v contention)
confirms the writeback engine behaves correctly under REAL shared-
bus arbitration between 2 slots, not just in isolation. **8/8
PASS, 0 errors, 8/8 positions completed**, $finish at
101204.9335 ns -- consistent with EXP-0087's own real ~100.6-100.7us
baseline for this same N=2/8-position workload (writeback adds a
small, real, expected overhead, not a regression).
Two real setup bugs found and fixed getting this run to compile:
result_writeback.v (a brand-new file) needed to be added to the
Vivado project's own `sources_1` fileset as a direct reference
(not just the xsim-only sim_1 fileset) -- confirmed via TCL query
it landed as a direct reference, not an imported copy, avoiding
the stale-import class of bug from the start.
DECISION: keep, real, verified, closes the last hard N-scaling blocker
this project's own docs had flagged since EXP-0074/0079. Real P&R
re-verification for this specific addition is the next real step
(deferred, together with the N=2/4/8/16 scaling tests it directly
unblocks, per the user's own next directive).
next_action: real P&R signoff for this change (confirm it doesn't
disturb the closed EXP-0086 timing), then real N=2/4/8/16 core-count
scaling tests (each with its own real P&R signoff, per the user's own
standing directive), then the 4x4 hybrid systolic architecture
(docs/ARCHITECTURE_ANALYSIS.md S5.6, currently exploratory/not built).