Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0303aacb9f | ||
|
|
cc6cfe168e | ||
|
|
2ab2ff7769 | ||
|
|
f9b366d747 |
@@ -81,6 +81,23 @@ unmodified by v3, e.g. `layer_prefetch_ctrl.v`/`layer_weight_buffer.v`).
|
||||
Real fix: explicitly `set_property top <newmodule> [get_filesets
|
||||
sources_1]` *before* calling `synth_design -top ...` — the `-top`
|
||||
command-line flag alone wasn't sufficient this time.
|
||||
- **A top-level module's own default parameter value can silently NOT
|
||||
apply**, even with no `-generic` override on the `synth_design`
|
||||
command line, an empty real `GENERIC` property on the run, and no
|
||||
stale imported RTL copy (EXP-0097) — a real elaboration bound
|
||||
`N_GROUPS` to a value from a DIFFERENT, EARLIER `-generic` override
|
||||
used against the SAME top module in the SAME Vivado session/project
|
||||
(an N=8 sweep run before an N=16 run), despite an intervening
|
||||
`reset_run`. Most likely Vivado's own "Incremental synthesis
|
||||
strategy default" silently carrying forward a parameter binding.
|
||||
Real fix: always pass every `-generic` value EXPLICITLY on every
|
||||
real `synth_design` call for a parameterized top-level, never rely
|
||||
on "no override = the RTL's own default" once that module has EVER
|
||||
been synthesized with a different override earlier in the same
|
||||
project — and confirm the real elaborated value afterward (e.g. a
|
||||
post-synth DSP48E1/cell count check) before trusting anything
|
||||
downstream, don't assume the log's own "Parameter ... bound to"
|
||||
line will be checked in time otherwise.
|
||||
- **Testbench stimulus must use nonblocking assignment (`<=`), not blocking
|
||||
(`=`), when driving a DUT's inputs from a separate `always`/`initial`
|
||||
block.** Blocking assignment races the DUT's own `posedge`-triggered
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
# FPGA-Neural V3 — Architecture Analysis: Timing, Bottlenecks, and Recommended Interventions
|
||||
|
||||
Scope: the current, real, P&R-verified V3 design (`hardware/v3/`, branch
|
||||
`v3-artix7`), updated through EXP-0095/0096 (N=8 hybrid systolic, real,
|
||||
closed timing — the current, definitive real deployment target, chosen
|
||||
by the user after a real, measured N=4/8/16 timing curve). The current
|
||||
real, trustworthy *timing* signoff is now EXP-0095/0096
|
||||
(`n8_system_ddr3_top.v`, 32-bit DDR3, 155.039MHz, WNS 0.000ns, 8 real
|
||||
parallel PEs), replacing EXP-0088's own N=2 pointer (which itself
|
||||
replaced EXP-0086's, which replaced EXP-0083's 16-bit-era one). See §5.6
|
||||
for the full real story, including N=16's own real RTL (functionally
|
||||
verified but NOT timing-closed, kept as documented future work). Every
|
||||
`v3-artix7` for the physically-fabricated board; branch
|
||||
`n16-timing-closure` for the real, consolidated follow-up below),
|
||||
updated through EXP-0097/0098 (a real MAC-pipeline fix that closes
|
||||
timing for the WHOLE real family — N=2/N=8/N=16 — not just N=8). The
|
||||
board **currently in physical fabrication** uses N=8's own original,
|
||||
unmodified EXP-0095/0096 signoff (`n8_system_ddr3_top.v`, 32-bit DDR3,
|
||||
155.039MHz, WNS=0.000ns exact, 8 real parallel PEs). Separately, on an
|
||||
isolated real branch, EXP-0097/0098 found that one shared-core fix
|
||||
closes N=16 too (WNS=+0.269ns) and improves N=2 (+0.389ns) and N=8
|
||||
(+0.108ns) as well — see §5.6 for the full real story. Every
|
||||
number in this document is either
|
||||
directly measured (real simulation trace, real P&R report) or a calculation
|
||||
built from directly-measured building blocks — the two are labeled
|
||||
@@ -664,7 +665,7 @@ specifically to document where/how it breaks rather than to succeed):
|
||||
|
||||
---
|
||||
|
||||
### 5.6 [N=8 IS THE REAL, DEFINITIVE DEPLOYMENT TARGET — `n8_system_ddr3_top.v`, EXP-0089…0096] Hybrid systolic scaling: up to 4 groups × 4-PE weight-stationary chains
|
||||
### 5.6 [WHOLE FAMILY CLOSED — N=2/N=8/N=16, EXP-0089…0098] Hybrid systolic scaling: up to 4 groups × 4-PE weight-stationary chains
|
||||
|
||||
Captured from a 2026-09-20 brainstorming session as a purely exploratory
|
||||
idea; the same day, per the user's own explicit reprioritization, the real
|
||||
@@ -857,16 +858,79 @@ lanes exercised exactly once): **real functional xsim, 16/16 PASS, 0
|
||||
errors** — closes the real, disclosed functional-verification gap
|
||||
this section previously flagged for N=8 specifically.
|
||||
|
||||
**Current real status: N=8 (`n8_system_ddr3_top.v`) is BOTH
|
||||
functionally verified AND timing-closed under its own permanent real
|
||||
name — the definitive deployment target.** N=2 (`n2_system_ddr3_top.v`,
|
||||
EXP-0088) remains documented as a real, valid, simpler fallback. N=16
|
||||
(`n16_system_ddr3_top.v`) remains real, functionally-verified RTL, not
|
||||
abandoned, just not timing-closed and not the current target. Real,
|
||||
disclosed caveat carried forward: N=8's WNS=0.000ns is an exact-zero
|
||||
margin — any future RTL change touching this top-level or its
|
||||
dependents needs a fresh real P&R (same `Explore`/`ExtraNetDelay_high`/
|
||||
`AggressiveExplore` directive stack) before trusting timing again.
|
||||
**Current real status (physical board): N=8 (`n8_system_ddr3_top.v`)
|
||||
is BOTH functionally verified AND timing-closed under its own
|
||||
permanent real name — the configuration currently in physical
|
||||
fabrication.** N=2 (`n2_system_ddr3_top.v`, EXP-0088) remains
|
||||
documented as a real, valid, simpler fallback. Real, disclosed caveat:
|
||||
N=8's original WNS=0.000ns is an exact-zero margin — any future RTL
|
||||
change touching this top-level or its dependents needs a fresh real
|
||||
P&R before trusting timing again. **This exact-zero margin is what
|
||||
motivated EXP-0097/0098 below.**
|
||||
|
||||
#### 5.6.1 [DONE, EXP-0097/0098] The whole family closes: a real MAC-pipeline fix, branch `n16-timing-closure`
|
||||
|
||||
**Context**: N=16's own real, traced remaining bottleneck (after the
|
||||
hierarchical arbiter + P&R directive tuning already closed most of the
|
||||
gap, WNS −0.913ns → −0.338ns, EXP-0094) was inside `neural_processor_
|
||||
packed.v`'s own DSP48E1 MAC datapath — a pre-existing, N=2-era design
|
||||
(unchanged since EXP-0059) with an already razor-thin real margin
|
||||
(+0.099962ns) that N=16's own higher real die congestion eroded past
|
||||
zero, and that N=8's own real signoff (§5.6, EXP-0095/0096) closed at
|
||||
an exact zero with no slack to spare.
|
||||
|
||||
**Real root cause** (traced to the exact register-to-register hop, not
|
||||
guessed): the critical path ran from a DSP48E1's own (Vivado-auto-
|
||||
retimed) product register, straight through the real carry-heavy
|
||||
INT8-unpack logic (`pb_comb`'s own shift + conditional +1
|
||||
carry-propagate add, CARRY4-dominated), into `proda1`/`prodb1` in a
|
||||
SINGLE real cycle.
|
||||
|
||||
**Real fix**: split the original single pipeline stage into two real
|
||||
stages — Stage 1a registers the RAW DSP48E1 product with zero logic in
|
||||
between (a real, explicit register boundary immediately after the
|
||||
multiply); Stage 1b does the carry-heavy unpack FROM the already-
|
||||
registered value (unchanged real math, now one real cycle later). Real,
|
||||
deliberate consequence: end-to-end per-tile latency grows by exactly
|
||||
ONE real clock cycle; throughput is unaffected (real valid/ready
|
||||
handshaking throughout, no fixed-latency assumption anywhere
|
||||
downstream).
|
||||
|
||||
**Real bug found and fixed in the testbench first** (not an RTL bug):
|
||||
`tb_neural_processor_packed.v`'s own comparison logic required all
|
||||
three cores (2 real reference `neural_processor.v` instances + the
|
||||
DUT) to assert `result_valid` simultaneously — correct only when all
|
||||
three share the exact same real pipeline depth, no longer true once
|
||||
the DUT is deliberately one real cycle deeper. Fixed by latching each
|
||||
core's own result independently the cycle its own `result_valid`
|
||||
first pulses, then comparing the three LATCHED values.
|
||||
|
||||
**Real, consolidated result — EVERY real top-level this project has,
|
||||
same shared pipelined core, real functional xsim + real, full P&R**:
|
||||
|
||||
| Target | Real WNS | Real DSP48E1 | Real functional test |
|
||||
|---|---|---|---|
|
||||
| N=2 (`n2_system_ddr3_top.v`) | +0.389ns (up from +0.099962ns) | 16 (6.67%) | 8/8 PASS |
|
||||
| N=8 (`n8_system_ddr3_top.v`) | +0.108ns (up from 0.000ns exact) | 64 (26.7%) | 16/16 PASS |
|
||||
| N=16 (`n16_system_ddr3_top.v`) | **+0.269ns** (up from −0.338ns FAILED) | 128 (53.3%) | 32/32 PASS |
|
||||
|
||||
A real margin-hunt attempt for even more N=16 headroom (alternate P&R
|
||||
directives) gave a WORSE real result (+0.168ns vs. the original
|
||||
+0.269ns) — confirms real P&R run-to-run variance, not a further
|
||||
systematic gain available without touching more RTL (the new N=16
|
||||
worst path, after this fix, moved to `neural_director_grouped.v`'s own
|
||||
queue update logic, still real route-dominated — not pursued further,
|
||||
current margin already comfortable).
|
||||
|
||||
**Decision**: this fix is a pure, unconditional real improvement
|
||||
across the whole real family — no regression anywhere, real margin
|
||||
improves at every N, N=16 goes from real FAILING to real CLOSED. It is
|
||||
**not yet applied to the physically fabricated board** (which uses the
|
||||
original, unmodified N=8 core) — adopting it (and/or moving to N=16)
|
||||
for a future board revision is a real, open hardware decision, not an
|
||||
RTL blocker. A real Vivado incremental-synthesis quirk was also found
|
||||
and fixed along the way (a parameter binding silently carried forward
|
||||
from an earlier `-generic` run despite `reset_run` — see CLAUDE.md).
|
||||
|
||||
**The problem it targets**: plain N=16 independent cores (§5.5's own
|
||||
"documentary, expected to break" framing) means 16 independent DDR3
|
||||
|
||||
@@ -165,25 +165,39 @@ 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-0095/0096, the current, definitive target — N=8 hybrid systolic, `n8_system_ddr3_top.v`, CLOSED)
|
||||
## 3. Real timing signoff — N=8 in physical fabrication; N=2/N=8/N=16 all real, closed on branch `n16-timing-closure` (EXP-0095…0098)
|
||||
|
||||
**This is the real, definitive deployment target as of 2026-09-21**
|
||||
(the user's own explicit decision, after a real, measured N=4/8/16
|
||||
timing curve found N=8 to be the largest real configuration that
|
||||
closes timing cleanly — see `docs/ARCHITECTURE_ANALYSIS.md` §5.6 for
|
||||
the full real story). Top-level RTL: `hardware/v3/rtl/
|
||||
n8_system_ddr3_top.v` — 2 groups × 4 PEs, shared-weight-broadcast
|
||||
systolic architecture (`systolic_group.v`/`packed_pe.v`,
|
||||
`neural_director_grouped.v`, `sdram_arbiter_hier.v`'s own real
|
||||
hierarchical 2-level arbiter), same real DDR3/SPI/flash bridge as
|
||||
every other real V3 top-level. Real in-context Vivado place-and-route
|
||||
(not out-of-context, not estimated):
|
||||
**Real, current, two-track status (2026-09-22)**: the **physical board
|
||||
currently being fabricated** uses **N=8** (`hardware/v3/rtl/
|
||||
n8_system_ddr3_top.v`, unmodified, EXP-0095/0096 signoff below) — this
|
||||
was the real, definitive decision made on 2026-09-21 after a real,
|
||||
measured N=4/8/16 timing curve found N=8 the largest configuration
|
||||
that closed cleanly at the time. Separately, on an isolated real
|
||||
development branch (`n16-timing-closure`, does NOT affect the board
|
||||
already in fabrication), a real fix (EXP-0097 — an extra real pipeline
|
||||
stage in `neural_processor_packed.v`'s own MAC datapath, see
|
||||
`docs/ARCHITECTURE_ANALYSIS.md` §5.6) has since **closed real timing
|
||||
for N=16 too, and improved N=2's and N=8's own margins** — the whole
|
||||
real product family (N=2/N=8/N=16) now closes with positive margin
|
||||
under that fix. Whether/when to adopt this fix for a future board
|
||||
revision (potentially N=16) is a real, open hardware decision, not an
|
||||
RTL blocker — see `docs/ARCHITECTURE_ANALYSIS.md` §5.6 for the full
|
||||
story.
|
||||
|
||||
### 3.0 N=8, in physical fabrication (`n8_system_ddr3_top.v`, EXP-0095/0096, unmodified core)
|
||||
|
||||
Top-level RTL: `hardware/v3/rtl/n8_system_ddr3_top.v` — 2 groups × 4
|
||||
PEs, shared-weight-broadcast systolic architecture
|
||||
(`systolic_group.v`/`packed_pe.v`, `neural_director_grouped.v`,
|
||||
`sdram_arbiter_hier.v`'s own real hierarchical 2-level arbiter), same
|
||||
real DDR3/SPI/flash bridge as every other real V3 top-level. Real
|
||||
in-context Vivado place-and-route (not out-of-context, not estimated):
|
||||
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| 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.000 ns** — real, closed, 0 failing setup endpoints (EXP-0095/0096). Re-verify with a fresh P&R after ANY further logic addition — this margin is exactly zero, no slack to spare. |
|
||||
| WNS (setup slack) | **0.000 ns** — real, closed, 0 failing setup endpoints (EXP-0095/0096). This is the exact RTL physically being fabricated. |
|
||||
| WHS (hold slack) | +0.017 ns |
|
||||
| Failing endpoints | 0 of 63212 (setup), 0 of 63209 (hold) |
|
||||
| LUTs used | 12535 / 63400 (19.77%) |
|
||||
@@ -200,20 +214,31 @@ real, closed, but with zero slack. Re-run a real P&R (same
|
||||
ExtraNetDelay_high` / `phys_opt_design -directive AggressiveExplore` /
|
||||
`route_design -directive AggressiveExplore` directive stack used to
|
||||
reach this result, EXP-0094/0095) after ANY further RTL change before
|
||||
trusting timing again — do not assume it still holds.
|
||||
trusting timing again — do not assume it still holds. This zero-margin
|
||||
result is exactly why EXP-0097's own pipeline fix (below) was pursued.
|
||||
|
||||
**Superseded, real predecessor signoff (N=2, flat architecture,
|
||||
`n2_system_ddr3_top.v`, EXP-0088)** — kept below for reference; still
|
||||
a real, valid, simpler fallback target if N=8's zero-margin closure
|
||||
ever proves too fragile in practice:
|
||||
### 3.0.1 The real, consolidated family — EXP-0097/0098, branch `n16-timing-closure` (not yet on the fabricated board)
|
||||
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| WNS (setup slack) | +0.099962 ns |
|
||||
| WHS (hold slack) | +0.036275 ns |
|
||||
| LUTs used | 6642 / 63400 (10.48%) |
|
||||
| DSP48E1 used | 16 / 240 (6.67%) — 8 per compute core × 2 cores |
|
||||
| Real parallelism | 2 real parallel processing elements |
|
||||
A single real fix (`neural_processor_packed.v`, an extra real pipeline
|
||||
stage in the packed-MAC datapath — full real root-cause story in
|
||||
`docs/ARCHITECTURE_ANALYSIS.md` §5.6) applies identically to every
|
||||
real top-level in the family (it's the SAME shared compute core). Real
|
||||
functional xsim + real, full P&R, every real configuration this
|
||||
project has a dedicated top-level for:
|
||||
|
||||
| Target | Top-level | Real WNS | Real DSP48E1 | Real functional test |
|
||||
|---|---|---|---|---|
|
||||
| N=2 | `n2_system_ddr3_top.v` | **+0.389 ns** (up from +0.100ns) | 16/240 (6.67%) | 8/8 PASS |
|
||||
| N=8 | `n8_system_ddr3_top.v` | **+0.108 ns** (up from 0.000ns exact) | 64/240 (26.7%) | 16/16 PASS |
|
||||
| N=16 | `n16_system_ddr3_top.v` | **+0.269 ns** (up from −0.338ns FAILED) | 128/240 (53.3%) | 32/32 PASS |
|
||||
|
||||
**Real, honest takeaway**: this fix is a pure, unconditional
|
||||
improvement across the whole real family — no regression found
|
||||
anywhere, real margin improves at every N, and N=16 goes from real
|
||||
FAILING to real CLOSED. It is not yet applied to the physically
|
||||
fabricated board (which uses the original, unmodified N=8 core,
|
||||
§3.0) — that board's own real signoff (WNS=0.000ns) remains accurate
|
||||
for the hardware actually being built.
|
||||
|
||||
Signoff history (every real change, in-context P&R):
|
||||
|
||||
@@ -230,7 +255,10 @@ Signoff history (every real change, in-context P&R):
|
||||
| 0088 (+result-writeback engine, `result_writeback.v`) | N=2 | +0.100 (CLOSED) | 6642 | 16 |
|
||||
| 0093 (hybrid systolic, flat 21-way arbiter) | N=16 | -0.913 (FAILED) | 19751 | 128 |
|
||||
| 0094 (+hierarchical 2-level arbiter, +P&R directive tuning) | N=16 | -0.338 (still FAILED) | 19936 | 128 |
|
||||
| 0095/0096 (`n8_system_ddr3_top.v`, hybrid systolic) | **N=8** | **0.000 (CLOSED)** | 12535 | 64 |
|
||||
| 0095/0096 (`n8_system_ddr3_top.v`, hybrid systolic — **in physical fabrication**) | **N=8** | **0.000 (CLOSED)** | 12535 | 64 |
|
||||
| 0097/0098 (+MAC pipeline fix, branch `n16-timing-closure`) | N=2 | +0.389 (CLOSED) | 6645 | 16 |
|
||||
| 0097/0098 (+MAC pipeline fix, branch `n16-timing-closure`) | N=8 | +0.108 (CLOSED) | 12536 | 64 |
|
||||
| 0097/0098 (+MAC pipeline fix, branch `n16-timing-closure`) | **N=16** | **+0.269 (CLOSED)** | 19903 | 128 |
|
||||
|
||||
### 3.1 Real 32-bit DDR3 widening — functionally verified AND timing CLOSED (EXP-0084 → EXP-0086)
|
||||
|
||||
@@ -397,17 +425,17 @@ when it actually fires, instead of polling every loop iteration.
|
||||
|
||||
## 7. Known-open items (honestly disclosed, not hidden)
|
||||
|
||||
- Scaling past N=2 compute cores: **RESOLVED, real N=8 is now the
|
||||
definitive deployment target** (§3, EXP-0095/0096, `n8_system_ddr3_
|
||||
top.v`, a real hybrid systolic shared-weight-broadcast architecture,
|
||||
2 groups × 4 PEs, WNS=0.000ns CLOSED). Real N=4/8/16 P&R curve
|
||||
measured: N=4 −0.005ns (2 failing endpoints, essentially at the
|
||||
edge), N=8 **0.000ns (CLOSED)**, N=16 −0.338ns (still failing even
|
||||
after a real hierarchical-arbiter fix + P&R directive tuning, EXP-
|
||||
0093/0094) — see `docs/ARCHITECTURE_ANALYSIS.md` §5.6 for the full
|
||||
real story. N=16 remains real, functionally verified RTL
|
||||
(`n16_system_ddr3_top.v`), just not yet timing-closed; not the
|
||||
current real deployment target.
|
||||
- Scaling past N=2 compute cores: **RESOLVED, N=8 is the configuration
|
||||
in physical fabrication** (§3.0, EXP-0095/0096, `n8_system_ddr3_
|
||||
top.v`, WNS=0.000ns CLOSED). **UPDATE (EXP-0097/0098)**: a real MAC-
|
||||
pipeline fix, developed on an isolated branch (`n16-timing-closure`,
|
||||
does not affect the board in fabrication), closes real timing for
|
||||
N=16 too (WNS=+0.269ns) and improves N=2's and N=8's own margins
|
||||
(+0.389ns, +0.108ns respectively) — see §3.0.1 and
|
||||
`docs/ARCHITECTURE_ANALYSIS.md` §5.6 for the full real story. N=16
|
||||
is no longer blocked by a real timing problem; whether to adopt it
|
||||
for a future board revision is now a real hardware decision, not an
|
||||
RTL one.
|
||||
- `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
|
||||
|
||||
+72
-19
@@ -6,12 +6,19 @@
|
||||
Il design realizzato in FPGA \`e un acceleratore per reti neurali
|
||||
quantizzate INT8, organizzato come un insieme di \textbf{elementi di
|
||||
elaborazione} (Processing Element, PE) paralleli che condividono un
|
||||
unico canale DDR3 reale. La configurazione attualmente definitiva
|
||||
(Capitolo~\ref{chap:tests}) \`e \textbf{N=8}: 8 PE reali, organizzati
|
||||
in \textbf{2 gruppi da 4 PE ciascuno}, secondo un'architettura
|
||||
sistolica a \emph{broadcast dei pesi condiviso} (\emph{shared-weight
|
||||
broadcast}). Il modulo top-level reale \`e
|
||||
\texttt{hardware/v3/rtl/n8\_system\_ddr3\_top.v}.
|
||||
unico canale DDR3 reale, secondo un'architettura sistolica a
|
||||
\emph{broadcast dei pesi condiviso} (\emph{shared-weight broadcast}).
|
||||
|
||||
\textbf{Stato reale a due configurazioni (Capitolo~\ref{chap:tests})}:
|
||||
\textbf{N=8} (\texttt{n8\_system\_ddr3\_top.v}, 2 gruppi da 4 PE) \`e
|
||||
la configurazione attualmente in fabbricazione fisica sulla scheda
|
||||
reale. \textbf{N=16} (\texttt{n16\_system\_ddr3\_top.v}, 4 gruppi da 4
|
||||
PE) \`e ora, a seguito di una correzione reale del datapath MAC
|
||||
(\S\ref{sec:mac-pipeline}), \textbf{funzionalmente verificata E con
|
||||
timing reale chiuso} su un branch di sviluppo dedicato
|
||||
(\texttt{n16-timing-closure}), non ancora promossa alla scheda fisica
|
||||
in produzione --- una decisione hardware reale, non RTL, ancora da
|
||||
prendere.
|
||||
|
||||
L'intero design gira in un unico dominio di clock reale a
|
||||
\textbf{155.039\,MHz} (\texttt{ui\_clk}, derivato dal MIG DDR3,
|
||||
@@ -38,9 +45,45 @@ Al clock reale di 155.039\,MHz, il picco teorico per singolo PE \`e:
|
||||
|
||||
Questo nucleo \`e rimasto \emph{invariato} (stesso numero di DSP,
|
||||
stessa struttura) dalla primissima sintesi reale del progetto fino
|
||||
alla configurazione N=8 attuale: \`e la parte pi\`u stabile ed efficiente
|
||||
del design, e non \`e mai stato il collo di bottiglia delle prestazioni
|
||||
(si veda \S\ref{sec:bottleneck}).
|
||||
alla configurazione N=8: \`e la parte pi\`u stabile ed efficiente
|
||||
del design. A N=16 (\S\ref{sec:mac-pipeline}) \`e per\`o diventato,
|
||||
per la prima volta, il vero collo di bottiglia di temporizzazione ---
|
||||
non del throughput di calcolo (\S\ref{sec:bottleneck}), ma della
|
||||
temporizzazione fisica del place-and-route.
|
||||
|
||||
\subsection{Correzione reale: pipeline aggiuntiva nel datapath MAC (per N=16)}
|
||||
\label{sec:mac-pipeline}
|
||||
|
||||
A N=16, il margine di temporizzazione reale --- gi\`a estremamente
|
||||
sottile a N=2 (+0.0999962\,ns) --- \`e stato eroso oltre lo zero dalla
|
||||
maggiore congestione fisica complessiva del die (WNS reale misurato:
|
||||
$-0.338$\,ns, dopo un arbitro gerarchico e un tuning delle direttive
|
||||
di place-and-route, Capitolo~\ref{chap:tests}). Il percorso critico
|
||||
reale, tracciato (non ipotizzato) fino al livello del singolo
|
||||
registro, va dall'uscita del moltiplicatore DSP48E1 (gi\`a
|
||||
ri-temporizzata automaticamente da Vivado) attraverso la logica di
|
||||
``spacchettamento'' dei due prodotti INT8 impacchettati (uno shift e
|
||||
una somma di riporto condizionale, dominata da primitive CARRY4) fino
|
||||
al registro che cattura il risultato --- tutto in un solo ciclo di
|
||||
clock.
|
||||
|
||||
\textbf{Correzione reale applicata}: lo stadio originale \`e stato
|
||||
spezzato in due stadi di pipeline reali distinti --- il primo registra
|
||||
il prodotto grezzo del DSP48E1 senza alcuna logica intermedia; il
|
||||
secondo esegue lo spacchettamento (matematica identica, invariata bit
|
||||
per bit) a partire dal valore gi\`a registrato. Il costo reale: un solo
|
||||
ciclo di clock aggiuntivo di latenza per operazione, senza alcun
|
||||
impatto sul throughput (l'interfaccia a maniglia valid/ready non
|
||||
assume mai una latenza fissa). Verificato bit-esatto in isolamento
|
||||
(18/18 PASS contro 2 core reali di riferimento) e funzionalmente
|
||||
sull'intero sistema N=16 su DDR3 reale (32/32 PASS).
|
||||
|
||||
\textbf{Risultato reale}: con questa sola correzione, il timing di
|
||||
N=16 \textbf{chiude realmente} (WNS $=+0.269$\,ns, WHS $=+0.026$\,ns,
|
||||
0 endpoint falliti) --- si veda il Capitolo~\ref{chap:tests} per i
|
||||
numeri completi. La stessa correzione, applicata anche a N=8, migliora
|
||||
pure il suo margine (da 0.000\,ns esatto a +0.108\,ns) senza alcuna
|
||||
regressione funzionale in nessuna delle due configurazioni.
|
||||
|
||||
\section{L'architettura sistolica a gruppi (N=8)}
|
||||
|
||||
@@ -180,20 +223,30 @@ margine di scalabilit\`a residuo del chip \`e ampio sul lato
|
||||
computazionale, ma \`e vincolato dal canale di memoria condiviso, non
|
||||
dalla logica di calcolo.
|
||||
|
||||
\section{Configurazioni alternative esplorate}
|
||||
\section{Configurazioni esplorate e stato reale attuale}
|
||||
|
||||
Lo stesso RTL sistolico, tramite il parametro reale \texttt{N\_GROUPS},
|
||||
\`e stato realmente sintetizzato e verificato anche a:
|
||||
\`e stato realmente sintetizzato e verificato a pi\`u configurazioni:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{N=4} (1 gruppo, 4 PE, 32 DSP48E1) --- funzionante,
|
||||
margine di timing quasi nullo.
|
||||
\item \textbf{N=16} (4 gruppi, 16 PE, 128 DSP48E1) --- funzionalmente
|
||||
verificato su DDR3 reale, ma con temporizzazione non ancora chiusa
|
||||
(si veda \S~\ref{sec:pnr-history}) --- mantenuto come lavoro futuro
|
||||
documentato, non abbandonato.
|
||||
margine di timing quasi nullo (non ancora rifinito con la
|
||||
correzione di \S\ref{sec:mac-pipeline}).
|
||||
\item \textbf{N=8} (\texttt{n8\_system\_ddr3\_top.v}) --- \textbf{la
|
||||
configurazione fisicamente in fabbricazione sulla scheda reale
|
||||
attuale.} Timing chiuso, WNS $=+0.108$\,ns con la correzione MAC.
|
||||
\item \textbf{N=16} (\texttt{n16\_system\_ddr3\_top.v}) --- dopo la
|
||||
correzione di \S\ref{sec:mac-pipeline}, \textbf{funzionalmente
|
||||
verificato E con timing reale chiuso} (WNS $=+0.269$\,ns), su un
|
||||
branch di sviluppo reale (\texttt{n16-timing-closure}) separato
|
||||
dalla scheda in produzione. \`E ora un candidato reale, verificato,
|
||||
per una futura revisione della scheda --- non ancora promosso alla
|
||||
produzione fisica corrente, una decisione hardware reale ancora da
|
||||
prendere con l'utente.
|
||||
\end{itemize}
|
||||
|
||||
N=8 \`e stata scelta come configurazione reale definitiva perch\'e
|
||||
\`e la pi\`u grande che chiude realmente il timing con margine
|
||||
positivo (Capitolo~\ref{chap:tests}).
|
||||
N=8 resta, ad oggi, la configurazione realmente fabbricata. N=16 non
|
||||
\`e pi\`u un limite architetturale reale (come inizialmente sembrava),
|
||||
ma una reale, verificata alternativa a parallelismo doppio, la cui
|
||||
adozione fisica dipende ora da una scelta dell'utente, non da un
|
||||
vincolo tecnico residuo.
|
||||
|
||||
+73
-53
@@ -106,12 +106,17 @@ shared-weight-broadcast systolic architecture), reducing redundant
|
||||
DDR3 weight traffic without the complexity of a literal PE-to-PE
|
||||
systolic pipeline.
|
||||
|
||||
N8 is the current, definitive, real deployment configuration of the
|
||||
FPGA-Neural family: it is the largest core count in the family that
|
||||
closes real static timing on the target part with a real, verified
|
||||
margin. Smaller (N=2, N=4) and larger (N=16) configurations of the
|
||||
same RTL exist and are documented (see \S\ref{sec:variants}), but N8
|
||||
is the one recommended for real board fabrication.
|
||||
N8 is the configuration currently in real physical board fabrication.
|
||||
A real MAC-datapath pipeline fix (developed on the
|
||||
\texttt{n16-timing-closure} branch, isolated from the board already in
|
||||
production) has since ALSO closed real static timing for \textbf{N16}
|
||||
with a comfortable real margin (WNS=+0.269\,ns) and improved N8's own
|
||||
margin further (WNS=+0.108\,ns, up from the original exact-zero
|
||||
0.000\,ns) -- both configurations are now real, functionally verified,
|
||||
timing-closed options (see \S\ref{sec:variants}). N8 remains the
|
||||
configuration in current physical production; N16 is a real, verified
|
||||
candidate for a future board revision, pending the user's own hardware
|
||||
decision.
|
||||
|
||||
\clearpage
|
||||
|
||||
@@ -132,14 +137,14 @@ for reference.
|
||||
\toprule
|
||||
\textbf{Variant} & \textbf{PEs} & \textbf{DSP48E1} & \textbf{Real WNS} & \textbf{Status} & \textbf{Top-level file} \\
|
||||
\midrule
|
||||
N2 & 2 & 16 & +0.100\,ns & \textbf{Closed, deployable fallback} & \texttt{n2\_system\_ddr3\_top.v} \\
|
||||
N4 & 4 & 32 & $-0.005$\,ns & Nearly closed (2 failing endpoints) & \emph{(generic override only)} \\
|
||||
N2 & 2 & 16 & +0.100\,ns & Closed, deployable fallback & \texttt{n2\_system\_ddr3\_top.v} \\
|
||||
N4 & 4 & 32 & $-0.005$\,ns & Nearly closed (2 failing endpoints), pre-fix & \emph{(generic override only)} \\
|
||||
\rowcolor{dslight}
|
||||
N8 & 8 & 64 & \textbf{0.000\,ns} & \textbf{RECOMMENDED --- closed} & \texttt{n8\_system\_ddr3\_top.v} \\
|
||||
N16 & 16 & 128 & $-0.338$\,ns & Functional, timing not closed & \texttt{n16\_system\_ddr3\_top.v} \\
|
||||
N8 & 8 & 64 & \textbf{+0.108\,ns} & \textbf{IN PHYSICAL PRODUCTION --- closed} & \texttt{n8\_system\_ddr3\_top.v} \\
|
||||
N16 & 16 & 128 & \textbf{+0.269\,ns} & \textbf{Closed, real candidate for next revision} & \texttt{n16\_system\_ddr3\_top.v} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Real family configurations. All share identical DDR3/SPI/flash physical connections and identical host protocol.}
|
||||
\caption{Real family configurations, post MAC-pipeline fix (\texttt{n16-timing-closure} branch) for N8/N16. All share identical DDR3/SPI/flash physical connections and identical host protocol.}
|
||||
\end{table}
|
||||
|
||||
%======================================================================
|
||||
@@ -322,46 +327,51 @@ CFGBVS & P8 & & & \\
|
||||
\clearpage
|
||||
\section{Timing Characteristics}
|
||||
|
||||
Real, in-context Vivado place-and-route on XC7A100T-CSG324-2, N8
|
||||
configuration (\texttt{n8\_system\_ddr3\_top.v}), directive stack
|
||||
Real, in-context Vivado place-and-route on XC7A100T-CSG324-2, both
|
||||
with the real MAC-pipeline fix (\texttt{n16-timing-closure} branch),
|
||||
directive stack
|
||||
\texttt{Explore}/\texttt{ExtraNetDelay\_high}/\texttt{AggressiveExplore}:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\renewcommand{\arraystretch}{1.2}
|
||||
\begin{tabular}{lll}
|
||||
\begin{tabular}{llll}
|
||||
\toprule
|
||||
\textbf{Parameter} & \textbf{Value} & \textbf{Condition} \\
|
||||
\textbf{Parameter} & \textbf{N8 (in production)} & \textbf{N16 (verified)} & \textbf{Condition} \\
|
||||
\midrule
|
||||
Setup slack (WNS) & \textbf{0.000\,ns} & \texttt{clk\_pll\_i}, 155.039\,MHz, 0 of 63212 failing \\
|
||||
Hold slack (WHS) & +0.017\,ns & 0 of 63209 failing \\
|
||||
Compute-domain clock period & 6.450\,ns & 155.039\,MHz \\
|
||||
DDR3 CAS latency (real, measured) & CL=5 (16.125\,ns) & matches MIG configuration \\
|
||||
Same-row read-to-read & 12.9\,ns & real JEDEC trace \\
|
||||
Row-switch (Activate$\to$Read) penalty & +25--130\% & vs.\ same-row access, real measured \\
|
||||
Setup slack (WNS) & +0.108\,ns & +0.269\,ns & \texttt{clk\_pll\_i}, 155.039\,MHz, 0 failing \\
|
||||
Hold slack (WHS) & +0.036\,ns & +0.026\,ns & 0 failing \\
|
||||
Compute-domain clock period & 6.450\,ns & 6.450\,ns & 155.039\,MHz \\
|
||||
DDR3 CAS latency (real, measured) & \multicolumn{2}{c}{CL=5 (16.125\,ns)} & matches MIG configuration \\
|
||||
Same-row read-to-read & \multicolumn{2}{c}{12.9\,ns} & real JEDEC trace \\
|
||||
Row-switch penalty & \multicolumn{2}{c}{+25--130\%} & vs.\ same-row access, real measured \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\noindent\textbf{Margin notice:} the real WNS is exactly zero --- closed,
|
||||
but with no spare slack. Any RTL modification to this configuration
|
||||
requires a fresh real place-and-route run (same directive stack) before
|
||||
the design may be considered timing-clean again.
|
||||
\noindent\textbf{Margin notice:} both real margins are positive and
|
||||
closed. N8's own pre-fix signoff (\texttt{n8\_system\_ddr3\_top.v}
|
||||
without the MAC-pipeline fix, the version currently in physical
|
||||
fabrication) closed at an exact-zero WNS=0.000\,ns, no spare slack ---
|
||||
the improved +0.108\,ns figure above applies once the pipeline fix
|
||||
(\S\ref{sec:variants}) is adopted. Any RTL modification requires a
|
||||
fresh real place-and-route run (same directive stack) before the
|
||||
design may be considered timing-clean again.
|
||||
|
||||
%======================================================================
|
||||
\section{Resource Utilization (XC7A100T-CSG324-2, N8)}
|
||||
\section{Resource Utilization (XC7A100T-CSG324-2)}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\renewcommand{\arraystretch}{1.2}
|
||||
\begin{tabular}{lrrr}
|
||||
\begin{tabular}{lrrrr}
|
||||
\toprule
|
||||
\textbf{Resource} & \textbf{Used} & \textbf{Available} & \textbf{Utilization} \\
|
||||
\textbf{Resource} & \textbf{N8 Used} & \textbf{N16 Used} & \textbf{Available} & \textbf{N8 / N16 Util.} \\
|
||||
\midrule
|
||||
Slice LUTs & 12535 & 63400 & 19.77\% \\
|
||||
Slice Registers & 19902 & 126800 & 15.70\% \\
|
||||
DSP48E1 & 64 & 240 & 26.7\% \\
|
||||
Block RAM & 0 & 135 & 0\% \\
|
||||
Slice LUTs & 12536 & 19903 & 63400 & 19.77\% / 31.39\% \\
|
||||
Slice Registers & 19910 & 35409 & 126800 & 15.70\% / 27.93\% \\
|
||||
DSP48E1 & 64 & 128 & 240 & 26.7\% / 53.33\% \\
|
||||
Block RAM & 0 & 0 & 135 & 0\% / 0\% \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
@@ -371,28 +381,29 @@ Block RAM & 0 & 135 & 0\% \\
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{ll}
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
\textbf{Parameter} & \textbf{Value} \\
|
||||
\textbf{Parameter} & \textbf{N8} & \textbf{N16} \\
|
||||
\midrule
|
||||
MAC operations per DSP48E1 per cycle & 2 (packed INT8, lane A + lane B) \\
|
||||
DSP48E1 per processing element & 8 \\
|
||||
MAC/cycle per PE & 16 \\
|
||||
Real parallel PEs & 8 \\
|
||||
Aggregate MAC/cycle & 128 \\
|
||||
\textbf{Aggregate peak throughput} (calculated, 155.039\,MHz) & \textbf{$\sim$19.84\,GMAC/s} \\
|
||||
Real DDR3 physical bandwidth ceiling & $\sim$2.48\,GB/s (shared across all 8 PEs) \\
|
||||
MAC operations per DSP48E1 per cycle & \multicolumn{2}{c}{2 (packed INT8, lane A + lane B)} \\
|
||||
DSP48E1 per processing element & \multicolumn{2}{c}{8} \\
|
||||
MAC/cycle per PE & \multicolumn{2}{c}{16} \\
|
||||
Real parallel PEs & 8 & 16 \\
|
||||
Aggregate MAC/cycle & 128 & 256 \\
|
||||
\textbf{Aggregate peak throughput} (calc., 155.039\,MHz) & \textbf{$\sim$19.84\,GMAC/s} & \textbf{$\sim$39.69\,GMAC/s} \\
|
||||
Real DDR3 bandwidth ceiling (shared) & \multicolumn{2}{c}{$\sim$2.48\,GB/s} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\noindent The peak MAC figure is a real calculation from measured
|
||||
\noindent The peak MAC figures are a real calculation from measured
|
||||
building blocks (DSP count $\times$ packing factor $\times$ measured
|
||||
Fmax) --- not itself an independently benchmarked sustained-throughput
|
||||
number. Sustained throughput on a real workload is bounded by the real
|
||||
DDR3 bandwidth ceiling above, shared across all active PEs; a direct
|
||||
measurement of sustained aggregate throughput at N8 has not yet been
|
||||
performed (see \S\ref{sec:openitems}).
|
||||
Fmax) --- not themselves independently benchmarked sustained-throughput
|
||||
numbers. Sustained throughput on a real workload is bounded by the real
|
||||
DDR3 bandwidth ceiling above, shared across all active PEs (more
|
||||
tightly at N16, twice the PEs on the same physical channel); a direct
|
||||
measurement of sustained aggregate throughput has not yet been
|
||||
performed at either N (see \S\ref{sec:openitems}).
|
||||
|
||||
%======================================================================
|
||||
\section{Host Communication Protocol}
|
||||
@@ -470,8 +481,8 @@ Host MCU & ESP32-S3-WROOM-1-N16R8 & In stock, LCSC C2913202 \\
|
||||
\label{sec:openitems}
|
||||
|
||||
\begin{itemize}
|
||||
\item Physical board has not yet been fabricated; no real
|
||||
electrical/thermal measurement exists yet.
|
||||
\item Physical board fabrication is underway (N8 configuration,
|
||||
unmodified); no real electrical/thermal measurement exists yet.
|
||||
\item \texttt{sys\_clk} oscillator requires a custom-programmed order
|
||||
(no off-the-shelf SKU at 310.077\,MHz).
|
||||
\item Power-tree regulators (VCCINT, VCCAUX, VTT, per-bank VCCO) not
|
||||
@@ -481,10 +492,14 @@ Host MCU & ESP32-S3-WROOM-1-N16R8 & In stock, LCSC C2913202 \\
|
||||
\item ESP32-side firmware (JTAG bit-banging, SPI host driver,
|
||||
result-readback address translation) not yet written --- software
|
||||
work outside this document's scope.
|
||||
\item Real, direct sustained-throughput benchmark at N8 (vs.\ a
|
||||
real ESP32-S3 baseline) not yet performed --- see the project's
|
||||
Tests \& Timing chapter for the honest historical comparison
|
||||
context.
|
||||
\item Real, direct sustained-throughput benchmark (vs.\ a real
|
||||
ESP32-S3 baseline) not yet performed at either N8 or N16 --- see
|
||||
the project's Tests \& Timing chapter for the honest historical
|
||||
comparison context and a real, clearly-labeled projection for N16.
|
||||
\item Whether to adopt N16 (real, functionally verified, timing-
|
||||
closed on the \texttt{n16-timing-closure} branch) for a future
|
||||
board revision is a real, open hardware decision, not an RTL
|
||||
blocker.
|
||||
\end{itemize}
|
||||
|
||||
%======================================================================
|
||||
@@ -497,6 +512,11 @@ Host MCU & ESP32-S3-WROOM-1-N16R8 & In stock, LCSC C2913202 \\
|
||||
\textbf{Rev.} & \textbf{Date} & \textbf{Description} \\
|
||||
\midrule
|
||||
1.0 & 2026-09-21 & Initial preliminary datasheet, N8 configuration \\
|
||||
1.1 & 2026-09-22 & N8 re-verified with a real MAC-pipeline fix
|
||||
(WNS improved 0.000\,ns $\to$ +0.108\,ns); N16 now also real,
|
||||
functionally verified, and timing-closed (WNS=+0.269\,ns) on a
|
||||
separate development branch, documented as a real candidate for a
|
||||
future board revision \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
+118
-37
@@ -75,12 +75,14 @@ di sintesi fuori contesto.
|
||||
0094 & + arbitro gerarchico a 2 livelli & 16 & $-0.646$ (ancora fallito) & 19936 & 128 \\
|
||||
0094 & + tuning delle direttive P\&R & 16 & $-0.338$ (ancora fallito) & 19936 & 128 \\
|
||||
0095 & curva N=4 (1 gruppo) & 4 & $-0.005$ (2 endpoint falliti) & 8794 & 32 \\
|
||||
\textbf{0095/0096} & \textbf{\texttt{n8\_system\_ddr3\_top.v}, definitivo} & \textbf{8} & \textbf{0.000 (CHIUSO)} & \textbf{12535} & \textbf{64} \\
|
||||
0095/0096 & \texttt{n8\_system\_ddr3\_top.v}, in fabbricazione fisica & 8 & 0.000 (CHIUSO) & 12535 & 64 \\
|
||||
0097 & + pipeline MAC aggiuntiva (branch \texttt{n16-timing-closure}) & 8 & +0.108 (CHIUSO, migliorato) & 12536 & 64 \\
|
||||
\textbf{0097} & \textbf{+ pipeline MAC aggiuntiva} & \textbf{16} & \textbf{+0.269 (CHIUSO)} & \textbf{19903} & \textbf{128} \\
|
||||
\bottomrule
|
||||
\caption{Cronologia reale dei segni di place-and-route, dal primo P\&R in contesto fino al target definitivo N=8.}
|
||||
\caption{Cronologia reale dei segni di place-and-route, dal primo P\&R in contesto fino ai risultati pi\`u recenti su entrambe le configurazioni N=8 e N=16.}
|
||||
\end{longtable}
|
||||
|
||||
\subsection{Il segno definitivo: N=8}
|
||||
\subsection{Il segno in fabbricazione fisica: N=8}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
@@ -90,40 +92,72 @@ di sintesi fuori contesto.
|
||||
\midrule
|
||||
Clock PHY DDR3 (sys\_clk\_p/n) & 310.078\,MHz (periodo 3.225\,ns) \\
|
||||
Clock di calcolo (ui\_clk/clk\_pll\_i) & 155.039\,MHz \\
|
||||
WNS (setup slack) & \textbf{0.000\,ns} --- chiuso, 0 endpoint falliti \\
|
||||
WHS (hold slack) & +0.017\,ns \\
|
||||
Endpoint falliti & 0 su 63212 (setup), 0 su 63209 (hold) \\
|
||||
LUT utilizzate & 12535 / 63400 (19.77\%) \\
|
||||
Registri utilizzati & 19902 / 126800 (15.70\%) \\
|
||||
WNS (setup slack) & \textbf{+0.108\,ns} --- chiuso, con la correzione pipeline MAC (EXP-0097) \\
|
||||
WHS (hold slack) & +0.036\,ns \\
|
||||
Endpoint falliti & 0 (setup e hold) \\
|
||||
LUT utilizzate & 12536 / 63400 (19.77\%) \\
|
||||
Registri utilizzati & 19910 / 126800 (15.70\%) \\
|
||||
DSP48E1 utilizzati & 64 / 240 (26.7\%) \\
|
||||
Parallelismo reale & 8 elementi di elaborazione paralleli \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Segno di temporizzazione reale, definitivo, per \texttt{n8\_system\_ddr3\_top.v} (EXP-0095/0096).}
|
||||
\caption{Segno di temporizzazione reale per \texttt{n8\_system\_ddr3\_top.v} con la correzione pipeline MAC (EXP-0097) --- la configurazione fisicamente in fabbricazione. Il segno originale (pre-correzione, EXP-0095/0096) chiudeva a WNS=0.000\,ns esatto, senza margine di riserva; questa correzione lo migliora senza alcuna regressione funzionale.}
|
||||
\end{table}
|
||||
|
||||
\textbf{Nota importante, dichiarata onestamente}: il margine WNS=0.000\,ns
|
||||
\`e \emph{esattamente} zero --- reale e chiuso, ma senza alcuno slack di
|
||||
riserva. Qualunque futura modifica RTL a questo top-level o ai suoi
|
||||
moduli dipendenti richiede un nuovo, vero place-and-route (con la
|
||||
stessa sequenza di direttive: \texttt{opt\_design -directive Explore},
|
||||
\texttt{place\_design -directive ExtraNetDelay\_high},
|
||||
\texttt{phys\_opt\_design -directive AggressiveExplore},
|
||||
\texttt{route\_design -directive AggressiveExplore}) prima di
|
||||
fidarsi nuovamente della temporizzazione.
|
||||
\subsection{Il nuovo segno reale: N=16 ORA CHIUDE}
|
||||
\label{sec:n16-closed}
|
||||
|
||||
\subsection{Il collo di bottiglia reale trovato a N=16}
|
||||
|
||||
Il percorso critico che impedisce la chiusura del timing a N=16
|
||||
\`e stato tracciato realmente (non ipotizzato) fino all'interno del
|
||||
datapath MAC di \texttt{neural\_processor\_packed.v} --- lo stesso
|
||||
Il percorso critico che inizialmente impediva la chiusura del timing a
|
||||
N=16 \`e stato tracciato realmente (non ipotizzato) fino all'interno
|
||||
del datapath MAC di \texttt{neural\_processor\_packed.v} --- lo stesso
|
||||
nucleo di calcolo descritto nel Capitolo~\ref{chap:architecture},
|
||||
invariato dalla prima versione del progetto, che a N=2 chiudeva gi\`a
|
||||
con un margine estremamente sottile (+0.0999\,ns). A N=16 la maggiore
|
||||
occupazione complessiva del die (31\% LUT) aumenta la congestione di
|
||||
piazzamento a sufficienza da erodere quel margine gi\`a minimo ---
|
||||
un problema diffuso di congestione, non un singolo collo di bottiglia
|
||||
strutturale come quello, gi\`a risolto, dell'arbitro.
|
||||
occupazione complessiva del die (31\% LUT) aumentava la congestione di
|
||||
piazzamento a sufficienza da erodere quel margine gi\`a minimo.
|
||||
|
||||
\textbf{Questo collo di bottiglia \`e stato risolto} (\S\ref{sec:mac-pipeline}
|
||||
del Capitolo~\ref{chap:architecture}) aggiungendo un vero stadio di
|
||||
pipeline nel datapath MAC. Risultato reale, su un branch di sviluppo
|
||||
dedicato (\texttt{n16-timing-closure}), isolato dalla scheda fisica in
|
||||
produzione:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{ll}
|
||||
\toprule
|
||||
\textbf{Metrica} & \textbf{Valore reale} \\
|
||||
\midrule
|
||||
WNS (setup slack) & \textbf{+0.269\,ns} --- \textbf{CHIUSO} \\
|
||||
WHS (hold slack) & +0.026\,ns \\
|
||||
Endpoint falliti & 0 su 106087 (setup), 0 su 106084 (hold) \\
|
||||
LUT utilizzate & 19903 / 63400 (31.39\%) \\
|
||||
Registri utilizzati & 35409 / 126800 (27.93\%) \\
|
||||
DSP48E1 utilizzati & 128 / 240 (53.33\%) \\
|
||||
Parallelismo reale & 16 elementi di elaborazione paralleli \\
|
||||
Verifica funzionale & 32/32 PASS su DDR3 reale (\texttt{tb\_n16\_system\_ddr3.v}) \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Segno di temporizzazione reale per \texttt{n16\_system\_ddr3\_top.v} con la correzione pipeline MAC (EXP-0097). Margine reale migliore di quello storico originale di N=2 (+0.0999\,ns).}
|
||||
\end{table}
|
||||
|
||||
Dopo questa correzione, il percorso critico si \`e spostato di nuovo
|
||||
(prova reale che la correzione ha effettivamente risolto il proprio
|
||||
problema specifico): ora si trova nella logica di aggiornamento della
|
||||
coda del Director (\texttt{neural\_director\_grouped.v}), ancora
|
||||
dominato dal routing fisico (73\%) pi\`u che dalla profondit\`a
|
||||
logica. Un secondo tentativo reale con direttive di place-and-route
|
||||
alternative ha dato un risultato \emph{peggiore} (+0.168\,ns), confermando
|
||||
che il margine attuale (+0.269\,ns) \`e gi\`a il migliore risultato
|
||||
reale ottenuto con il solo tuning delle direttive, senza ulteriori
|
||||
modifiche RTL.
|
||||
|
||||
\textbf{Stato reale onesto}: N=16 non \`e pi\`u limitato da un vincolo
|
||||
tecnico di temporizzazione. La sua adozione come scheda fisica \`e ora
|
||||
una decisione dell'utente, non un problema RTL ancora aperto. La
|
||||
scheda fisica attualmente in fabbricazione resta N=8 (fabbricazione
|
||||
gi\`a avviata al momento di questa correzione); N=16 \`e un candidato
|
||||
reale, verificato, per una futura revisione.
|
||||
|
||||
\section{Confronto con un riferimento reale: ESP32-S3}
|
||||
\label{sec:esp32-comparison}
|
||||
@@ -153,22 +187,69 @@ come attuale un dato ormai superato.
|
||||
|
||||
\textbf{Stato onesto attuale}: non esiste ancora una misura reale,
|
||||
diretta, di throughput aggregato (inferenze al secondo, o MAC/s
|
||||
sostenuti) della configurazione N=8 definitiva confrontata con un
|
||||
benchmark ESP32-S3 aggiornato. Il picco teorico calcolabile per N=8
|
||||
\`e:
|
||||
sostenuti) di nessuna configurazione (N=8 o N=16) confrontata con un
|
||||
benchmark ESP32-S3 aggiornato. Il picco teorico calcolabile,
|
||||
puramente computazionale, \`e:
|
||||
|
||||
\[
|
||||
8~\text{PE} \times 16~\text{MAC/ciclo} \times 155.039\times10^6~\text{cicli/s} = 19.84~\text{GMAC/s di picco teorico aggregato}
|
||||
8~\text{PE} \times 16~\text{MAC/ciclo} \times 155.039\times10^6~\text{cicli/s} = 19.84~\text{GMAC/s di picco teorico aggregato (N=8)}
|
||||
\]
|
||||
\[
|
||||
16~\text{PE} \times 16~\text{MAC/ciclo} \times 155.039\times10^6~\text{cicli/s} = 39.69~\text{GMAC/s di picco teorico aggregato (N=16)}
|
||||
\]
|
||||
|
||||
ma questo \`e un limite superiore puramente computazionale: dato il
|
||||
vincolo reale di banda DDR3 (2.48\,GB/s fisici, condivisi tra tutti
|
||||
gli 8 PE), il throughput realmente sostenibile in un carico di lavoro
|
||||
vincolo reale di banda DDR3 (2.48\,GB/s fisici, condivisi tra tutti i
|
||||
PE attivi), il throughput realmente sostenibile in un carico di lavoro
|
||||
reale sar\`a inferiore, nella stessa misura gi\`a documentata a N=1/2
|
||||
(\S~\ref{sec:bottleneck}) --- una misura reale e diretta di questo
|
||||
throughput sostenuto a N=8, e un nuovo confronto onesto con un
|
||||
benchmark ESP32-S3 aggiornato, restano lavoro futuro non ancora
|
||||
eseguito.
|
||||
(\S~\ref{sec:bottleneck}) --- e la condivisione dello stesso canale
|
||||
DDR3 tra il doppio dei PE a N=16 rende questo vincolo ANCORA PI\`U
|
||||
stringente rispetto a N=8, non meno.
|
||||
|
||||
\subsection{Una nuova stima onesta per N=16 (proiezione, non misura)}
|
||||
|
||||
Su richiesta esplicita, ecco una stima reale --- costruita da dati
|
||||
reali gi\`a misurati, ma essa stessa \textbf{una proiezione, non una
|
||||
nuova misura diretta} --- pi\`u solida della vecchia proiezione
|
||||
$\sim$55--85$\times$ ormai superata (sopra), perch\'e confronta lo
|
||||
\textbf{stesso numero di core} (16 vs 16) invece di ipotizzare 30 core
|
||||
limitati solo dai DSP:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
\textbf{Fattore di scala reale} & \textbf{Base} & \textbf{Valore} \\
|
||||
\midrule
|
||||
Numero di core & 16 (ECP5) vs 16 (Artix-7 N=16) & $1\times$ (confronto diretto) \\
|
||||
Rapporto di clock & 155.039\,MHz vs 64--97\,MHz (reale, ECP5) & $1.60\times$--$2.42\times$ \\
|
||||
Packing INT8 per DSP & 2 MAC/DSP (Artix-7) vs 1 MAC/DSP (ECP5, non impacchettato) & $2\times$ \\
|
||||
\midrule
|
||||
\textbf{Fattore di scala combinato} & & \textbf{$3.20\times$--$4.85\times$} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Fattori di scala reali (misurati) tra l'architettura ECP5 (v2) e Artix-7 N=16 (v3), a parit\`a di numero di core.}
|
||||
\end{table}
|
||||
|
||||
Applicando questo fattore di scala al dato reale, misurato,
|
||||
$\sim$9.5--14$\times$ su ESP32-S3 (stesso workload MobileFaceNet-class):
|
||||
|
||||
\[
|
||||
9.5\times3.20 \approx \mathbf{30\times} \qquad\qquad 14\times4.85 \approx \mathbf{68\times}
|
||||
\]
|
||||
|
||||
\textbf{Stima onesta}: N=16, su un carico di lavoro simile a quello
|
||||
del confronto originale, \`e plausibilmente nell'ordine di
|
||||
\textbf{30--68$\times$ ESP32-S3} (punto centrale $\sim$45$\times$) ---
|
||||
una proiezione di puro throughput di calcolo, costruita da fattori di
|
||||
scala reali, \textbf{non una misura diretta}. Il dato esatto reale
|
||||
richiede un vero benchmark end-to-end (stesso workload, hardware
|
||||
reale o simulazione a livello di sistema completo) --- non ancora
|
||||
eseguito. Come per N=8, il vincolo reale di banda DDR3
|
||||
(\S~\ref{sec:bottleneck}) former\`a probabilmente un tetto reale
|
||||
inferiore a questa proiezione puramente computazionale su un carico
|
||||
di lavoro reale, specialmente a N=16 dove pi\`u PE condividono lo
|
||||
stesso canale fisico.
|
||||
|
||||
\section{Lezioni reali dal processo di verifica}
|
||||
|
||||
|
||||
@@ -6784,3 +6784,230 @@ docs/PINOUT.md, BOM confirmation from docs/BOM.md -- both already real
|
||||
and unaffected by the N_GROUPS choice, since DDR3/SPI/flash/config
|
||||
pins are package-level, not internal-core-count-dependent) is the real
|
||||
next milestone now that a real, closed, deployable RTL target exists.
|
||||
|
||||
EXP-0097 -- N=16 REAL TIMING CLOSED: extra MAC pipeline stage (branch
|
||||
`n16-timing-closure`), the previously-deferred real fix now built and
|
||||
verified (2026-09-21/22, user's own explicit direction: "creare una
|
||||
branch del progetto e lavora per scoprire come fare funzionare il
|
||||
timing", physical board fabrication continues in parallel on the
|
||||
already-fixed N=8 design, unaffected by this branch)
|
||||
|
||||
CONTEXT: EXP-0094's own real, traced remaining N=16 bottleneck (after
|
||||
the hierarchical arbiter + P&R directive tuning already closed most of
|
||||
the gap, WNS -0.913ns -> -0.338ns) was inside `neural_processor_
|
||||
packed.v`'s own DSP48E1 MAC datapath -- a pre-existing, N=2-era design
|
||||
(unchanged since EXP-0059) with an already razor-thin real margin
|
||||
(+0.099962ns) that N=16's own higher real die congestion eroded past
|
||||
zero. EXP-0094's own `next_action` flagged real MAC-datapath pipelining
|
||||
as the most direct remaining fix, deliberately not attempted then
|
||||
(shared, load-bearing module, needed explicit direction + isolation
|
||||
from the definitive N=8 signoff -- hence the real, separate branch).
|
||||
|
||||
METHOD: real, traced worst-violated-path analysis (EXP-0094's own real
|
||||
post-route report) pinpointed the exact real gap: a DSP48E1's own
|
||||
(Vivado-auto-retimed) product register feeding STRAIGHT THROUGH the
|
||||
real carry-heavy INT8-unpack logic (`pb_comb`'s own shift + conditional
|
||||
+1 carry-propagate add, CARRY4-dominated) into `proda1`/`prodb1` in a
|
||||
SINGLE real cycle. Real fix: split the original single "Stage 1" into
|
||||
two real stages -- **Stage 1a** registers the RAW DSP48E1 product with
|
||||
zero logic in between (`product_reg`, a real, explicit register
|
||||
boundary immediately after the multiply); **Stage 1b** does the
|
||||
carry-heavy unpack FROM the already-registered `product_reg` and
|
||||
registers the result into `proda1`/`prodb1` (unchanged real math,
|
||||
now one real cycle later). Real, deliberate consequence: end-to-end
|
||||
per-tile latency grows by exactly ONE real clock cycle; throughput is
|
||||
unaffected (still accepts one new operand per cycle, real valid/ready
|
||||
handshaking throughout, no fixed-latency assumption anywhere
|
||||
downstream). `pipeline_busy`/`valid_tree`'s own level-0 input and the
|
||||
module's own header comment updated to match.
|
||||
|
||||
REAL BUG FOUND AND FIXED IN THE TESTBENCH BEFORE A TRUSTWORTHY RESULT
|
||||
WAS POSSIBLE (not an RTL bug): `tb_neural_processor_packed.v`'s own
|
||||
comparison logic required all three cores (2 real reference `neural_
|
||||
processor.v` instances + the DUT) to assert `result_valid`
|
||||
SIMULTANEOUSLY -- correct only when all three share the exact same
|
||||
real pipeline depth. Since `result_valid` is a genuine ONE-SHOT pulse
|
||||
in every one of these FSMs (self-clears the cycle after `result_ready`
|
||||
is seen, identical pattern in both v2 and v3 cores), and the DUT is
|
||||
now deliberately one real cycle deeper than the reference cores, the
|
||||
reference cores' own `result_valid` had already dropped by the time
|
||||
the DUT's own pulse arrived -- the original three-way AND never
|
||||
triggered again, a real 18/18 watchdog-timeout false-failure, not an
|
||||
actual DUT bug (confirmed via a real, controlled A/B: the SAME failure
|
||||
does NOT reproduce against the unmodified reference-only comparison
|
||||
path). Fixed by latching each core's own result independently the
|
||||
cycle its own `result_valid` first pulses, then comparing the three
|
||||
LATCHED values once all three have arrived -- correct regardless of
|
||||
real relative pipeline depth.
|
||||
|
||||
Also hit and root-caused (real, not guessed): `tb_np_packed_layer_
|
||||
reuse.v` fails (3/16 PASS) identically against BOTH the modified AND
|
||||
the original, unmodified `neural_processor_packed.v` (confirmed via a
|
||||
real, direct A/B comparison) -- a real, PRE-EXISTING, already-broken/
|
||||
stale testbench (real port-width mismatch warning on `layer_prefetch_
|
||||
ctrl.v`'s own `ctrl_wdata`/`ctrl_rdata`, 128 bits wired against a
|
||||
256-bit real port -- dates from before EXP-0084's own 32-bit DDR3
|
||||
widening, apparently never updated), unrelated to this real fix, out
|
||||
of scope for this branch's own task.
|
||||
|
||||
Also hit and root-caused (real Vivado project-state quirk, not an RTL
|
||||
bug): a first real P&R attempt on this branch elaborated with
|
||||
`N_GROUPS` bound to 2, not the RTL's own real default of 4, despite no
|
||||
`-generic` override on the actual `synth_design` command line, an
|
||||
empty real `GENERIC` property on the `synth_1` run, the correct real
|
||||
`top` property, and no stale imported copy of `n16_system_ddr3_top.v`
|
||||
anywhere in the project (all confirmed via direct real queries, not
|
||||
assumed) -- most likely Vivado's own "Incremental synthesis strategy
|
||||
default" silently carrying forward a parameter binding from this
|
||||
session's own earlier `-generic N_GROUPS=2` sweep run (EXP-0095),
|
||||
despite an intervening `reset_run`. Real fix: pass `-generic
|
||||
N_GROUPS=4` explicitly on the `synth_design` command line rather than
|
||||
relying on the RTL's own default resolving correctly -- confirmed via
|
||||
a real, explicit post-synth DSP48E1 cell-count check (128, matching
|
||||
real N=16) before trusting anything downstream this time.
|
||||
|
||||
REAL RESULT: (1) isolated bit-exact verification,
|
||||
`tb_neural_processor_packed.v` (real Icarus xsim, against 2x real
|
||||
`hardware/v2/rtl/neural_processor.v`): **18/18 PASS, 0 errors**. (2)
|
||||
real, full-system functional xsim, `tb_n16_system_ddr3.v` (real DDR3
|
||||
model, real Vivado xsim): **32/32 PASS, 0 errors**, `$finish` at
|
||||
197735.6335ns (same real completion time as the pre-fix EXP-0094
|
||||
result -- the extra real pipeline cycle is fully absorbed by DDR3's
|
||||
own already-dominant real latency, no observable end-to-end slowdown
|
||||
at this scale). (3) real, full P&R (`n16_system_ddr3_top.v`, real
|
||||
XC7A100T-CSG324-2, `Explore`/`ExtraNetDelay_high`/`AggressiveExplore`
|
||||
directive stack, EXP-0094's own real N_GROUPS=4 explicitly confirmed
|
||||
via a real post-synth DSP48E1 count of 128): **WNS=+0.269ns,
|
||||
WHS=+0.026ns, TNS=0.000ns, 0 FAILING SETUP OR HOLD ENDPOINTS --
|
||||
TIMING CONSTRAINTS ARE MET.** Real utilization: 19903 LUTs (31.39%),
|
||||
35409 registers (27.93%, up from 19936/27.92%... i.e. genuinely more
|
||||
registers than the pre-fix EXP-0094 result, matching the real,
|
||||
expected cost of the added pipeline stage across 16 real PE
|
||||
instances), 128 DSP48E1 (53.33%).
|
||||
|
||||
DECISION: N=16 hybrid systolic (`n16_system_ddr3_top.v`) is now REAL,
|
||||
functionally verified, AND timing-CLOSED, on this real, isolated
|
||||
branch (`n16-timing-closure`) -- does not touch or affect the physical
|
||||
board fabrication already underway on N=8 (`v3-artix7`, unmodified).
|
||||
This is a real, significant milestone: it confirms the N=16 hybrid
|
||||
systolic architecture is fundamentally viable at full real scale, not
|
||||
just "close" -- the earlier N=8-as-definitive decision was a real,
|
||||
reasonable engineering choice under the "ship something real now"
|
||||
constraint (WNS=0.000ns exact-zero margin vs. this fix's own real,
|
||||
more comfortable +0.269ns), not a permanent architectural ceiling.
|
||||
|
||||
next_action: (1) real, dedicated functional xsim + P&R re-confirmation
|
||||
specifically for the definitive N=8 configuration WITH this same MAC
|
||||
pipeline fix applied (verify it does not regress N=8's own real,
|
||||
already-closed signoff, and ideally IMPROVES its own already-thin
|
||||
future margin) -- not yet done on this branch. (2) a real, explicit
|
||||
decision with the user on whether/when to promote this fix back to
|
||||
`v3-artix7` (the physical board's own branch) -- given the board is
|
||||
already in fabrication as the UNMODIFIED N=8 design, this is a real
|
||||
question about a FUTURE board revision, not the current one. (3) this
|
||||
branch's own real bug findings (the testbench latching fix, the
|
||||
Vivado incremental-synthesis generic-binding quirk) are worth folding
|
||||
into CLAUDE.md's own hard-won-lessons section regardless of the
|
||||
promotion decision.
|
||||
|
||||
EXP-0097 (continued) -- real N=8 re-verification with the same MAC
|
||||
pipeline fix (improves, does not regress) + a real margin-hunt attempt
|
||||
for N=16 (2026-09-21/22, user's own explicit direction: "verifica
|
||||
anche su N=8 e verifica se possiamo guadagnare qualcosina ancora su
|
||||
N16 perche' io implemento N16 se funziona")
|
||||
|
||||
REAL RESULT (1), N=8 with the same pipelined `neural_processor_
|
||||
packed.v`: real functional xsim (`tb_n8_system_ddr3.v`, real DDR3
|
||||
model) **16/16 PASS, 0 errors**, identical real completion time to the
|
||||
pre-fix result (131145.8335ns) -- no functional regression. Real, full
|
||||
P&R (`n8_system_ddr3_top.v`, `-generic N_GROUPS=2` explicit, confirmed
|
||||
via a real post-synth 64 DSP48E1 count): **WNS=+0.108ns (up from the
|
||||
pre-fix exact-zero 0.000ns), WHS=+0.036ns, 0 failing endpoints, 64
|
||||
DSP48E1 (26.7%), 12536 LUTs (19.77%)**. The same fix that closes N=16
|
||||
also gives N=8 real, comfortable margin instead of the exact-zero
|
||||
margin its original (unmodified-core) signoff had -- a real, additive
|
||||
improvement, not a tradeoff.
|
||||
|
||||
REAL RESULT (2), N=16 margin-hunt: the real worst path has moved AGAIN
|
||||
(confirming the MAC-datapath fix genuinely resolved ITS OWN real
|
||||
bottleneck) -- now inside `neural_director_grouped.v`'s own queue
|
||||
update logic (`u_dir/q_head_reg[3]` -> `q_count_reg[0]/CE`), still
|
||||
real route-dominated (73%), not logic-depth-dominated. A second real
|
||||
P&R attempt with alternate directives (`opt_design -directive
|
||||
ExploreWithRemap`, `place_design -directive Explore`, same
|
||||
`phys_opt_design`/`route_design -directive AggressiveExplore`) gave
|
||||
**WNS=+0.168ns -- WORSE than the first attempt's own real +0.269ns**,
|
||||
confirming real run-to-run/directive-to-directive P&R variance, not a
|
||||
systematic further improvement available from directive-tuning alone.
|
||||
|
||||
DECISION: the original directive stack (`Explore`/`ExtraNetDelay_
|
||||
high`/`AggressiveExplore`) remains the best real N=16 result found
|
||||
(WNS=+0.269ns). Further real margin would require touching `neural_
|
||||
director_grouped.v`'s own queue RTL (a new, separate, not-yet-scoped
|
||||
piece of real engineering) -- NOT attempted, given the current real
|
||||
margin is already comfortably closed (better than N=2's own original
|
||||
historical +0.0999962ns real signoff margin) and blind further P&R-
|
||||
directive search already showed diminishing/negative real returns.
|
||||
|
||||
Real, cumulative state on this branch (`n16-timing-closure`), both
|
||||
configurations using the SAME real pipelined `neural_processor_
|
||||
packed.v`:
|
||||
N=8 (`n8_system_ddr3_top.v`): WNS=+0.108ns, 64 DSP48E1, 16/16 functional PASS
|
||||
N=16 (`n16_system_ddr3_top.v`): WNS=+0.269ns, 128 DSP48E1, 32/32 functional PASS
|
||||
|
||||
next_action: real, explicit decision with the user on whether to (1)
|
||||
promote this fix to `v3-artix7` for a FUTURE board revision (the
|
||||
current physical board is already in fabrication as the unmodified
|
||||
N=8 design, unaffected), and/or (2) actually build the NEXT physical
|
||||
board as N=16 instead of N=8, given N=16 is now real, functionally
|
||||
verified, AND timing-closed with a real, comfortable margin -- a real,
|
||||
consequential hardware decision, not an RTL one.
|
||||
|
||||
EXP-0098 -- consolidating N=16 to the same real rigor as N=8: N=2
|
||||
re-verified too (the pipelined MAC core is SHARED across the whole
|
||||
real family), full real family state now closed at every N
|
||||
(2026-09-22, user's own explicit direction: "consolidare N=16 come
|
||||
faccio con N=8")
|
||||
|
||||
CONTEXT: EXP-0097 verified the pipelined `neural_processor_packed.v`
|
||||
against N=8 and N=16, but never against N=2 -- a real, disclosed gap,
|
||||
since N=2 (`n2_system_ddr3_top.v`) is the SAME shared core and remains
|
||||
this project's own documented real fallback signoff (EXP-0088).
|
||||
Consolidating N=16 to N=8's own level of rigor means confirming the
|
||||
WHOLE real family, not just the two configurations directly asked
|
||||
about.
|
||||
|
||||
REAL RESULT: functional xsim (`tb_n2_system_ddr3.v`, real DDR3 model):
|
||||
**8/8 PASS, 0 errors**, `$finish` at the EXACT SAME real completion
|
||||
time as the pre-fix baseline (101204.9335ns) -- zero observable
|
||||
end-to-end effect at this scale, same as N=16's own real finding.
|
||||
Real, full P&R (`n2_system_ddr3_top.v`, real XC7A100T-CSG324-2, same
|
||||
directive stack): **WNS=+0.389ns (up from the original real
|
||||
+0.099962ns), WHS=+0.017ns, 0 failing endpoints, 16 DSP48E1 (6.67%),
|
||||
6645 LUTs (10.48%)** -- a real, substantial margin improvement, no
|
||||
regression.
|
||||
|
||||
REAL, CONSOLIDATED FAMILY STATE (branch `n16-timing-closure`, all
|
||||
three real top-levels sharing the SAME pipelined `neural_processor_
|
||||
packed.v`, all real, functionally verified AND timing-closed):
|
||||
|
||||
N=2 (`n2_system_ddr3_top.v`): WNS=+0.389ns, 16 DSP48E1, 8/8 functional PASS
|
||||
N=8 (`n8_system_ddr3_top.v`): WNS=+0.108ns, 64 DSP48E1, 16/16 functional PASS
|
||||
N=16 (`n16_system_ddr3_top.v`): WNS=+0.269ns, 128 DSP48E1, 32/32 functional PASS
|
||||
|
||||
DECISION: the real MAC-pipeline fix (EXP-0097) is a pure, unconditional
|
||||
improvement across the entire real product family -- every real
|
||||
configuration this project has ever built a dedicated top-level for
|
||||
now closes with real, comfortable, positive margin, not just N=16. No
|
||||
real regression found anywhere. This is now a real, trustworthy,
|
||||
fully-consolidated state for this branch, at the same level of rigor
|
||||
EXP-0096 established for N=8 alone.
|
||||
|
||||
next_action: update the project's own primary real docs
|
||||
(`docs/PHYSICAL_REALIZATION.md` §3, `docs/ARCHITECTURE_ANALYSIS.md`
|
||||
§5.6 and its own top-of-document pointer) on this branch to reflect
|
||||
this consolidated real family state -- not yet done, the LaTeX
|
||||
deliverables (`docs/latex/*.tex`) were updated first per the user's
|
||||
own more immediate request, but the markdown docs are this project's
|
||||
own real, authoritative source of truth per CLAUDE.md's own "Read
|
||||
first" section and deserve the same update.
|
||||
|
||||
@@ -12,12 +12,20 @@
|
||||
// to keep this module's own pipeline depth/stage count identical to
|
||||
// the V2 original for a direct structural comparison).
|
||||
//
|
||||
// Pipeline stages match V2's neural_processor.v exactly, just doubled
|
||||
// on the accumulator side (one accumulate/bias/activation/saturation
|
||||
// path per job, A and B, sharing the SAME multiply/adder-tree stages
|
||||
// since they consume the SAME weight stream):
|
||||
// Stage 0 input alignment (x0_a, x0_b, w0 -- ONE shared weight)
|
||||
// Stage 1 P_IN packed-MAC lanes: p0[i]=x0_a[i]*w0[i], p1[i]=x0_b[i]*w0[i]
|
||||
// Pipeline stages, originally matched V2's neural_processor.v exactly
|
||||
// (one accumulate/bias/activation/saturation path per job, A and B,
|
||||
// sharing the SAME multiply/adder-tree stages since they consume the
|
||||
// SAME weight stream). EXTENDED BY ONE REAL STAGE on the
|
||||
// n16-timing-closure branch (real fix for EXP-0094's own real, traced
|
||||
// N=16 P&R timing failure -- see Stage 1a/1b's own header comments for
|
||||
// the full real root-cause story):
|
||||
// Stage 0 input alignment (x0_a, x0_b, w0 -- ONE shared weight)
|
||||
// Stage 1a P_IN real DSP48E1 packed multiplies, registered raw
|
||||
// (product_reg) -- NEW real stage
|
||||
// Stage 1b unpack the two packed INT8 products from product_reg:
|
||||
// p0[i]=x0_a[i]*w0[i], p1[i]=x0_b[i]*w0[i] -- same real
|
||||
// math as the original single "Stage 1", now one real
|
||||
// cycle later
|
||||
// Stage 2..(1+TREE_LEVELS) TWO balanced adder trees (A and B)
|
||||
// Stage (2+TREE_LEVELS) TWO accumulators
|
||||
// Stage (3+TREE_LEVELS) bias add (shared bias/activation -- same
|
||||
@@ -25,6 +33,14 @@
|
||||
// + activation, per job
|
||||
// Stage (4+TREE_LEVELS) INT8 saturation / output register, per job
|
||||
//
|
||||
// Real, deliberate consequence: end-to-end per-tile latency grows by
|
||||
// exactly ONE real clock cycle versus the original design (throughput
|
||||
// is unaffected -- the pipeline still accepts one new operand per
|
||||
// cycle in steady state). Functional behavior (the actual packed-MAC
|
||||
// arithmetic) is byte-for-byte unchanged -- verified bit-exact against
|
||||
// the same real reference used since EXP-0059 (2x real
|
||||
// hardware/v2/rtl/neural_processor.v), `tb_neural_processor_packed.v`.
|
||||
//
|
||||
// job_bias/job_activation are SHARED between A and B (same resident
|
||||
// neuron), matching this project's own weight-reuse semantics (a
|
||||
// neuron/filter's bias and activation type don't vary by spatial
|
||||
@@ -116,17 +132,28 @@ module neural_processor_packed #(
|
||||
end
|
||||
|
||||
// ============================================================
|
||||
// STAGE 1 -- P_IN packed-MAC lanes (mac2_dsp_packed.v's own
|
||||
// verified combinational formula, inlined per lane)
|
||||
// STAGE 1a -- P_IN real DSP48E1 packed multiplies, registered RAW
|
||||
// (n16-timing-closure branch, real fix for EXP-0094's own real,
|
||||
// traced N=16 critical path). EXP-0093/0094's own real post-route
|
||||
// reports found the worst violated path running from a DSP48E1's
|
||||
// own (Vivado-auto-retimed) product register straight through the
|
||||
// pb_comb unpack logic below (a real, CARRY4-heavy shift + carry-
|
||||
// propagate add) into proda1/prodb1 in a SINGLE cycle -- already
|
||||
// razor-thin at N=2 (WNS=+0.0999962ns, EXP-0088) and pushed
|
||||
// negative by N=16's own extra real placement congestion (EXP-
|
||||
// 0093/0094). This stage makes the DSP's own real output register
|
||||
// explicit in RTL (captures the WHOLE raw packed product, zero
|
||||
// logic in between) instead of relying on the tool to retime one
|
||||
// in automatically -- the actual, additional real pipeline stage
|
||||
// this fix needs is STAGE 1b below, which now has its own full
|
||||
// real clock period to do the unpack work in.
|
||||
// ============================================================
|
||||
reg valid1, last1;
|
||||
reg signed [ACC_WIDTH-1:0] proda1 [0:P_IN-1];
|
||||
reg signed [ACC_WIDTH-1:0] prodb1 [0:P_IN-1];
|
||||
|
||||
localparam A_WIDTH = 3*DATA_WIDTH + 1;
|
||||
localparam PRODUCT_WIDTH = A_WIDTH + DATA_WIDTH;
|
||||
|
||||
wire signed [PROD_WIDTH-1:0] pa_comb [0:P_IN-1];
|
||||
wire signed [PROD_WIDTH-1:0] pb_comb [0:P_IN-1];
|
||||
wire signed [PRODUCT_WIDTH-1:0] product_comb [0:P_IN-1];
|
||||
reg signed [PRODUCT_WIDTH-1:0] product_reg [0:P_IN-1];
|
||||
|
||||
genvar gm;
|
||||
generate
|
||||
@@ -134,12 +161,7 @@ module neural_processor_packed #(
|
||||
wire signed [A_WIDTH-1:0] x0_sext25 = {{(A_WIDTH-DATA_WIDTH){xa0[gm][DATA_WIDTH-1]}}, xa0[gm]};
|
||||
wire signed [A_WIDTH-1:0] x1_shifted = $signed(xb0[gm]) <<< (2*DATA_WIDTH);
|
||||
wire signed [A_WIDTH-1:0] packed_a = x1_shifted + x0_sext25;
|
||||
wire signed [A_WIDTH+DATA_WIDTH-1:0] product = packed_a * w0[gm];
|
||||
|
||||
assign pa_comb[gm] = product[PROD_WIDTH-1:0];
|
||||
wire signed [A_WIDTH+DATA_WIDTH-2*DATA_WIDTH-1:0] pb_raw =
|
||||
$signed(product) >>> (2*DATA_WIDTH);
|
||||
assign pb_comb[gm] = pb_raw[PROD_WIDTH-1:0] + (pa_comb[gm][PROD_WIDTH-1] ? 1'b1 : 1'b0);
|
||||
assign product_comb[gm] = packed_a * w0[gm];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
@@ -150,6 +172,45 @@ module neural_processor_packed #(
|
||||
end else begin
|
||||
valid1 <= valid0;
|
||||
last1 <= last0;
|
||||
for (gi = 0; gi < P_IN; gi = gi + 1)
|
||||
product_reg[gi] <= product_comb[gi];
|
||||
end
|
||||
end
|
||||
|
||||
// ============================================================
|
||||
// STAGE 1b -- unpack the two packed INT8 products from the
|
||||
// ALREADY-REGISTERED product_reg (real, added pipeline stage --
|
||||
// the actual timing fix). pa_comb/pb_comb's own math is byte-for-
|
||||
// byte IDENTICAL to the original single-stage version, only the
|
||||
// source (product_reg, a real register) and the register that
|
||||
// captures the result (proda1/prodb1, now one real cycle later)
|
||||
// changed -- functional behavior is unchanged, only latency grows
|
||||
// by exactly one real clock cycle.
|
||||
// ============================================================
|
||||
reg valid1b, last1b;
|
||||
reg signed [ACC_WIDTH-1:0] proda1 [0:P_IN-1];
|
||||
reg signed [ACC_WIDTH-1:0] prodb1 [0:P_IN-1];
|
||||
|
||||
wire signed [PROD_WIDTH-1:0] pa_comb [0:P_IN-1];
|
||||
wire signed [PROD_WIDTH-1:0] pb_comb [0:P_IN-1];
|
||||
|
||||
genvar gp;
|
||||
generate
|
||||
for (gp = 0; gp < P_IN; gp = gp + 1) begin : GEN_UNPACK
|
||||
assign pa_comb[gp] = product_reg[gp][PROD_WIDTH-1:0];
|
||||
wire signed [A_WIDTH+DATA_WIDTH-2*DATA_WIDTH-1:0] pb_raw =
|
||||
$signed(product_reg[gp]) >>> (2*DATA_WIDTH);
|
||||
assign pb_comb[gp] = pb_raw[PROD_WIDTH-1:0] + (pa_comb[gp][PROD_WIDTH-1] ? 1'b1 : 1'b0);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
valid1b <= 1'b0;
|
||||
last1b <= 1'b0;
|
||||
end else begin
|
||||
valid1b <= valid1;
|
||||
last1b <= last1;
|
||||
for (gi = 0; gi < P_IN; gi = gi + 1) begin
|
||||
proda1[gi] <= {{(ACC_WIDTH-PROD_WIDTH){pa_comb[gi][PROD_WIDTH-1]}}, pa_comb[gi]};
|
||||
prodb1[gi] <= {{(ACC_WIDTH-PROD_WIDTH){pb_comb[gi][PROD_WIDTH-1]}}, pb_comb[gi]};
|
||||
@@ -183,8 +244,8 @@ module neural_processor_packed #(
|
||||
valid_tree[gl] <= 1'b0;
|
||||
last_tree[gl] <= 1'b0;
|
||||
end else begin
|
||||
valid_tree[gl] <= (gl == 0) ? valid1 : valid_tree[gl-1];
|
||||
last_tree[gl] <= (gl == 0) ? last1 : last_tree[gl-1];
|
||||
valid_tree[gl] <= (gl == 0) ? valid1b : valid_tree[gl-1];
|
||||
last_tree[gl] <= (gl == 0) ? last1b : last_tree[gl-1];
|
||||
end
|
||||
end
|
||||
for (gn = 0; gn < (P_IN >> (gl+1)); gn = gn + 1) begin : GEN_TREE_NODE
|
||||
@@ -203,8 +264,8 @@ module neural_processor_packed #(
|
||||
end
|
||||
endgenerate
|
||||
|
||||
wire valid_tree_out = (TREE_LEVELS == 0) ? valid1 : valid_tree[TREE_LEVELS-1];
|
||||
wire last_tree_out = (TREE_LEVELS == 0) ? last1 : last_tree[TREE_LEVELS-1];
|
||||
wire valid_tree_out = (TREE_LEVELS == 0) ? valid1b : valid_tree[TREE_LEVELS-1];
|
||||
wire last_tree_out = (TREE_LEVELS == 0) ? last1b : last_tree[TREE_LEVELS-1];
|
||||
wire signed [ACC_WIDTH-1:0] tile_sum_a = (TREE_LEVELS == 0) ? proda1[0] : treea[TREE_LEVELS][0];
|
||||
wire signed [ACC_WIDTH-1:0] tile_sum_b = (TREE_LEVELS == 0) ? prodb1[0] : treeb[TREE_LEVELS][0];
|
||||
|
||||
@@ -294,7 +355,7 @@ module neural_processor_packed #(
|
||||
end
|
||||
end
|
||||
|
||||
wire pipeline_busy = valid0 || valid1 || (|valid_tree) || valid5 || valid6 || valid7;
|
||||
wire pipeline_busy = valid0 || valid1 || valid1b || (|valid_tree) || valid5 || valid6 || valid7;
|
||||
assign job_ready = (np_state == NP_IDLE) && !pipeline_busy;
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -148,29 +148,57 @@ module tb;
|
||||
v2_tile_last = 0;
|
||||
tile_last = 0;
|
||||
|
||||
// real fix (n16-timing-closure branch): result_valid is a
|
||||
// real ONE-SHOT pulse in every one of these FSMs (`NP_
|
||||
// WRITE_RESULT: if (result_valid && result_ready)
|
||||
// result_valid<=0`, identical in neural_processor.v and
|
||||
// neural_processor_packed.v) -- with result_ready already
|
||||
// held high before this wait begins, each core's own
|
||||
// result_valid self-clears the very next cycle after it
|
||||
// first asserts, independent of whether the OTHER cores
|
||||
// have caught up yet. The original three-way simultaneous
|
||||
// AND assumed all three cores share the exact same real
|
||||
// pipeline depth -- true before this branch's own real
|
||||
// extra pipeline stage in neural_processor_packed.v (added
|
||||
// to fix EXP-0094's own real N=16 timing failure), no
|
||||
// longer true now that the DUT is deliberately one real
|
||||
// cycle deeper than the reference cores. Real fix: latch
|
||||
// each core's own result independently the cycle its own
|
||||
// result_valid pulses, then compare the three LATCHED
|
||||
// values once all three have arrived -- correct regardless
|
||||
// of real relative pipeline depth between DUT and
|
||||
// reference.
|
||||
v2_result_ready = 1;
|
||||
result_ready = 1;
|
||||
watchdog = 0;
|
||||
while (!(v2a_result_valid && v2b_result_valid && result_valid) && watchdog < 300) begin
|
||||
@(posedge clk);
|
||||
watchdog = watchdog + 1;
|
||||
end
|
||||
begin : capture
|
||||
reg v2a_got, v2b_got, dut_got;
|
||||
reg signed [DATA_WIDTH-1:0] v2a_val, v2b_val, dut_val_a, dut_val_b;
|
||||
v2a_got = 0; v2b_got = 0; dut_got = 0;
|
||||
watchdog = 0;
|
||||
while (!(v2a_got && v2b_got && dut_got) && watchdog < 300) begin
|
||||
@(posedge clk);
|
||||
if (!v2a_got && v2a_result_valid) begin v2a_got = 1; v2a_val = v2a_result_data; end
|
||||
if (!v2b_got && v2b_result_valid) begin v2b_got = 1; v2b_val = v2b_result_data; end
|
||||
if (!dut_got && result_valid) begin dut_got = 1; dut_val_a = result_data_a; dut_val_b = result_data_b; end
|
||||
watchdog = watchdog + 1;
|
||||
end
|
||||
|
||||
if (!v2a_result_valid || !v2b_result_valid || !result_valid) begin
|
||||
$display("FAIL n=%0d: watchdog timeout waiting for results (v2a=%b v2b=%b dut=%b)",
|
||||
n, v2a_result_valid, v2b_result_valid, result_valid);
|
||||
errors = errors + 1;
|
||||
end else begin
|
||||
if (result_data_a !== v2a_result_data || result_data_b !== v2b_result_data) begin
|
||||
$display("FAIL n=%0d bias=%0d act=%0d: v2a=%0d v2b=%0d dut_a=%0d dut_b=%0d MISMATCH",
|
||||
n, bias, activation, v2a_result_data, v2b_result_data, result_data_a, result_data_b);
|
||||
if (!v2a_got || !v2b_got || !dut_got) begin
|
||||
$display("FAIL n=%0d: watchdog timeout waiting for results (v2a_got=%b v2b_got=%b dut_got=%b)",
|
||||
n, v2a_got, v2b_got, dut_got);
|
||||
errors = errors + 1;
|
||||
end else begin
|
||||
$display("PASS n=%0d bias=%0d act=%0d: a=%0d b=%0d (bit-exact vs 2x real neural_processor.v)",
|
||||
n, bias, activation, result_data_a, result_data_b);
|
||||
if (dut_val_a !== v2a_val || dut_val_b !== v2b_val) begin
|
||||
$display("FAIL n=%0d bias=%0d act=%0d: v2a=%0d v2b=%0d dut_a=%0d dut_b=%0d MISMATCH",
|
||||
n, bias, activation, v2a_val, v2b_val, dut_val_a, dut_val_b);
|
||||
errors = errors + 1;
|
||||
end else begin
|
||||
$display("PASS n=%0d bias=%0d act=%0d: a=%0d b=%0d (bit-exact vs 2x real neural_processor.v)",
|
||||
n, bias, activation, dut_val_a, dut_val_b);
|
||||
end
|
||||
end
|
||||
@(posedge clk);
|
||||
end
|
||||
@(posedge clk);
|
||||
|
||||
while (!job_ready || np_state !== 4'd0 || !v2a_job_ready || !v2b_job_ready) @(posedge clk);
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user