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:
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user