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
+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,
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):
| Metric | Value |
|---|---|
| DDR3 PHY clock (sys_clk_i) | **310.078 MHz** (3.225ns period) |
| Compute domain clock (ui_clk, PLL-derived 2:1 from sys_clk_i) | **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. |
| WHS (hold slack) | +0.036 ns |
| Failing endpoints | 0 (setup), 0 (hold) |
| LUTs used | 5644 / 63400 (8.90%) |
| DDR3 PHY clock (sys_clk_p/n) | **310.078 MHz** (3.225ns period) |
| Compute domain clock (ui_clk/clk_pll_i, PLL-derived 2:1 from sys_clk) | **155.039 MHz** |
| WNS (setup slack) | **+0.095707 ns** — real, closed. Re-verify with a fresh P&R after ANY further logic addition. |
| WHS (hold slack) | +0.036275 ns |
| Failing endpoints | 0 of 25172 (setup), 0 of 25169 (hold), 0 of 9505 (pulse width) |
| LUTs used | 6382 / 63400 (10.07%) |
| 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 |
| 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):
@@ -190,47 +193,55 @@ Signoff history (every real change, same target, in-context P&R):
| 0078 (+flash bridge, STARTUPE2) | +0.013 | 5213 | 16 |
| 0079 (+real activation engine) | +0.030 | 5379 | 16 |
| 0082 (+denser activation packing) | +0.068 | 5437 | 16 |
| 0083 (+DDRManager phase 1, `ddr_prefetch_mgr.v`) | **+0.073** | 5644 | 16 |
| 0084 (32-bit DDR3 widening, 2900ps/172.414MHz) | **-0.618 (FAILS)** | 6418 | 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 (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
MT41J128M16JT-125:K chips) is **functionally complete and real-verified**
(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) — but its own real P&R does
**not** close timing at the paired clock speedup (2900ps sys_clk /
172.414MHz ui_clk): **WNS = -0.618ns, 213 failing endpoints**. Route
completed 100% (a real, physically valid placement/routing), only timing
itself fails.
**Real, closed result**: the real 32-bit channel (two MT41J128M16JT-125:K
chips) is functionally complete (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) **and** its real P&R closes timing at the proven-safe
3225ps/155.039MHz clock: **WNS = +0.095707ns, WHS = +0.036275ns, 0 failing
endpoints** (EXP-0086). Route completed 100%, all user-specified timing
constraints met.
**Real root cause** (traced to the actual worst path): the violation is
inside `neural_processor_packed.v`'s own packed-MAC accumulation tree (a
DSP48E1 output through a 4-deep CARRY4 chain), **unchanged since EXP-0059**
— it had real positive margin at the old 155.039MHz `ui_clk` (EXP-0083:
+0.073ns) but not at the new 172.414MHz (an 11.2% real frequency increase).
This is **not caused by the 32-bit width change itself**every module
touched for the widening (adapter, arbiter, activation fetch, etc.) is
real-verified functionally correct; the failure is in unrelated, pre-
existing compute logic that simply wasn't budgeted for a faster clock.
**How this was reached (the honest path, not a straight line)**: EXP-0084
first tried the width change stacked with an ADDITIONAL clock speedup
(2900ps/172.414MHz) in the same MIG wizard session — that combination
failed timing (WNS=-0.618ns), traced to the real worst path inside
`neural_processor_packed.v`'s own packed-MAC accumulation tree (a DSP48E1
output through a 4-deep CARRY4 chain, **unchanged since EXP-0059**)it
had real margin at 155.039MHz but not at the faster 172.414MHz. This
was **not caused by the 32-bit width change itself**; every module touched
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
rate. Widening to 32 bits **alone**, even at the OLD, already-closing
3225ps/155.039MHz, already delivers the FULL intended 2× bandwidth gain
(1.24 → ~2.48 GB/s physical ceiling) — the clock speedup to 2900ps was a
separate, additional optimization stacked on top in the same wizard
session, and it is specifically *that* stacking, not the width, that
breaks timing.
**A second real bug surfaced on the way to this result (EXP-0086)**: the
user's second real MIG wizard regeneration (needed only to revert Clock
Period) triggered Vivado to silently re-import the ENTIRE v3 RTL source
tree (9 files) plus the top XDC back to stale, pre-EXP-0084 copies — a
wholesale recurrence of the stale-import bug (see CLAUDE.md), not limited
to the single file touched by the regeneration. Root-caused via the
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,
155.039MHz, +0.073ns) until a real P&R closes for the 32-bit configuration
— expected once the Input Clock Period is reverted toward 3225ps (keeping
Data Width=32), a real, user-gated MIG wizard step, not yet done. See
`hardware/v2/logs/experiments.log` EXP-0084 for the complete story,
including 5 real P&R/XDC bugs found and fixed along the way (stale
differential-clock ports, a real VCCO bank conflict, a stale imported XDC,
missing IOSTANDARDs, and two cosmetic but previously-silently-broken XDC
property bugs).
This section (§3, §3.1) and the signoff table above are now the current,
trustworthy real baseline — replacing the EXP-0083 16-bit-era pointer.
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 (stale differential-clock ports, a real VCCO bank conflict, stale
imported XDC/RTL — twice, missing IOSTANDARDs, and two cosmetic but
previously-silently-broken XDC property bugs).
## 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**
(`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/`),
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 *
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
(not a runtime-indexed part-select expression) — extending the exact
same discipline EXP-0081 established for the 1-bit case to 2 bits.
This mux is real and functionally verified, but is unrelated to
§3.1's own real timing failure (that violation is inside the compute
core's accumulation tree, not this fetch path).
This mux is real and functionally verified; §3.1's earlier real timing
failure (now closed, EXP-0086) was unrelated to this fetch path — it
was inside the compute core's own accumulation tree.
- 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
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 —
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
circuit, status LEDs, etc.) is decided.
- **The 32-bit DDR3 channel's own real timing does NOT currently close**
(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).
circuit, status LEDs, etc.) is decided. Confirmed real, routed, and
timing-clean at the closed EXP-0086 signoff.
- 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.
- `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
benefit (2.86% real reduction in total simulated time) is modest — see
`docs/ARCHITECTURE_ANALYSIS.md` §5.2 for the honest number and why. The
larger multi-slot DDRManager is not built. Its own real benefit should be
re-measured once the 32-bit channel's timing actually closes.
timing-verified, but was only measured for benefit at the OLD 16-bit/
155MHz signoff (2.86% real reduction in total simulated time, modest —
see `docs/ARCHITECTURE_ANALYSIS.md` §5.2). The larger multi-slot
DDRManager is not built. Its own real benefit should now be re-measured
against the closed 32-bit channel (EXP-0086) — not yet done.
- `data_ready_n` (EXP-0085, user-requested active-low sticky IRQ) is real,
built, and functionally verified (49/49 including 10 new checks,
`tb_spi_host_bridge_v3.v`) — see §6. Its own real P&R verification is
deferred to the same next real P&R run already needed to close §3.1's
timing gap, not run separately against a config already known to fail
timing for unrelated reasons.
built, functionally verified (49/49 including 10 new checks,
`tb_spi_host_bridge_v3.v`), AND real P&R-verified at the closed EXP-0086
signoff (D14/LVCMOS33, confirmed via a direct query on the routed
checkpoint) — see §6.
- 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`.