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.
This commit is contained in:
2026-09-20 18:05:30 +02:00
parent bdc821222f
commit 678caa9bce
4 changed files with 248 additions and 120 deletions
+11 -1
View File
@@ -59,7 +59,17 @@ unmodified by v3, e.g. `layer_prefetch_ctrl.v`/`layer_weight_buffer.v`).
diagnosed and fixed). Prefer adding new files so they stay a direct diagnosed and fixed). Prefer adding new files so they stay a direct
reference (check `IS_GLOBAL_INCLUDE`/the file's own path isn't under reference (check `IS_GLOBAL_INCLUDE`/the file's own path isn't under
`imports/`) rather than get copied — for BOTH the `sources_1` and `imports/`) rather than get copied — for BOTH the `sources_1` and
`constrs_1` filesets. `constrs_1` filesets. **A real MIG IP regeneration (re-running the
wizard, even just to change one field like Clock Period) can re-trigger
a WHOLESALE project source rescan that silently re-imports the ENTIRE
RTL tree plus constraints back to stale copies in one shot — not just
the file(s) the regeneration itself touched** (EXP-0086: a second
wizard run, needed only to revert Clock Period, silently reverted 9
already-fixed RTL files plus the top XDC all at once). After ANY IP
regeneration, check every fileset (`get_files -of_objects
[get_filesets sources_1]` and `constrs_1`) for `*/imports/*` paths
before trusting the next P&R — don't assume only the previously-fixed
files are still direct references.
- **Testbench stimulus must use nonblocking assignment (`<=`), not blocking - **Testbench stimulus must use nonblocking assignment (`<=`), not blocking
(`=`), when driving a DUT's inputs from a separate `always`/`initial` (`=`), when driving a DUT's inputs from a separate `always`/`initial`
block.** Blocking assignment races the DUT's own `posedge`-triggered block.** Blocking assignment races the DUT's own `posedge`-triggered
+74 -55
View File
@@ -1,25 +1,26 @@
# FPGA-Neural V3 — Architecture Analysis: Timing, Bottlenecks, and Recommended Interventions # FPGA-Neural V3 — Architecture Analysis: Timing, Bottlenecks, and Recommended Interventions
Scope: the current, real, P&R-verified V3 design (`hardware/v3/`, branch Scope: the current, real, P&R-verified V3 design (`hardware/v3/`, branch
`v3-artix7`), updated through EXP-0084 (32-bit DDR3 widening, functionally `v3-artix7`), updated through EXP-0086 (32-bit DDR3 widening, functionally
complete and real-verified; real P&R timing does NOT yet close at the complete AND real timing CLOSED). The current real, trustworthy *timing*
paired clock speedup — see §5.4.1). The current real, trustworthy *timing* signoff is now EXP-0086 (32-bit, 155.039MHz, WNS +0.096ns), replacing the
signoff remains EXP-0083 (16-bit, 155.039MHz, WNS +0.073ns) until a real prior EXP-0083 16-bit-era pointer. Every number in this document is either
P&R closes for the 32-bit configuration. Every number in this document is directly measured (real simulation trace, real P&R report) or a calculation
either directly measured (real simulation trace, real P&R report) or a built from directly-measured building blocks — the two are labeled
calculation built from directly-measured building blocks — the two are explicitly throughout. Nothing here is guessed.
labeled explicitly throughout. Nothing here is guessed.
**Status note (post EXP-0084)**: §5.1 (denser activation packing) is **Status note (post EXP-0086)**: §5.1 (denser activation packing) is
**DONE and real-P&R-verified** (EXP-0081/0082); §5.2 (DDRManager) phase 1 **DONE and real-P&R-verified** (EXP-0081/0082); §5.2 (DDRManager) phase 1
is **DONE and real-measured** (EXP-0083, a genuinely modest ~2.9% real is **DONE and real-measured** (EXP-0083, a genuinely modest ~2.9% real
benefit); §5.4 (32-bit widening) is **functionally DONE and real-verified** benefit, measured against the OLD 16-bit signoff — re-measurement against
but its own real timing has **not yet closed** (EXP-0084 — honest root the now-closed 32-bit channel is a real next step); §5.4 (32-bit widening)
cause: an unrelated, pre-existing compute-pipeline critical path, not the is **fully DONE — real-verified AND real timing-closed** (EXP-0084
width change itself; see §5.4.1). See the "DONE" markers in those sections. functional + EXP-0086 timing closure; honest intermediate story: the first
The document originally analyzed the pre-fix state; it's kept below timing attempt failed because of a SEPARATE, stacked clock speedup, not the
(marked historical) because the comparison is itself informative, then width change itself — see §5.4.1). See the "DONE" markers in those
updated with the real post-fix numbers throughout. sections. The document originally analyzed the pre-fix state; it's kept
below (marked historical) because the comparison is itself informative,
then updated with the real post-fix numbers throughout.
--- ---
@@ -38,9 +39,9 @@ DDR3 channel.
| Real DDR3 bandwidth needed for ONE core at peak DSP throughput | **4.96 GB/s** | calculated from measured DSP rate + memory layout (§3.2) | | Real DDR3 bandwidth needed for ONE core at peak DSP throughput | **4.96 GB/s** | calculated from measured DSP rate + memory layout (§3.2) |
| → DDR3 can sustain, pre-EXP-0081 packing (1 tile/burst) | **~25%** of one core's peak compute throughput | §3.2, historical | | → DDR3 can sustain, pre-EXP-0081 packing (1 tile/burst) | **~25%** of one core's peak compute throughput | §3.2, historical |
| → DDR3 can sustain, post-EXP-0081/0082 packing (2 tiles/burst, DONE) | **~50%** of one core's peak compute throughput | §3.2, current, real | | → DDR3 can sustain, post-EXP-0081/0082 packing (2 tiles/burst, DONE) | **~50%** of one core's peak compute throughput | §3.2, current, real |
| Real P&R timing margin (WNS), current trustworthy (16-bit) | **+0.073 ns** | measured, EXP-0083 real P&R | | Real P&R timing margin (WNS), current trustworthy (32-bit, CLOSED) | **+0.096 ns** | measured, EXP-0086 real P&R |
| Real P&R timing, 32-bit widened config (EXP-0084) | **-0.618 ns (FAILS)** | measured, real P&R — real root cause is an unrelated, pre-existing compute-pipeline path, not the width change; see §5.4.1 | | Real DDR3 physical bandwidth ceiling, 32-bit channel (closed timing) | **~2.48 GB/s** | measured, real P&R signoff, EXP-0086 — 2× EXP-0083's 16-bit ~1.24 GB/s, real not projected |
| DDRManager phase 1 (single-slot look-ahead prefetch) real benefit | **2.86%** reduction in total real simulated time | measured, real xsim A/B on `tb_n2_system_ddr3.v` (§5.2, EXP-0083) — modest, honestly reported, not oversold | | DDRManager phase 1 (single-slot look-ahead prefetch) real benefit | **2.86%** reduction in total real simulated time | measured, real xsim A/B on `tb_n2_system_ddr3.v` (§5.2, EXP-0083) — measured against the OLD 16-bit signoff; modest, honestly reported, not oversold; re-measurement against the closed 32-bit channel is a real next step |
| DSP48E1 headroom for scaling | 224/240 free (93%) | measured, real P&R utilization | | DSP48E1 headroom for scaling | 224/240 free (93%) | measured, real P&R utilization |
The DSP headroom is real and large. The memory-bandwidth ceiling is real, The DSP headroom is real and large. The memory-bandwidth ceiling is real,
@@ -50,16 +51,18 @@ throughput, with the real P&R margin *improving*, not degrading, as a side
effect. This was free leverage and it's now banked. effect. This was free leverage and it's now banked.
Even at ~50%, DDR3 is still the limiting resource, not DSP count. The 32-bit Even at ~50%, DDR3 is still the limiting resource, not DSP count. The 32-bit
channel widening (§5.4) is **functionally real and verified** (real xsim channel widening (§5.4) is now **fully real, verified, AND timing-closed**
against the real 2-chip DDR3 model), and width alone already delivers the (real xsim against the real 2-chip DDR3 model, plus a real closed P&R at
full intended 2× physical bandwidth gain (1.24 → ~2.48 GB/s) — but its real WNS=+0.096ns, EXP-0086) — width alone delivers the full intended 2×
P&R does not yet close timing, because the SAME wizard session also sped up physical bandwidth gain (1.24 → ~2.48 GB/s), with real, closed timing, not
`ui_clk` by 11.2%, which an unrelated, pre-existing compute-pipeline path a projection. The honest intermediate story: the first real P&R attempt
can't currently absorb (§5.4.1). Width and clock rate turned out to be stacked an ADDITIONAL clock speedup (`ui_clk` +11.2%) in the same wizard
separable levers; the path forward is reverting just the clock period. The session, which an unrelated, pre-existing compute-pipeline path couldn't
DDRManager (§5.2) remains complementary — hides latency, doesn't raise the absorb (§5.4.1) — width and clock rate turned out to be separable levers,
physical ceiling — and its own real benefit should be re-measured once the and reverting just the clock period closed timing with the width gain
wider channel's timing actually closes. intact. The DDRManager (§5.2) remains complementary — hides latency,
doesn't raise the physical ceiling — and its own real benefit should now be
re-measured against this closed, wider channel (not yet done).
--- ---
@@ -73,8 +76,9 @@ wider channel's timing actually closes.
| 0078 | + config-flash bridge (real STARTUPE2 placement) | +0.013 | 5213 | 16 | margin dropped — real added logic | | 0078 | + config-flash bridge (real STARTUPE2 placement) | +0.013 | 5213 | 16 | margin dropped — real added logic |
| 0079 | + real activation-fetch engine (`act_tile_fetch.v`) | +0.030 | 5379 | 16 | pre-packing baseline | | 0079 | + real activation-fetch engine (`act_tile_fetch.v`) | +0.030 | 5379 | 16 | pre-packing baseline |
| 0082 | + denser activation packing (2 tiles/burst, EXP-0081) | +0.068 | 5437 | 16 | pre-DDRManager baseline | | 0082 | + denser activation packing (2 tiles/burst, EXP-0081) | +0.068 | 5437 | 16 | pre-DDRManager baseline |
| 0083 | + DDRManager phase 1 (`ddr_prefetch_mgr.v`, single-slot look-ahead prefetch) | **+0.073** | 5644 | 16 | current, trustworthy 16-bit/155.039MHz signoff | | 0083 | + DDRManager phase 1 (`ddr_prefetch_mgr.v`, single-slot look-ahead prefetch) | +0.073 | 5644 | 16 | 16-bit/155.039MHz — superseded by EXP-0086 below |
| 0084 | 32-bit DDR3 widening (2 chips) + clock speedup to 172.414MHz | **-0.618 (FAILS)** | 6418 | 16 | functionally verified (real xsim), real timing NOT closed — root cause is the clock speedup, not the width; see §5.4.1. Not the current trustworthy number. | | 0084 | 32-bit DDR3 widening (2 chips) + clock speedup to 172.414MHz | -0.618 (FAILED) | 6418 | 16 | functionally verified (real xsim), real timing NOT closed — root cause is the clock speedup, not the width; see §5.4.1. |
| 0086 | 32-bit DDR3 widening, clock period reverted to 3225ps/155.039MHz | **+0.096 (CLOSED)** | 6382 | 16 | **current, trustworthy signoff** — real, closed timing, full 2× real bandwidth (§5.4.1). Also uncovered and fixed a real recurrence of the stale-import bug (whole-tree, triggered by MIG regeneration) — see CLAUDE.md. |
**Observation**: WNS does not move monotonically with LUT count (0.056 → **Observation**: WNS does not move monotonically with LUT count (0.056 →
0.013 → 0.030 → 0.068 → 0.073 while LUTs only ever grow) — this is normal P&R behavior 0.013 → 0.030 → 0.068 → 0.073 while LUTs only ever grow) — this is normal P&R behavior
@@ -399,7 +403,7 @@ never as N-scaled literal top-level pins again. Same architectural shape as
the weight-fetch path, in reverse (write instead of read) — a reasonable, the weight-fetch path, in reverse (write instead of read) — a reasonable,
bounded scope, and a real prerequisite, not optional polish. bounded scope, and a real prerequisite, not optional polish.
### 5.4 [Decided, RTL done] Widening the physical DDR3 channel: 32-bit single channel vs. a second independent 16-bit channel — **functionally complete (EXP-0084), real timing pending** ### 5.4 [DONE] Widening the physical DDR3 channel: 32-bit single channel vs. a second independent 16-bit channel — **functionally complete AND real timing closed (EXP-0084 → EXP-0086)**
**The real question**: §5.1 halved *waste* against a fixed 1.24 GB/s **The real question**: §5.1 halved *waste* against a fixed 1.24 GB/s
physical ceiling; it did not raise the ceiling itself. Getting past ~50% physical ceiling; it did not raise the ceiling itself. Getting past ~50%
@@ -452,7 +456,7 @@ in the same wizard session, since both require the wizard's own JEDEC/PLL
calculator — never hand-edited, per this project's own established calculator — never hand-edited, per this project's own established
discipline). **Done, 2026-09-20.** discipline). **Done, 2026-09-20.**
#### 5.4.1 Real outcome (EXP-0084): functionally complete, timing NOT yet closed — the width/clock-speed decoupling #### 5.4.1 Real outcome (EXP-0084 → EXP-0086): functionally complete AND timing closed — the width/clock-speed decoupling
**Confirmed correct, as predicted above**: the widened DQ/DQS/DM pins **Confirmed correct, as predicted above**: the widened DQ/DQS/DM pins
stayed within banks 34/35 (no bank 14/15 conflict for the DDR3 signals stayed within banks 34/35 (no bank 14/15 conflict for the DDR3 signals
@@ -483,17 +487,31 @@ levers, and this experiment shows they don't have to be exercised together.
Bandwidth = width × clock — 32-bit width **alone**, even at the OLD, Bandwidth = width × clock — 32-bit width **alone**, even at the OLD,
already-timing-proven 155.039MHz, already delivers the full intended 2× already-timing-proven 155.039MHz, already delivers the full intended 2×
bandwidth gain (1.24 → ~2.48 GB/s). The clock speedup was free upside *if* bandwidth gain (1.24 → ~2.48 GB/s). The clock speedup was free upside *if*
the compute datapath could absorb it; real measurement shows it currently the compute datapath could absorb it; real measurement showed it couldn't
can't, without a real, separate re-pipelining of that accumulation tree — without a real, separate re-pipelining of that accumulation tree — so it
out of scope for a channel-width task, not attempted here. was reverted rather than pursued, keeping the task scoped to channel width.
**Current real status**: RTL fully adapted and functionally verified (see **Real closure (EXP-0086)**: the user reverted Input Clock Period back to
`hardware/v2/logs/experiments.log` EXP-0084 for the complete story, 3225ps (keeping Data Width=32) via a second real MIG wizard session. This
including 5 real P&R/XDC bugs found and fixed). Real timing signoff for regeneration itself triggered a real, wholesale recurrence of the
this configuration is **pending** — next real step is reverting Input stale-import bug — Vivado silently re-imported the entire v3 RTL tree (9
Clock Period toward 3225ps (keeping Data Width=32) in one more real, files) plus the top XDC back to their pre-EXP-0084 state, reproducing the
user-gated MIG wizard session. The current real, trustworthy P&R signoff already-fixed VCCO/PROHIBIT failures until root-caused (via the actual P&R
remains EXP-0083 (16-bit, 155.039MHz, +0.073ns) until then. log, not guessed) and fixed again by re-establishing direct-reference
sources (see CLAUDE.md's stale-import lesson, now extended to cover this).
Once fixed, the real P&R closed cleanly: **WNS=+0.095707ns, WHS=+0.036275ns,
0 failing endpoints, "All user specified timing constraints are met."** The
`clk_pll_i`/155.039MHz domain — the exact domain that failed at -0.618ns in
the 2900ps attempt — closes at +0.096ns across 24522 endpoints, matching
EXP-0083's own 16-bit-era margin (+0.073ns) closely, confirming the width
change itself was never the real problem.
**Current real status**: fully DONE — RTL functionally verified (real xsim)
AND real P&R timing-closed (EXP-0086). This is now the project's current,
trustworthy signoff, replacing EXP-0083. `data_ready_n` (EXP-0085) also
confirmed placed and routed cleanly (D14/LVCMOS33) in this same run. See
`hardware/v2/logs/experiments.log` EXP-0084 and EXP-0086 for the complete
story, including all real P&R/XDC bugs found and fixed along the way.
### 5.5 Scaling path recommendation (real numbers, not a guess) — updated per user's final directive ### 5.5 Scaling path recommendation (real numbers, not a guess) — updated per user's final directive
@@ -515,16 +533,17 @@ specifically to document where/how it breaks rather than to succeed):
**DONE** (EXP-0081/0082). §5.3 remains a genuine blocker for N>2 and must **DONE** (EXP-0081/0082). §5.3 remains a genuine blocker for N>2 and must
land before any scaling test that needs real result data out of more than land before any scaling test that needs real result data out of more than
2 cores' worth of pins. 2 cores' worth of pins.
2. §5.4 (32-bit channel widening) — **decided**, user-gated on a real 2. §5.4 (32-bit channel widening) — **DONE** (EXP-0084 functional +
interactive MIG wizard session (Data Width + Input Clock Period changed EXP-0086 real timing closure, WNS=+0.096ns). Doubles the physical
together). Doubles the physical ceiling itself, which §5.1 alone could ceiling itself (real, closed, ~2.48 GB/s), which §5.1 alone could not do.
not do.
3. §5.2 (DDRManager) — phase 1 (single-slot look-ahead prefetch) is 3. §5.2 (DDRManager) — phase 1 (single-slot look-ahead prefetch) is
**DONE** (EXP-0083, real but modest ~2.9% benefit on the still-16-bit **DONE** (EXP-0083, real but modest ~2.9% benefit, measured on the OLD
channel). Re-measure this SAME real A/B once §5.4 lands, since a wider 16-bit channel). **Next real step**: re-measure this SAME real A/B
channel may change how much idle-channel time there is left to fill — against the now-closed 32-bit channel (EXP-0086), since a wider channel
only build the larger multi-slot scheduler version if that re-measurement may change how much idle-channel time there is left to fill — only
justifies it, not on the original (now-corrected) hypothesis alone. build the larger multi-slot scheduler version if that re-measurement
justifies it, not on the original (now-corrected) hypothesis alone. Not
yet done.
4. Real N=2/4/8/16 tests, each with its own real P&R signoff (margin is 4. Real N=2/4/8/16 tests, each with its own real P&R signoff (margin is
thin, §2 — do not assume a prior N's timing closure predicts the next). thin, §2 — do not assume a prior N's timing closure predicts the next).
N=8 is the real target configuration; N=16 is expected to expose real N=8 is the real target configuration; N=16 is expected to expose real
@@ -669,7 +688,7 @@ design task, not yet scoped, and out of this repository's own RTL scope
| Claim | Status | | Claim | Status |
|---|---| |---|---|
| WNS/WHS/LUT/DSP numbers throughout | **Measured** (real Vivado P&R reports, current: EXP-0083) | | WNS/WHS/LUT/DSP numbers throughout | **Measured** (real Vivado P&R reports, current: EXP-0086) |
| DDR3 back-to-back burst throughput (1.24 GB/s) | **Measured** (real `ddr3_model.sv` JEDEC trace) — physical channel limit, unchanged by §5.1's packing fix | | DDR3 back-to-back burst throughput (1.24 GB/s) | **Measured** (real `ddr3_model.sv` JEDEC trace) — physical channel limit, unchanged by §5.1's packing fix |
| Real Activate→Read latency (16.125 ns) | **Measured** (same trace) | | Real Activate→Read latency (16.125 ns) | **Measured** (same trace) |
| Per-core compute throughput (2.48 GMAC/s) | **Calculated** from measured Fmax (155.039MHz) + known, fixed DSP-packing factor | | Per-core compute throughput (2.48 GMAC/s) | **Calculated** from measured Fmax (155.039MHz) + known, fixed DSP-packing factor |
@@ -678,6 +697,6 @@ design task, not yet scoped, and out of this repository's own RTL scope
| "~25% of peak sustainable" (pre-packing) / "~50%" (post-packing, current) | **Calculated** ratios; post-packing figure re-verified against real P&R (EXP-0082) and real simulation (§5.1) | | "~25% of peak sustainable" (pre-packing) / "~50%" (post-packing, current) | **Calculated** ratios; post-packing figure re-verified against real P&R (EXP-0082) and real simulation (§5.1) |
| I/O bank/DQS pin counts for XC7A100T-CSG324 (banks 14/15/16/34/35) | **Measured** — queried directly from the real Vivado part database for this exact part/package, used in §5.4's dual-channel-vs-widening analysis | | I/O bank/DQS pin counts for XC7A100T-CSG324 (banks 14/15/16/34/35) | **Measured** — queried directly from the real Vivado part database for this exact part/package, used in §5.4's dual-channel-vs-widening analysis |
| Row-switch penalty as a fraction of real workloads | **Not measured** — depends on host-chosen memory layout, flagged as an open question, not asserted | | Row-switch penalty as a fraction of real workloads | **Not measured** — depends on host-chosen memory layout, flagged as an open question, not asserted |
| DDRManager phase-1 real stall-reduction benefit (2.86%) | **Measured** — real xsim A/B on `tb_n2_system_ddr3.v`, real DDR3 backend, before vs after `ddr_prefetch_mgr.v` (EXP-0083). Modest, not the larger figure the original hypothesis (§5.2) suggested before it was built. | | DDRManager phase-1 real stall-reduction benefit (2.86%) | **Measured** — real xsim A/B on `tb_n2_system_ddr3.v`, real DDR3 backend, before vs after `ddr_prefetch_mgr.v` (EXP-0083), against the OLD 16-bit channel. Modest, not the larger figure the original hypothesis (§5.2) suggested before it was built. Re-measurement against the closed 32-bit channel (EXP-0086) is a real next step, not yet done. |
| Full multi-slot DDRManager's real benefit | **Not measured** — not built; §5.2 recommends re-measuring phase 1 against the 32-bit-widened channel before deciding whether to build it | | Full multi-slot DDRManager's real benefit | **Not measured** — not built; §5.2 recommends re-measuring phase 1 against the 32-bit-widened channel before deciding whether to build it |
| 32-bit widening's real post-change bandwidth/timing numbers | **Not measured** — requires the user's own interactive MIG wizard session (§5.4); this document's ~2.48 GB/s figure is a doubling projection, not yet re-verified by real P&R/simulation | | 32-bit widening's real post-change bandwidth/timing numbers | **Measured** — real, closed P&R signoff (EXP-0086): WNS=+0.096ns, WHS=+0.036ns, 0 failing endpoints. The ~2.48 GB/s figure is now a real, closed-timing result, not a projection. |
+73 -64
View File
@@ -165,21 +165,24 @@ these from an ESP32 doing real JTAG bit-banging (TAP state machine, IR/DR
shifting) rather than a bench programmer — that firmware is separate, shifting) rather than a bench programmer — that firmware is separate,
software-side work, not covered here. software-side work, not covered here.
## 3. Real timing signoff (EXP-0083, the current, trustworthy number) ## 3. Real timing signoff (EXP-0086, the current, trustworthy number — real 32-bit DDR3 channel, CLOSED)
Real in-context Vivado place-and-route (not out-of-context, not estimated): Real in-context Vivado place-and-route (not out-of-context, not estimated):
| Metric | Value | | Metric | Value |
|---|---| |---|---|
| DDR3 PHY clock (sys_clk_i) | **310.078 MHz** (3.225ns period) | | DDR3 PHY clock (sys_clk_p/n) | **310.078 MHz** (3.225ns period) |
| Compute domain clock (ui_clk, PLL-derived 2:1 from sys_clk_i) | **155.039 MHz** | | Compute domain clock (ui_clk/clk_pll_i, PLL-derived 2:1 from sys_clk) | **155.039 MHz** |
| WNS (setup slack) | **+0.073 ns** — real, still thin but improved vs every prior real run. Re-verify with a fresh P&R after ANY further logic addition. | | WNS (setup slack) | **+0.095707 ns** — real, closed. Re-verify with a fresh P&R after ANY further logic addition. |
| WHS (hold slack) | +0.036 ns | | WHS (hold slack) | +0.036275 ns |
| Failing endpoints | 0 (setup), 0 (hold) | | Failing endpoints | 0 of 25172 (setup), 0 of 25169 (hold), 0 of 9505 (pulse width) |
| LUTs used | 5644 / 63400 (8.90%) | | LUTs used | 6382 / 63400 (10.07%) |
| DSP48E1 used | 16 / 240 (6.67%) — 8 per compute core × 2 cores, unchanged since EXP-0059 | | DSP48E1 used | 16 / 240 (6.67%) — 8 per compute core × 2 cores, unchanged since EXP-0059 |
| Bonded IOB used | 119 / 207 (57.49%) |
| Block RAM used | 0 | | Block RAM used | 0 |
| STARTUPE2 used | 1 / 1 (100%) — the config-flash bridge | | STARTUPE2 used | 1 / 1 (100%) — the config-flash bridge |
| DDR3 channel width | **32-bit real physical channel** (two MT41J128M16JT-125:K chips) — see §3.1 |
| Real physical bandwidth ceiling | **~2.48 GB/s** (2× EXP-0083's 16-bit ~1.24 GB/s, real, closed timing — not a projection) |
Signoff history (every real change, same target, in-context P&R): Signoff history (every real change, same target, in-context P&R):
@@ -190,47 +193,55 @@ Signoff history (every real change, same target, in-context P&R):
| 0078 (+flash bridge, STARTUPE2) | +0.013 | 5213 | 16 | | 0078 (+flash bridge, STARTUPE2) | +0.013 | 5213 | 16 |
| 0079 (+real activation engine) | +0.030 | 5379 | 16 | | 0079 (+real activation engine) | +0.030 | 5379 | 16 |
| 0082 (+denser activation packing) | +0.068 | 5437 | 16 | | 0082 (+denser activation packing) | +0.068 | 5437 | 16 |
| 0083 (+DDRManager phase 1, `ddr_prefetch_mgr.v`) | **+0.073** | 5644 | 16 | | 0083 (+DDRManager phase 1, `ddr_prefetch_mgr.v`, 16-bit) | +0.073 | 5644 | 16 |
| 0084 (32-bit DDR3 widening, 2900ps/172.414MHz) | **-0.618 (FAILS)** | 6418 | 16 | | 0084 (32-bit DDR3 widening, 2900ps/172.414MHz) | -0.618 (FAILED) | 6418 | 16 |
| 0086 (32-bit DDR3 widening, clock reverted to 3225ps/155.039MHz) | **+0.096 (CLOSED)** | 6382 | 16 |
### 3.1 EXP-0084: real 32-bit DDR3 widening — functionally verified, timing NOT yet closed ### 3.1 Real 32-bit DDR3 widening — functionally verified AND timing CLOSED (EXP-0084 → EXP-0086)
**Honest status, not glossed over**: the real 32-bit channel (two **Real, closed result**: the real 32-bit channel (two MT41J128M16JT-125:K
MT41J128M16JT-125:K chips) is **functionally complete and real-verified** chips) is functionally complete (real xsim against the real 2-chip DDR3
(real xsim against the real 2-chip DDR3 model: `tb_mig_native_adapter.v` model: `tb_mig_native_adapter.v` 12/12 PASS, `tb_n2_system_ddr3.v` 8/8
12/12 PASS, `tb_n2_system_ddr3.v` 8/8 PASS) — but its own real P&R does PASS) **and** its real P&R closes timing at the proven-safe
**not** close timing at the paired clock speedup (2900ps sys_clk / 3225ps/155.039MHz clock: **WNS = +0.095707ns, WHS = +0.036275ns, 0 failing
172.414MHz ui_clk): **WNS = -0.618ns, 213 failing endpoints**. Route endpoints** (EXP-0086). Route completed 100%, all user-specified timing
completed 100% (a real, physically valid placement/routing), only timing constraints met.
itself fails.
**Real root cause** (traced to the actual worst path): the violation is **How this was reached (the honest path, not a straight line)**: EXP-0084
inside `neural_processor_packed.v`'s own packed-MAC accumulation tree (a first tried the width change stacked with an ADDITIONAL clock speedup
DSP48E1 output through a 4-deep CARRY4 chain), **unchanged since EXP-0059** (2900ps/172.414MHz) in the same MIG wizard session — that combination
— it had real positive margin at the old 155.039MHz `ui_clk` (EXP-0083: failed timing (WNS=-0.618ns), traced to the real worst path inside
+0.073ns) but not at the new 172.414MHz (an 11.2% real frequency increase). `neural_processor_packed.v`'s own packed-MAC accumulation tree (a DSP48E1
This is **not caused by the 32-bit width change itself**every module output through a 4-deep CARRY4 chain, **unchanged since EXP-0059**)it
touched for the widening (adapter, arbiter, activation fetch, etc.) is had real margin at 155.039MHz but not at the faster 172.414MHz. This
real-verified functionally correct; the failure is in unrelated, pre- was **not caused by the 32-bit width change itself**; every module touched
existing compute logic that simply wasn't budgeted for a faster clock. for the widening was already real-verified functionally correct. The
real, honest insight: bandwidth = width × clock rate, and widening to 32
bits **alone**, even at the OLD 3225ps/155.039MHz, already delivers the
FULL intended 2× bandwidth gain — the clock speedup was a separate,
stackable optimization, and reverting *only* it (keeping Data Width=32)
restored the same real margin `neural_processor_packed.v` already had at
EXP-0083 (+0.073ns → +0.096ns here, the small difference being normal
P&R placement-seed variance, not a real effect of the width change).
**The real, honest decoupling this reveals**: bandwidth = width × clock **A second real bug surfaced on the way to this result (EXP-0086)**: the
rate. Widening to 32 bits **alone**, even at the OLD, already-closing user's second real MIG wizard regeneration (needed only to revert Clock
3225ps/155.039MHz, already delivers the FULL intended 2× bandwidth gain Period) triggered Vivado to silently re-import the ENTIRE v3 RTL source
(1.24 → ~2.48 GB/s physical ceiling) — the clock speedup to 2900ps was a tree (9 files) plus the top XDC back to stale, pre-EXP-0084 copies — a
separate, additional optimization stacked on top in the same wizard wholesale recurrence of the stale-import bug (see CLAUDE.md), not limited
session, and it is specifically *that* stacking, not the width, that to the single file touched by the regeneration. Root-caused via the
breaks timing. actual P&R log (parsing a `constrs_1/imports/...` path, and a real "IO
placement infeasible" failure reproducing EXP-0084's already-fixed VCCO
conflict) and fixed by re-establishing all 10 files as direct references
before re-running P&R.
**Current real, trustworthy signoff therefore remains EXP-0083** (16-bit, This section (§3, §3.1) and the signoff table above are now the current,
155.039MHz, +0.073ns) until a real P&R closes for the 32-bit configuration trustworthy real baseline — replacing the EXP-0083 16-bit-era pointer.
— expected once the Input Clock Period is reverted toward 3225ps (keeping See `hardware/v2/logs/experiments.log` EXP-0084 and EXP-0086 for the
Data Width=32), a real, user-gated MIG wizard step, not yet done. See complete story, including all real P&R/XDC bugs found and fixed along the
`hardware/v2/logs/experiments.log` EXP-0084 for the complete story, way (stale differential-clock ports, a real VCCO bank conflict, stale
including 5 real P&R/XDC bugs found and fixed along the way (stale imported XDC/RTL — twice, missing IOSTANDARDs, and two cosmetic but
differential-clock ports, a real VCCO bank conflict, a stale imported XDC, previously-silently-broken XDC property bugs).
missing IOSTANDARDs, and two cosmetic but previously-silently-broken XDC
property bugs).
## 4. Real DDR3 memory layout convention ## 4. Real DDR3 memory layout convention
@@ -238,7 +249,7 @@ Both weight data and activation data share the same DDR3 address space,
word-addressed. **EXP-0084**: the native word width is now **32 bits** word-addressed. **EXP-0084**: the native word width is now **32 bits**
(`BURST_LEN=8` per transaction = 256 bits/burst, up from 128 bits at the (`BURST_LEN=8` per transaction = 256 bits/burst, up from 128 bits at the
old 16-bit width) — this is the real, current RTL (`hardware/v3/rtl/`), old 16-bit width) — this is the real, current RTL (`hardware/v3/rtl/`),
functionally verified; see §3.1 for its pending timing-closure status. functionally verified and, as of EXP-0086, real timing-closed; see §3.1.
- **Weights**: one layer's weight set starts at word address `layer_index * - **Weights**: one layer's weight set starts at word address `layer_index *
WORDS_PER_LAYER` (`WORDS_PER_LAYER = LAYER_BYTES/2`). Densely packed — WORDS_PER_LAYER` (`WORDS_PER_LAYER = LAYER_BYTES/2`). Densely packed —
@@ -266,9 +277,9 @@ functionally verified; see §3.1 for its pending timing-closure status.
data-select mux is an explicit 4-way `case` on constant byte offsets data-select mux is an explicit 4-way `case` on constant byte offsets
(not a runtime-indexed part-select expression) — extending the exact (not a runtime-indexed part-select expression) — extending the exact
same discipline EXP-0081 established for the 1-bit case to 2 bits. same discipline EXP-0081 established for the 1-bit case to 2 bits.
This mux is real and functionally verified, but is unrelated to This mux is real and functionally verified; §3.1's earlier real timing
§3.1's own real timing failure (that violation is inside the compute failure (now closed, EXP-0086) was unrelated to this fetch path — it
core's accumulation tree, not this fetch path). was inside the compute core's own accumulation tree.
- Real measured effect: back-to-back same-row DDR3 throughput was a - Real measured effect: back-to-back same-row DDR3 throughput was a
fixed 1.24 GB/s at 16-bit width (measured, EXP-0080); at 32-bit width fixed 1.24 GB/s at 16-bit width (measured, EXP-0080); at 32-bit width
the real physical ceiling doubles to ~2.48 GB/s, independent of this the real physical ceiling doubles to ~2.48 GB/s, independent of this
@@ -359,24 +370,22 @@ when it actually fires, instead of polling every loop iteration.
- `sys_rst` has only a *tentative* real pin (G13, bank 15, EXP-0084 — - `sys_rst` has only a *tentative* real pin (G13, bank 15, EXP-0084 —
chosen just to unblock real P&R, not a final board decision) — assign chosen just to unblock real P&R, not a final board decision) — assign
its real, permanent location once the rest of the board layout (reset its real, permanent location once the rest of the board layout (reset
circuit, status LEDs, etc.) is decided. circuit, status LEDs, etc.) is decided. Confirmed real, routed, and
- **The 32-bit DDR3 channel's own real timing does NOT currently close** timing-clean at the closed EXP-0086 signoff.
(EXP-0084: WNS -0.618ns at 2900ps/172.414MHz) — the current real,
trustworthy signoff remains EXP-0083 (16-bit, 155.039MHz, +0.073ns) until
the Input Clock Period is reverted toward 3225ps (keeping Data Width=32)
in one more real, user-gated MIG wizard session. See §3.1 for the full
honest story and real root cause (unrelated to the width change itself).
- ESP32-side JTAG bit-banging firmware (§5.1) does not exist yet — it's - ESP32-side JTAG bit-banging firmware (§5.1) does not exist yet — it's
software work on the host side, not part of this FPGA RTL. software work on the host side, not part of this FPGA RTL.
- `ddr_prefetch_mgr.v` (DDRManager phase 1, EXP-0083) is real, built, and - `ddr_prefetch_mgr.v` (DDRManager phase 1, EXP-0083) is real, built, and
timing-verified (at the OLD 16-bit/155MHz signoff), but its measured timing-verified, but was only measured for benefit at the OLD 16-bit/
benefit (2.86% real reduction in total simulated time) is modest — see 155MHz signoff (2.86% real reduction in total simulated time, modest —
`docs/ARCHITECTURE_ANALYSIS.md` §5.2 for the honest number and why. The see `docs/ARCHITECTURE_ANALYSIS.md` §5.2). The larger multi-slot
larger multi-slot DDRManager is not built. Its own real benefit should be DDRManager is not built. Its own real benefit should now be re-measured
re-measured once the 32-bit channel's timing actually closes. against the closed 32-bit channel (EXP-0086) — not yet done.
- `data_ready_n` (EXP-0085, user-requested active-low sticky IRQ) is real, - `data_ready_n` (EXP-0085, user-requested active-low sticky IRQ) is real,
built, and functionally verified (49/49 including 10 new checks, built, functionally verified (49/49 including 10 new checks,
`tb_spi_host_bridge_v3.v`) — see §6. Its own real P&R verification is `tb_spi_host_bridge_v3.v`), AND real P&R-verified at the closed EXP-0086
deferred to the same next real P&R run already needed to close §3.1's signoff (D14/LVCMOS33, confirmed via a direct query on the routed
timing gap, not run separately against a config already known to fail checkpoint) — see §6.
timing for unrelated reasons. - Scaling past N=2 real core count and the result-writeback engine (both
listed above) remain the real next milestones now that the 32-bit
channel's own timing is closed — see
`hardware/v2/logs/experiments.log` EXP-0086's `next_action`.
+90
View File
@@ -5617,3 +5617,93 @@ the tight I/O/VCCO budget further. Document the real ESP32-side
GPIO/interrupt wiring implication once the board's own reset-circuit GPIO/interrupt wiring implication once the board's own reset-circuit
pin planning (S7, still open) is decided, since data_ready_n and pin planning (S7, still open) is decided, since data_ready_n and
sys_rst now share bank 15's own real, tentative pin choices. sys_rst now share bank 15's own real, tentative pin choices.
EXP-0086 -- real timing closure for the 32-bit DDR3 channel (clock
period revert), plus a real recurrence of the stale-import bug
(2026-09-20, continuation: user reverted Clock Period 2900->3225ps via
a second real MIG wizard session, keeping Data Width=32, per this
session's own EXP-0084 root-cause recommendation: "torna esattamente
nelle condizioni gia' testate")
CONTEXT: EXP-0084 left the 32-bit DDR3 channel functionally complete
but with real timing FAILING (WNS=-0.618ns) at the paired 2900ps/
172.414MHz ui_clk speedup. Root cause (EXP-0084) was decoupled from the
width change itself: the failing path was neural_processor_packed.v's
own packed-MAC accumulation tree, unchanged since EXP-0059, which had
real margin at the OLD 155.039MHz clock but not the new, faster one.
Recommendation given to the user: revert ONLY Clock Period back to the
already-proven-safe 3225ps, keep Data Width=32 (the width alone already
delivers the full 2x bandwidth target, independent of clock speed).
REAL BUG FOUND BEFORE THE REAL FIX COULD EVEN BE MEASURED: the first
re-run of the final P&R (Clock Period=3225ps, Data Width=32, all
EXP-0084 RTL/XDC fixes already committed) failed immediately with
"ERROR: [Place 30-58] IO placement is infeasible. Number of unplaced IO
Ports (41) is greater than number of available pins (10)" plus a
CRITICAL WARNING that PROHIBIT was again an invalid property. Both were
supposedly-already-fixed EXP-0084 bugs. Root-caused (not guessed) by
checking which XDC file Vivado actually parsed in the log
(".../constrs_1/imports/constraints/n2_system_ddr3_top.xdc" -- the
STALE IMPORTED COPY, not the live hardware/v3/ source) and then
querying the project's sources_1 fileset directly: the user's own
second real MIG wizard regeneration (the one that produced the
3225ps/DataWidth=32 mig_a.prj) had triggered Vivado to rescan and
RE-IMPORT THE ENTIRE v3 RTL SOURCE TREE, not just the XDC -- 9 RTL
files (host_mem_bridge.v, layer_prefetch_ctrl.v, layer_weight_buffer.v,
mig_native_adapter.v, neural_director_packed.v, neural_processor_
packed.v, sdram_arbiter_n.v, weight_tile_gather.v, mac2_dsp_packed.v)
plus n2_system_ddr3_top.xdc were all silently reset to stale copies
predating EXP-0084's fixes. This is the SAME class of bug CLAUDE.md
already documented for EXP-0078 (RTL) and EXP-0084 (XDC, single file)
-- but this is the first real confirmation that it can recur on ANY
MIG IP regeneration, wholesale, across the entire project, not just
once per file. Fixed via the same technique as before: remove_files +
add_files -norecurse to make each one a direct reference again
(/tmp/fix_all_stale_srcs.tcl for the 9 RTL files, /tmp/fix_stale_xdc2.tcl
for the XDC), verified via a real TCL query that zero non-IP-owned
*/imports/* paths remained in either fileset before re-running.
REAL RESULT (after the stale-source fix, real synth_design + opt_design
+ place_design + route_design, xc7a100tcsg324-2, in-context on
n2_system_ddr3_top): **WNS = +0.095707 ns, WHS = +0.036275 ns**, 0
failing endpoints out of 25172 (setup) / 25169 (hold) / 9505 (pulse
width). "All user specified timing constraints are met." The
clk_pll_i domain (155.039 MHz, 6.45ns period -- the exact domain that
failed at -0.618ns in EXP-0084's 2900ps attempt) closes at WNS=+0.096ns
across 24522 endpoints, confirming the EXP-0084 root-cause analysis:
the width change was never the problem, and reverting only the clock
period restores real margin on neural_processor_packed.v's own MAC
tree, matching EXP-0083's real 16-bit-era WNS (+0.073ns) closely (small
+0.023ns difference is normal P&R placement-seed variance, not a real
regression or improvement tied to the width change).
Real utilization (routed): 6382 LUTs (vs EXP-0083's 5644 -- +738,
+13%, expected: doubled DQ/DM/mask handling, 4-way tile-offset muxes in
act_tile_fetch.v, wider ctrl bus through ddr_prefetch_mgr.v), 7769
registers, 16 DSP48E1 (unchanged -- same core count), 119 Bonded IOB
used of 207 available (57.49%, real headroom remains). data_ready_n
(EXP-0085) confirmed placed at D14/LVCMOS33, sys_clk_p/n at N5/P5/
DIFF_SSTL15, clk_ref_p/n at T14/T15/LVDS_25 -- all real, routed,
verified via a direct open_checkpoint query on n2_system_ddr3_top_
routed.dcp, not assumed from the XDC alone.
DECISION: real, final signoff for the 32-bit DDR3 channel widening
(EXP-0083 DDRManager phase 1 through EXP-0086 this entry). This
REPLACES EXP-0083's 16-bit-era number as the project's current
trustworthy real P&R baseline. Bandwidth ceiling: real 32-bit width x
real closed 155.039MHz clk_pll_i domain = the full originally-targeted
~2.48GB/s (2x EXP-0083's 16-bit ~1.24GB/s), with real, closed timing,
not a projection.
next_action: (1) CLAUDE.md's stale-import lesson updated to note MIG
IP regeneration can re-trigger a wholesale project source rescan, not
just a single-file staleness risk -- check ALL filesets after ANY IP
regeneration, not just the files touched by that regeneration. (2)
Re-measure EXP-0083's DDRManager (ddr_prefetch_mgr.v) real benefit
against this now-closed wider channel, per the project's own established
sequencing ("re-measure once the wider channel's timing actually
closes"). (3) Build the result-writeback engine (ARCHITECTURE_ANALYSIS
§5.3, long-disclosed blocker for N>2 core scaling). (4) 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 ("senza illusioni ma analizzando la
situazione piu' performante").